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