]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/read_record.c
Merge the Bacula Encryption branch to HEAD.
[bacula/bacula] / bacula / src / stored / read_record.c
1 /*
2  *
3  *  This routine provides a routine that will handle all
4  *    the gory little details of reading a record from a Bacula
5  *    archive. It uses a callback to pass you each record in turn,
6  *    as well as a callback for mounting the next tape.  It takes
7  *    care of reading blocks, applying the bsr, ...
8  *    Note, this routine is really the heart of the restore routines,
9  *    and we are *really* bit pushing here so be careful about making
10  *    any modifications.
11  *
12  *    Kern E. Sibbald, August MMII
13  *
14  *   Version $Id$
15  */
16 /*
17    Copyright (C) 2000-2005 Kern Sibbald
18
19    This program is free software; you can redistribute it and/or
20    modify it under the terms of the GNU General Public License
21    version 2 as amended with additional clauses defined in the
22    file LICENSE in the main source directory.
23
24    This program is distributed in the hope that it will be useful,
25    but WITHOUT ANY WARRANTY; without even the implied warranty of
26    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
27    the file LICENSE for additional details.
28
29  */
30
31 #include "bacula.h"
32 #include "stored.h"
33
34 /* Forward referenced functions */
35 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
36 static BSR *position_to_first_file(JCR *jcr, DEVICE *dev);
37 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DEVICE *dev);
38 #ifdef DEBUG
39 static char *rec_state_to_str(DEV_RECORD *rec);
40 #endif
41
42 bool read_records(DCR *dcr,
43        bool record_cb(DCR *dcr, DEV_RECORD *rec),
44        bool mount_cb(DCR *dcr))
45 {
46    JCR *jcr = dcr->jcr;
47    DEVICE *dev = dcr->dev;
48    DEV_BLOCK *block = dcr->block;
49    DEV_RECORD *rec = NULL;
50    uint32_t record;
51    bool ok = true;
52    bool done = false;
53    SESSION_LABEL sessrec;
54    dlist *recs;                         /* linked list of rec packets open */
55
56    recs = New(dlist(rec, &rec->link));
57    position_to_first_file(jcr, dev);
58    jcr->mount_next_volume = false;
59
60    for ( ; ok && !done; ) {
61       if (job_canceled(jcr)) {
62          ok = false;
63          break;
64       }
65       if (!read_block_from_device(dcr, CHECK_BLOCK_NUMBERS)) {
66          if (dev->at_eot()) {
67             DEV_RECORD *trec = new_record();
68             Jmsg(jcr, M_INFO, 0, _("End of Volume at file %u on device %s, Volume \"%s\"\n"),
69                  dev->file, dev->print_name(), dcr->VolumeName);
70             if (!mount_cb(dcr)) {
71                Jmsg(jcr, M_INFO, 0, _("End of all volumes.\n"));
72                ok = false;            /* Stop everything */
73                /*
74                 * Create EOT Label so that Media record may
75                 *  be properly updated because this is the last
76                 *  tape.
77                 */
78                trec->FileIndex = EOT_LABEL;
79                trec->File = dev->file;
80                ok = record_cb(dcr, trec);
81                free_record(trec);
82                if (jcr->mount_next_volume) {
83                   jcr->mount_next_volume = false;
84                   dev->clear_eot();
85                }
86                break;
87             }
88             jcr->mount_next_volume = false;
89             /*
90              * We just have a new tape up, now read the label (first record)
91              *  and pass it off to the callback routine, then continue
92              *  most likely reading the previous record.
93              */
94             read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
95             read_record_from_block(block, trec);
96             handle_session_record(dev, trec, &sessrec);
97             ok = record_cb(dcr, trec);
98             free_record(trec);
99             position_to_first_file(jcr, dev);
100             /* After reading label, we must read first data block */
101             continue;
102
103          } else if (dev->at_eof()) {
104             if (verbose) {
105                Jmsg(jcr, M_INFO, 0, _("Got EOF at file %u  on device %s, Volume \"%s\"\n"),
106                   dev->file, dev->print_name(), dcr->VolumeName);
107             }
108             Dmsg3(200, "Got EOF at file %u  on device %s, Volume \"%s\"\n",
109                   dev->file, dev->print_name(), dcr->VolumeName);
110             continue;
111          } else if (dev->is_short_block()) {
112             Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
113             continue;
114          } else {
115             /* I/O error or strange end of tape */
116             display_tape_error_status(jcr, dev);
117             if (forge_on || jcr->ignore_label_errors) {
118                dev->fsr(1);       /* try skipping bad record */
119                Pmsg0(000, _("Did fsr\n"));
120                continue;              /* try to continue */
121             }
122             ok = false;               /* stop everything */
123             break;
124          }
125       }
126       Dmsg2(300, "New block at position=(file:block) %u:%u\n", dev->file, dev->block_num);
127 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
128       /* this does not stop when file/block are too big */
129       if (!match_bsr_block(jcr->bsr, block)) {
130          if (try_repositioning(jcr, rec, dev)) {
131             break;                    /* get next volume */
132          }
133          continue;                    /* skip this record */
134       }
135 #endif
136
137       /*
138        * Get a new record for each Job as defined by
139        *   VolSessionId and VolSessionTime
140        */
141       bool found = false;
142       foreach_dlist(rec, recs) {
143          if (rec->VolSessionId == block->VolSessionId &&
144              rec->VolSessionTime == block->VolSessionTime) {
145             found = true;
146             break;
147           }
148       }
149       if (!found) {
150          rec = new_record();
151          recs->prepend(rec);
152          Dmsg2(300, "New record for SI=%d ST=%d\n",
153              block->VolSessionId, block->VolSessionTime);
154       }
155       Dmsg3(300, "After mount next vol. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
156             block->BlockNumber, rec->remainder);
157       record = 0;
158       rec->state = 0;
159       Dmsg1(300, "Block empty %d\n", is_block_empty(rec));
160       for (rec->state=0; !is_block_empty(rec); ) {
161          if (!read_record_from_block(block, rec)) {
162             Dmsg3(400, "!read-break. state=%s blk=%d rem=%d\n", rec_state_to_str(rec),
163                   block->BlockNumber, rec->remainder);
164             break;
165          }
166          Dmsg5(300, "read-OK. state=%s blk=%d rem=%d file:block=%u:%u\n",
167                  rec_state_to_str(rec), block->BlockNumber, rec->remainder,
168                  dev->file, dev->block_num);
169          /*
170           * At this point, we have at least a record header.
171           *  Now decide if we want this record or not, but remember
172           *  before accessing the record, we may need to read again to
173           *  get all the data.
174           */
175          record++;
176          Dmsg6(300, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
177             rec_state_to_str(rec), block->BlockNumber,
178             rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
179
180          if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
181             Dmsg0(40, "Get EOM LABEL\n");
182             break;                         /* yes, get out */
183          }
184
185          /* Some sort of label? */
186          if (rec->FileIndex < 0) {
187             handle_session_record(dev, rec, &sessrec);
188             ok = record_cb(dcr, rec);
189             if (rec->FileIndex == EOS_LABEL) {
190                Dmsg2(300, "Remove rec. SI=%d ST=%d\n", rec->VolSessionId,
191                   rec->VolSessionTime);
192                recs->remove(rec);
193                free_record(rec);
194             }
195             continue;
196          } /* end if label record */
197
198          /*
199           * Apply BSR filter
200           */
201          if (jcr->bsr) {
202             int stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec);
203             if (stat == -1) { /* no more possible matches */
204                done = true;   /* all items found, stop */
205                Dmsg2(300, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
206                break;
207             } else if (stat == 0) {  /* no match */
208                Dmsg4(300, "Clear rem=%d FI=%d before set_eof pos %u:%u\n",
209                   rec->remainder, rec->FileIndex, dev->file, dev->block_num);
210                rec->remainder = 0;
211                rec->state &= ~REC_PARTIAL_RECORD;
212                if (try_repositioning(jcr, rec, dev)) {
213                   break;
214                }
215                continue;              /* we don't want record, read next one */
216             }
217          }
218          if (is_partial_record(rec)) {
219             Dmsg6(300, "Partial, break. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
220                rec_state_to_str(rec), block->BlockNumber,
221                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
222             break;                    /* read second part of record */
223          }
224          ok = record_cb(dcr, rec);
225          if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
226             Dmsg3(300, "Done FI=%u before set_eof pos %u:%u\n", rec->FileIndex,
227                   dev->file, dev->block_num);
228             if (match_set_eof(jcr->bsr, rec) && try_repositioning(jcr, rec, dev)) {
229                Dmsg2(300, "Break after match_set_eof pos %u:%u\n",
230                      dev->file, dev->block_num);
231                break;
232             }
233             Dmsg2(300, "After set_eof pos %u:%u\n", dev->file, dev->block_num);
234          }
235       } /* end for loop over records */
236       Dmsg2(300, "After end records position=(file:block) %u:%u\n", dev->file, dev->block_num);
237    } /* end for loop over blocks */
238 // Dmsg2(300, "Position=(file:block) %u:%u\n", dev->file, dev->block_num);
239
240    /* Walk down list and free all remaining allocated recs */
241    while (!recs->empty()) {
242       rec = (DEV_RECORD *)recs->first();
243       recs->remove(rec);
244       free_record(rec);
245    }
246    delete recs;
247    print_block_read_errors(jcr, block);
248    return ok;
249 }
250
251 /*
252  * See if we can reposition.
253  *   Returns:  true  if at end of volume
254  *             false otherwise
255  */
256 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DEVICE *dev)
257 {
258    BSR *bsr;
259    bsr = find_next_bsr(jcr->bsr, dev);
260    if (bsr == NULL && jcr->bsr->mount_next_volume) {
261       Dmsg0(300, "Would mount next volume here\n");
262       Dmsg2(300, "Current postion (file:block) %u:%u\n",
263          dev->file, dev->block_num);
264       jcr->bsr->mount_next_volume = false;
265       if (!dev->at_eot()) {
266          /* Set EOT flag to force mount of next Volume */
267          jcr->mount_next_volume = true;
268          dev->set_eot();
269       }
270       rec->Block = 0;
271       return true;
272    }
273    if (bsr) {
274       if (verbose) {
275          Jmsg(jcr, M_INFO, 0, _("Reposition from (file:block) %u:%u to %u:%u\n"),
276             dev->file, dev->block_num, bsr->volfile->sfile,
277             bsr->volblock->sblock);
278       }
279       Dmsg4(300, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
280             dev->file, dev->block_num, bsr->volfile->sfile,
281             bsr->volblock->sblock);
282       reposition_dev(dev, bsr->volfile->sfile, bsr->volblock->sblock);
283       rec->Block = 0;
284    }
285    return false;
286 }
287
288 /*
289  * Position to the first file on this volume
290  */
291 static BSR *position_to_first_file(JCR *jcr, DEVICE *dev)
292 {
293    BSR *bsr = NULL;
294    /*
295     * Now find and position to first file and block
296     *   on this tape.
297     */
298    if (jcr->bsr) {
299       jcr->bsr->reposition = true;    /* force repositioning */
300       bsr = find_next_bsr(jcr->bsr, dev);
301       if (bsr && (bsr->volfile->sfile != 0 || bsr->volblock->sblock != 0)) {
302          Jmsg(jcr, M_INFO, 0, _("Forward spacing to file:block %u:%u.\n"),
303             bsr->volfile->sfile, bsr->volblock->sblock);
304          Dmsg2(300, "Forward spacing to file:block %u:%u.\n",
305             bsr->volfile->sfile, bsr->volblock->sblock);
306          reposition_dev(dev, bsr->volfile->sfile, bsr->volblock->sblock);
307       }
308    }
309    return bsr;
310 }
311
312
313 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
314 {
315    const char *rtype;
316    char buf[100];
317
318    memset(sessrec, 0, sizeof(sessrec));
319    switch (rec->FileIndex) {
320    case PRE_LABEL:
321       rtype = _("Fresh Volume Label");
322       break;
323    case VOL_LABEL:
324       rtype = _("Volume Label");
325       unser_volume_label(dev, rec);
326       break;
327    case SOS_LABEL:
328       rtype = _("Begin Session");
329       unser_session_label(sessrec, rec);
330       break;
331    case EOS_LABEL:
332       rtype = _("End Session");
333       break;
334    case EOM_LABEL:
335       rtype = _("End of Media");
336       break;
337    default:
338       bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
339       rtype = buf;
340       break;
341    }
342    Dmsg5(300, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
343          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
344 }
345
346 #ifdef DEBUG
347 static char *rec_state_to_str(DEV_RECORD *rec)
348 {
349    static char buf[200];
350    buf[0] = 0;
351    if (rec->state & REC_NO_HEADER) {
352       bstrncat(buf, "Nohdr,", sizeof(buf));
353    }
354    if (is_partial_record(rec)) {
355       bstrncat(buf, "partial,", sizeof(buf));
356    }
357    if (rec->state & REC_BLOCK_EMPTY) {
358       bstrncat(buf, "empty,", sizeof(buf));
359    }
360    if (rec->state & REC_NO_MATCH) {
361       bstrncat(buf, "Nomatch,", sizeof(buf));
362    }
363    if (rec->state & REC_CONTINUATION) {
364       bstrncat(buf, "cont,", sizeof(buf));
365    }
366    if (buf[0]) {
367       buf[strlen(buf)-1] = 0;
368    }
369    return buf;
370 }
371 #endif