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-2005 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 bool read_records(DCR *dcr,
43 bool record_cb(DCR *dcr, DEV_RECORD *rec),
44 bool mount_cb(DCR *dcr))
47 DEVICE *dev = dcr->dev;
48 DEV_BLOCK *block = dcr->block;
49 DEV_RECORD *rec = NULL;
53 SESSION_LABEL sessrec;
54 dlist *recs; /* linked list of rec packets open */
56 recs = New(dlist(rec, &rec->link));
57 position_to_first_file(jcr, dev);
58 jcr->mount_next_volume = false;
60 for ( ; ok && !done; ) {
61 if (job_canceled(jcr)) {
65 if (!read_block_from_device(dcr, CHECK_BLOCK_NUMBERS)) {
67 DEV_RECORD *trec = new_record();
68 Jmsg(jcr, M_INFO, 0, _("End of Volume at file %u on device %s, Volume \"%s\"\n"),
69 dev->file, dev->print_name(), dcr->VolumeName);
71 Jmsg(jcr, M_INFO, 0, _("End of all volumes.\n"));
72 ok = false; /* Stop everything */
74 * Create EOT Label so that Media record may
75 * be properly updated because this is the last
78 trec->FileIndex = EOT_LABEL;
79 trec->File = dev->file;
80 ok = record_cb(dcr, trec);
82 if (jcr->mount_next_volume) {
83 jcr->mount_next_volume = false;
88 jcr->mount_next_volume = false;
90 * We just have a new tape up, now read the label (first record)
91 * and pass it off to the callback routine, then continue
92 * most likely reading the previous record.
94 read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
95 read_record_from_block(block, trec);
96 handle_session_record(dev, trec, &sessrec);
97 ok = record_cb(dcr, trec);
99 position_to_first_file(jcr, dev);
100 /* After reading label, we must read first data block */
103 } else if (dev->at_eof()) {
105 Jmsg(jcr, M_INFO, 0, _("Got EOF at file %u on device %s, Volume \"%s\"\n"),
106 dev->file, dev->print_name(), dcr->VolumeName);
108 Dmsg3(200, "Got EOF at file %u on device %s, Volume \"%s\"\n",
109 dev->file, dev->print_name(), dcr->VolumeName);
111 } else if (dev->is_short_block()) {
112 Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
115 /* I/O error or strange end of tape */
116 display_tape_error_status(jcr, dev);
117 if (forge_on || jcr->ignore_label_errors) {
118 dev->fsr(1); /* try skipping bad record */
119 Pmsg0(000, _("Did fsr\n"));
120 continue; /* try to continue */
122 ok = false; /* stop everything */
126 Dmsg2(300, "New block at position=(file:block) %u:%u\n", dev->file, dev->block_num);
127 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
128 /* this does not stop when file/block are too big */
129 if (!match_bsr_block(jcr->bsr, block)) {
130 if (try_repositioning(jcr, rec, dev)) {
131 break; /* get next volume */
133 continue; /* skip this record */
138 * Get a new record for each Job as defined by
139 * VolSessionId and VolSessionTime
142 foreach_dlist(rec, recs) {
143 if (rec->VolSessionId == block->VolSessionId &&
144 rec->VolSessionTime == block->VolSessionTime) {
152 Dmsg2(300, "New record for SI=%d ST=%d\n",
153 block->VolSessionId, block->VolSessionTime);
155 Dmsg3(300, "After mount next vol. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
156 block->BlockNumber, rec->remainder);
159 Dmsg1(300, "Block empty %d\n", is_block_empty(rec));
160 for (rec->state=0; !is_block_empty(rec); ) {
161 if (!read_record_from_block(block, rec)) {
162 Dmsg3(400, "!read-break. state=%s blk=%d rem=%d\n", rec_state_to_str(rec),
163 block->BlockNumber, rec->remainder);
166 Dmsg5(300, "read-OK. state=%s blk=%d rem=%d file:block=%u:%u\n",
167 rec_state_to_str(rec), block->BlockNumber, rec->remainder,
168 dev->file, dev->block_num);
170 * At this point, we have at least a record header.
171 * Now decide if we want this record or not, but remember
172 * before accessing the record, we may need to read again to
176 Dmsg6(300, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
177 rec_state_to_str(rec), block->BlockNumber,
178 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
180 if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
181 Dmsg0(40, "Get EOM LABEL\n");
182 break; /* yes, get out */
185 /* Some sort of label? */
186 if (rec->FileIndex < 0) {
187 handle_session_record(dev, rec, &sessrec);
188 ok = record_cb(dcr, rec);
189 if (rec->FileIndex == EOS_LABEL) {
190 Dmsg2(300, "Remove rec. SI=%d ST=%d\n", rec->VolSessionId,
191 rec->VolSessionTime);
196 } /* end if label record */
202 int stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec);
203 if (stat == -1) { /* no more possible matches */
204 done = true; /* all items found, stop */
205 Dmsg2(300, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
207 } else if (stat == 0) { /* no match */
208 Dmsg4(300, "Clear rem=%d FI=%d before set_eof pos %u:%u\n",
209 rec->remainder, rec->FileIndex, dev->file, dev->block_num);
211 rec->state &= ~REC_PARTIAL_RECORD;
212 if (try_repositioning(jcr, rec, dev)) {
215 continue; /* we don't want record, read next one */
218 if (is_partial_record(rec)) {
219 Dmsg6(300, "Partial, break. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
220 rec_state_to_str(rec), block->BlockNumber,
221 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
222 break; /* read second part of record */
224 ok = record_cb(dcr, rec);
225 if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
226 Dmsg3(300, "Done FI=%u before set_eof pos %u:%u\n", rec->FileIndex,
227 dev->file, dev->block_num);
228 if (match_set_eof(jcr->bsr, rec) && try_repositioning(jcr, rec, dev)) {
229 Dmsg2(300, "Break after match_set_eof pos %u:%u\n",
230 dev->file, dev->block_num);
233 Dmsg2(300, "After set_eof pos %u:%u\n", dev->file, dev->block_num);
235 } /* end for loop over records */
236 Dmsg2(300, "After end records position=(file:block) %u:%u\n", dev->file, dev->block_num);
237 } /* end for loop over blocks */
238 // Dmsg2(300, "Position=(file:block) %u:%u\n", dev->file, dev->block_num);
240 /* Walk down list and free all remaining allocated recs */
241 while (!recs->empty()) {
242 rec = (DEV_RECORD *)recs->first();
247 print_block_read_errors(jcr, block);
252 * See if we can reposition.
253 * Returns: true if at end of volume
256 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DEVICE *dev)
259 bsr = find_next_bsr(jcr->bsr, dev);
260 if (bsr == NULL && jcr->bsr->mount_next_volume) {
261 Dmsg0(300, "Would mount next volume here\n");
262 Dmsg2(300, "Current postion (file:block) %u:%u\n",
263 dev->file, dev->block_num);
264 jcr->bsr->mount_next_volume = false;
265 if (!dev->at_eot()) {
266 /* Set EOT flag to force mount of next Volume */
267 jcr->mount_next_volume = true;
275 Jmsg(jcr, M_INFO, 0, _("Reposition from (file:block) %u:%u to %u:%u\n"),
276 dev->file, dev->block_num, bsr->volfile->sfile,
277 bsr->volblock->sblock);
279 Dmsg4(300, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
280 dev->file, dev->block_num, bsr->volfile->sfile,
281 bsr->volblock->sblock);
282 reposition_dev(dev, bsr->volfile->sfile, bsr->volblock->sblock);
289 * Position to the first file on this volume
291 static BSR *position_to_first_file(JCR *jcr, DEVICE *dev)
295 * Now find and position to first file and block
299 jcr->bsr->reposition = true; /* force repositioning */
300 bsr = find_next_bsr(jcr->bsr, dev);
301 if (bsr && (bsr->volfile->sfile != 0 || bsr->volblock->sblock != 0)) {
302 Jmsg(jcr, M_INFO, 0, _("Forward spacing to file:block %u:%u.\n"),
303 bsr->volfile->sfile, bsr->volblock->sblock);
304 Dmsg2(300, "Forward spacing to file:block %u:%u.\n",
305 bsr->volfile->sfile, bsr->volblock->sblock);
306 reposition_dev(dev, bsr->volfile->sfile, bsr->volblock->sblock);
313 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
318 memset(sessrec, 0, sizeof(sessrec));
319 switch (rec->FileIndex) {
321 rtype = _("Fresh Volume Label");
324 rtype = _("Volume Label");
325 unser_volume_label(dev, rec);
328 rtype = _("Begin Session");
329 unser_session_label(sessrec, rec);
332 rtype = _("End Session");
335 rtype = _("End of Media");
338 bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
342 Dmsg5(300, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
343 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
347 static char *rec_state_to_str(DEV_RECORD *rec)
349 static char buf[200];
351 if (rec->state & REC_NO_HEADER) {
352 bstrncat(buf, "Nohdr,", sizeof(buf));
354 if (is_partial_record(rec)) {
355 bstrncat(buf, "partial,", sizeof(buf));
357 if (rec->state & REC_BLOCK_EMPTY) {
358 bstrncat(buf, "empty,", sizeof(buf));
360 if (rec->state & REC_NO_MATCH) {
361 bstrncat(buf, "Nomatch,", sizeof(buf));
363 if (rec->state & REC_CONTINUATION) {
364 bstrncat(buf, "cont,", sizeof(buf));
367 buf[strlen(buf)-1] = 0;