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 */
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;
127 for ( ; ok && !done; ) {
128 if (job_canceled(jcr)) {
132 ASSERT2(!dcr->dev->adata, "Called with adata block. Wrong!");
134 if (dev->at_eot() || !dcr->read_block_from_device(CHECK_BLOCK_NUMBERS)) {
137 _("End of Volume \"%s\" at addr=%s on device %s.\n"),
139 dev->print_addr(ed1, sizeof(ed1), dev->EndAddr),
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 */
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);
156 } else if (dev->is_short_block()) {
157 Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
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 */
167 ok = false; /* stop everything */
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 */
178 continue; /* skip this record */
182 * Get a new record for each Job as defined by
183 * VolSessionId and VolSessionTime
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
193 if (rec->remainder) {
194 if (rec->BlockNumber != (block->BlockNumber - 1)
196 rec->BlockNumber != block->BlockNumber)
198 Dmsg3(0, "invalid: rec=%ld block=%ld state=%s\n",
199 rec->BlockNumber, block->BlockNumber, rec_state_bits_to_str(rec));
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.
205 if (dcr->discard_invalid_records) {
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);
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);
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);
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);
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
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);
248 if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
249 Dmsg0(40, "Get EOM LABEL\n");
250 break; /* yes, get out */
253 /* Some sort of label? */
254 if (rec->FileIndex < 0) {
255 handle_session_record(dev, rec, &sessrec);
257 /* We just check block FI and FT not FileIndex */
258 rec->match_stat = match_bsr_block(jcr->bsr, block);
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);
266 //ASSERTD(!rec->invalid || dcr->discard_invalid_records, "Got an invalid record");
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 */
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.
281 if (rec->FileIndex == EOS_LABEL) {
282 Dmsg2(dbglvl, "Remove EOS rec. SI=%d ST=%d\n", rec->VolSessionId,
283 rec->VolSessionTime);
289 } /* end if label record */
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)));
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)));
306 rec->state_bits &= ~REC_PARTIAL_RECORD;
307 if (try_repositioning(jcr, rec, dcr)) {
308 break; /* We moved on the volume, read next block */
310 continue; /* we don't want record, read next one */
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 */
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)));
330 Dmsg2(dbglvl, "==== inside LastIndex=%d FileIndex=%d\n", lastFileIndex, rec->FileIndex);
332 Dmsg2(dbglvl, "==== LastIndex=%d FileIndex=%d\n", lastFileIndex, rec->FileIndex);
333 lastFileIndex = rec->FileIndex;
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);
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 */
343 * If we have a digest stream, we check to see if we have
344 * finished the current bsr, and if so, repositioning will
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)));
356 Dmsg2(900, "After is_bsr_done pos %s\n", dev->print_addr(ed1, sizeof(ed1));
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 */
363 /* Walk down list and free all remaining allocated recs */
364 while (!recs->empty()) {
365 rec = (DEV_RECORD *)recs->first();
370 print_block_read_errors(jcr, block);
375 * See if we can reposition.
376 * Returns: true if at end of volume
379 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr)
382 DEVICE *dev = dcr->dev;
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;
402 * ***FIXME*** gross kludge to make disk seeking work. Remove
403 * when find_next_bsr() is fixed not to return a bsr already
406 uint64_t dev_addr = dev->get_full_addr();
407 uint64_t bsr_addr = get_bsr_start_addr(bsr);
409 /* Do not position backwards */
410 if (dev_addr > bsr_addr) {
413 Dmsg2(dbglvl, "Try_Reposition from addr=%llu to %llu\n",
415 dev->reposition(dcr, bsr_addr);
417 return true; /* We want the next block */
423 * Position to the first file on this volume
425 static BSR *position_to_first_file(JCR *jcr, DCR *dcr, BSR *bsr)
427 DEVICE *dev = dcr->dev;
429 char ed1[50], ed2[50];
433 * Now find and position to first file and block
437 bsr->reposition = true; /* force repositioning */
438 bsr = find_next_bsr(bsr, dev);
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);
455 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
460 memset(sessrec, 0, sizeof(SESSION_LABEL));
461 switch (rec->FileIndex) {
463 rtype = _("Fresh Volume Label");
466 rtype = _("Volume Label");
467 unser_volume_label(dev, rec);
470 rtype = _("Begin Session");
471 unser_session_label(sessrec, rec);
474 rtype = _("End Session");
477 rtype = _("End of Media");
480 bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
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);
489 static char *rec_state_bits_to_str(DEV_RECORD *rec)
491 static char buf[200];
493 if (rec->state_bits & REC_NO_HEADER) {
494 bstrncat(buf, "Nohdr,", sizeof(buf));
496 if (is_partial_record(rec)) {
497 bstrncat(buf, "partial,", sizeof(buf));
499 if (rec->state_bits & REC_BLOCK_EMPTY) {
500 bstrncat(buf, "empty,", sizeof(buf));
502 if (rec->state_bits & REC_NO_MATCH) {
503 bstrncat(buf, "Nomatch,", sizeof(buf));
505 if (rec->state_bits & REC_CONTINUATION) {
506 bstrncat(buf, "cont,", sizeof(buf));
509 buf[strlen(buf)-1] = 0;