2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 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 Bacula® is a registered trademark of Kern Sibbald.
18 * This routine provides a routine that will handle all
19 * the gory little details of reading a record from a Bacula
20 * archive. It uses a callback to pass you each record in turn,
21 * as well as a callback for mounting the next tape. It takes
22 * care of reading blocks, applying the bsr, ...
23 * Note, this routine is really the heart of the restore routines,
24 * and we are *really* bit pushing here so be careful about making
27 * Kern E. Sibbald, August MMII
34 /* Forward referenced functions */
35 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
36 static BSR *position_to_first_file(JCR *jcr, DCR *dcr);
37 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr);
39 static char *rec_state_bits_to_str(DEV_RECORD *rec);
42 static const int dbglvl = 190;
43 static const int no_FileIndex = -999999;
46 * This subroutine reads all the records and passes them back to your
47 * callback routine (also mount routine at EOM).
48 * You must not change any values in the DEV_RECORD packet
50 bool read_records(DCR *dcr,
51 bool record_cb(DCR *dcr, DEV_RECORD *rec),
52 bool mount_cb(DCR *dcr))
55 DEVICE *dev = dcr->dev;
56 DEV_BLOCK *block = dcr->block;
57 DEV_RECORD *rec = NULL;
59 int32_t lastFileIndex;
62 SESSION_LABEL sessrec;
63 dlist *recs; /* linked list of rec packets open */
65 recs = New(dlist(rec, &rec->link));
66 position_to_first_file(jcr, dcr);
67 jcr->mount_next_volume = false;
69 for ( ; ok && !done; ) {
70 if (job_canceled(jcr)) {
74 if (!dcr->read_block_from_device(CHECK_BLOCK_NUMBERS)) {
76 DEV_RECORD *trec = new_record();
77 Jmsg(jcr, M_INFO, 0, _("End of Volume at file %u on device %s, Volume \"%s\"\n"),
78 dev->file, dev->print_name(), dcr->VolumeName);
79 volume_unused(dcr); /* mark volume unused */
81 Jmsg(jcr, M_INFO, 0, _("End of all volumes.\n"));
82 ok = false; /* Stop everything */
84 * Create EOT Label so that Media record may
85 * be properly updated because this is the last
88 trec->FileIndex = EOT_LABEL;
89 trec->File = dev->file;
90 ok = record_cb(dcr, trec);
92 if (jcr->mount_next_volume) {
93 jcr->mount_next_volume = false;
98 jcr->mount_next_volume = false;
100 * The Device can change at the end of a tape, so refresh it
101 * and the block from the dcr.
106 * We just have a new tape up, now read the label (first record)
107 * and pass it off to the callback routine, then continue
108 * most likely reading the previous record.
110 dcr->read_block_from_device(NO_BLOCK_NUMBER_CHECK);
111 read_record_from_block(dcr, trec);
112 handle_session_record(dev, trec, &sessrec);
113 ok = record_cb(dcr, trec);
115 position_to_first_file(jcr, dcr);
116 /* After reading label, we must read first data block */
119 } else if (dev->at_eof()) {
120 Dmsg3(200, "End of file %u on device %s, Volume \"%s\"\n",
121 dev->file, dev->print_name(), dcr->VolumeName);
123 } else if (dev->is_short_block()) {
124 Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
127 /* I/O error or strange end of tape */
128 display_tape_error_status(jcr, dev);
129 if (forge_on || jcr->ignore_label_errors) {
130 dev->fsr(1); /* try skipping bad record */
131 Pmsg0(000, _("Did fsr in attemp to skip bad record.\n"));
132 continue; /* try to continue */
134 ok = false; /* stop everything */
138 Dmsg2(dbglvl, "Read new block at pos=%u:%u\n", dev->file, dev->block_num);
139 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
140 /* this does not stop when file/block are too big */
141 if (!match_bsr_block(jcr->bsr, block)) {
142 if (try_repositioning(jcr, rec, dcr)) {
143 break; /* get next volume */
145 continue; /* skip this record */
150 * Get a new record for each Job as defined by
151 * VolSessionId and VolSessionTime
154 foreach_dlist(rec, recs) {
155 if (rec->VolSessionId == block->VolSessionId &&
156 rec->VolSessionTime == block->VolSessionTime) {
164 Dmsg3(dbglvl, "New record for state=%s SI=%d ST=%d\n",
165 rec_state_bits_to_str(rec),
166 block->VolSessionId, block->VolSessionTime);
168 Dmsg3(dbglvl, "Before read rec loop. stat=%s blk=%d rem=%d\n", rec_state_bits_to_str(rec),
169 block->BlockNumber, rec->remainder);
172 lastFileIndex = no_FileIndex;
173 Dmsg1(dbglvl, "Block %s empty\n", is_block_marked_empty(rec)?"is":"NOT");
174 for (rec->state_bits=0; ok && !is_block_marked_empty(rec); ) {
175 if (!read_record_from_block(dcr, rec)) {
176 Dmsg3(200, "!read-break. state_bits=%s blk=%d rem=%d\n", rec_state_bits_to_str(rec),
177 block->BlockNumber, rec->remainder);
180 Dmsg5(dbglvl, "read-OK. state_bits=%s blk=%d rem=%d file:block=%u:%u\n",
181 rec_state_bits_to_str(rec), block->BlockNumber, rec->remainder,
182 dev->file, dev->block_num);
184 * At this point, we have at least a record header.
185 * Now decide if we want this record or not, but remember
186 * before accessing the record, we may need to read again to
190 Dmsg6(dbglvl, "recno=%d state_bits=%s blk=%d SI=%d ST=%d FI=%d\n", record,
191 rec_state_bits_to_str(rec), block->BlockNumber,
192 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
194 if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
195 Dmsg0(40, "Get EOM LABEL\n");
196 break; /* yes, get out */
199 /* Some sort of label? */
200 if (rec->FileIndex < 0) {
201 handle_session_record(dev, rec, &sessrec);
203 /* We just check block FI and FT not FileIndex */
204 rec->match_stat = match_bsr_block(jcr->bsr, block);
209 * Note, we pass *all* labels to the callback routine. If
210 * he wants to know if they matched the bsr, then he must
211 * check the match_stat in the record */
212 ok = record_cb(dcr, rec);
215 * If this is the end of the Session (EOS) for this record
216 * we can remove the record. Note, there is a separate
217 * record to read each session. If a new session is seen
218 * a new record will be created at approx line 157 above.
219 * However, it seg faults in the for line at lineno 196.
221 if (rec->FileIndex == EOS_LABEL) {
222 Dmsg2(dbglvl, "Remove EOS rec. SI=%d ST=%d\n", rec->VolSessionId,
223 rec->VolSessionTime);
229 } /* end if label record */
235 rec->match_stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec, jcr);
236 if (rec->match_stat == -1) { /* no more possible matches */
237 done = true; /* all items found, stop */
238 Dmsg2(dbglvl, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
240 } else if (rec->match_stat == 0) { /* no match */
241 Dmsg4(dbglvl, "BSR no match: clear rem=%d FI=%d before set_eof pos %u:%u\n",
242 rec->remainder, rec->FileIndex, dev->file, dev->block_num);
244 rec->state_bits &= ~REC_PARTIAL_RECORD;
245 if (try_repositioning(jcr, rec, dcr)) {
248 continue; /* we don't want record, read next one */
251 dcr->VolLastIndex = rec->FileIndex; /* let caller know where we are */
252 if (is_partial_record(rec)) {
253 Dmsg6(dbglvl, "Partial, break. recno=%d state_bits=%s blk=%d SI=%d ST=%d FI=%d\n", record,
254 rec_state_bits_to_str(rec), block->BlockNumber,
255 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
256 break; /* read second part of record */
259 Dmsg6(dbglvl, "OK callback. recno=%d state_bits=%s blk=%d SI=%d ST=%d FI=%d\n", record,
260 rec_state_bits_to_str(rec), block->BlockNumber,
261 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
262 if (lastFileIndex != no_FileIndex && lastFileIndex != rec->FileIndex) {
263 if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
264 Dmsg2(dbglvl, "This bsr done, break pos %u:%u\n",
265 dev->file, dev->block_num);
268 Dmsg2(dbglvl, "==== inside LastIndex=%d FileIndex=%d\n", lastFileIndex, rec->FileIndex);
270 Dmsg2(dbglvl, "==== LastIndex=%d FileIndex=%d\n", lastFileIndex, rec->FileIndex);
271 lastFileIndex = rec->FileIndex;
272 ok = record_cb(dcr, rec);
275 * If we have a digest stream, we check to see if we have
276 * finished the current bsr, and if so, repositioning will
279 if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
280 Dmsg3(dbglvl, "=== Have digest FI=%u before bsr check pos %u:%u\n", rec->FileIndex,
281 dev->file, dev->block_num);
282 if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
283 Dmsg1(dbglvl, "==== BSR done at FI=%d\n", rec->FileIndex);
284 Dmsg2(dbglvl, "This bsr done, break pos %u:%u\n",
285 dev->file, dev->block_num);
288 Dmsg2(900, "After is_bsr_done pos %u:%u\n", dev->file, dev->block_num);
291 } /* end for loop over records */
292 Dmsg2(dbglvl, "After end recs in block. pos=%u:%u\n", dev->file, dev->block_num);
293 } /* end for loop over blocks */
294 // Dmsg2(dbglvl, "Position=(file:block) %u:%u\n", dev->file, dev->block_num);
296 /* Walk down list and free all remaining allocated recs */
297 while (!recs->empty()) {
298 rec = (DEV_RECORD *)recs->first();
303 print_block_read_errors(jcr, block);
308 * See if we can reposition.
309 * Returns: true if at end of volume
312 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr)
315 DEVICE *dev = dcr->dev;
317 bsr = find_next_bsr(jcr->bsr, dev);
318 if (bsr == NULL && jcr->bsr->mount_next_volume) {
319 Dmsg0(dbglvl, "Would mount next volume here\n");
320 Dmsg2(dbglvl, "Current postion (file:block) %u:%u\n",
321 dev->file, dev->block_num);
322 jcr->bsr->mount_next_volume = false;
323 if (!dev->at_eot()) {
324 /* Set EOT flag to force mount of next Volume */
325 jcr->mount_next_volume = true;
333 * ***FIXME*** gross kludge to make disk seeking work. Remove
334 * when find_next_bsr() is fixed not to return a bsr already
337 uint32_t block, file;
338 /* TODO: use dev->file_addr ? */
339 uint64_t dev_addr = (((uint64_t) dev->file)<<32) | dev->block_num;
340 uint64_t bsr_addr = get_bsr_start_addr(bsr, &file, &block);
342 if (dev_addr > bsr_addr) {
345 Dmsg4(dbglvl, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
346 dev->file, dev->block_num, file, block);
347 dev->reposition(dcr, file, block);
354 * Position to the first file on this volume
356 static BSR *position_to_first_file(JCR *jcr, DCR *dcr)
359 DEVICE *dev = dcr->dev;
360 uint32_t file, block;
362 * Now find and position to first file and block
366 jcr->bsr->reposition = true; /* force repositioning */
367 bsr = find_next_bsr(jcr->bsr, dev);
369 if (get_bsr_start_addr(bsr, &file, &block) > 0) {
370 Jmsg(jcr, M_INFO, 0, _("Forward spacing Volume \"%s\" to file:block %u:%u.\n"),
371 dev->VolHdr.VolumeName, file, block);
372 dev->reposition(dcr, file, block);
379 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
384 memset(sessrec, 0, sizeof(SESSION_LABEL));
385 switch (rec->FileIndex) {
387 rtype = _("Fresh Volume Label");
390 rtype = _("Volume Label");
391 unser_volume_label(dev, rec);
394 rtype = _("Begin Session");
395 unser_session_label(sessrec, rec);
398 rtype = _("End Session");
401 rtype = _("End of Media");
404 bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
408 Dmsg5(dbglvl, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
409 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
413 static char *rec_state_bits_to_str(DEV_RECORD *rec)
415 static char buf[200];
417 if (rec->state_bits & REC_NO_HEADER) {
418 bstrncat(buf, "Nohdr,", sizeof(buf));
420 if (is_partial_record(rec)) {
421 bstrncat(buf, "partial,", sizeof(buf));
423 if (rec->state_bits & REC_BLOCK_EMPTY) {
424 bstrncat(buf, "empty,", sizeof(buf));
426 if (rec->state_bits & REC_NO_MATCH) {
427 bstrncat(buf, "Nomatch,", sizeof(buf));
429 if (rec->state_bits & REC_CONTINUATION) {
430 bstrncat(buf, "cont,", sizeof(buf));
433 buf[strlen(buf)-1] = 0;