2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2007 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * This routine provides a routine that will handle all
31 * the gory little details of reading a record from a Bacula
32 * archive. It uses a callback to pass you each record in turn,
33 * as well as a callback for mounting the next tape. It takes
34 * care of reading blocks, applying the bsr, ...
35 * Note, this routine is really the heart of the restore routines,
36 * and we are *really* bit pushing here so be careful about making
39 * Kern E. Sibbald, August MMII
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
106 * and the block from the dcr.
111 * We just have a new tape up, now read the label (first record)
112 * and pass it off to the callback routine, then continue
113 * most likely reading the previous record.
115 read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
116 read_record_from_block(dcr, block, trec);
117 handle_session_record(dev, trec, &sessrec);
118 ok = record_cb(dcr, trec);
120 position_to_first_file(jcr, dcr);
121 /* After reading label, we must read first data block */
124 } else if (dev->at_eof()) {
135 Jmsg(jcr, M_INFO, 0, _("End of %s %u on device %s, Volume \"%s\"\n"),
136 fp, fp_num, dev->print_name(), dcr->VolumeName);
138 Dmsg3(200, "End of file %u on device %s, Volume \"%s\"\n",
139 dev->file, dev->print_name(), dcr->VolumeName);
141 } else if (dev->is_short_block()) {
142 Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
145 /* I/O error or strange end of tape */
146 display_tape_error_status(jcr, dev);
147 if (forge_on || jcr->ignore_label_errors) {
148 dev->fsr(1); /* try skipping bad record */
149 Pmsg0(000, _("Did fsr in attemp to skip bad record.\n"));
150 continue; /* try to continue */
152 ok = false; /* stop everything */
156 Dmsg2(dbglvl, "Read new block at pos=%u:%u\n", dev->file, dev->block_num);
157 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
158 /* this does not stop when file/block are too big */
159 if (!match_bsr_block(jcr->bsr, block)) {
160 if (try_repositioning(jcr, rec, dcr)) {
161 break; /* get next volume */
163 continue; /* skip this record */
168 * Get a new record for each Job as defined by
169 * VolSessionId and VolSessionTime
172 foreach_dlist(rec, recs) {
173 if (rec->VolSessionId == block->VolSessionId &&
174 rec->VolSessionTime == block->VolSessionTime) {
182 Dmsg3(dbglvl, "New record for state=%s SI=%d ST=%d\n",
183 rec_state_to_str(rec),
184 block->VolSessionId, block->VolSessionTime);
186 Dmsg3(dbglvl, "Before read rec loop. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
187 block->BlockNumber, rec->remainder);
190 Dmsg1(dbglvl, "Block %s empty\n", is_block_empty(rec)?"is":"NOT");
191 for (rec->state=0; ok && !is_block_empty(rec); ) {
192 if (!read_record_from_block(dcr, block, rec)) {
193 Dmsg3(400, "!read-break. state=%s blk=%d rem=%d\n", rec_state_to_str(rec),
194 block->BlockNumber, rec->remainder);
197 Dmsg5(dbglvl, "read-OK. state=%s blk=%d rem=%d file:block=%u:%u\n",
198 rec_state_to_str(rec), block->BlockNumber, rec->remainder,
199 dev->file, dev->block_num);
201 * At this point, we have at least a record header.
202 * Now decide if we want this record or not, but remember
203 * before accessing the record, we may need to read again to
207 Dmsg6(dbglvl, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
208 rec_state_to_str(rec), block->BlockNumber,
209 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
211 if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
212 Dmsg0(40, "Get EOM LABEL\n");
213 break; /* yes, get out */
216 /* Some sort of label? */
217 if (rec->FileIndex < 0) {
218 handle_session_record(dev, rec, &sessrec);
220 /* We just check block FI and FT not FileIndex */
221 rec->match_stat = match_bsr_block(jcr->bsr, block);
226 * Note, we pass *all* labels to the callback routine. If
227 * he wants to know if they matched the bsr, then he must
228 * check the match_stat in the record */
229 ok = record_cb(dcr, rec);
231 * If this is the end of the Session (EOS) for this record
232 * we can remove the record. Note, there is a separate
233 * record to read each session. If a new session is seen
234 * a new record will be created at approx line 157 above.
236 if (rec->FileIndex == EOS_LABEL) {
237 Dmsg2(dbglvl, "Remove EOS rec. SI=%d ST=%d\n", rec->VolSessionId,
238 rec->VolSessionTime);
243 } /* end if label record */
249 rec->match_stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec);
250 if (rec->match_stat == -1) { /* no more possible matches */
251 done = true; /* all items found, stop */
252 Dmsg2(dbglvl, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
254 } else if (rec->match_stat == 0) { /* no match */
255 Dmsg4(dbglvl, "BSR no match: clear rem=%d FI=%d before set_eof pos %u:%u\n",
256 rec->remainder, rec->FileIndex, dev->file, dev->block_num);
258 rec->state &= ~REC_PARTIAL_RECORD;
259 if (try_repositioning(jcr, rec, dcr)) {
262 continue; /* we don't want record, read next one */
265 dcr->VolLastIndex = rec->FileIndex; /* let caller know where we are */
266 if (is_partial_record(rec)) {
267 Dmsg6(dbglvl, "Partial, break. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
268 rec_state_to_str(rec), block->BlockNumber,
269 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
270 break; /* read second part of record */
272 Dmsg6(dbglvl, "OK callback. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
273 rec_state_to_str(rec), block->BlockNumber,
274 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
275 ok = record_cb(dcr, rec);
277 * If we have a digest stream, we check to see if we have
278 * finished the current bsr, and if so, repositioning will
281 if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
282 Dmsg3(dbglvl, "Have digest FI=%u before bsr check pos %u:%u\n", rec->FileIndex,
283 dev->file, dev->block_num);
284 if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
285 Dmsg2(dbglvl, "This bsr done, break pos %u:%u\n",
286 dev->file, dev->block_num);
289 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 if (dev->file > bsr->volfile->sfile ||
338 (dev->file == bsr->volfile->sfile && dev->block_num > bsr->volblock->sblock)) {
342 Jmsg(jcr, M_INFO, 0, _("Reposition from (file:block) %u:%u to %u:%u\n"),
343 dev->file, dev->block_num, bsr->volfile->sfile,
344 bsr->volblock->sblock);
346 Dmsg4(10, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
347 dev->file, dev->block_num, bsr->volfile->sfile,
348 bsr->volblock->sblock);
349 dev->reposition(dcr, bsr->volfile->sfile, bsr->volblock->sblock);
356 * Position to the first file on this volume
358 static BSR *position_to_first_file(JCR *jcr, DCR *dcr)
361 DEVICE *dev = dcr->dev;
363 * Now find and position to first file and block
367 jcr->bsr->reposition = true; /* force repositioning */
368 bsr = find_next_bsr(jcr->bsr, dev);
369 if (bsr && (bsr->volfile->sfile != 0 || bsr->volblock->sblock != 0)) {
370 Jmsg(jcr, M_INFO, 0, _("Forward spacing Volume \"%s\" to file:block %u:%u.\n"),
371 dev->VolHdr.VolumeName,
372 bsr->volfile->sfile, bsr->volblock->sblock);
373 dev->reposition(dcr, bsr->volfile->sfile, bsr->volblock->sblock);
380 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
385 memset(sessrec, 0, sizeof(sessrec));
386 switch (rec->FileIndex) {
388 rtype = _("Fresh Volume Label");
391 rtype = _("Volume Label");
392 unser_volume_label(dev, rec);
395 rtype = _("Begin Session");
396 unser_session_label(sessrec, rec);
399 rtype = _("End Session");
402 rtype = _("End of Media");
405 bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
409 Dmsg5(dbglvl, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
410 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
414 static char *rec_state_to_str(DEV_RECORD *rec)
416 static char buf[200];
418 if (rec->state & REC_NO_HEADER) {
419 bstrncat(buf, "Nohdr,", sizeof(buf));
421 if (is_partial_record(rec)) {
422 bstrncat(buf, "partial,", sizeof(buf));
424 if (rec->state & REC_BLOCK_EMPTY) {
425 bstrncat(buf, "empty,", sizeof(buf));
427 if (rec->state & REC_NO_MATCH) {
428 bstrncat(buf, "Nomatch,", sizeof(buf));
430 if (rec->state & REC_CONTINUATION) {
431 bstrncat(buf, "cont,", sizeof(buf));
434 buf[strlen(buf)-1] = 0;