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;
57 * This subroutine reads all the records and passes them back to your
58 * callback routine (also mount routine at EOM).
59 * You must not change any values in the DEV_RECORD packet
61 bool read_records(DCR *dcr,
62 bool record_cb(DCR *dcr, DEV_RECORD *rec),
63 bool mount_cb(DCR *dcr))
66 DEVICE *dev = dcr->dev;
67 DEV_BLOCK *block = dcr->block;
68 DEV_RECORD *rec = NULL;
72 SESSION_LABEL sessrec;
73 dlist *recs; /* linked list of rec packets open */
75 recs = New(dlist(rec, &rec->link));
76 position_to_first_file(jcr, dcr);
77 jcr->mount_next_volume = false;
79 for ( ; ok && !done; ) {
80 if (job_canceled(jcr)) {
84 if (!read_block_from_device(dcr, CHECK_BLOCK_NUMBERS)) {
86 DEV_RECORD *trec = new_record();
87 Jmsg(jcr, M_INFO, 0, _("End of Volume at file %u on device %s, Volume \"%s\"\n"),
88 dev->file, dev->print_name(), dcr->VolumeName);
89 volume_unused(dcr); /* mark volume unused */
91 Jmsg(jcr, M_INFO, 0, _("End of all volumes.\n"));
92 ok = false; /* Stop everything */
94 * Create EOT Label so that Media record may
95 * be properly updated because this is the last
98 trec->FileIndex = EOT_LABEL;
99 trec->File = dev->file;
100 ok = record_cb(dcr, trec);
102 if (jcr->mount_next_volume) {
103 jcr->mount_next_volume = false;
108 jcr->mount_next_volume = false;
110 * The Device can change at the end of a tape, so refresh it
111 * and the block from the dcr.
116 * We just have a new tape up, now read the label (first record)
117 * and pass it off to the callback routine, then continue
118 * most likely reading the previous record.
120 read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
121 read_record_from_block(dcr, block, trec);
122 handle_session_record(dev, trec, &sessrec);
123 ok = record_cb(dcr, trec);
125 position_to_first_file(jcr, dcr);
126 /* After reading label, we must read first data block */
129 } else if (dev->at_eof()) {
141 Jmsg(jcr, M_INFO, 0, _("End of %s %u on device %s, Volume \"%s\"\n"),
142 fp, fp_num, dev->print_name(), dcr->VolumeName);
145 Dmsg3(200, "End of file %u on device %s, Volume \"%s\"\n",
146 dev->file, dev->print_name(), dcr->VolumeName);
148 } else if (dev->is_short_block()) {
149 Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
152 /* I/O error or strange end of tape */
153 display_tape_error_status(jcr, dev);
154 if (forge_on || jcr->ignore_label_errors) {
155 dev->fsr(1); /* try skipping bad record */
156 Pmsg0(000, _("Did fsr in attemp to skip bad record.\n"));
157 continue; /* try to continue */
159 ok = false; /* stop everything */
163 Dmsg2(dbglvl, "Read new block at pos=%u:%u\n", dev->file, dev->block_num);
164 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
165 /* this does not stop when file/block are too big */
166 if (!match_bsr_block(jcr->bsr, block)) {
167 if (try_repositioning(jcr, rec, dcr)) {
168 break; /* get next volume */
170 continue; /* skip this record */
175 * Get a new record for each Job as defined by
176 * VolSessionId and VolSessionTime
179 foreach_dlist(rec, recs) {
180 if (rec->VolSessionId == block->VolSessionId &&
181 rec->VolSessionTime == block->VolSessionTime) {
189 Dmsg3(dbglvl, "New record for state=%s SI=%d ST=%d\n",
190 rec_state_to_str(rec),
191 block->VolSessionId, block->VolSessionTime);
193 Dmsg3(dbglvl, "Before read rec loop. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
194 block->BlockNumber, rec->remainder);
197 Dmsg1(dbglvl, "Block %s empty\n", is_block_empty(rec)?"is":"NOT");
198 for (rec->state=0; ok && !is_block_empty(rec); ) {
199 if (!read_record_from_block(dcr, block, rec)) {
200 Dmsg3(400, "!read-break. state=%s blk=%d rem=%d\n", rec_state_to_str(rec),
201 block->BlockNumber, rec->remainder);
204 Dmsg5(dbglvl, "read-OK. state=%s blk=%d rem=%d file:block=%u:%u\n",
205 rec_state_to_str(rec), block->BlockNumber, rec->remainder,
206 dev->file, dev->block_num);
208 * At this point, we have at least a record header.
209 * Now decide if we want this record or not, but remember
210 * before accessing the record, we may need to read again to
214 Dmsg6(dbglvl, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
215 rec_state_to_str(rec), block->BlockNumber,
216 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
218 if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
219 Dmsg0(40, "Get EOM LABEL\n");
220 break; /* yes, get out */
223 /* Some sort of label? */
224 if (rec->FileIndex < 0) {
225 handle_session_record(dev, rec, &sessrec);
227 /* We just check block FI and FT not FileIndex */
228 rec->match_stat = match_bsr_block(jcr->bsr, block);
233 * Note, we pass *all* labels to the callback routine. If
234 * he wants to know if they matched the bsr, then he must
235 * check the match_stat in the record */
236 ok = record_cb(dcr, rec);
239 * If this is the end of the Session (EOS) for this record
240 * we can remove the record. Note, there is a separate
241 * record to read each session. If a new session is seen
242 * a new record will be created at approx line 157 above.
243 * However, it seg faults in the for line at lineno 196.
245 if (rec->FileIndex == EOS_LABEL) {
246 Dmsg2(dbglvl, "Remove EOS rec. SI=%d ST=%d\n", rec->VolSessionId,
247 rec->VolSessionTime);
253 } /* end if label record */
259 rec->match_stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec, jcr);
260 if (rec->match_stat == -1) { /* no more possible matches */
261 done = true; /* all items found, stop */
262 Dmsg2(dbglvl, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
264 } else if (rec->match_stat == 0) { /* no match */
265 Dmsg4(dbglvl, "BSR no match: clear rem=%d FI=%d before set_eof pos %u:%u\n",
266 rec->remainder, rec->FileIndex, dev->file, dev->block_num);
268 rec->state &= ~REC_PARTIAL_RECORD;
269 if (try_repositioning(jcr, rec, dcr)) {
272 continue; /* we don't want record, read next one */
275 dcr->VolLastIndex = rec->FileIndex; /* let caller know where we are */
276 if (is_partial_record(rec)) {
277 Dmsg6(dbglvl, "Partial, break. 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 break; /* read second part of record */
283 Dmsg6(dbglvl, "OK callback. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
284 rec_state_to_str(rec), block->BlockNumber,
285 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
286 ok = record_cb(dcr, rec);
288 * If we have a digest stream, we check to see if we have
289 * finished the current bsr, and if so, repositioning will
292 if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
293 Dmsg3(dbglvl, "Have digest FI=%u before bsr check pos %u:%u\n", rec->FileIndex,
294 dev->file, dev->block_num);
295 if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
296 Dmsg2(dbglvl, "This bsr done, break pos %u:%u\n",
297 dev->file, dev->block_num);
300 Dmsg2(900, "After is_bsr_done pos %u:%u\n", dev->file, dev->block_num);
302 } /* end for loop over records */
303 Dmsg2(dbglvl, "After end recs in block. pos=%u:%u\n", dev->file, dev->block_num);
304 } /* end for loop over blocks */
305 // Dmsg2(dbglvl, "Position=(file:block) %u:%u\n", dev->file, dev->block_num);
307 /* Walk down list and free all remaining allocated recs */
308 while (!recs->empty()) {
309 rec = (DEV_RECORD *)recs->first();
314 print_block_read_errors(jcr, block);
319 * See if we can reposition.
320 * Returns: true if at end of volume
323 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr)
326 DEVICE *dev = dcr->dev;
328 bsr = find_next_bsr(jcr->bsr, dev);
329 if (bsr == NULL && jcr->bsr->mount_next_volume) {
330 Dmsg0(dbglvl, "Would mount next volume here\n");
331 Dmsg2(dbglvl, "Current postion (file:block) %u:%u\n",
332 dev->file, dev->block_num);
333 jcr->bsr->mount_next_volume = false;
334 if (!dev->at_eot()) {
335 /* Set EOT flag to force mount of next Volume */
336 jcr->mount_next_volume = true;
344 * ***FIXME*** gross kludge to make disk seeking work. Remove
345 * when find_next_bsr() is fixed not to return a bsr already
348 uint32_t block, file;
349 /* TODO: use dev->file_addr ? */
350 uint64_t dev_addr = (((uint64_t) dev->file)<<32) | dev->block_num;
351 uint64_t bsr_addr = get_bsr_start_addr(bsr, &file, &block);
353 if (dev_addr > bsr_addr) {
356 Dmsg4(10, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
357 dev->file, dev->block_num, file, block);
358 dev->reposition(dcr, file, block);
365 * Position to the first file on this volume
367 static BSR *position_to_first_file(JCR *jcr, DCR *dcr)
370 DEVICE *dev = dcr->dev;
371 uint32_t file, block;
373 * Now find and position to first file and block
377 jcr->bsr->reposition = true; /* force repositioning */
378 bsr = find_next_bsr(jcr->bsr, dev);
380 if (get_bsr_start_addr(bsr, &file, &block) > 0) {
381 Jmsg(jcr, M_INFO, 0, _("Forward spacing Volume \"%s\" to file:block %u:%u.\n"),
382 dev->VolHdr.VolumeName, file, block);
383 dev->reposition(dcr, file, block);
390 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
395 memset(sessrec, 0, sizeof(sessrec));
396 switch (rec->FileIndex) {
398 rtype = _("Fresh Volume Label");
401 rtype = _("Volume Label");
402 unser_volume_label(dev, rec);
405 rtype = _("Begin Session");
406 unser_session_label(sessrec, rec);
409 rtype = _("End Session");
412 rtype = _("End of Media");
415 bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
419 Dmsg5(dbglvl, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
420 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
424 static char *rec_state_to_str(DEV_RECORD *rec)
426 static char buf[200];
428 if (rec->state & REC_NO_HEADER) {
429 bstrncat(buf, "Nohdr,", sizeof(buf));
431 if (is_partial_record(rec)) {
432 bstrncat(buf, "partial,", sizeof(buf));
434 if (rec->state & REC_BLOCK_EMPTY) {
435 bstrncat(buf, "empty,", sizeof(buf));
437 if (rec->state & REC_NO_MATCH) {
438 bstrncat(buf, "Nomatch,", sizeof(buf));
440 if (rec->state & REC_CONTINUATION) {
441 bstrncat(buf, "cont,", sizeof(buf));
444 buf[strlen(buf)-1] = 0;