3 * This routine provides a routine that will handle all
4 * the gory little details of reading a record from a Bacula
5 * archive. It uses a callback to pass you each record in turn,
6 * as well as a callback for mounting the next tape. It takes
7 * care of reading blocks, applying the bsr, ...
8 * Note, this routine is really the heart of the restore routines,
9 * and we are *really* bit pushing here so be careful about making
12 * Kern E. Sibbald, August MMII
17 Copyright (C) 2000-2006 Kern Sibbald
19 This program is free software; you can redistribute it and/or
20 modify it under the terms of the GNU General Public License
21 version 2 as amended with additional clauses defined in the
22 file LICENSE in the main source directory.
24 This program is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 the file LICENSE for additional details.
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, DEVICE *dev);
37 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DEVICE *dev);
39 static char *rec_state_to_str(DEV_RECORD *rec);
42 static const int dbglvl = 1000;
44 bool read_records(DCR *dcr,
45 bool record_cb(DCR *dcr, DEV_RECORD *rec),
46 bool mount_cb(DCR *dcr))
49 DEVICE *dev = dcr->dev;
50 DEV_BLOCK *block = dcr->block;
51 DEV_RECORD *rec = NULL;
55 SESSION_LABEL sessrec;
56 dlist *recs; /* linked list of rec packets open */
58 recs = New(dlist(rec, &rec->link));
59 position_to_first_file(jcr, dev);
60 jcr->mount_next_volume = false;
62 for ( ; ok && !done; ) {
63 if (job_canceled(jcr)) {
67 if (!read_block_from_device(dcr, CHECK_BLOCK_NUMBERS)) {
69 DEV_RECORD *trec = new_record();
70 Jmsg(jcr, M_INFO, 0, _("End of Volume at file %u on device %s, Volume \"%s\"\n"),
71 dev->file, dev->print_name(), dcr->VolumeName);
73 Jmsg(jcr, M_INFO, 0, _("End of all volumes.\n"));
74 ok = false; /* Stop everything */
76 * Create EOT Label so that Media record may
77 * be properly updated because this is the last
80 trec->FileIndex = EOT_LABEL;
81 trec->File = dev->file;
82 ok = record_cb(dcr, trec);
84 if (jcr->mount_next_volume) {
85 jcr->mount_next_volume = false;
90 jcr->mount_next_volume = false;
92 * The Device can change at the end of a tape, so refresh it
97 * We just have a new tape up, now read the label (first record)
98 * and pass it off to the callback routine, then continue
99 * most likely reading the previous record.
101 read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
102 read_record_from_block(block, trec);
103 handle_session_record(dev, trec, &sessrec);
104 ok = record_cb(dcr, trec);
106 position_to_first_file(jcr, dev);
107 /* After reading label, we must read first data block */
110 } else if (dev->at_eof()) {
114 bsnprintf(dvdpart, sizeof(dvdpart), _("part %d "), dev->part);
118 Jmsg(jcr, M_INFO, 0, _("End of file %u %son device %s, Volume \"%s\"\n"),
119 dev->file, dvdpart, dev->print_name(), dcr->VolumeName);
121 Dmsg3(200, "End of file %u on device %s, Volume \"%s\"\n",
122 dev->file, dev->print_name(), dcr->VolumeName);
124 } else if (dev->is_short_block()) {
125 Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
128 /* I/O error or strange end of tape */
129 display_tape_error_status(jcr, dev);
130 if (forge_on || jcr->ignore_label_errors) {
131 dev->fsr(1); /* try skipping bad record */
132 Pmsg0(000, _("Did fsr\n"));
133 continue; /* try to continue */
135 ok = false; /* stop everything */
139 Dmsg2(dbglvl, "Read new block at pos=%u:%u\n", dev->file, dev->block_num);
140 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
141 /* this does not stop when file/block are too big */
142 if (!match_bsr_block(jcr->bsr, block)) {
143 if (try_repositioning(jcr, rec, dev)) {
144 break; /* get next volume */
146 continue; /* skip this record */
151 * Get a new record for each Job as defined by
152 * VolSessionId and VolSessionTime
155 foreach_dlist(rec, recs) {
156 if (rec->VolSessionId == block->VolSessionId &&
157 rec->VolSessionTime == block->VolSessionTime) {
165 Dmsg3(dbglvl, "New record for state=%s SI=%d ST=%d\n",
166 rec_state_to_str(rec),
167 block->VolSessionId, block->VolSessionTime);
169 Dmsg3(dbglvl, "Before read rec loop. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
170 block->BlockNumber, rec->remainder);
173 Dmsg1(dbglvl, "Block %s empty\n", is_block_empty(rec)?"is":"NOT");
174 for (rec->state=0; ok && !is_block_empty(rec); ) {
175 if (!read_record_from_block(block, rec)) {
176 Dmsg3(400, "!read-break. state=%s blk=%d rem=%d\n", rec_state_to_str(rec),
177 block->BlockNumber, rec->remainder);
180 Dmsg5(dbglvl, "read-OK. state=%s blk=%d rem=%d file:block=%u:%u\n",
181 rec_state_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=%s blk=%d SI=%d ST=%d FI=%d\n", record,
191 rec_state_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);
214 * If this is the end of the Session (EOS) for this record
215 * we can remove the record. Note, there is a separate
216 * record to read each session. If a new session is seen
217 * a new record will be created at approx line 157 above.
219 if (rec->FileIndex == EOS_LABEL) {
220 Dmsg2(dbglvl, "Remove EOS rec. SI=%d ST=%d\n", rec->VolSessionId,
221 rec->VolSessionTime);
226 } /* end if label record */
232 rec->match_stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec);
233 if (rec->match_stat == -1) { /* no more possible matches */
234 done = true; /* all items found, stop */
235 Dmsg2(dbglvl, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
237 } else if (rec->match_stat == 0) { /* no match */
238 Dmsg4(dbglvl, "BSR no match: clear rem=%d FI=%d before set_eof pos %u:%u\n",
239 rec->remainder, rec->FileIndex, dev->file, dev->block_num);
241 rec->state &= ~REC_PARTIAL_RECORD;
242 if (try_repositioning(jcr, rec, dev)) {
245 continue; /* we don't want record, read next one */
248 dcr->VolLastIndex = rec->FileIndex; /* let caller know where we are */
249 if (is_partial_record(rec)) {
250 Dmsg6(dbglvl, "Partial, break. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
251 rec_state_to_str(rec), block->BlockNumber,
252 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
253 break; /* read second part of record */
255 Dmsg6(dbglvl, "OK callback. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
256 rec_state_to_str(rec), block->BlockNumber,
257 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
258 ok = record_cb(dcr, rec);
260 * If we have a digest stream, we check to see if we have
261 * finished the current bsr, and if so, repositioning will
264 if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
265 Dmsg3(dbglvl, "Have digest FI=%u before bsr check pos %u:%u\n", rec->FileIndex,
266 dev->file, dev->block_num);
267 if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dev)) {
268 Dmsg2(dbglvl, "This bsr done, break pos %u:%u\n",
269 dev->file, dev->block_num);
272 Dmsg2(900, "After is_bsr_done pos %u:%u\n", dev->file, dev->block_num);
274 } /* end for loop over records */
275 Dmsg2(dbglvl, "After end recs in block. pos=%u:%u\n", dev->file, dev->block_num);
276 } /* end for loop over blocks */
277 // Dmsg2(dbglvl, "Position=(file:block) %u:%u\n", dev->file, dev->block_num);
279 /* Walk down list and free all remaining allocated recs */
280 while (!recs->empty()) {
281 rec = (DEV_RECORD *)recs->first();
286 print_block_read_errors(jcr, block);
291 * See if we can reposition.
292 * Returns: true if at end of volume
295 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DEVICE *dev)
298 bsr = find_next_bsr(jcr->bsr, dev);
299 if (bsr == NULL && jcr->bsr->mount_next_volume) {
300 Dmsg0(dbglvl, "Would mount next volume here\n");
301 Dmsg2(dbglvl, "Current postion (file:block) %u:%u\n",
302 dev->file, dev->block_num);
303 jcr->bsr->mount_next_volume = false;
304 if (!dev->at_eot()) {
305 /* Set EOT flag to force mount of next Volume */
306 jcr->mount_next_volume = true;
314 Jmsg(jcr, M_INFO, 0, _("Reposition from (file:block) %u:%u to %u:%u\n"),
315 dev->file, dev->block_num, bsr->volfile->sfile,
316 bsr->volblock->sblock);
318 Dmsg4(dbglvl, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
319 dev->file, dev->block_num, bsr->volfile->sfile,
320 bsr->volblock->sblock);
321 dev->reposition(bsr->volfile->sfile, bsr->volblock->sblock);
328 * Position to the first file on this volume
330 static BSR *position_to_first_file(JCR *jcr, DEVICE *dev)
334 * Now find and position to first file and block
338 jcr->bsr->reposition = true; /* force repositioning */
339 bsr = find_next_bsr(jcr->bsr, dev);
340 if (bsr && (bsr->volfile->sfile != 0 || bsr->volblock->sblock != 0)) {
341 Jmsg(jcr, M_INFO, 0, _("Forward spacing to file:block %u:%u.\n"),
342 bsr->volfile->sfile, bsr->volblock->sblock);
343 Dmsg2(dbglvl, "Forward spacing to file:block %u:%u.\n",
344 bsr->volfile->sfile, bsr->volblock->sblock);
345 dev->reposition(bsr->volfile->sfile, bsr->volblock->sblock);
352 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
357 memset(sessrec, 0, sizeof(sessrec));
358 switch (rec->FileIndex) {
360 rtype = _("Fresh Volume Label");
363 rtype = _("Volume Label");
364 unser_volume_label(dev, rec);
367 rtype = _("Begin Session");
368 unser_session_label(sessrec, rec);
371 rtype = _("End Session");
374 rtype = _("End of Media");
377 bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
381 Dmsg5(dbglvl, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
382 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
386 static char *rec_state_to_str(DEV_RECORD *rec)
388 static char buf[200];
390 if (rec->state & REC_NO_HEADER) {
391 bstrncat(buf, "Nohdr,", sizeof(buf));
393 if (is_partial_record(rec)) {
394 bstrncat(buf, "partial,", sizeof(buf));
396 if (rec->state & REC_BLOCK_EMPTY) {
397 bstrncat(buf, "empty,", sizeof(buf));
399 if (rec->state & REC_NO_MATCH) {
400 bstrncat(buf, "Nomatch,", sizeof(buf));
402 if (rec->state & REC_CONTINUATION) {
403 bstrncat(buf, "cont,", sizeof(buf));
406 buf[strlen(buf)-1] = 0;