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 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;
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))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 */
read_records(DCR * dcr,bool record_cb (DCR * dcr,DEV_RECORD * rec),bool mount_cb (DCR * dcr))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 bool first_block = true;
120
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;
127
128 for ( ; ok && !done; ) {
129 if (job_canceled(jcr)) {
130 ok = false;
131 break;
132 }
133 ASSERT2(!dcr->dev->adata, "Called with adata block. Wrong!");
134
135
136 if (! first_block || dev->dev_type != B_FIFO_DEV ) {
137 if (dev->at_eot() || !dcr->read_block_from_device(CHECK_BLOCK_NUMBERS)) {
138 if (dev->at_eot()) {
139 Jmsg(jcr, M_INFO, 0,
140 _("End of Volume \"%s\" at addr=%s on device %s.\n"),
141 dcr->VolumeName,
142 dev->print_addr(ed1, sizeof(ed1), dev->EndAddr),
143 dev->print_name());
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 */
147 dev = dcr->dev;
148 block = dcr->block;
149 if (should_stop) {
150 break;
151 }
152 continue;
153
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);
158 continue;
159 } else if (dev->is_short_block()) {
160 Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
161 continue;
162 } else {
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 attempt to skip bad record.\n"));
168 continue; /* try to continue */
169 }
170 ok = false; /* stop everything */
171 break;
172 }
173 }
174 Dmsg1(dbglvl, "Read new block at pos=%s\n", dev->print_addr(ed1, sizeof(ed1)));
175 }
176 first_block = false;
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 */
182 }
183 continue; /* skip this record */
184 }
185 #endif
186 /*
187 * Get a new record for each Job as defined by
188 * VolSessionId and VolSessionTime
189 */
190 bool found = false;
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
197 */
198 if (rec->remainder) {
199 if (rec->BlockNumber != (block->BlockNumber - 1)
200 &&
201 rec->BlockNumber != block->BlockNumber)
202 {
203 Dmsg3(0, "invalid: rec=%ld block=%ld state=%s\n",
204 rec->BlockNumber, block->BlockNumber, rec_state_bits_to_str(rec));
205 rec->invalid = true;
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.
209 */
210 if (dcr->discard_invalid_records) {
211 empty_record(rec);
212 }
213 }
214 }
215 found = true;
216 break;
217 }
218 }
219 if (!found) {
220 rec = new_record();
221 recs->prepend(rec);
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);
225 }
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);
228 record = 0;
229 rec->state_bits = 0;
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);
237 break;
238 }
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);
242 /*
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
246 * get all the data.
247 */
248 record++;
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);
252
253 if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
254 Dmsg0(40, "Get EOM LABEL\n");
255 break; /* yes, get out */
256 }
257
258 /* Some sort of label? */
259 if (rec->FileIndex < 0) {
260 handle_session_record(dev, rec, &sessrec);
261 if (jcr->bsr) {
262 /* We just check block FI and FT not FileIndex */
263 rec->match_stat = match_bsr_block(jcr->bsr, block);
264 } else {
265 rec->match_stat = 0;
266 }
267 if (rec->invalid) {
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);
270 }
271 //ASSERTD(!rec->invalid || dcr->discard_invalid_records, "Got an invalid record");
272 /*
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 */
278 #ifdef xxx
279 /*
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.
285 */
286 if (rec->FileIndex == EOS_LABEL) {
287 Dmsg2(dbglvl, "Remove EOS rec. SI=%d ST=%d\n", rec->VolSessionId,
288 rec->VolSessionTime);
289 recs->remove(rec);
290 free_record(rec);
291 }
292 #endif
293 continue;
294 } /* end if label record */
295
296 /*
297 * Apply BSR filter
298 */
299 if (jcr->bsr) {
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)));
306 break;
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)));
310 rec->remainder = 0;
311 rec->state_bits &= ~REC_PARTIAL_RECORD;
312 if (try_repositioning(jcr, rec, dcr)) {
313 break; /* We moved on the volume, read next block */
314 }
315 continue; /* we don't want record, read next one */
316 }
317 }
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 */
324 }
325
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)));
333 break;
334 }
335 Dmsg2(dbglvl, "==== inside LastIndex=%d FileIndex=%d\n", lastFileIndex, rec->FileIndex);
336 }
337 Dmsg2(dbglvl, "==== LastIndex=%d FileIndex=%d\n", lastFileIndex, rec->FileIndex);
338 lastFileIndex = rec->FileIndex;
339 if (rec->invalid) {
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);
342 }
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 */
346 #if 0
347 /*
348 * If we have a digest stream, we check to see if we have
349 * finished the current bsr, and if so, repositioning will
350 * be turned on.
351 */
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)));
359 break;
360 }
361 Dmsg2(900, "After is_bsr_done pos %s\n", dev->print_addr(ed1, sizeof(ed1));
362 }
363 #endif
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 */
367
368 /* Walk down list and free all remaining allocated recs */
369 while (!recs->empty()) {
370 rec = (DEV_RECORD *)recs->first();
371 recs->remove(rec);
372 free_record(rec);
373 }
374 delete recs;
375 print_block_read_errors(jcr, block);
376 return ok;
377 }
378
379 /*
380 * See if we can reposition.
381 * Returns: true if at end of volume
382 * false otherwise
383 */
try_repositioning(JCR * jcr,DEV_RECORD * rec,DCR * dcr)384 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr)
385 {
386 BSR *bsr;
387 DEVICE *dev = dcr->dev;
388 char ed1[50];
389
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;
400 dev->set_eot();
401 }
402 rec->Addr = 0;
403 return true;
404 }
405 if (bsr) {
406 /*
407 * ***FIXME*** gross kludge to make disk seeking work. Remove
408 * when find_next_bsr() is fixed not to return a bsr already
409 * completed.
410 */
411 uint64_t dev_addr = dev->get_full_addr();
412 uint64_t bsr_addr = get_bsr_start_addr(bsr);
413
414 /* Do not position backwards */
415 if (dev_addr > bsr_addr) {
416 return false;
417 }
418 Dmsg2(dbglvl, "Try_Reposition from addr=%llu to %llu\n",
419 dev_addr, bsr_addr);
420 dev->reposition(dcr, bsr_addr);
421 rec->Addr = 0;
422 return true; /* We want the next block */
423 }
424 return false;
425 }
426
427 /*
428 * Position to the first file on this volume
429 */
position_to_first_file(JCR * jcr,DCR * dcr,BSR * bsr)430 static BSR *position_to_first_file(JCR *jcr, DCR *dcr, BSR *bsr)
431 {
432 DEVICE *dev = dcr->dev;
433 uint64_t bsr_addr;
434 char ed1[50], ed2[50];
435
436 Enter(150);
437 /*
438 * Now find and position to first file and block
439 * on this tape.
440 */
441 if (bsr) {
442 bsr->reposition = true; /* force repositioning */
443 bsr = find_next_bsr(bsr, dev);
444
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);
453 }
454 }
455 Leave(150);
456 return bsr;
457 }
458
459
handle_session_record(DEVICE * dev,DEV_RECORD * rec,SESSION_LABEL * sessrec)460 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
461 {
462 const char *rtype;
463 char buf[100];
464
465 memset(sessrec, 0, sizeof(SESSION_LABEL));
466 switch (rec->FileIndex) {
467 case PRE_LABEL:
468 rtype = _("Fresh Volume Label");
469 break;
470 case VOL_LABEL:
471 rtype = _("Volume Label");
472 unser_volume_label(dev, rec);
473 break;
474 case SOS_LABEL:
475 rtype = _("Begin Session");
476 unser_session_label(sessrec, rec);
477 break;
478 case EOS_LABEL:
479 rtype = _("End Session");
480 break;
481 case EOM_LABEL:
482 rtype = _("End of Media");
483 break;
484 default:
485 bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
486 rtype = buf;
487 break;
488 }
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);
491 }
492
493 #ifdef DEBUG
rec_state_bits_to_str(DEV_RECORD * rec)494 static char *rec_state_bits_to_str(DEV_RECORD *rec)
495 {
496 static char buf[200];
497 buf[0] = 0;
498 if (rec->state_bits & REC_NO_HEADER) {
499 bstrncat(buf, "Nohdr,", sizeof(buf));
500 }
501 if (is_partial_record(rec)) {
502 bstrncat(buf, "partial,", sizeof(buf));
503 }
504 if (rec->state_bits & REC_BLOCK_EMPTY) {
505 bstrncat(buf, "empty,", sizeof(buf));
506 }
507 if (rec->state_bits & REC_NO_MATCH) {
508 bstrncat(buf, "Nomatch,", sizeof(buf));
509 }
510 if (rec->state_bits & REC_CONTINUATION) {
511 bstrncat(buf, "cont,", sizeof(buf));
512 }
513 if (buf[0]) {
514 buf[strlen(buf)-1] = 0;
515 }
516 return buf;
517 }
518 #endif
519