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