]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/read_records.c
Backport from BEE
[bacula/bacula] / bacula / src / stored / read_records.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2014 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from many
7    others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    Bacula® is a registered trademark of Kern Sibbald.
15 */
16 /*
17  *
18  *  This routine provides a routine that will handle all
19  *    the gory little details of reading a record from a Bacula
20  *    archive. It uses a callback to pass you each record in turn,
21  *    as well as a callback for mounting the next tape.  It takes
22  *    care of reading blocks, applying the bsr, ...
23  *    Note, this routine is really the heart of the restore routines,
24  *    and we are *really* bit pushing here so be careful about making
25  *    any modifications.
26  *
27  *    Kern E. Sibbald, August MMII
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, DCR *dcr);
37 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr);
38 #ifdef DEBUG
39 static char *rec_state_bits_to_str(DEV_RECORD *rec);
40 #endif
41
42 static const int dbglvl = 190;
43 static const int no_FileIndex = -999999;
44
45 /*
46  * This subroutine reads all the records and passes them back to your
47  *  callback routine (also mount routine at EOM).
48  * You must not change any values in the DEV_RECORD packet
49  */
50 bool read_records(DCR *dcr,
51        bool record_cb(DCR *dcr, DEV_RECORD *rec),
52        bool mount_cb(DCR *dcr))
53 {
54    JCR *jcr = dcr->jcr;
55    DEVICE *dev = dcr->dev;
56    DEV_BLOCK *block = dcr->block;
57    DEV_RECORD *rec = NULL;
58    uint32_t record;
59    int32_t lastFileIndex;
60    bool ok = true;
61    bool done = false;
62    SESSION_LABEL sessrec;
63    dlist *recs;                         /* linked list of rec packets open */
64
65    recs = New(dlist(rec, &rec->link));
66    position_to_first_file(jcr, dcr);
67    jcr->mount_next_volume = false;
68
69    for ( ; ok && !done; ) {
70       if (job_canceled(jcr)) {
71          ok = false;
72          break;
73       }
74       if (!dcr->read_block_from_device(CHECK_BLOCK_NUMBERS)) {
75          if (dev->at_eot()) {
76             DEV_RECORD *trec = new_record();
77             Jmsg(jcr, M_INFO, 0, _("End of Volume at file %u on device %s, Volume \"%s\"\n"),
78                  dev->file, dev->print_name(), dcr->VolumeName);
79             volume_unused(dcr);       /* mark volume unused */
80             if (!mount_cb(dcr)) {
81                Jmsg(jcr, M_INFO, 0, _("End of all volumes.\n"));
82                ok = false;            /* Stop everything */
83                /*
84                 * Create EOT Label so that Media record may
85                 *  be properly updated because this is the last
86                 *  tape.
87                 */
88                trec->FileIndex = EOT_LABEL;
89                trec->File = dev->file;
90                ok = record_cb(dcr, trec);
91                free_record(trec);
92                if (jcr->mount_next_volume) {
93                   jcr->mount_next_volume = false;
94                   dev->clear_eot();
95                }
96                break;
97             }
98             jcr->mount_next_volume = false;
99             /*
100              * The Device can change at the end of a tape, so refresh it
101              *   and the block from the dcr.
102              */
103             dev = dcr->dev;
104             block = dcr->block;
105             /*
106              * We just have a new tape up, now read the label (first record)
107              *  and pass it off to the callback routine, then continue
108              *  most likely reading the previous record.
109              */
110             dcr->read_block_from_device(NO_BLOCK_NUMBER_CHECK);
111             read_record_from_block(dcr, trec);
112             handle_session_record(dev, trec, &sessrec);
113             ok = record_cb(dcr, trec);
114             free_record(trec);
115             position_to_first_file(jcr, dcr);
116             /* After reading label, we must read first data block */
117             continue;
118
119          } else if (dev->at_eof()) {
120             Dmsg3(200, "End of file %u  on device %s, Volume \"%s\"\n",
121                   dev->file, dev->print_name(), dcr->VolumeName);
122             continue;
123          } else if (dev->is_short_block()) {
124             Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
125             continue;
126          } else {
127             /* I/O error or strange end of tape */
128             display_tape_error_status(jcr, dev);
129             if (forge_on || jcr->ignore_label_errors) {
130                dev->fsr(1);       /* try skipping bad record */
131                Pmsg0(000, _("Did fsr in attemp to skip bad record.\n"));
132                continue;              /* try to continue */
133             }
134             ok = false;               /* stop everything */
135             break;
136          }
137       }
138       Dmsg2(dbglvl, "Read new block at pos=%u:%u\n", dev->file, dev->block_num);
139 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
140       /* this does not stop when file/block are too big */
141       if (!match_bsr_block(jcr->bsr, block)) {
142          if (try_repositioning(jcr, rec, dcr)) {
143             break;                    /* get next volume */
144          }
145          continue;                    /* skip this record */
146       }
147 #endif
148
149       /*
150        * Get a new record for each Job as defined by
151        *   VolSessionId and VolSessionTime
152        */
153       bool found = false;
154       foreach_dlist(rec, recs) {
155          if (rec->VolSessionId == block->VolSessionId &&
156              rec->VolSessionTime == block->VolSessionTime) {
157             found = true;
158             break;
159           }
160       }
161       if (!found) {
162          rec = new_record();
163          recs->prepend(rec);
164          Dmsg3(dbglvl, "New record for state=%s SI=%d ST=%d\n",
165              rec_state_bits_to_str(rec),
166              block->VolSessionId, block->VolSessionTime);
167       }
168       Dmsg3(dbglvl, "Before read rec loop. stat=%s blk=%d rem=%d\n", rec_state_bits_to_str(rec),
169             block->BlockNumber, rec->remainder);
170       record = 0;
171       rec->state_bits = 0;
172       lastFileIndex = no_FileIndex;
173       Dmsg1(dbglvl, "Block %s empty\n", is_block_marked_empty(rec)?"is":"NOT");
174       for (rec->state_bits=0; ok && !is_block_marked_empty(rec); ) {
175          if (!read_record_from_block(dcr, rec)) {
176             Dmsg3(200, "!read-break. state_bits=%s blk=%d rem=%d\n", rec_state_bits_to_str(rec),
177                   block->BlockNumber, rec->remainder);
178             break;
179          }
180          Dmsg5(dbglvl, "read-OK. state_bits=%s blk=%d rem=%d file:block=%u:%u\n",
181                  rec_state_bits_to_str(rec), block->BlockNumber, rec->remainder,
182                  dev->file, dev->block_num);
183          /*
184           * At this point, we have at least a record header.
185           *  Now decide if we want this record or not, but remember
186           *  before accessing the record, we may need to read again to
187           *  get all the data.
188           */
189          record++;
190          Dmsg6(dbglvl, "recno=%d state_bits=%s blk=%d SI=%d ST=%d FI=%d\n", record,
191             rec_state_bits_to_str(rec), block->BlockNumber,
192             rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
193
194          if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
195             Dmsg0(40, "Get EOM LABEL\n");
196             break;                         /* yes, get out */
197          }
198
199          /* Some sort of label? */
200          if (rec->FileIndex < 0) {
201             handle_session_record(dev, rec, &sessrec);
202             if (jcr->bsr) {
203                /* We just check block FI and FT not FileIndex */
204                rec->match_stat = match_bsr_block(jcr->bsr, block);
205             } else {
206                rec->match_stat = 0;
207             }
208             /*
209              * Note, we pass *all* labels to the callback routine. If
210              *  he wants to know if they matched the bsr, then he must
211              *  check the match_stat in the record */
212             ok = record_cb(dcr, rec);
213 #ifdef xxx
214             /*
215              * If this is the end of the Session (EOS) for this record
216              *  we can remove the record.  Note, there is a separate
217              *  record to read each session. If a new session is seen
218              *  a new record will be created at approx line 157 above.
219              * However, it seg faults in the for line at lineno 196.
220              */
221             if (rec->FileIndex == EOS_LABEL) {
222                Dmsg2(dbglvl, "Remove EOS rec. SI=%d ST=%d\n", rec->VolSessionId,
223                   rec->VolSessionTime);
224                recs->remove(rec);
225                free_record(rec);
226             }
227 #endif
228             continue;
229          } /* end if label record */
230
231          /*
232           * Apply BSR filter
233           */
234          if (jcr->bsr) {
235             rec->match_stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec, jcr);
236             if (rec->match_stat == -1) { /* no more possible matches */
237                done = true;   /* all items found, stop */
238                Dmsg2(dbglvl, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
239                break;
240             } else if (rec->match_stat == 0) {  /* no match */
241                Dmsg4(dbglvl, "BSR no match: clear rem=%d FI=%d before set_eof pos %u:%u\n",
242                   rec->remainder, rec->FileIndex, dev->file, dev->block_num);
243                rec->remainder = 0;
244                rec->state_bits &= ~REC_PARTIAL_RECORD;
245                if (try_repositioning(jcr, rec, dcr)) {
246                   break;
247                }
248                continue;              /* we don't want record, read next one */
249             }
250          }
251          dcr->VolLastIndex = rec->FileIndex;  /* let caller know where we are */
252          if (is_partial_record(rec)) {
253             Dmsg6(dbglvl, "Partial, break. recno=%d state_bits=%s blk=%d SI=%d ST=%d FI=%d\n", record,
254                rec_state_bits_to_str(rec), block->BlockNumber,
255                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
256             break;                    /* read second part of record */
257          }
258
259          Dmsg6(dbglvl, "OK callback. recno=%d state_bits=%s blk=%d SI=%d ST=%d FI=%d\n", record,
260                rec_state_bits_to_str(rec), block->BlockNumber,
261                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
262          if (lastFileIndex != no_FileIndex && lastFileIndex != rec->FileIndex) {
263             if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
264                Dmsg2(dbglvl, "This bsr done, break pos %u:%u\n",
265                      dev->file, dev->block_num);
266                break;
267             }
268             Dmsg2(dbglvl, "==== inside LastIndex=%d FileIndex=%d\n", lastFileIndex, rec->FileIndex);
269          }
270          Dmsg2(dbglvl, "==== LastIndex=%d FileIndex=%d\n", lastFileIndex, rec->FileIndex);
271          lastFileIndex = rec->FileIndex;
272          ok = record_cb(dcr, rec);
273 #if 0
274          /*
275           * If we have a digest stream, we check to see if we have
276           *  finished the current bsr, and if so, repositioning will
277           *  be turned on.
278           */
279          if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
280             Dmsg3(dbglvl, "=== Have digest FI=%u before bsr check pos %u:%u\n", rec->FileIndex,
281                   dev->file, dev->block_num);
282             if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
283                Dmsg1(dbglvl, "==== BSR done at FI=%d\n", rec->FileIndex);
284                Dmsg2(dbglvl, "This bsr done, break pos %u:%u\n",
285                      dev->file, dev->block_num);
286                break;
287             }
288             Dmsg2(900, "After is_bsr_done pos %u:%u\n", dev->file, dev->block_num);
289          }
290 #endif
291       } /* end for loop over records */
292       Dmsg2(dbglvl, "After end recs in block. pos=%u:%u\n", dev->file, dev->block_num);
293    } /* end for loop over blocks */
294 // Dmsg2(dbglvl, "Position=(file:block) %u:%u\n", dev->file, dev->block_num);
295
296    /* Walk down list and free all remaining allocated recs */
297    while (!recs->empty()) {
298       rec = (DEV_RECORD *)recs->first();
299       recs->remove(rec);
300       free_record(rec);
301    }
302    delete recs;
303    print_block_read_errors(jcr, block);
304    return ok;
305 }
306
307 /*
308  * See if we can reposition.
309  *   Returns:  true  if at end of volume
310  *             false otherwise
311  */
312 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr)
313 {
314    BSR *bsr;
315    DEVICE *dev = dcr->dev;
316
317    bsr = find_next_bsr(jcr->bsr, dev);
318    if (bsr == NULL && jcr->bsr->mount_next_volume) {
319       Dmsg0(dbglvl, "Would mount next volume here\n");
320       Dmsg2(dbglvl, "Current postion (file:block) %u:%u\n",
321          dev->file, dev->block_num);
322       jcr->bsr->mount_next_volume = false;
323       if (!dev->at_eot()) {
324          /* Set EOT flag to force mount of next Volume */
325          jcr->mount_next_volume = true;
326          dev->set_eot();
327       }
328       rec->Block = 0;
329       return true;
330    }
331    if (bsr) {
332       /*
333        * ***FIXME*** gross kludge to make disk seeking work.  Remove
334        *   when find_next_bsr() is fixed not to return a bsr already
335        *   completed.
336        */
337       uint32_t block, file;
338       /* TODO: use dev->file_addr ? */
339       uint64_t dev_addr = (((uint64_t) dev->file)<<32) | dev->block_num;
340       uint64_t bsr_addr = get_bsr_start_addr(bsr, &file, &block);
341
342       if (dev_addr > bsr_addr) {
343          return false;
344       }
345       Dmsg4(dbglvl, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
346             dev->file, dev->block_num, file, block);
347       dev->reposition(dcr, file, block);
348       rec->Block = 0;
349    }
350    return false;
351 }
352
353 /*
354  * Position to the first file on this volume
355  */
356 static BSR *position_to_first_file(JCR *jcr, DCR *dcr)
357 {
358    BSR *bsr = NULL;
359    DEVICE *dev = dcr->dev;
360    uint32_t file, block;
361    /*
362     * Now find and position to first file and block
363     *   on this tape.
364     */
365    if (jcr->bsr) {
366       jcr->bsr->reposition = true;    /* force repositioning */
367       bsr = find_next_bsr(jcr->bsr, dev);
368
369       if (get_bsr_start_addr(bsr, &file, &block) > 0) {
370          Jmsg(jcr, M_INFO, 0, _("Forward spacing Volume \"%s\" to file:block %u:%u.\n"),
371               dev->VolHdr.VolumeName, file, block);
372          dev->reposition(dcr, file, block);
373       }
374    }
375    return bsr;
376 }
377
378
379 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
380 {
381    const char *rtype;
382    char buf[100];
383
384    memset(sessrec, 0, sizeof(SESSION_LABEL));
385    switch (rec->FileIndex) {
386    case PRE_LABEL:
387       rtype = _("Fresh Volume Label");
388       break;
389    case VOL_LABEL:
390       rtype = _("Volume Label");
391       unser_volume_label(dev, rec);
392       break;
393    case SOS_LABEL:
394       rtype = _("Begin Session");
395       unser_session_label(sessrec, rec);
396       break;
397    case EOS_LABEL:
398       rtype = _("End Session");
399       break;
400    case EOM_LABEL:
401       rtype = _("End of Media");
402       break;
403    default:
404       bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
405       rtype = buf;
406       break;
407    }
408    Dmsg5(dbglvl, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
409          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
410 }
411
412 #ifdef DEBUG
413 static char *rec_state_bits_to_str(DEV_RECORD *rec)
414 {
415    static char buf[200];
416    buf[0] = 0;
417    if (rec->state_bits & REC_NO_HEADER) {
418       bstrncat(buf, "Nohdr,", sizeof(buf));
419    }
420    if (is_partial_record(rec)) {
421       bstrncat(buf, "partial,", sizeof(buf));
422    }
423    if (rec->state_bits & REC_BLOCK_EMPTY) {
424       bstrncat(buf, "empty,", sizeof(buf));
425    }
426    if (rec->state_bits & REC_NO_MATCH) {
427       bstrncat(buf, "Nomatch,", sizeof(buf));
428    }
429    if (rec->state_bits & REC_CONTINUATION) {
430       bstrncat(buf, "cont,", sizeof(buf));
431    }
432    if (buf[0]) {
433       buf[strlen(buf)-1] = 0;
434    }
435    return buf;
436 }
437 #endif