3 * This routine provides a routine that will handle all
4 * the gory little details of reading a record from a Bacula
5 * archive. It uses a callback to pass you each record in turn,
6 * as well as a callback for mounting the next tape. It takes
7 * care of reading blocks, applying the bsr, ...
8 * Note, this routine is really the heart of the restore routines,
9 * and we are *really* bit pushing here so be careful about making
12 * Kern E. Sibbald, August MMII
17 Copyright (C) 2000-2006 Kern Sibbald
19 This program is free software; you can redistribute it and/or
20 modify it under the terms of the GNU General Public License
21 version 2 as amended with additional clauses defined in the
22 file LICENSE in the main source directory.
24 This program is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 the file LICENSE for additional details.
34 /* Forward referenced functions */
35 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
36 static BSR *position_to_first_file(JCR *jcr, DEVICE *dev);
37 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DEVICE *dev);
39 static char *rec_state_to_str(DEV_RECORD *rec);
42 static const int dbglvl = 1000;
44 bool read_records(DCR *dcr,
45 bool record_cb(DCR *dcr, DEV_RECORD *rec),
46 bool mount_cb(DCR *dcr))
49 DEVICE *dev = dcr->dev;
50 DEV_BLOCK *block = dcr->block;
51 DEV_RECORD *rec = NULL;
55 SESSION_LABEL sessrec;
56 dlist *recs; /* linked list of rec packets open */
58 recs = New(dlist(rec, &rec->link));
59 position_to_first_file(jcr, dev);
60 jcr->mount_next_volume = false;
62 for ( ; ok && !done; ) {
63 if (job_canceled(jcr)) {
67 if (!read_block_from_device(dcr, CHECK_BLOCK_NUMBERS)) {
69 DEV_RECORD *trec = new_record();
70 Jmsg(jcr, M_INFO, 0, _("End of Volume at file %u on device %s, Volume \"%s\"\n"),
71 dev->file, dev->print_name(), dcr->VolumeName);
73 Jmsg(jcr, M_INFO, 0, _("End of all volumes.\n"));
74 ok = false; /* Stop everything */
76 * Create EOT Label so that Media record may
77 * be properly updated because this is the last
80 trec->FileIndex = EOT_LABEL;
81 trec->File = dev->file;
82 ok = record_cb(dcr, trec);
84 if (jcr->mount_next_volume) {
85 jcr->mount_next_volume = false;
90 jcr->mount_next_volume = false;
92 * The Device can change at the end of a tape, so refresh it
97 * We just have a new tape up, now read the label (first record)
98 * and pass it off to the callback routine, then continue
99 * most likely reading the previous record.
101 read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
102 read_record_from_block(block, trec);
103 handle_session_record(dev, trec, &sessrec);
104 ok = record_cb(dcr, trec);
106 position_to_first_file(jcr, dev);
107 /* After reading label, we must read first data block */
110 } else if (dev->at_eof()) {
112 Jmsg(jcr, M_INFO, 0, _("End of file %u on device %s, Volume \"%s\"\n"),
113 dev->file, dev->print_name(), dcr->VolumeName);
115 Dmsg3(200, "End of file %u on device %s, Volume \"%s\"\n",
116 dev->file, dev->print_name(), dcr->VolumeName);
118 } else if (dev->is_short_block()) {
119 Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
122 /* I/O error or strange end of tape */
123 display_tape_error_status(jcr, dev);
124 if (forge_on || jcr->ignore_label_errors) {
125 dev->fsr(1); /* try skipping bad record */
126 Pmsg0(000, _("Did fsr\n"));
127 continue; /* try to continue */
129 ok = false; /* stop everything */
133 Dmsg2(dbglvl, "Read new block at pos=%u:%u\n", dev->file, dev->block_num);
134 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
135 /* this does not stop when file/block are too big */
136 if (!match_bsr_block(jcr->bsr, block)) {
137 if (try_repositioning(jcr, rec, dev)) {
138 break; /* get next volume */
140 continue; /* skip this record */
145 * Get a new record for each Job as defined by
146 * VolSessionId and VolSessionTime
149 foreach_dlist(rec, recs) {
150 if (rec->VolSessionId == block->VolSessionId &&
151 rec->VolSessionTime == block->VolSessionTime) {
159 Dmsg3(dbglvl, "New record for state=%s SI=%d ST=%d\n",
160 rec_state_to_str(rec),
161 block->VolSessionId, block->VolSessionTime);
163 Dmsg3(dbglvl, "Before read rec loop. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
164 block->BlockNumber, rec->remainder);
167 Dmsg1(dbglvl, "Block %s empty\n", is_block_empty(rec)?"is":"NOT");
168 for (rec->state=0; ok && !is_block_empty(rec); ) {
169 if (!read_record_from_block(block, rec)) {
170 Dmsg3(400, "!read-break. state=%s blk=%d rem=%d\n", rec_state_to_str(rec),
171 block->BlockNumber, rec->remainder);
174 Dmsg5(dbglvl, "read-OK. state=%s blk=%d rem=%d file:block=%u:%u\n",
175 rec_state_to_str(rec), block->BlockNumber, rec->remainder,
176 dev->file, dev->block_num);
178 * At this point, we have at least a record header.
179 * Now decide if we want this record or not, but remember
180 * before accessing the record, we may need to read again to
184 Dmsg6(dbglvl, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
185 rec_state_to_str(rec), block->BlockNumber,
186 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
188 if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
189 Dmsg0(40, "Get EOM LABEL\n");
190 break; /* yes, get out */
193 /* Some sort of label? */
194 if (rec->FileIndex < 0) {
195 handle_session_record(dev, rec, &sessrec);
197 /* We just check block FI and FT not FileIndex */
198 rec->match_stat = match_bsr_block(jcr->bsr, block);
203 * Note, we pass *all* labels to the callback routine. If
204 * he wants to know if they matched the bsr, then he must
205 * check the match_stat in the record */
206 ok = record_cb(dcr, rec);
208 * If this is the end of the Session (EOS) for this record
209 * we can remove the record. Note, there is a separate
210 * record to read each session. If a new session is seen
211 * a new record will be created at approx line 157 above.
213 if (rec->FileIndex == EOS_LABEL) {
214 Dmsg2(dbglvl, "Remove EOS rec. SI=%d ST=%d\n", rec->VolSessionId,
215 rec->VolSessionTime);
220 } /* end if label record */
226 rec->match_stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec);
227 if (rec->match_stat == -1) { /* no more possible matches */
228 done = true; /* all items found, stop */
229 Dmsg2(dbglvl, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
231 } else if (rec->match_stat == 0) { /* no match */
232 Dmsg4(dbglvl, "BSR no match: clear rem=%d FI=%d before set_eof pos %u:%u\n",
233 rec->remainder, rec->FileIndex, dev->file, dev->block_num);
235 rec->state &= ~REC_PARTIAL_RECORD;
236 if (try_repositioning(jcr, rec, dev)) {
239 continue; /* we don't want record, read next one */
242 dcr->VolLastIndex = rec->FileIndex; /* let caller know where we are */
243 if (is_partial_record(rec)) {
244 Dmsg6(dbglvl, "Partial, break. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
245 rec_state_to_str(rec), block->BlockNumber,
246 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
247 break; /* read second part of record */
249 ok = record_cb(dcr, rec);
251 * If we have a digest stream, we check to see if we have
252 * finished the current bsr, and if so, repositioning will
255 if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
256 Dmsg3(dbglvl, "Have digest FI=%u before bsr check pos %u:%u\n", rec->FileIndex,
257 dev->file, dev->block_num);
258 if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dev)) {
259 Dmsg2(dbglvl, "This bsr done, break pos %u:%u\n",
260 dev->file, dev->block_num);
263 Dmsg2(900, "After is_bsr_done pos %u:%u\n", dev->file, dev->block_num);
265 } /* end for loop over records */
266 Dmsg2(dbglvl, "After end recs in block. pos=%u:%u\n", dev->file, dev->block_num);
267 } /* end for loop over blocks */
268 // Dmsg2(dbglvl, "Position=(file:block) %u:%u\n", dev->file, dev->block_num);
270 /* Walk down list and free all remaining allocated recs */
271 while (!recs->empty()) {
272 rec = (DEV_RECORD *)recs->first();
277 print_block_read_errors(jcr, block);
282 * See if we can reposition.
283 * Returns: true if at end of volume
286 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DEVICE *dev)
289 bsr = find_next_bsr(jcr->bsr, dev);
290 if (bsr == NULL && jcr->bsr->mount_next_volume) {
291 Dmsg0(dbglvl, "Would mount next volume here\n");
292 Dmsg2(dbglvl, "Current postion (file:block) %u:%u\n",
293 dev->file, dev->block_num);
294 jcr->bsr->mount_next_volume = false;
295 if (!dev->at_eot()) {
296 /* Set EOT flag to force mount of next Volume */
297 jcr->mount_next_volume = true;
305 Jmsg(jcr, M_INFO, 0, _("Reposition from (file:block) %u:%u to %u:%u\n"),
306 dev->file, dev->block_num, bsr->volfile->sfile,
307 bsr->volblock->sblock);
309 Dmsg4(dbglvl, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
310 dev->file, dev->block_num, bsr->volfile->sfile,
311 bsr->volblock->sblock);
312 dev->reposition(bsr->volfile->sfile, bsr->volblock->sblock);
319 * Position to the first file on this volume
321 static BSR *position_to_first_file(JCR *jcr, DEVICE *dev)
325 * Now find and position to first file and block
329 jcr->bsr->reposition = true; /* force repositioning */
330 bsr = find_next_bsr(jcr->bsr, dev);
331 if (bsr && (bsr->volfile->sfile != 0 || bsr->volblock->sblock != 0)) {
332 Jmsg(jcr, M_INFO, 0, _("Forward spacing to file:block %u:%u.\n"),
333 bsr->volfile->sfile, bsr->volblock->sblock);
334 Dmsg2(dbglvl, "Forward spacing to file:block %u:%u.\n",
335 bsr->volfile->sfile, bsr->volblock->sblock);
336 dev->reposition(bsr->volfile->sfile, bsr->volblock->sblock);
343 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
348 memset(sessrec, 0, sizeof(sessrec));
349 switch (rec->FileIndex) {
351 rtype = _("Fresh Volume Label");
354 rtype = _("Volume Label");
355 unser_volume_label(dev, rec);
358 rtype = _("Begin Session");
359 unser_session_label(sessrec, rec);
362 rtype = _("End Session");
365 rtype = _("End of Media");
368 bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
372 Dmsg5(dbglvl, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
373 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
377 static char *rec_state_to_str(DEV_RECORD *rec)
379 static char buf[200];
381 if (rec->state & REC_NO_HEADER) {
382 bstrncat(buf, "Nohdr,", sizeof(buf));
384 if (is_partial_record(rec)) {
385 bstrncat(buf, "partial,", sizeof(buf));
387 if (rec->state & REC_BLOCK_EMPTY) {
388 bstrncat(buf, "empty,", sizeof(buf));
390 if (rec->state & REC_NO_MATCH) {
391 bstrncat(buf, "Nomatch,", sizeof(buf));
393 if (rec->state & REC_CONTINUATION) {
394 bstrncat(buf, "cont,", sizeof(buf));
397 buf[strlen(buf)-1] = 0;