2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2010 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
46 /* Forward referenced functions */
47 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
48 static BSR *position_to_first_file(JCR *jcr, DCR *dcr);
49 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr);
51 static char *rec_state_to_str(DEV_RECORD *rec);
54 static const int dbglvl = 500;
55 static const int no_FileIndex = -999999;
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;
71 int32_t lastFileIndex;
74 SESSION_LABEL sessrec;
75 dlist *recs; /* linked list of rec packets open */
77 recs = New(dlist(rec, &rec->link));
78 position_to_first_file(jcr, dcr);
79 jcr->mount_next_volume = false;
81 for ( ; ok && !done; ) {
82 if (job_canceled(jcr)) {
86 if (!read_block_from_device(dcr, CHECK_BLOCK_NUMBERS)) {
88 DEV_RECORD *trec = new_record();
89 Jmsg(jcr, M_INFO, 0, _("End of Volume at file %u on device %s, Volume \"%s\"\n"),
90 dev->file, dev->print_name(), dcr->VolumeName);
91 volume_unused(dcr); /* mark volume unused */
93 Jmsg(jcr, M_INFO, 0, _("End of all volumes.\n"));
94 ok = false; /* Stop everything */
96 * Create EOT Label so that Media record may
97 * be properly updated because this is the last
100 trec->FileIndex = EOT_LABEL;
101 trec->File = dev->file;
102 ok = record_cb(dcr, trec);
104 if (jcr->mount_next_volume) {
105 jcr->mount_next_volume = false;
110 jcr->mount_next_volume = false;
112 * The Device can change at the end of a tape, so refresh it
113 * and the block from the dcr.
118 * We just have a new tape up, now read the label (first record)
119 * and pass it off to the callback routine, then continue
120 * most likely reading the previous record.
122 read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
123 read_record_from_block(dcr, block, trec);
124 handle_session_record(dev, trec, &sessrec);
125 ok = record_cb(dcr, trec);
127 position_to_first_file(jcr, dcr);
128 /* After reading label, we must read first data block */
131 } else if (dev->at_eof()) {
143 Jmsg(jcr, M_INFO, 0, _("End of %s %u on device %s, Volume \"%s\"\n"),
144 fp, fp_num, dev->print_name(), dcr->VolumeName);
147 Dmsg3(200, "End of file %u on device %s, Volume \"%s\"\n",
148 dev->file, dev->print_name(), dcr->VolumeName);
150 } else if (dev->is_short_block()) {
151 Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
154 /* I/O error or strange end of tape */
155 display_tape_error_status(jcr, dev);
156 if (forge_on || jcr->ignore_label_errors) {
157 dev->fsr(1); /* try skipping bad record */
158 Pmsg0(000, _("Did fsr in attemp to skip bad record.\n"));
159 continue; /* try to continue */
161 ok = false; /* stop everything */
165 Dmsg2(dbglvl, "Read new block at pos=%u:%u\n", dev->file, dev->block_num);
166 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
167 /* this does not stop when file/block are too big */
168 if (!match_bsr_block(jcr->bsr, block)) {
169 if (try_repositioning(jcr, rec, dcr)) {
170 break; /* get next volume */
172 continue; /* skip this record */
177 * Get a new record for each Job as defined by
178 * VolSessionId and VolSessionTime
181 foreach_dlist(rec, recs) {
182 if (rec->VolSessionId == block->VolSessionId &&
183 rec->VolSessionTime == block->VolSessionTime) {
191 Dmsg3(dbglvl, "New record for state=%s SI=%d ST=%d\n",
192 rec_state_to_str(rec),
193 block->VolSessionId, block->VolSessionTime);
195 Dmsg3(dbglvl, "Before read rec loop. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
196 block->BlockNumber, rec->remainder);
199 lastFileIndex = no_FileIndex;
200 Dmsg1(dbglvl, "Block %s empty\n", is_block_empty(rec)?"is":"NOT");
201 for (rec->state=0; ok && !is_block_empty(rec); ) {
202 if (!read_record_from_block(dcr, block, rec)) {
203 Dmsg3(400, "!read-break. state=%s blk=%d rem=%d\n", rec_state_to_str(rec),
204 block->BlockNumber, rec->remainder);
207 Dmsg5(dbglvl, "read-OK. state=%s blk=%d rem=%d file:block=%u:%u\n",
208 rec_state_to_str(rec), block->BlockNumber, rec->remainder,
209 dev->file, dev->block_num);
211 * At this point, we have at least a record header.
212 * Now decide if we want this record or not, but remember
213 * before accessing the record, we may need to read again to
217 Dmsg6(dbglvl, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
218 rec_state_to_str(rec), block->BlockNumber,
219 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
221 if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
222 Dmsg0(40, "Get EOM LABEL\n");
223 break; /* yes, get out */
226 /* Some sort of label? */
227 if (rec->FileIndex < 0) {
228 handle_session_record(dev, rec, &sessrec);
230 /* We just check block FI and FT not FileIndex */
231 rec->match_stat = match_bsr_block(jcr->bsr, block);
236 * Note, we pass *all* labels to the callback routine. If
237 * he wants to know if they matched the bsr, then he must
238 * check the match_stat in the record */
239 ok = record_cb(dcr, rec);
242 * If this is the end of the Session (EOS) for this record
243 * we can remove the record. Note, there is a separate
244 * record to read each session. If a new session is seen
245 * a new record will be created at approx line 157 above.
246 * However, it seg faults in the for line at lineno 196.
248 if (rec->FileIndex == EOS_LABEL) {
249 Dmsg2(dbglvl, "Remove EOS rec. SI=%d ST=%d\n", rec->VolSessionId,
250 rec->VolSessionTime);
256 } /* end if label record */
262 rec->match_stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec, jcr);
263 if (rec->match_stat == -1) { /* no more possible matches */
264 done = true; /* all items found, stop */
265 Dmsg2(dbglvl, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
267 } else if (rec->match_stat == 0) { /* no match */
268 Dmsg4(dbglvl, "BSR no match: clear rem=%d FI=%d before set_eof pos %u:%u\n",
269 rec->remainder, rec->FileIndex, dev->file, dev->block_num);
271 rec->state &= ~REC_PARTIAL_RECORD;
272 if (try_repositioning(jcr, rec, dcr)) {
275 continue; /* we don't want record, read next one */
278 dcr->VolLastIndex = rec->FileIndex; /* let caller know where we are */
279 if (is_partial_record(rec)) {
280 Dmsg6(dbglvl, "Partial, break. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
281 rec_state_to_str(rec), block->BlockNumber,
282 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
283 break; /* read second part of record */
286 Dmsg6(dbglvl, "OK callback. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
287 rec_state_to_str(rec), block->BlockNumber,
288 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
289 if (lastFileIndex != no_FileIndex && lastFileIndex != rec->FileIndex) {
290 if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
291 Dmsg2(dbglvl, "This bsr done, break pos %u:%u\n",
292 dev->file, dev->block_num);
295 Dmsg2(dbglvl, "==== inside LastIndex=%d FileIndex=%d\n", lastFileIndex, rec->FileIndex);
297 Dmsg2(dbglvl, "==== LastIndex=%d FileIndex=%d\n", lastFileIndex, rec->FileIndex);
298 lastFileIndex = rec->FileIndex;
299 ok = record_cb(dcr, rec);
302 * If we have a digest stream, we check to see if we have
303 * finished the current bsr, and if so, repositioning will
306 if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
307 Dmsg3(dbglvl, "=== Have digest FI=%u before bsr check pos %u:%u\n", rec->FileIndex,
308 dev->file, dev->block_num);
309 if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
310 Dmsg1(dbglvl, "==== BSR done at FI=%d\n", rec->FileIndex);
311 Dmsg2(dbglvl, "This bsr done, break pos %u:%u\n",
312 dev->file, dev->block_num);
315 Dmsg2(900, "After is_bsr_done pos %u:%u\n", dev->file, dev->block_num);
318 } /* end for loop over records */
319 Dmsg2(dbglvl, "After end recs in block. pos=%u:%u\n", dev->file, dev->block_num);
320 } /* end for loop over blocks */
321 // Dmsg2(dbglvl, "Position=(file:block) %u:%u\n", dev->file, dev->block_num);
323 /* Walk down list and free all remaining allocated recs */
324 while (!recs->empty()) {
325 rec = (DEV_RECORD *)recs->first();
330 print_block_read_errors(jcr, block);
335 * See if we can reposition.
336 * Returns: true if at end of volume
339 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr)
342 DEVICE *dev = dcr->dev;
344 bsr = find_next_bsr(jcr->bsr, dev);
345 if (bsr == NULL && jcr->bsr->mount_next_volume) {
346 Dmsg0(dbglvl, "Would mount next volume here\n");
347 Dmsg2(dbglvl, "Current postion (file:block) %u:%u\n",
348 dev->file, dev->block_num);
349 jcr->bsr->mount_next_volume = false;
350 if (!dev->at_eot()) {
351 /* Set EOT flag to force mount of next Volume */
352 jcr->mount_next_volume = true;
360 * ***FIXME*** gross kludge to make disk seeking work. Remove
361 * when find_next_bsr() is fixed not to return a bsr already
364 uint32_t block, file;
365 /* TODO: use dev->file_addr ? */
366 uint64_t dev_addr = (((uint64_t) dev->file)<<32) | dev->block_num;
367 uint64_t bsr_addr = get_bsr_start_addr(bsr, &file, &block);
369 if (dev_addr > bsr_addr) {
372 Dmsg4(10, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
373 dev->file, dev->block_num, file, block);
374 dev->reposition(dcr, file, block);
381 * Position to the first file on this volume
383 static BSR *position_to_first_file(JCR *jcr, DCR *dcr)
386 DEVICE *dev = dcr->dev;
387 uint32_t file, block;
389 * Now find and position to first file and block
393 jcr->bsr->reposition = true; /* force repositioning */
394 bsr = find_next_bsr(jcr->bsr, dev);
396 if (get_bsr_start_addr(bsr, &file, &block) > 0) {
397 Jmsg(jcr, M_INFO, 0, _("Forward spacing Volume \"%s\" to file:block %u:%u.\n"),
398 dev->VolHdr.VolumeName, file, block);
399 dev->reposition(dcr, file, block);
406 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
411 memset(sessrec, 0, sizeof(sessrec));
412 switch (rec->FileIndex) {
414 rtype = _("Fresh Volume Label");
417 rtype = _("Volume Label");
418 unser_volume_label(dev, rec);
421 rtype = _("Begin Session");
422 unser_session_label(sessrec, rec);
425 rtype = _("End Session");
428 rtype = _("End of Media");
431 bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
435 Dmsg5(dbglvl, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
436 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
440 static char *rec_state_to_str(DEV_RECORD *rec)
442 static char buf[200];
444 if (rec->state & REC_NO_HEADER) {
445 bstrncat(buf, "Nohdr,", sizeof(buf));
447 if (is_partial_record(rec)) {
448 bstrncat(buf, "partial,", sizeof(buf));
450 if (rec->state & REC_BLOCK_EMPTY) {
451 bstrncat(buf, "empty,", sizeof(buf));
453 if (rec->state & REC_NO_MATCH) {
454 bstrncat(buf, "Nomatch,", sizeof(buf));
456 if (rec->state & REC_CONTINUATION) {
457 bstrncat(buf, "cont,", sizeof(buf));
460 buf[strlen(buf)-1] = 0;