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 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()) {
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);
144 Dmsg3(200, "End of file %u on device %s, Volume \"%s\"\n",
145 dev->file, dev->print_name(), dcr->VolumeName);
147 } else if (dev->is_short_block()) {
148 Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
151 /* I/O error or strange end of tape */
152 display_tape_error_status(jcr, dev);
153 if (forge_on || jcr->ignore_label_errors) {
154 dev->fsr(1); /* try skipping bad record */
155 Pmsg0(000, _("Did fsr in attemp to skip bad record.\n"));
156 continue; /* try to continue */
158 ok = false; /* stop everything */
162 Dmsg2(dbglvl, "Read new block at pos=%u:%u\n", dev->file, dev->block_num);
163 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
164 /* this does not stop when file/block are too big */
165 if (!match_bsr_block(jcr->bsr, block)) {
166 if (try_repositioning(jcr, rec, dcr)) {
167 break; /* get next volume */
169 continue; /* skip this record */
174 * Get a new record for each Job as defined by
175 * VolSessionId and VolSessionTime
178 foreach_dlist(rec, recs) {
179 if (rec->VolSessionId == block->VolSessionId &&
180 rec->VolSessionTime == block->VolSessionTime) {
188 Dmsg3(dbglvl, "New record for state=%s SI=%d ST=%d\n",
189 rec_state_to_str(rec),
190 block->VolSessionId, block->VolSessionTime);
192 Dmsg3(dbglvl, "Before read rec loop. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
193 block->BlockNumber, rec->remainder);
196 Dmsg1(dbglvl, "Block %s empty\n", is_block_empty(rec)?"is":"NOT");
197 for (rec->state=0; ok && !is_block_empty(rec); ) {
198 if (!read_record_from_block(dcr, block, rec)) {
199 Dmsg3(400, "!read-break. state=%s blk=%d rem=%d\n", rec_state_to_str(rec),
200 block->BlockNumber, rec->remainder);
203 Dmsg5(dbglvl, "read-OK. state=%s blk=%d rem=%d file:block=%u:%u\n",
204 rec_state_to_str(rec), block->BlockNumber, rec->remainder,
205 dev->file, dev->block_num);
207 * At this point, we have at least a record header.
208 * Now decide if we want this record or not, but remember
209 * before accessing the record, we may need to read again to
213 Dmsg6(dbglvl, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
214 rec_state_to_str(rec), block->BlockNumber,
215 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
217 if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
218 Dmsg0(40, "Get EOM LABEL\n");
219 break; /* yes, get out */
222 /* Some sort of label? */
223 if (rec->FileIndex < 0) {
224 handle_session_record(dev, rec, &sessrec);
226 /* We just check block FI and FT not FileIndex */
227 rec->match_stat = match_bsr_block(jcr->bsr, block);
232 * Note, we pass *all* labels to the callback routine. If
233 * he wants to know if they matched the bsr, then he must
234 * check the match_stat in the record */
235 ok = record_cb(dcr, rec);
238 * If this is the end of the Session (EOS) for this record
239 * we can remove the record. Note, there is a separate
240 * record to read each session. If a new session is seen
241 * a new record will be created at approx line 157 above.
242 * However, it seg faults in the for line at lineno 196.
244 if (rec->FileIndex == EOS_LABEL) {
245 Dmsg2(dbglvl, "Remove EOS rec. SI=%d ST=%d\n", rec->VolSessionId,
246 rec->VolSessionTime);
252 } /* end if label record */
258 rec->match_stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec, jcr);
259 if (rec->match_stat == -1) { /* no more possible matches */
260 done = true; /* all items found, stop */
261 Dmsg2(100, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
263 } else if (rec->match_stat == 0) { /* no match */
264 Dmsg4(100, "BSR no match: clear rem=%d FI=%d before set_eof pos %u:%u\n",
265 rec->remainder, rec->FileIndex, dev->file, dev->block_num);
267 rec->state &= ~REC_PARTIAL_RECORD;
268 if (try_repositioning(jcr, rec, dcr)) {
271 continue; /* we don't want record, read next one */
274 dcr->VolLastIndex = rec->FileIndex; /* let caller know where we are */
275 if (is_partial_record(rec)) {
276 Dmsg6(dbglvl, "Partial, break. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
277 rec_state_to_str(rec), block->BlockNumber,
278 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
279 break; /* read second part of record */
282 Dmsg6(dbglvl, "OK callback. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
283 rec_state_to_str(rec), block->BlockNumber,
284 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
285 ok = record_cb(dcr, rec);
287 * If we have a digest stream, we check to see if we have
288 * finished the current bsr, and if so, repositioning will
291 if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
292 Dmsg3(dbglvl, "Have digest FI=%u before bsr check pos %u:%u\n", rec->FileIndex,
293 dev->file, dev->block_num);
294 if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
295 Dmsg2(dbglvl, "This bsr done, break pos %u:%u\n",
296 dev->file, dev->block_num);
299 Dmsg2(900, "After is_bsr_done pos %u:%u\n", dev->file, dev->block_num);
301 } /* end for loop over records */
302 Dmsg2(dbglvl, "After end recs in block. pos=%u:%u\n", dev->file, dev->block_num);
303 } /* end for loop over blocks */
304 // Dmsg2(dbglvl, "Position=(file:block) %u:%u\n", dev->file, dev->block_num);
306 /* Walk down list and free all remaining allocated recs */
307 while (!recs->empty()) {
308 rec = (DEV_RECORD *)recs->first();
313 print_block_read_errors(jcr, block);
318 * See if we can reposition.
319 * Returns: true if at end of volume
322 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr)
325 DEVICE *dev = dcr->dev;
327 bsr = find_next_bsr(jcr->bsr, dev);
328 if (bsr == NULL && jcr->bsr->mount_next_volume) {
329 Dmsg0(dbglvl, "Would mount next volume here\n");
330 Dmsg2(dbglvl, "Current postion (file:block) %u:%u\n",
331 dev->file, dev->block_num);
332 jcr->bsr->mount_next_volume = false;
333 if (!dev->at_eot()) {
334 /* Set EOT flag to force mount of next Volume */
335 jcr->mount_next_volume = true;
343 * ***FIXME*** gross kludge to make disk seeking work. Remove
344 * when find_next_bsr() is fixed not to return a bsr already
347 uint32_t block, file;
348 /* TODO: use dev->file_addr ? */
349 uint64_t dev_addr = (((uint64_t) dev->file)<<32) | dev->block_num;
350 uint64_t bsr_addr = get_bsr_start_addr(bsr, &file, &block);
352 if (dev_addr > bsr_addr) {
356 Jmsg(jcr, M_INFO,0, _("Reposition from (file:block) %u:%u to %u:%u\n"),
357 dev->file, dev->block_num, file, block);
359 Dmsg4(10, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
360 dev->file, dev->block_num, file, block);
361 dev->reposition(dcr, file, block);
368 * Position to the first file on this volume
370 static BSR *position_to_first_file(JCR *jcr, DCR *dcr)
373 DEVICE *dev = dcr->dev;
374 uint32_t file, block;
376 * Now find and position to first file and block
380 jcr->bsr->reposition = true; /* force repositioning */
381 bsr = find_next_bsr(jcr->bsr, dev);
383 if (get_bsr_start_addr(bsr, &file, &block) > 0) {
384 Jmsg(jcr, M_INFO, 0, _("Forward spacing Volume \"%s\" to file:block %u:%u.\n"),
385 dev->VolHdr.VolumeName, file, block);
386 dev->reposition(dcr, file, block);
393 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
398 memset(sessrec, 0, sizeof(sessrec));
399 switch (rec->FileIndex) {
401 rtype = _("Fresh Volume Label");
404 rtype = _("Volume Label");
405 unser_volume_label(dev, rec);
408 rtype = _("Begin Session");
409 unser_session_label(sessrec, rec);
412 rtype = _("End Session");
415 rtype = _("End of Media");
418 bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
422 Dmsg5(dbglvl, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
423 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
427 static char *rec_state_to_str(DEV_RECORD *rec)
429 static char buf[200];
431 if (rec->state & REC_NO_HEADER) {
432 bstrncat(buf, "Nohdr,", sizeof(buf));
434 if (is_partial_record(rec)) {
435 bstrncat(buf, "partial,", sizeof(buf));
437 if (rec->state & REC_BLOCK_EMPTY) {
438 bstrncat(buf, "empty,", sizeof(buf));
440 if (rec->state & REC_NO_MATCH) {
441 bstrncat(buf, "Nomatch,", sizeof(buf));
443 if (rec->state & REC_CONTINUATION) {
444 bstrncat(buf, "cont,", sizeof(buf));
447 buf[strlen(buf)-1] = 0;