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