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, DCR *dcr);
37 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr);
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, dcr);
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, dcr);
107 /* After reading label, we must read first data block */
110 } else if (dev->at_eof()) {
121 Jmsg(jcr, M_INFO, 0, _("End of %s %u on device %s, Volume \"%s\"\n"),
122 fp, fp_num, dev->print_name(), dcr->VolumeName);
124 Dmsg3(200, "End of file %u on device %s, Volume \"%s\"\n",
125 dev->file, dev->print_name(), dcr->VolumeName);
127 } else if (dev->is_short_block()) {
128 Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
131 /* I/O error or strange end of tape */
132 display_tape_error_status(jcr, dev);
133 if (forge_on || jcr->ignore_label_errors) {
134 dev->fsr(1); /* try skipping bad record */
135 Pmsg0(000, _("Did fsr\n"));
136 continue; /* try to continue */
138 ok = false; /* stop everything */
142 Dmsg2(dbglvl, "Read new block at pos=%u:%u\n", dev->file, dev->block_num);
143 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
144 /* this does not stop when file/block are too big */
145 if (!match_bsr_block(jcr->bsr, block)) {
146 if (try_repositioning(jcr, rec, dcr)) {
147 break; /* get next volume */
149 continue; /* skip this record */
154 * Get a new record for each Job as defined by
155 * VolSessionId and VolSessionTime
158 foreach_dlist(rec, recs) {
159 if (rec->VolSessionId == block->VolSessionId &&
160 rec->VolSessionTime == block->VolSessionTime) {
168 Dmsg3(dbglvl, "New record for state=%s SI=%d ST=%d\n",
169 rec_state_to_str(rec),
170 block->VolSessionId, block->VolSessionTime);
172 Dmsg3(dbglvl, "Before read rec loop. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
173 block->BlockNumber, rec->remainder);
176 Dmsg1(dbglvl, "Block %s empty\n", is_block_empty(rec)?"is":"NOT");
177 for (rec->state=0; ok && !is_block_empty(rec); ) {
178 if (!read_record_from_block(block, rec)) {
179 Dmsg3(400, "!read-break. state=%s blk=%d rem=%d\n", rec_state_to_str(rec),
180 block->BlockNumber, rec->remainder);
183 Dmsg5(dbglvl, "read-OK. state=%s blk=%d rem=%d file:block=%u:%u\n",
184 rec_state_to_str(rec), block->BlockNumber, rec->remainder,
185 dev->file, dev->block_num);
187 * At this point, we have at least a record header.
188 * Now decide if we want this record or not, but remember
189 * before accessing the record, we may need to read again to
193 Dmsg6(dbglvl, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
194 rec_state_to_str(rec), block->BlockNumber,
195 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
197 if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
198 Dmsg0(40, "Get EOM LABEL\n");
199 break; /* yes, get out */
202 /* Some sort of label? */
203 if (rec->FileIndex < 0) {
204 handle_session_record(dev, rec, &sessrec);
206 /* We just check block FI and FT not FileIndex */
207 rec->match_stat = match_bsr_block(jcr->bsr, block);
212 * Note, we pass *all* labels to the callback routine. If
213 * he wants to know if they matched the bsr, then he must
214 * check the match_stat in the record */
215 ok = record_cb(dcr, rec);
217 * If this is the end of the Session (EOS) for this record
218 * we can remove the record. Note, there is a separate
219 * record to read each session. If a new session is seen
220 * a new record will be created at approx line 157 above.
222 if (rec->FileIndex == EOS_LABEL) {
223 Dmsg2(dbglvl, "Remove EOS rec. SI=%d ST=%d\n", rec->VolSessionId,
224 rec->VolSessionTime);
229 } /* end if label record */
235 rec->match_stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec);
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 &= ~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=%s blk=%d SI=%d ST=%d FI=%d\n", record,
254 rec_state_to_str(rec), block->BlockNumber,
255 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
256 break; /* read second part of record */
258 Dmsg6(dbglvl, "OK callback. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
259 rec_state_to_str(rec), block->BlockNumber,
260 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
261 ok = record_cb(dcr, rec);
263 * If we have a digest stream, we check to see if we have
264 * finished the current bsr, and if so, repositioning will
267 if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
268 Dmsg3(dbglvl, "Have digest FI=%u before bsr check pos %u:%u\n", rec->FileIndex,
269 dev->file, dev->block_num);
270 if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
271 Dmsg2(dbglvl, "This bsr done, break pos %u:%u\n",
272 dev->file, dev->block_num);
275 Dmsg2(900, "After is_bsr_done pos %u:%u\n", dev->file, dev->block_num);
277 } /* end for loop over records */
278 Dmsg2(dbglvl, "After end recs in block. pos=%u:%u\n", dev->file, dev->block_num);
279 } /* end for loop over blocks */
280 // Dmsg2(dbglvl, "Position=(file:block) %u:%u\n", dev->file, dev->block_num);
282 /* Walk down list and free all remaining allocated recs */
283 while (!recs->empty()) {
284 rec = (DEV_RECORD *)recs->first();
289 print_block_read_errors(jcr, block);
294 * See if we can reposition.
295 * Returns: true if at end of volume
298 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr)
301 DEVICE *dev = dcr->dev;
303 bsr = find_next_bsr(jcr->bsr, dev);
304 if (bsr == NULL && jcr->bsr->mount_next_volume) {
305 Dmsg0(dbglvl, "Would mount next volume here\n");
306 Dmsg2(dbglvl, "Current postion (file:block) %u:%u\n",
307 dev->file, dev->block_num);
308 jcr->bsr->mount_next_volume = false;
309 if (!dev->at_eot()) {
310 /* Set EOT flag to force mount of next Volume */
311 jcr->mount_next_volume = true;
319 Jmsg(jcr, M_INFO, 0, _("Reposition from (file:block) %u:%u to %u:%u\n"),
320 dev->file, dev->block_num, bsr->volfile->sfile,
321 bsr->volblock->sblock);
323 Dmsg4(dbglvl, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
324 dev->file, dev->block_num, bsr->volfile->sfile,
325 bsr->volblock->sblock);
326 dev->reposition(dcr, bsr->volfile->sfile, bsr->volblock->sblock);
333 * Position to the first file on this volume
335 static BSR *position_to_first_file(JCR *jcr, DCR *dcr)
338 DEVICE *dev = dcr->dev;
340 * Now find and position to first file and block
344 jcr->bsr->reposition = true; /* force repositioning */
345 bsr = find_next_bsr(jcr->bsr, dev);
346 if (bsr && (bsr->volfile->sfile != 0 || bsr->volblock->sblock != 0)) {
347 Jmsg(jcr, M_INFO, 0, _("Forward spacing to file:block %u:%u.\n"),
348 bsr->volfile->sfile, bsr->volblock->sblock);
349 Dmsg2(dbglvl, "Forward spacing to file:block %u:%u.\n",
350 bsr->volfile->sfile, bsr->volblock->sblock);
351 dev->reposition(dcr, bsr->volfile->sfile, bsr->volblock->sblock);
358 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
363 memset(sessrec, 0, sizeof(sessrec));
364 switch (rec->FileIndex) {
366 rtype = _("Fresh Volume Label");
369 rtype = _("Volume Label");
370 unser_volume_label(dev, rec);
373 rtype = _("Begin Session");
374 unser_session_label(sessrec, rec);
377 rtype = _("End Session");
380 rtype = _("End of Media");
383 bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
387 Dmsg5(dbglvl, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
388 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
392 static char *rec_state_to_str(DEV_RECORD *rec)
394 static char buf[200];
396 if (rec->state & REC_NO_HEADER) {
397 bstrncat(buf, "Nohdr,", sizeof(buf));
399 if (is_partial_record(rec)) {
400 bstrncat(buf, "partial,", sizeof(buf));
402 if (rec->state & REC_BLOCK_EMPTY) {
403 bstrncat(buf, "empty,", sizeof(buf));
405 if (rec->state & REC_NO_MATCH) {
406 bstrncat(buf, "Nomatch,", sizeof(buf));
408 if (rec->state & REC_CONTINUATION) {
409 bstrncat(buf, "cont,", sizeof(buf));
412 buf[strlen(buf)-1] = 0;