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 three of the GNU Affero 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 Affero 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 Kern Sibbald.
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 = 500;
58 * This subroutine reads all the records and passes them back to your
59 * callback routine (also mount routine at EOM).
60 * You must not change any values in the DEV_RECORD packet
62 bool read_records(DCR *dcr,
63 bool record_cb(DCR *dcr, DEV_RECORD *rec),
64 bool mount_cb(DCR *dcr))
67 DEVICE *dev = dcr->dev;
68 DEV_BLOCK *block = dcr->block;
69 DEV_RECORD *rec = NULL;
73 SESSION_LABEL sessrec;
74 dlist *recs; /* linked list of rec packets open */
76 recs = New(dlist(rec, &rec->link));
77 position_to_first_file(jcr, dcr);
78 jcr->mount_next_volume = false;
80 for ( ; ok && !done; ) {
81 if (job_canceled(jcr)) {
85 if (!read_block_from_device(dcr, CHECK_BLOCK_NUMBERS)) {
87 DEV_RECORD *trec = new_record();
88 Jmsg(jcr, M_INFO, 0, _("End of Volume at file %u on device %s, Volume \"%s\"\n"),
89 dev->file, dev->print_name(), dcr->VolumeName);
90 volume_unused(dcr); /* mark volume unused */
92 Jmsg(jcr, M_INFO, 0, _("End of all volumes.\n"));
93 ok = false; /* Stop everything */
95 * Create EOT Label so that Media record may
96 * be properly updated because this is the last
99 trec->FileIndex = EOT_LABEL;
100 trec->File = dev->file;
101 ok = record_cb(dcr, trec);
103 if (jcr->mount_next_volume) {
104 jcr->mount_next_volume = false;
109 jcr->mount_next_volume = false;
111 * The Device can change at the end of a tape, so refresh it
112 * and the block from the dcr.
117 * We just have a new tape up, now read the label (first record)
118 * and pass it off to the callback routine, then continue
119 * most likely reading the previous record.
121 read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
122 read_record_from_block(dcr, block, trec);
123 handle_session_record(dev, trec, &sessrec);
124 ok = record_cb(dcr, trec);
126 position_to_first_file(jcr, dcr);
127 /* After reading label, we must read first data block */
130 } else if (dev->at_eof()) {
142 Jmsg(jcr, M_INFO, 0, _("End of %s %u on device %s, Volume \"%s\"\n"),
143 fp, fp_num, dev->print_name(), dcr->VolumeName);
146 Dmsg3(200, "End of file %u on device %s, Volume \"%s\"\n",
147 dev->file, dev->print_name(), dcr->VolumeName);
149 } else if (dev->is_short_block()) {
150 Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
153 /* I/O error or strange end of tape */
154 display_tape_error_status(jcr, dev);
155 if (forge_on || jcr->ignore_label_errors) {
156 dev->fsr(1); /* try skipping bad record */
157 Pmsg0(000, _("Did fsr in attemp to skip bad record.\n"));
158 continue; /* try to continue */
160 ok = false; /* stop everything */
164 Dmsg2(dbglvl, "Read new block at pos=%u:%u\n", dev->file, dev->block_num);
165 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
166 /* this does not stop when file/block are too big */
167 if (!match_bsr_block(jcr->bsr, block)) {
168 if (try_repositioning(jcr, rec, dcr)) {
169 break; /* get next volume */
171 continue; /* skip this record */
176 * Get a new record for each Job as defined by
177 * VolSessionId and VolSessionTime
180 foreach_dlist(rec, recs) {
181 if (rec->VolSessionId == block->VolSessionId &&
182 rec->VolSessionTime == block->VolSessionTime) {
190 Dmsg3(dbglvl, "New record for state=%s SI=%d ST=%d\n",
191 rec_state_to_str(rec),
192 block->VolSessionId, block->VolSessionTime);
194 Dmsg3(dbglvl, "Before read rec loop. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
195 block->BlockNumber, rec->remainder);
198 Dmsg1(dbglvl, "Block %s empty\n", is_block_empty(rec)?"is":"NOT");
199 for (rec->state=0; ok && !is_block_empty(rec); ) {
200 if (!read_record_from_block(dcr, block, rec)) {
201 Dmsg3(400, "!read-break. state=%s blk=%d rem=%d\n", rec_state_to_str(rec),
202 block->BlockNumber, rec->remainder);
205 Dmsg5(dbglvl, "read-OK. state=%s blk=%d rem=%d file:block=%u:%u\n",
206 rec_state_to_str(rec), block->BlockNumber, rec->remainder,
207 dev->file, dev->block_num);
209 * At this point, we have at least a record header.
210 * Now decide if we want this record or not, but remember
211 * before accessing the record, we may need to read again to
215 Dmsg6(dbglvl, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
216 rec_state_to_str(rec), block->BlockNumber,
217 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
219 if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
220 Dmsg0(40, "Get EOM LABEL\n");
221 break; /* yes, get out */
224 /* Some sort of label? */
225 if (rec->FileIndex < 0) {
226 handle_session_record(dev, rec, &sessrec);
228 /* We just check block FI and FT not FileIndex */
229 rec->match_stat = match_bsr_block(jcr->bsr, block);
234 * Note, we pass *all* labels to the callback routine. If
235 * he wants to know if they matched the bsr, then he must
236 * check the match_stat in the record */
237 ok = record_cb(dcr, rec);
240 * If this is the end of the Session (EOS) for this record
241 * we can remove the record. Note, there is a separate
242 * record to read each session. If a new session is seen
243 * a new record will be created at approx line 157 above.
244 * However, it seg faults in the for line at lineno 196.
246 if (rec->FileIndex == EOS_LABEL) {
247 Dmsg2(dbglvl, "Remove EOS rec. SI=%d ST=%d\n", rec->VolSessionId,
248 rec->VolSessionTime);
254 } /* end if label record */
260 rec->match_stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec, jcr);
261 if (rec->match_stat == -1) { /* no more possible matches */
262 done = true; /* all items found, stop */
263 Dmsg2(dbglvl, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
265 } else if (rec->match_stat == 0) { /* no match */
266 Dmsg4(dbglvl, "BSR no match: clear rem=%d FI=%d before set_eof pos %u:%u\n",
267 rec->remainder, rec->FileIndex, dev->file, dev->block_num);
269 rec->state &= ~REC_PARTIAL_RECORD;
270 if (try_repositioning(jcr, rec, dcr)) {
273 continue; /* we don't want record, read next one */
276 dcr->VolLastIndex = rec->FileIndex; /* let caller know where we are */
277 if (is_partial_record(rec)) {
278 Dmsg6(dbglvl, "Partial, break. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
279 rec_state_to_str(rec), block->BlockNumber,
280 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
281 break; /* read second part of record */
284 Dmsg6(dbglvl, "OK callback. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
285 rec_state_to_str(rec), block->BlockNumber,
286 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
287 ok = record_cb(dcr, rec);
289 * If we have a digest stream, we check to see if we have
290 * finished the current bsr, and if so, repositioning will
293 if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
294 Dmsg3(dbglvl, "Have digest FI=%u before bsr check pos %u:%u\n", rec->FileIndex,
295 dev->file, dev->block_num);
296 if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
297 Dmsg2(dbglvl, "This bsr done, break pos %u:%u\n",
298 dev->file, dev->block_num);
301 Dmsg2(900, "After is_bsr_done pos %u:%u\n", dev->file, dev->block_num);
303 } /* end for loop over records */
304 Dmsg2(dbglvl, "After end recs in block. pos=%u:%u\n", dev->file, dev->block_num);
305 } /* end for loop over blocks */
306 // Dmsg2(dbglvl, "Position=(file:block) %u:%u\n", dev->file, dev->block_num);
308 /* Walk down list and free all remaining allocated recs */
309 while (!recs->empty()) {
310 rec = (DEV_RECORD *)recs->first();
315 print_block_read_errors(jcr, block);
320 * See if we can reposition.
321 * Returns: true if at end of volume
324 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr)
327 DEVICE *dev = dcr->dev;
329 bsr = find_next_bsr(jcr->bsr, dev);
330 if (bsr == NULL && jcr->bsr->mount_next_volume) {
331 Dmsg0(dbglvl, "Would mount next volume here\n");
332 Dmsg2(dbglvl, "Current postion (file:block) %u:%u\n",
333 dev->file, dev->block_num);
334 jcr->bsr->mount_next_volume = false;
335 if (!dev->at_eot()) {
336 /* Set EOT flag to force mount of next Volume */
337 jcr->mount_next_volume = true;
345 * ***FIXME*** gross kludge to make disk seeking work. Remove
346 * when find_next_bsr() is fixed not to return a bsr already
349 uint32_t block, file;
350 /* TODO: use dev->file_addr ? */
351 uint64_t dev_addr = (((uint64_t) dev->file)<<32) | dev->block_num;
352 uint64_t bsr_addr = get_bsr_start_addr(bsr, &file, &block);
354 if (dev_addr > bsr_addr) {
357 Dmsg4(10, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
358 dev->file, dev->block_num, file, block);
359 dev->reposition(dcr, file, block);
366 * Position to the first file on this volume
368 static BSR *position_to_first_file(JCR *jcr, DCR *dcr)
371 DEVICE *dev = dcr->dev;
372 uint32_t file, block;
374 * Now find and position to first file and block
378 jcr->bsr->reposition = true; /* force repositioning */
379 bsr = find_next_bsr(jcr->bsr, dev);
381 if (get_bsr_start_addr(bsr, &file, &block) > 0) {
382 Jmsg(jcr, M_INFO, 0, _("Forward spacing Volume \"%s\" to file:block %u:%u.\n"),
383 dev->VolHdr.VolumeName, file, block);
384 dev->reposition(dcr, file, block);
391 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
396 memset(sessrec, 0, sizeof(sessrec));
397 switch (rec->FileIndex) {
399 rtype = _("Fresh Volume Label");
402 rtype = _("Volume Label");
403 unser_volume_label(dev, rec);
406 rtype = _("Begin Session");
407 unser_session_label(sessrec, rec);
410 rtype = _("End Session");
413 rtype = _("End of Media");
416 bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
420 Dmsg5(dbglvl, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
421 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
425 static char *rec_state_to_str(DEV_RECORD *rec)
427 static char buf[200];
429 if (rec->state & REC_NO_HEADER) {
430 bstrncat(buf, "Nohdr,", sizeof(buf));
432 if (is_partial_record(rec)) {
433 bstrncat(buf, "partial,", sizeof(buf));
435 if (rec->state & REC_BLOCK_EMPTY) {
436 bstrncat(buf, "empty,", sizeof(buf));
438 if (rec->state & REC_NO_MATCH) {
439 bstrncat(buf, "Nomatch,", sizeof(buf));
441 if (rec->state & REC_CONTINUATION) {
442 bstrncat(buf, "cont,", sizeof(buf));
445 buf[strlen(buf)-1] = 0;