2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2008 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);
85 volume_unused(dcr); /* mark volume unused */
87 Jmsg(jcr, M_INFO, 0, _("End of all volumes.\n"));
88 ok = false; /* Stop everything */
90 * Create EOT Label so that Media record may
91 * be properly updated because this is the last
94 trec->FileIndex = EOT_LABEL;
95 trec->File = dev->file;
96 ok = record_cb(dcr, trec);
98 if (jcr->mount_next_volume) {
99 jcr->mount_next_volume = false;
104 jcr->mount_next_volume = false;
106 * The Device can change at the end of a tape, so refresh it
107 * and the block from the dcr.
112 * We just have a new tape up, now read the label (first record)
113 * and pass it off to the callback routine, then continue
114 * most likely reading the previous record.
116 read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
117 read_record_from_block(dcr, block, trec);
118 handle_session_record(dev, trec, &sessrec);
119 ok = record_cb(dcr, trec);
121 position_to_first_file(jcr, dcr);
122 /* After reading label, we must read first data block */
125 } else if (dev->at_eof()) {
136 Jmsg(jcr, M_INFO, 0, _("End of %s %u on device %s, Volume \"%s\"\n"),
137 fp, fp_num, dev->print_name(), dcr->VolumeName);
139 Dmsg3(200, "End of file %u on device %s, Volume \"%s\"\n",
140 dev->file, dev->print_name(), dcr->VolumeName);
142 } else if (dev->is_short_block()) {
143 Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
146 /* I/O error or strange end of tape */
147 display_tape_error_status(jcr, dev);
148 if (forge_on || jcr->ignore_label_errors) {
149 dev->fsr(1); /* try skipping bad record */
150 Pmsg0(000, _("Did fsr in attemp to skip bad record.\n"));
151 continue; /* try to continue */
153 ok = false; /* stop everything */
157 Dmsg2(dbglvl, "Read new block at pos=%u:%u\n", dev->file, dev->block_num);
158 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
159 /* this does not stop when file/block are too big */
160 if (!match_bsr_block(jcr->bsr, block)) {
161 if (try_repositioning(jcr, rec, dcr)) {
162 break; /* get next volume */
164 continue; /* skip this record */
169 * Get a new record for each Job as defined by
170 * VolSessionId and VolSessionTime
173 foreach_dlist(rec, recs) {
174 if (rec->VolSessionId == block->VolSessionId &&
175 rec->VolSessionTime == block->VolSessionTime) {
183 Dmsg3(dbglvl, "New record for state=%s SI=%d ST=%d\n",
184 rec_state_to_str(rec),
185 block->VolSessionId, block->VolSessionTime);
187 Dmsg3(dbglvl, "Before read rec loop. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
188 block->BlockNumber, rec->remainder);
191 Dmsg1(dbglvl, "Block %s empty\n", is_block_empty(rec)?"is":"NOT");
192 for (rec->state=0; ok && !is_block_empty(rec); ) {
193 if (!read_record_from_block(dcr, block, rec)) {
194 Dmsg3(400, "!read-break. state=%s blk=%d rem=%d\n", rec_state_to_str(rec),
195 block->BlockNumber, rec->remainder);
198 Dmsg5(dbglvl, "read-OK. state=%s blk=%d rem=%d file:block=%u:%u\n",
199 rec_state_to_str(rec), block->BlockNumber, rec->remainder,
200 dev->file, dev->block_num);
202 * At this point, we have at least a record header.
203 * Now decide if we want this record or not, but remember
204 * before accessing the record, we may need to read again to
208 Dmsg6(dbglvl, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
209 rec_state_to_str(rec), block->BlockNumber,
210 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
212 if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
213 Dmsg0(40, "Get EOM LABEL\n");
214 break; /* yes, get out */
217 /* Some sort of label? */
218 if (rec->FileIndex < 0) {
219 handle_session_record(dev, rec, &sessrec);
221 /* We just check block FI and FT not FileIndex */
222 rec->match_stat = match_bsr_block(jcr->bsr, block);
227 * Note, we pass *all* labels to the callback routine. If
228 * he wants to know if they matched the bsr, then he must
229 * check the match_stat in the record */
230 ok = record_cb(dcr, rec);
233 * If this is the end of the Session (EOS) for this record
234 * we can remove the record. Note, there is a separate
235 * record to read each session. If a new session is seen
236 * a new record will be created at approx line 157 above.
238 * This code causes a seg fault in the enclosing for() loop.
240 if (rec->FileIndex == EOS_LABEL) {
241 Dmsg2(dbglvl, "Remove EOS rec. SI=%d ST=%d\n", rec->VolSessionId,
242 rec->VolSessionTime);
248 } /* end if label record */
254 rec->match_stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec);
255 if (rec->match_stat == -1) { /* no more possible matches */
256 done = true; /* all items found, stop */
257 Dmsg2(dbglvl, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
259 } else if (rec->match_stat == 0) { /* no match */
260 Dmsg4(dbglvl, "BSR no match: clear rem=%d FI=%d before set_eof pos %u:%u\n",
261 rec->remainder, rec->FileIndex, dev->file, dev->block_num);
263 rec->state &= ~REC_PARTIAL_RECORD;
264 if (try_repositioning(jcr, rec, dcr)) {
267 continue; /* we don't want record, read next one */
270 dcr->VolLastIndex = rec->FileIndex; /* let caller know where we are */
271 if (is_partial_record(rec)) {
272 Dmsg6(dbglvl, "Partial, break. 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 break; /* read second part of record */
277 Dmsg6(dbglvl, "OK callback. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
278 rec_state_to_str(rec), block->BlockNumber,
279 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
280 ok = record_cb(dcr, rec);
282 * If we have a digest stream, we check to see if we have
283 * finished the current bsr, and if so, repositioning will
286 if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
287 Dmsg3(dbglvl, "Have digest FI=%u before bsr check pos %u:%u\n", rec->FileIndex,
288 dev->file, dev->block_num);
289 if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
290 Dmsg2(dbglvl, "This bsr done, break pos %u:%u\n",
291 dev->file, dev->block_num);
294 Dmsg2(900, "After is_bsr_done pos %u:%u\n", dev->file, dev->block_num);
296 } /* end for loop over records */
297 Dmsg2(dbglvl, "After end recs in block. pos=%u:%u\n", dev->file, dev->block_num);
298 } /* end for loop over blocks */
299 // Dmsg2(dbglvl, "Position=(file:block) %u:%u\n", dev->file, dev->block_num);
301 /* Walk down list and free all remaining allocated recs */
302 while (!recs->empty()) {
303 rec = (DEV_RECORD *)recs->first();
308 print_block_read_errors(jcr, block);
313 * See if we can reposition.
314 * Returns: true if at end of volume
317 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr)
320 DEVICE *dev = dcr->dev;
322 bsr = find_next_bsr(jcr->bsr, dev);
323 if (bsr == NULL && jcr->bsr->mount_next_volume) {
324 Dmsg0(dbglvl, "Would mount next volume here\n");
325 Dmsg2(dbglvl, "Current postion (file:block) %u:%u\n",
326 dev->file, dev->block_num);
327 jcr->bsr->mount_next_volume = false;
328 if (!dev->at_eot()) {
329 /* Set EOT flag to force mount of next Volume */
330 jcr->mount_next_volume = true;
338 * ***FIXME*** gross kludge to make disk seeking work. Remove
339 * when find_next_bsr() is fixed not to return a bsr already
342 if (dev->file > bsr->volfile->sfile ||
343 (dev->file == bsr->volfile->sfile && dev->block_num > bsr->volblock->sblock)) {
347 Jmsg(jcr, M_INFO, 0, _("Reposition from (file:block) %u:%u to %u:%u\n"),
348 dev->file, dev->block_num, bsr->volfile->sfile,
349 bsr->volblock->sblock);
351 Dmsg4(10, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
352 dev->file, dev->block_num, bsr->volfile->sfile,
353 bsr->volblock->sblock);
354 dev->reposition(dcr, bsr->volfile->sfile, bsr->volblock->sblock);
361 * Position to the first file on this volume
363 static BSR *position_to_first_file(JCR *jcr, DCR *dcr)
366 DEVICE *dev = dcr->dev;
368 * Now find and position to first file and block
372 jcr->bsr->reposition = true; /* force repositioning */
373 bsr = find_next_bsr(jcr->bsr, dev);
374 if (bsr && (bsr->volfile->sfile != 0 || bsr->volblock->sblock != 0)) {
375 Jmsg(jcr, M_INFO, 0, _("Forward spacing Volume \"%s\" to file:block %u:%u.\n"),
376 dev->VolHdr.VolumeName,
377 bsr->volfile->sfile, bsr->volblock->sblock);
378 dev->reposition(dcr, bsr->volfile->sfile, bsr->volblock->sblock);
385 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
390 memset(sessrec, 0, sizeof(sessrec));
391 switch (rec->FileIndex) {
393 rtype = _("Fresh Volume Label");
396 rtype = _("Volume Label");
397 unser_volume_label(dev, rec);
400 rtype = _("Begin Session");
401 unser_session_label(sessrec, rec);
404 rtype = _("End Session");
407 rtype = _("End of Media");
410 bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
414 Dmsg5(dbglvl, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
415 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
419 static char *rec_state_to_str(DEV_RECORD *rec)
421 static char buf[200];
423 if (rec->state & REC_NO_HEADER) {
424 bstrncat(buf, "Nohdr,", sizeof(buf));
426 if (is_partial_record(rec)) {
427 bstrncat(buf, "partial,", sizeof(buf));
429 if (rec->state & REC_BLOCK_EMPTY) {
430 bstrncat(buf, "empty,", sizeof(buf));
432 if (rec->state & REC_NO_MATCH) {
433 bstrncat(buf, "Nomatch,", sizeof(buf));
435 if (rec->state & REC_CONTINUATION) {
436 bstrncat(buf, "cont,", sizeof(buf));
439 buf[strlen(buf)-1] = 0;