]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/read_record.c
24Mar06
[bacula/bacula] / bacula / src / stored / read_record.c
1 /*
2  *
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
10  *    any modifications.
11  *
12  *    Kern E. Sibbald, August MMII
13  *
14  *   Version $Id$
15  */
16 /*
17    Copyright (C) 2000-2006 Kern Sibbald
18
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.
23
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.
28
29  */
30
31 #include "bacula.h"
32 #include "stored.h"
33
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);
38 #ifdef DEBUG
39 static char *rec_state_to_str(DEV_RECORD *rec);
40 #endif
41
42 bool read_records(DCR *dcr,
43        bool record_cb(DCR *dcr, DEV_RECORD *rec),
44        bool mount_cb(DCR *dcr))
45 {
46    JCR *jcr = dcr->jcr;
47    DEVICE *dev = dcr->dev;
48    DEV_BLOCK *block = dcr->block;
49    DEV_RECORD *rec = NULL;
50    uint32_t record;
51    bool ok = true;
52    bool done = false;
53    SESSION_LABEL sessrec;
54    dlist *recs;                         /* linked list of rec packets open */
55
56    recs = New(dlist(rec, &rec->link));
57    position_to_first_file(jcr, dev);
58    jcr->mount_next_volume = false;
59
60    for ( ; ok && !done; ) {
61       if (job_canceled(jcr)) {
62          ok = false;
63          break;
64       }
65       if (!read_block_from_device(dcr, CHECK_BLOCK_NUMBERS)) {
66          if (dev->at_eot()) {
67             DEV_RECORD *trec = new_record();
68             Jmsg(jcr, M_INFO, 0, _("End of Volume at file %u on device %s, Volume \"%s\"\n"),
69                  dev->file, dev->print_name(), dcr->VolumeName);
70             if (!mount_cb(dcr)) {
71                Jmsg(jcr, M_INFO, 0, _("End of all volumes.\n"));
72                ok = false;            /* Stop everything */
73                /*
74                 * Create EOT Label so that Media record may
75                 *  be properly updated because this is the last
76                 *  tape.
77                 */
78                trec->FileIndex = EOT_LABEL;
79                trec->File = dev->file;
80                ok = record_cb(dcr, trec);
81                free_record(trec);
82                if (jcr->mount_next_volume) {
83                   jcr->mount_next_volume = false;
84                   dev->clear_eot();
85                }
86                break;
87             }
88             jcr->mount_next_volume = false;
89             /*  
90              * The Device can change at the end of a tape, so refresh it
91              *   from the dcr.
92              */
93             dev = dcr->dev;
94             /*
95              * We just have a new tape up, now read the label (first record)
96              *  and pass it off to the callback routine, then continue
97              *  most likely reading the previous record.
98              */
99             read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
100             read_record_from_block(block, trec);
101             handle_session_record(dev, trec, &sessrec);
102             ok = record_cb(dcr, trec);
103             free_record(trec);
104             position_to_first_file(jcr, dev);
105             /* After reading label, we must read first data block */
106             continue;
107
108          } else if (dev->at_eof()) {
109             if (verbose) {
110                Jmsg(jcr, M_INFO, 0, _("End of file %u  on device %s, Volume \"%s\"\n"),
111                   dev->file, dev->print_name(), dcr->VolumeName);
112             }
113             Dmsg3(200, "End of file %u  on device %s, Volume \"%s\"\n",
114                   dev->file, dev->print_name(), dcr->VolumeName);
115             continue;
116          } else if (dev->is_short_block()) {
117             Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
118             continue;
119          } else {
120             /* I/O error or strange end of tape */
121             display_tape_error_status(jcr, dev);
122             if (forge_on || jcr->ignore_label_errors) {
123                dev->fsr(1);       /* try skipping bad record */
124                Pmsg0(000, _("Did fsr\n"));
125                continue;              /* try to continue */
126             }
127             ok = false;               /* stop everything */
128             break;
129          }
130       }
131       Dmsg2(300, "New block at position=(file:block) %u:%u\n", dev->file, dev->block_num);
132 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
133       /* this does not stop when file/block are too big */
134       if (!match_bsr_block(jcr->bsr, block)) {
135          if (try_repositioning(jcr, rec, dev)) {
136             break;                    /* get next volume */
137          }
138          continue;                    /* skip this record */
139       }
140 #endif
141
142       /*
143        * Get a new record for each Job as defined by
144        *   VolSessionId and VolSessionTime
145        */
146       bool found = false;
147       foreach_dlist(rec, recs) {
148          if (rec->VolSessionId == block->VolSessionId &&
149              rec->VolSessionTime == block->VolSessionTime) {
150             found = true;
151             break;
152           }
153       }
154       if (!found) {
155          rec = new_record();
156          recs->prepend(rec);
157          Dmsg2(300, "New record for SI=%d ST=%d\n",
158              block->VolSessionId, block->VolSessionTime);
159       }
160       Dmsg3(300, "After mount next vol. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
161             block->BlockNumber, rec->remainder);
162       record = 0;
163       rec->state = 0;
164       Dmsg1(300, "Block empty %d\n", is_block_empty(rec));
165       for (rec->state=0; ok && !is_block_empty(rec); ) {
166          if (!read_record_from_block(block, rec)) {
167             Dmsg3(400, "!read-break. state=%s blk=%d rem=%d\n", rec_state_to_str(rec),
168                   block->BlockNumber, rec->remainder);
169             break;
170          }
171          Dmsg5(300, "read-OK. state=%s blk=%d rem=%d file:block=%u:%u\n",
172                  rec_state_to_str(rec), block->BlockNumber, rec->remainder,
173                  dev->file, dev->block_num);
174          /*
175           * At this point, we have at least a record header.
176           *  Now decide if we want this record or not, but remember
177           *  before accessing the record, we may need to read again to
178           *  get all the data.
179           */
180          record++;
181          Dmsg6(300, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
182             rec_state_to_str(rec), block->BlockNumber,
183             rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
184
185          if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
186             Dmsg0(40, "Get EOM LABEL\n");
187             break;                         /* yes, get out */
188          }
189
190          /* Some sort of label? */
191          if (rec->FileIndex < 0) {
192             handle_session_record(dev, rec, &sessrec);
193             ok = record_cb(dcr, rec);
194             if (rec->FileIndex == EOS_LABEL) {
195                Dmsg2(300, "Remove rec. SI=%d ST=%d\n", rec->VolSessionId,
196                   rec->VolSessionTime);
197                recs->remove(rec);
198                free_record(rec);
199             }
200             continue;
201          } /* end if label record */
202
203          /*
204           * Apply BSR filter
205           */
206          if (jcr->bsr) {
207             int stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec);
208             if (stat == -1) { /* no more possible matches */
209                done = true;   /* all items found, stop */
210                Dmsg2(300, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
211                break;
212             } else if (stat == 0) {  /* no match */
213                Dmsg4(300, "BSR no match: clear rem=%d FI=%d before set_eof pos %u:%u\n",
214                   rec->remainder, rec->FileIndex, dev->file, dev->block_num);
215                rec->remainder = 0;
216                rec->state &= ~REC_PARTIAL_RECORD;
217                if (try_repositioning(jcr, rec, dev)) {
218                   break;
219                }
220                continue;              /* we don't want record, read next one */
221             }
222          }
223          dcr->VolLastIndex = rec->FileIndex;  /* let caller know where we are */
224          if (is_partial_record(rec)) {
225             Dmsg6(300, "Partial, break. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
226                rec_state_to_str(rec), block->BlockNumber,
227                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
228             break;                    /* read second part of record */
229          }
230          ok = record_cb(dcr, rec);
231          if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
232             Dmsg3(300, "Done FI=%u before set_eof pos %u:%u\n", rec->FileIndex,
233                   dev->file, dev->block_num);
234             if (match_set_eof(jcr->bsr, rec) && try_repositioning(jcr, rec, dev)) {
235                Dmsg2(300, "Break after match_set_eof pos %u:%u\n",
236                      dev->file, dev->block_num);
237                break;
238             }
239             Dmsg2(300, "After set_eof pos %u:%u\n", dev->file, dev->block_num);
240          }
241       } /* end for loop over records */
242       Dmsg2(300, "After end records position=(file:block) %u:%u\n", dev->file, dev->block_num);
243    } /* end for loop over blocks */
244 // Dmsg2(300, "Position=(file:block) %u:%u\n", dev->file, dev->block_num);
245
246    /* Walk down list and free all remaining allocated recs */
247    while (!recs->empty()) {
248       rec = (DEV_RECORD *)recs->first();
249       recs->remove(rec);
250       free_record(rec);
251    }
252    delete recs;
253    print_block_read_errors(jcr, block);
254    return ok;
255 }
256
257 /*
258  * See if we can reposition.
259  *   Returns:  true  if at end of volume
260  *             false otherwise
261  */
262 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DEVICE *dev)
263 {
264    BSR *bsr;
265    bsr = find_next_bsr(jcr->bsr, dev);
266    if (bsr == NULL && jcr->bsr->mount_next_volume) {
267       Dmsg0(300, "Would mount next volume here\n");
268       Dmsg2(300, "Current postion (file:block) %u:%u\n",
269          dev->file, dev->block_num);
270       jcr->bsr->mount_next_volume = false;
271       if (!dev->at_eot()) {
272          /* Set EOT flag to force mount of next Volume */
273          jcr->mount_next_volume = true;
274          dev->set_eot();
275       }
276       rec->Block = 0;
277       return true;
278    }
279    if (bsr) {
280       if (verbose) {
281          Jmsg(jcr, M_INFO, 0, _("Reposition from (file:block) %u:%u to %u:%u\n"),
282             dev->file, dev->block_num, bsr->volfile->sfile,
283             bsr->volblock->sblock);
284       }
285       Dmsg4(300, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
286             dev->file, dev->block_num, bsr->volfile->sfile,
287             bsr->volblock->sblock);
288       dev->reposition(bsr->volfile->sfile, bsr->volblock->sblock);
289       rec->Block = 0;
290    }
291    return false;
292 }
293
294 /*
295  * Position to the first file on this volume
296  */
297 static BSR *position_to_first_file(JCR *jcr, DEVICE *dev)
298 {
299    BSR *bsr = NULL;
300    /*
301     * Now find and position to first file and block
302     *   on this tape.
303     */
304    if (jcr->bsr) {
305       jcr->bsr->reposition = true;    /* force repositioning */
306       bsr = find_next_bsr(jcr->bsr, dev);
307       if (bsr && (bsr->volfile->sfile != 0 || bsr->volblock->sblock != 0)) {
308          Jmsg(jcr, M_INFO, 0, _("Forward spacing to file:block %u:%u.\n"),
309             bsr->volfile->sfile, bsr->volblock->sblock);
310          Dmsg2(300, "Forward spacing to file:block %u:%u.\n",
311             bsr->volfile->sfile, bsr->volblock->sblock);
312          dev->reposition(bsr->volfile->sfile, bsr->volblock->sblock);
313       }
314    }
315    return bsr;
316 }
317
318
319 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
320 {
321    const char *rtype;
322    char buf[100];
323
324    memset(sessrec, 0, sizeof(sessrec));
325    switch (rec->FileIndex) {
326    case PRE_LABEL:
327       rtype = _("Fresh Volume Label");
328       break;
329    case VOL_LABEL:
330       rtype = _("Volume Label");
331       unser_volume_label(dev, rec);
332       break;
333    case SOS_LABEL:
334       rtype = _("Begin Session");
335       unser_session_label(sessrec, rec);
336       break;
337    case EOS_LABEL:
338       rtype = _("End Session");
339       break;
340    case EOM_LABEL:
341       rtype = _("End of Media");
342       break;
343    default:
344       bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
345       rtype = buf;
346       break;
347    }
348    Dmsg5(300, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
349          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
350 }
351
352 #ifdef DEBUG
353 static char *rec_state_to_str(DEV_RECORD *rec)
354 {
355    static char buf[200];
356    buf[0] = 0;
357    if (rec->state & REC_NO_HEADER) {
358       bstrncat(buf, "Nohdr,", sizeof(buf));
359    }
360    if (is_partial_record(rec)) {
361       bstrncat(buf, "partial,", sizeof(buf));
362    }
363    if (rec->state & REC_BLOCK_EMPTY) {
364       bstrncat(buf, "empty,", sizeof(buf));
365    }
366    if (rec->state & REC_NO_MATCH) {
367       bstrncat(buf, "Nomatch,", sizeof(buf));
368    }
369    if (rec->state & REC_CONTINUATION) {
370       bstrncat(buf, "cont,", sizeof(buf));
371    }
372    if (buf[0]) {
373       buf[strlen(buf)-1] = 0;
374    }
375    return buf;
376 }
377 #endif