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 Bacula® - The Network Backup Solution
19 Copyright (C) 2002-2006 Free Software Foundation Europe e.V.
21 The main author of Bacula is Kern Sibbald, with contributions from
22 many others, a complete list can be found in the file AUTHORS.
23 This program is Free Software; you can redistribute it and/or
24 modify it under the terms of version two of the GNU General Public
25 License as published by the Free Software Foundation plus additions
26 that are listed in the file LICENSE.
28 This program is distributed in the hope that it will be useful, but
29 WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
31 General Public License for more details.
33 You should have received a copy of the GNU General Public License
34 along with this program; if not, write to the Free Software
35 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
38 Bacula® is a registered trademark of John Walker.
39 The licensor of Bacula is the Free Software Foundation Europe
40 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
41 Switzerland, email:ftf@fsfeurope.org.
47 /* Forward referenced functions */
48 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
49 static BSR *position_to_first_file(JCR *jcr, DCR *dcr);
50 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr);
52 static char *rec_state_to_str(DEV_RECORD *rec);
55 static const int dbglvl = 1000;
57 bool read_records(DCR *dcr,
58 bool record_cb(DCR *dcr, DEV_RECORD *rec),
59 bool mount_cb(DCR *dcr))
62 DEVICE *dev = dcr->dev;
63 DEV_BLOCK *block = dcr->block;
64 DEV_RECORD *rec = NULL;
68 SESSION_LABEL sessrec;
69 dlist *recs; /* linked list of rec packets open */
71 recs = New(dlist(rec, &rec->link));
72 position_to_first_file(jcr, dcr);
73 jcr->mount_next_volume = false;
75 for ( ; ok && !done; ) {
76 if (job_canceled(jcr)) {
80 if (!read_block_from_device(dcr, CHECK_BLOCK_NUMBERS)) {
82 DEV_RECORD *trec = new_record();
83 Jmsg(jcr, M_INFO, 0, _("End of Volume at file %u on device %s, Volume \"%s\"\n"),
84 dev->file, dev->print_name(), dcr->VolumeName);
86 Jmsg(jcr, M_INFO, 0, _("End of all volumes.\n"));
87 ok = false; /* Stop everything */
89 * Create EOT Label so that Media record may
90 * be properly updated because this is the last
93 trec->FileIndex = EOT_LABEL;
94 trec->File = dev->file;
95 ok = record_cb(dcr, trec);
97 if (jcr->mount_next_volume) {
98 jcr->mount_next_volume = false;
103 jcr->mount_next_volume = false;
105 * The Device can change at the end of a tape, so refresh it
110 * We just have a new tape up, now read the label (first record)
111 * and pass it off to the callback routine, then continue
112 * most likely reading the previous record.
114 read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
115 read_record_from_block(dcr, block, trec);
116 handle_session_record(dev, trec, &sessrec);
117 ok = record_cb(dcr, trec);
119 position_to_first_file(jcr, dcr);
120 /* After reading label, we must read first data block */
123 } else if (dev->at_eof()) {
134 Jmsg(jcr, M_INFO, 0, _("End of %s %u on device %s, Volume \"%s\"\n"),
135 fp, fp_num, dev->print_name(), dcr->VolumeName);
137 Dmsg3(200, "End of file %u on device %s, Volume \"%s\"\n",
138 dev->file, dev->print_name(), dcr->VolumeName);
140 } else if (dev->is_short_block()) {
141 Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
144 /* I/O error or strange end of tape */
145 display_tape_error_status(jcr, dev);
146 if (forge_on || jcr->ignore_label_errors) {
147 dev->fsr(1); /* try skipping bad record */
148 Pmsg0(000, _("Did fsr in attemp to skip bad record.\n"));
149 continue; /* try to continue */
151 ok = false; /* stop everything */
155 Dmsg2(dbglvl, "Read new block at pos=%u:%u\n", dev->file, dev->block_num);
156 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
157 /* this does not stop when file/block are too big */
158 if (!match_bsr_block(jcr->bsr, block)) {
159 if (try_repositioning(jcr, rec, dcr)) {
160 break; /* get next volume */
162 continue; /* skip this record */
167 * Get a new record for each Job as defined by
168 * VolSessionId and VolSessionTime
171 foreach_dlist(rec, recs) {
172 if (rec->VolSessionId == block->VolSessionId &&
173 rec->VolSessionTime == block->VolSessionTime) {
181 Dmsg3(dbglvl, "New record for state=%s SI=%d ST=%d\n",
182 rec_state_to_str(rec),
183 block->VolSessionId, block->VolSessionTime);
185 Dmsg3(dbglvl, "Before read rec loop. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
186 block->BlockNumber, rec->remainder);
189 Dmsg1(dbglvl, "Block %s empty\n", is_block_empty(rec)?"is":"NOT");
190 for (rec->state=0; ok && !is_block_empty(rec); ) {
191 if (!read_record_from_block(dcr, block, rec)) {
192 Dmsg3(400, "!read-break. state=%s blk=%d rem=%d\n", rec_state_to_str(rec),
193 block->BlockNumber, rec->remainder);
196 Dmsg5(dbglvl, "read-OK. state=%s blk=%d rem=%d file:block=%u:%u\n",
197 rec_state_to_str(rec), block->BlockNumber, rec->remainder,
198 dev->file, dev->block_num);
200 * At this point, we have at least a record header.
201 * Now decide if we want this record or not, but remember
202 * before accessing the record, we may need to read again to
206 Dmsg6(dbglvl, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
207 rec_state_to_str(rec), block->BlockNumber,
208 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
210 if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
211 Dmsg0(40, "Get EOM LABEL\n");
212 break; /* yes, get out */
215 /* Some sort of label? */
216 if (rec->FileIndex < 0) {
217 handle_session_record(dev, rec, &sessrec);
219 /* We just check block FI and FT not FileIndex */
220 rec->match_stat = match_bsr_block(jcr->bsr, block);
225 * Note, we pass *all* labels to the callback routine. If
226 * he wants to know if they matched the bsr, then he must
227 * check the match_stat in the record */
228 ok = record_cb(dcr, rec);
230 * If this is the end of the Session (EOS) for this record
231 * we can remove the record. Note, there is a separate
232 * record to read each session. If a new session is seen
233 * a new record will be created at approx line 157 above.
235 if (rec->FileIndex == EOS_LABEL) {
236 Dmsg2(dbglvl, "Remove EOS rec. SI=%d ST=%d\n", rec->VolSessionId,
237 rec->VolSessionTime);
242 } /* end if label record */
248 rec->match_stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec);
249 if (rec->match_stat == -1) { /* no more possible matches */
250 done = true; /* all items found, stop */
251 Dmsg2(dbglvl, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
253 } else if (rec->match_stat == 0) { /* no match */
254 Dmsg4(dbglvl, "BSR no match: clear rem=%d FI=%d before set_eof pos %u:%u\n",
255 rec->remainder, rec->FileIndex, dev->file, dev->block_num);
257 rec->state &= ~REC_PARTIAL_RECORD;
258 if (try_repositioning(jcr, rec, dcr)) {
261 continue; /* we don't want record, read next one */
264 dcr->VolLastIndex = rec->FileIndex; /* let caller know where we are */
265 if (is_partial_record(rec)) {
266 Dmsg6(dbglvl, "Partial, break. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
267 rec_state_to_str(rec), block->BlockNumber,
268 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
269 break; /* read second part of record */
271 Dmsg6(dbglvl, "OK callback. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
272 rec_state_to_str(rec), block->BlockNumber,
273 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
274 ok = record_cb(dcr, rec);
276 * If we have a digest stream, we check to see if we have
277 * finished the current bsr, and if so, repositioning will
280 if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
281 Dmsg3(dbglvl, "Have digest FI=%u before bsr check pos %u:%u\n", rec->FileIndex,
282 dev->file, dev->block_num);
283 if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
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);
290 } /* end for loop over records */
291 Dmsg2(dbglvl, "After end recs in block. pos=%u:%u\n", dev->file, dev->block_num);
292 } /* end for loop over blocks */
293 // Dmsg2(dbglvl, "Position=(file:block) %u:%u\n", dev->file, dev->block_num);
295 /* Walk down list and free all remaining allocated recs */
296 while (!recs->empty()) {
297 rec = (DEV_RECORD *)recs->first();
302 print_block_read_errors(jcr, block);
307 * See if we can reposition.
308 * Returns: true if at end of volume
311 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr)
314 DEVICE *dev = dcr->dev;
316 bsr = find_next_bsr(jcr->bsr, dev);
317 if (bsr == NULL && jcr->bsr->mount_next_volume) {
318 Dmsg0(dbglvl, "Would mount next volume here\n");
319 Dmsg2(dbglvl, "Current postion (file:block) %u:%u\n",
320 dev->file, dev->block_num);
321 jcr->bsr->mount_next_volume = false;
322 if (!dev->at_eot()) {
323 /* Set EOT flag to force mount of next Volume */
324 jcr->mount_next_volume = true;
332 * ***FIXME*** gross kludge to make disk seeking work. Remove
333 * when find_next_bsr() is fixed not to return a bsr already
336 if (dev->file > bsr->volfile->sfile ||
337 (dev->file == bsr->volfile->sfile && dev->block_num > bsr->volblock->sblock)) {
341 Jmsg(jcr, M_INFO, 0, _("Reposition from (file:block) %u:%u to %u:%u\n"),
342 dev->file, dev->block_num, bsr->volfile->sfile,
343 bsr->volblock->sblock);
345 Dmsg4(10, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
346 dev->file, dev->block_num, bsr->volfile->sfile,
347 bsr->volblock->sblock);
348 dev->reposition(dcr, bsr->volfile->sfile, bsr->volblock->sblock);
355 * Position to the first file on this volume
357 static BSR *position_to_first_file(JCR *jcr, DCR *dcr)
360 DEVICE *dev = dcr->dev;
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);
368 if (bsr && (bsr->volfile->sfile != 0 || bsr->volblock->sblock != 0)) {
369 Jmsg(jcr, M_INFO, 0, _("Forward spacing Volume \"%s\" to file:block %u:%u.\n"),
370 dev->VolHdr.VolumeName,
371 bsr->volfile->sfile, bsr->volblock->sblock);
372 dev->reposition(dcr, bsr->volfile->sfile, bsr->volblock->sblock);
379 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
384 memset(sessrec, 0, sizeof(sessrec));
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_to_str(DEV_RECORD *rec)
415 static char buf[200];
417 if (rec->state & 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 & REC_BLOCK_EMPTY) {
424 bstrncat(buf, "empty,", sizeof(buf));
426 if (rec->state & REC_NO_MATCH) {
427 bstrncat(buf, "Nomatch,", sizeof(buf));
429 if (rec->state & REC_CONTINUATION) {
430 bstrncat(buf, "cont,", sizeof(buf));
433 buf[strlen(buf)-1] = 0;