2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
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.
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.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
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
30 * Kern E. Sibbald, August MMII
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);
42 static char *rec_state_bits_to_str(DEV_RECORD *rec);
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))
53 DEVICE *dev = dcr->dev;
56 /* We need an other volume */
57 volume_unused(dcr); /* mark volume unused */
61 * Create EOT Label so that Media record may
62 * be properly updated because this is the last
65 DEV_RECORD *trec = new_record();
66 trec->FileIndex = EOT_LABEL;
67 trec->Addr = dev->get_full_addr();
68 ok = record_cb(dcr, trec);
70 if (jcr->mount_next_volume) {
71 jcr->mount_next_volume = false;
76 jcr->mount_next_volume = false;
78 * The Device can change at the end of a tape, so refresh it
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.
87 dcr->read_block_from_device(NO_BLOCK_NUMBER_CHECK);
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);
94 position_to_first_file(jcr, dcr, bsr); /* We jump to the specified position */
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
103 bool read_records(DCR *dcr,
104 bool record_cb(DCR *dcr, DEV_RECORD *rec),
105 bool mount_cb(DCR *dcr))
108 DEVICE *dev = dcr->dev;
109 DEV_BLOCK *block = dcr->block;
110 DEV_RECORD *rec = NULL;
112 int32_t lastFileIndex;
116 SESSION_LABEL sessrec;
117 dlist *recs; /* linked list of rec packets open */
119 bool first_block = true;
121 recs = New(dlist(rec, &rec->link));
122 /* We go to the first_file unless we need to reposition during an
123 * interactive restore session (the reposition will be done with a different
124 * BSR in the for loop */
125 position_to_first_file(jcr, dcr, jcr->bsr);
126 jcr->mount_next_volume = false;
128 for ( ; ok && !done; ) {
129 if (job_canceled(jcr)) {
133 ASSERT2(!dcr->dev->adata, "Called with adata block. Wrong!");
136 if (! first_block || dev->dev_type != B_FIFO_DEV ) {
137 if (dev->at_eot() || !dcr->read_block_from_device(CHECK_BLOCK_NUMBERS)) {
140 _("End of Volume \"%s\" at addr=%s on device %s.\n"),
142 dev->print_addr(ed1, sizeof(ed1), dev->EndAddr),
144 ok = mount_next_vol(jcr, dcr, jcr->bsr, &sessrec, &should_stop,
145 record_cb, mount_cb);
146 /* Might have changed after the mount request */
154 } else if (dev->at_eof()) {
155 Dmsg3(200, "EOF at addr=%s on device %s, Volume \"%s\"\n",
156 dev->print_addr(ed1, sizeof(ed1), dev->EndAddr),
157 dev->print_name(), dcr->VolumeName);
159 } else if (dev->is_short_block()) {
160 Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
163 /* I/O error or strange end of tape */
164 display_tape_error_status(jcr, dev);
165 if (forge_on || jcr->ignore_label_errors) {
166 dev->fsr(1); /* try skipping bad record */
167 Pmsg0(000, _("Did fsr in attemp to skip bad record.\n"));
168 continue; /* try to continue */
170 ok = false; /* stop everything */
174 Dmsg1(dbglvl, "Read new block at pos=%s\n", dev->print_addr(ed1, sizeof(ed1)));
177 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
178 /* this does not stop when file/block are too big */
179 if (!match_bsr_block(jcr->bsr, block)) {
180 if (try_repositioning(jcr, rec, dcr)) {
181 break; /* get next volume */
183 continue; /* skip this record */
187 * Get a new record for each Job as defined by
188 * VolSessionId and VolSessionTime
191 foreach_dlist(rec, recs) {
192 if (rec->VolSessionId == block->VolSessionId &&
193 rec->VolSessionTime == block->VolSessionTime) {
194 /* When the previous Block of the current record is not correctly ordered,
195 * if we concat the previous record to the next one, the result is probably
196 * incorrect. At least the vacuum command should not use this kind of record
198 if (rec->remainder) {
199 if (rec->BlockNumber != (block->BlockNumber - 1)
201 rec->BlockNumber != block->BlockNumber)
203 Dmsg3(0, "invalid: rec=%ld block=%ld state=%s\n",
204 rec->BlockNumber, block->BlockNumber, rec_state_bits_to_str(rec));
206 /* We can discard the current data if needed. The code is very
207 * tricky in the read_records loop, so it's better to not
208 * introduce new subtle errors.
210 if (dcr->discard_invalid_records) {
222 Dmsg3(dbglvl, "New record for state=%s SI=%d ST=%d\n",
223 rec_state_bits_to_str(rec),
224 block->VolSessionId, block->VolSessionTime);
226 Dmsg4(dbglvl, "Before read rec loop. stat=%s blk=%d rem=%d invalid=%d\n",
227 rec_state_bits_to_str(rec), block->BlockNumber, rec->remainder, rec->invalid);
230 rec->BlockNumber = block->BlockNumber;
231 lastFileIndex = no_FileIndex;
232 Dmsg1(dbglvl, "Block %s empty\n", is_block_marked_empty(rec)?"is":"NOT");
233 for (rec->state_bits=0; ok && !is_block_marked_empty(rec); ) {
234 if (!read_record_from_block(dcr, rec)) {
235 Dmsg3(200, "!read-break. state_bits=%s blk=%d rem=%d\n", rec_state_bits_to_str(rec),
236 block->BlockNumber, rec->remainder);
239 Dmsg5(dbglvl, "read-OK. state_bits=%s blk=%d rem=%d volume:addr=%s:%llu\n",
240 rec_state_bits_to_str(rec), block->BlockNumber, rec->remainder,
241 NPRT(rec->VolumeName), rec->Addr);
243 * At this point, we have at least a record header.
244 * Now decide if we want this record or not, but remember
245 * before accessing the record, we may need to read again to
249 Dmsg6(dbglvl, "recno=%d state_bits=%s blk=%d SI=%d ST=%d FI=%d\n", record,
250 rec_state_bits_to_str(rec), block->BlockNumber,
251 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
253 if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
254 Dmsg0(40, "Get EOM LABEL\n");
255 break; /* yes, get out */
258 /* Some sort of label? */
259 if (rec->FileIndex < 0) {
260 handle_session_record(dev, rec, &sessrec);
262 /* We just check block FI and FT not FileIndex */
263 rec->match_stat = match_bsr_block(jcr->bsr, block);
268 Dmsg5(0, "The record %d in block %ld SI=%ld ST=%ld FI=%ld was marked as invalid\n",
269 rec->RecNum, rec->BlockNumber, rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
271 //ASSERTD(!rec->invalid || dcr->discard_invalid_records, "Got an invalid record");
273 * Note, we pass *all* labels to the callback routine. If
274 * he wants to know if they matched the bsr, then he must
275 * check the match_stat in the record */
276 ok = record_cb(dcr, rec);
277 rec->invalid = false; /* The record was maybe invalid, but the next one is probably good */
280 * If this is the end of the Session (EOS) for this record
281 * we can remove the record. Note, there is a separate
282 * record to read each session. If a new session is seen
283 * a new record will be created at approx line 157 above.
284 * However, it seg faults in the for line at lineno 196.
286 if (rec->FileIndex == EOS_LABEL) {
287 Dmsg2(dbglvl, "Remove EOS rec. SI=%d ST=%d\n", rec->VolSessionId,
288 rec->VolSessionTime);
294 } /* end if label record */
300 rec->match_stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec, jcr);
301 Dmsg2(dbglvl, "match_bsr=%d bsr->reposition=%d\n", rec->match_stat,
302 jcr->bsr->reposition);
303 if (rec->match_stat == -1) { /* no more possible matches */
304 done = true; /* all items found, stop */
305 Dmsg1(dbglvl, "All done Addr=%s\n", dev->print_addr(ed1, sizeof(ed1)));
307 } else if (rec->match_stat == 0) { /* no match */
308 Dmsg3(dbglvl, "BSR no match: clear rem=%d FI=%d before set_eof pos %s\n",
309 rec->remainder, rec->FileIndex, dev->print_addr(ed1, sizeof(ed1)));
311 rec->state_bits &= ~REC_PARTIAL_RECORD;
312 if (try_repositioning(jcr, rec, dcr)) {
313 break; /* We moved on the volume, read next block */
315 continue; /* we don't want record, read next one */
318 dcr->VolLastIndex = rec->FileIndex; /* let caller know where we are */
319 if (is_partial_record(rec)) {
320 Dmsg6(dbglvl, "Partial, break. recno=%d state_bits=%s blk=%d SI=%d ST=%d FI=%d\n", record,
321 rec_state_bits_to_str(rec), block->BlockNumber,
322 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
323 break; /* read second part of record */
326 Dmsg6(dbglvl, "OK callback. recno=%d state_bits=%s blk=%d SI=%d ST=%d FI=%d\n", record,
327 rec_state_bits_to_str(rec), block->BlockNumber,
328 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
329 if (lastFileIndex != no_FileIndex && lastFileIndex != rec->FileIndex) {
330 if (is_this_bsr_done(jcr, jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
331 Dmsg1(dbglvl, "This bsr done, break pos %s\n",
332 dev->print_addr(ed1, sizeof(ed1)));
335 Dmsg2(dbglvl, "==== inside LastIndex=%d FileIndex=%d\n", lastFileIndex, rec->FileIndex);
337 Dmsg2(dbglvl, "==== LastIndex=%d FileIndex=%d\n", lastFileIndex, rec->FileIndex);
338 lastFileIndex = rec->FileIndex;
340 Dmsg5(0, "The record %d in block %ld SI=%ld ST=%ld FI=%ld was marked as invalid\n",
341 rec->RecNum, rec->BlockNumber, rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
343 //ASSERTD(!rec->invalid || dcr->discard_invalid_records, "Got an invalid record");
344 ok = record_cb(dcr, rec);
345 rec->invalid = false; /* The record was maybe invalid, but the next one is probably good */
348 * If we have a digest stream, we check to see if we have
349 * finished the current bsr, and if so, repositioning will
352 if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
353 Dmsg3(dbglvl, "=== Have digest FI=%u before bsr check pos %s\n", rec->FileIndex,
354 dev->print_addr(ed1, sizeof(ed1));
355 if (is_this_bsr_done(jcr, jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
356 Dmsg1(dbglvl, "==== BSR done at FI=%d\n", rec->FileIndex);
357 Dmsg1(dbglvl, "This bsr done, break pos=%s\n",
358 dev->print_addr(ed1, sizeof(ed1)));
361 Dmsg2(900, "After is_bsr_done pos %s\n", dev->print_addr(ed1, sizeof(ed1));
364 } /* end for loop over records */
365 Dmsg1(dbglvl, "After end recs in block. pos=%s\n", dev->print_addr(ed1, sizeof(ed1)));
366 } /* end for loop over blocks */
368 /* Walk down list and free all remaining allocated recs */
369 while (!recs->empty()) {
370 rec = (DEV_RECORD *)recs->first();
375 print_block_read_errors(jcr, block);
380 * See if we can reposition.
381 * Returns: true if at end of volume
384 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr)
387 DEVICE *dev = dcr->dev;
390 bsr = find_next_bsr(jcr->bsr, dev);
391 Dmsg2(dbglvl, "nextbsr=%p mount_next_volume=%d\n", bsr, jcr->bsr->mount_next_volume);
392 if (bsr == NULL && jcr->bsr->mount_next_volume) {
393 Dmsg0(dbglvl, "Would mount next volume here\n");
394 Dmsg1(dbglvl, "Current position Addr=%s\n",
395 dev->print_addr(ed1, sizeof(ed1)));
396 jcr->bsr->mount_next_volume = false;
397 if (!dev->at_eot()) {
398 /* Set EOT flag to force mount of next Volume */
399 jcr->mount_next_volume = true;
407 * ***FIXME*** gross kludge to make disk seeking work. Remove
408 * when find_next_bsr() is fixed not to return a bsr already
411 uint64_t dev_addr = dev->get_full_addr();
412 uint64_t bsr_addr = get_bsr_start_addr(bsr);
414 /* Do not position backwards */
415 if (dev_addr > bsr_addr) {
418 Dmsg2(dbglvl, "Try_Reposition from addr=%llu to %llu\n",
420 dev->reposition(dcr, bsr_addr);
422 return true; /* We want the next block */
428 * Position to the first file on this volume
430 static BSR *position_to_first_file(JCR *jcr, DCR *dcr, BSR *bsr)
432 DEVICE *dev = dcr->dev;
434 char ed1[50], ed2[50];
438 * Now find and position to first file and block
442 bsr->reposition = true; /* force repositioning */
443 bsr = find_next_bsr(bsr, dev);
445 if ((bsr_addr=get_bsr_start_addr(bsr)) > 0) {
446 Jmsg(jcr, M_INFO, 0, _("Forward spacing Volume \"%s\" to addr=%s\n"),
447 dev->VolHdr.VolumeName, dev->print_addr(ed1, sizeof(ed1), bsr_addr));
448 dev->clear_eot(); /* TODO: See where to put this clear() exactly */
449 Dmsg2(dbglvl, "pos_to_first_file from addr=%s to %s\n",
450 dev->print_addr(ed1, sizeof(ed1)),
451 dev->print_addr(ed2, sizeof(ed2), bsr_addr));
452 dev->reposition(dcr, bsr_addr);
460 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
465 memset(sessrec, 0, sizeof(SESSION_LABEL));
466 switch (rec->FileIndex) {
468 rtype = _("Fresh Volume Label");
471 rtype = _("Volume Label");
472 unser_volume_label(dev, rec);
475 rtype = _("Begin Session");
476 unser_session_label(sessrec, rec);
479 rtype = _("End Session");
482 rtype = _("End of Media");
485 bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
489 Dmsg5(dbglvl, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
490 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
494 static char *rec_state_bits_to_str(DEV_RECORD *rec)
496 static char buf[200];
498 if (rec->state_bits & REC_NO_HEADER) {
499 bstrncat(buf, "Nohdr,", sizeof(buf));
501 if (is_partial_record(rec)) {
502 bstrncat(buf, "partial,", sizeof(buf));
504 if (rec->state_bits & REC_BLOCK_EMPTY) {
505 bstrncat(buf, "empty,", sizeof(buf));
507 if (rec->state_bits & REC_NO_MATCH) {
508 bstrncat(buf, "Nomatch,", sizeof(buf));
510 if (rec->state_bits & REC_CONTINUATION) {
511 bstrncat(buf, "cont,", sizeof(buf));
514 buf[strlen(buf)-1] = 0;