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