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