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