]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/read_record.c
kes Correct seg fault when switching tape drives during restore.
[bacula/bacula] / bacula / src / stored / read_record.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2007 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *  This routine provides a routine that will handle all
31  *    the gory little details of reading a record from a Bacula
32  *    archive. It uses a callback to pass you each record in turn,
33  *    as well as a callback for mounting the next tape.  It takes
34  *    care of reading blocks, applying the bsr, ...
35  *    Note, this routine is really the heart of the restore routines,
36  *    and we are *really* bit pushing here so be careful about making
37  *    any modifications.
38  *
39  *    Kern E. Sibbald, August MMII
40  *
41  *   Version $Id$
42  */
43
44 #include "bacula.h"
45 #include "stored.h"
46
47 /* Forward referenced functions */
48 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
49 static BSR *position_to_first_file(JCR *jcr, DCR *dcr);
50 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr);
51 #ifdef DEBUG
52 static char *rec_state_to_str(DEV_RECORD *rec);
53 #endif
54
55 static const int dbglvl = 1000;
56
57 bool read_records(DCR *dcr,
58        bool record_cb(DCR *dcr, DEV_RECORD *rec),
59        bool mount_cb(DCR *dcr))
60 {
61    JCR *jcr = dcr->jcr;
62    DEVICE *dev = dcr->dev;
63    DEV_BLOCK *block = dcr->block;
64    DEV_RECORD *rec = NULL;
65    uint32_t record;
66    bool ok = true;
67    bool done = false;
68    SESSION_LABEL sessrec;
69    dlist *recs;                         /* linked list of rec packets open */
70
71    recs = New(dlist(rec, &rec->link));
72    position_to_first_file(jcr, dcr);
73    jcr->mount_next_volume = false;
74
75    for ( ; ok && !done; ) {
76       if (job_canceled(jcr)) {
77          ok = false;
78          break;
79       }
80       if (!read_block_from_device(dcr, CHECK_BLOCK_NUMBERS)) {
81          if (dev->at_eot()) {
82             DEV_RECORD *trec = new_record();
83             Jmsg(jcr, M_INFO, 0, _("End of Volume at file %u on device %s, Volume \"%s\"\n"),
84                  dev->file, dev->print_name(), dcr->VolumeName);
85             if (!mount_cb(dcr)) {
86                Jmsg(jcr, M_INFO, 0, _("End of all volumes.\n"));
87                ok = false;            /* Stop everything */
88                /*
89                 * Create EOT Label so that Media record may
90                 *  be properly updated because this is the last
91                 *  tape.
92                 */
93                trec->FileIndex = EOT_LABEL;
94                trec->File = dev->file;
95                ok = record_cb(dcr, trec);
96                free_record(trec);
97                if (jcr->mount_next_volume) {
98                   jcr->mount_next_volume = false;
99                   dev->clear_eot();
100                }
101                break;
102             }
103             jcr->mount_next_volume = false;
104             /*  
105              * The Device can change at the end of a tape, so refresh it
106              *   and the block from the dcr.
107              */
108             dev = dcr->dev;
109             block = dcr->block;
110             /*
111              * We just have a new tape up, now read the label (first record)
112              *  and pass it off to the callback routine, then continue
113              *  most likely reading the previous record.
114              */
115             read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
116             read_record_from_block(dcr, block, trec);
117             handle_session_record(dev, trec, &sessrec);
118             ok = record_cb(dcr, trec);
119             free_record(trec);
120             position_to_first_file(jcr, dcr);
121             /* After reading label, we must read first data block */
122             continue;
123
124          } else if (dev->at_eof()) {
125             if (verbose) {
126                char *fp;
127                uint32_t fp_num;
128                if (dev->is_dvd()) {
129                   fp = _("part");
130                   fp_num = dev->part;
131                } else {
132                   fp = _("file");
133                   fp_num = dev->file;
134                }
135                Jmsg(jcr, M_INFO, 0, _("End of %s %u on device %s, Volume \"%s\"\n"),
136                   fp, fp_num, dev->print_name(), dcr->VolumeName);
137             }
138             Dmsg3(200, "End of file %u  on device %s, Volume \"%s\"\n",
139                   dev->file, dev->print_name(), dcr->VolumeName);
140             continue;
141          } else if (dev->is_short_block()) {
142             Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
143             continue;
144          } else {
145             /* I/O error or strange end of tape */
146             display_tape_error_status(jcr, dev);
147             if (forge_on || jcr->ignore_label_errors) {
148                dev->fsr(1);       /* try skipping bad record */
149                Pmsg0(000, _("Did fsr in attemp to skip bad record.\n"));
150                continue;              /* try to continue */
151             }
152             ok = false;               /* stop everything */
153             break;
154          }
155       }
156       Dmsg2(dbglvl, "Read new block at pos=%u:%u\n", dev->file, dev->block_num);
157 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
158       /* this does not stop when file/block are too big */
159       if (!match_bsr_block(jcr->bsr, block)) {
160          if (try_repositioning(jcr, rec, dcr)) {
161             break;                    /* get next volume */
162          }
163          continue;                    /* skip this record */
164       }
165 #endif
166
167       /*
168        * Get a new record for each Job as defined by
169        *   VolSessionId and VolSessionTime
170        */
171       bool found = false;
172       foreach_dlist(rec, recs) {
173          if (rec->VolSessionId == block->VolSessionId &&
174              rec->VolSessionTime == block->VolSessionTime) {
175             found = true;
176             break;
177           }
178       }
179       if (!found) {
180          rec = new_record();
181          recs->prepend(rec);
182          Dmsg3(dbglvl, "New record for state=%s SI=%d ST=%d\n",
183              rec_state_to_str(rec),
184              block->VolSessionId, block->VolSessionTime);
185       }
186       Dmsg3(dbglvl, "Before read rec loop. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
187             block->BlockNumber, rec->remainder);
188       record = 0;
189       rec->state = 0;
190       Dmsg1(dbglvl, "Block %s empty\n", is_block_empty(rec)?"is":"NOT");
191       for (rec->state=0; ok && !is_block_empty(rec); ) {
192          if (!read_record_from_block(dcr, block, rec)) {
193             Dmsg3(400, "!read-break. state=%s blk=%d rem=%d\n", rec_state_to_str(rec),
194                   block->BlockNumber, rec->remainder);
195             break;
196          }
197          Dmsg5(dbglvl, "read-OK. state=%s blk=%d rem=%d file:block=%u:%u\n",
198                  rec_state_to_str(rec), block->BlockNumber, rec->remainder,
199                  dev->file, dev->block_num);
200          /*
201           * At this point, we have at least a record header.
202           *  Now decide if we want this record or not, but remember
203           *  before accessing the record, we may need to read again to
204           *  get all the data.
205           */
206          record++;
207          Dmsg6(dbglvl, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
208             rec_state_to_str(rec), block->BlockNumber,
209             rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
210
211          if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
212             Dmsg0(40, "Get EOM LABEL\n");
213             break;                         /* yes, get out */
214          }
215
216          /* Some sort of label? */
217          if (rec->FileIndex < 0) {
218             handle_session_record(dev, rec, &sessrec);
219             if (jcr->bsr) {
220                /* We just check block FI and FT not FileIndex */
221                rec->match_stat = match_bsr_block(jcr->bsr, block);
222             } else {
223                rec->match_stat = 0;
224             }
225             /*
226              * Note, we pass *all* labels to the callback routine. If
227              *  he wants to know if they matched the bsr, then he must
228              *  check the match_stat in the record */
229             ok = record_cb(dcr, rec);
230             /*
231              * If this is the end of the Session (EOS) for this record
232              *  we can remove the record.  Note, there is a separate
233              *  record to read each session. If a new session is seen
234              *  a new record will be created at approx line 157 above.
235              */
236             if (rec->FileIndex == EOS_LABEL) {
237                Dmsg2(dbglvl, "Remove EOS rec. SI=%d ST=%d\n", rec->VolSessionId,
238                   rec->VolSessionTime);
239                recs->remove(rec);
240                free_record(rec);
241             }
242             continue;
243          } /* end if label record */
244
245          /*
246           * Apply BSR filter
247           */
248          if (jcr->bsr) {
249             rec->match_stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec);
250             if (rec->match_stat == -1) { /* no more possible matches */
251                done = true;   /* all items found, stop */
252                Dmsg2(dbglvl, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
253                break;
254             } else if (rec->match_stat == 0) {  /* no match */
255                Dmsg4(dbglvl, "BSR no match: clear rem=%d FI=%d before set_eof pos %u:%u\n",
256                   rec->remainder, rec->FileIndex, dev->file, dev->block_num);
257                rec->remainder = 0;
258                rec->state &= ~REC_PARTIAL_RECORD;
259                if (try_repositioning(jcr, rec, dcr)) {
260                   break;
261                }
262                continue;              /* we don't want record, read next one */
263             }
264          }
265          dcr->VolLastIndex = rec->FileIndex;  /* let caller know where we are */
266          if (is_partial_record(rec)) {
267             Dmsg6(dbglvl, "Partial, break. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
268                rec_state_to_str(rec), block->BlockNumber,
269                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
270             break;                    /* read second part of record */
271          }
272          Dmsg6(dbglvl, "OK callback. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
273                rec_state_to_str(rec), block->BlockNumber,
274                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
275          ok = record_cb(dcr, rec);
276          /*
277           * If we have a digest stream, we check to see if we have 
278           *  finished the current bsr, and if so, repositioning will
279           *  be turned on.
280           */
281          if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
282             Dmsg3(dbglvl, "Have digest FI=%u before bsr check pos %u:%u\n", rec->FileIndex,
283                   dev->file, dev->block_num);
284             if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
285                Dmsg2(dbglvl, "This bsr done, break pos %u:%u\n",
286                      dev->file, dev->block_num);
287                break;
288             }
289             Dmsg2(900, "After is_bsr_done pos %u:%u\n", dev->file, dev->block_num);
290          }
291       } /* end for loop over records */
292       Dmsg2(dbglvl, "After end recs in block. pos=%u:%u\n", dev->file, dev->block_num);
293    } /* end for loop over blocks */
294 // Dmsg2(dbglvl, "Position=(file:block) %u:%u\n", dev->file, dev->block_num);
295
296    /* Walk down list and free all remaining allocated recs */
297    while (!recs->empty()) {
298       rec = (DEV_RECORD *)recs->first();
299       recs->remove(rec);
300       free_record(rec);
301    }
302    delete recs;
303    print_block_read_errors(jcr, block);
304    return ok;
305 }
306
307 /*
308  * See if we can reposition.
309  *   Returns:  true  if at end of volume
310  *             false otherwise
311  */
312 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr)
313 {
314    BSR *bsr;
315    DEVICE *dev = dcr->dev;
316
317    bsr = find_next_bsr(jcr->bsr, dev);
318    if (bsr == NULL && jcr->bsr->mount_next_volume) {
319       Dmsg0(dbglvl, "Would mount next volume here\n");
320       Dmsg2(dbglvl, "Current postion (file:block) %u:%u\n",
321          dev->file, dev->block_num);
322       jcr->bsr->mount_next_volume = false;
323       if (!dev->at_eot()) {
324          /* Set EOT flag to force mount of next Volume */
325          jcr->mount_next_volume = true;
326          dev->set_eot();
327       }
328       rec->Block = 0;
329       return true;
330    }
331    if (bsr) {
332       /*
333        * ***FIXME*** gross kludge to make disk seeking work.  Remove
334        *   when find_next_bsr() is fixed not to return a bsr already
335        *   completed.
336        */
337       if (dev->file > bsr->volfile->sfile ||             
338          (dev->file == bsr->volfile->sfile && dev->block_num > bsr->volblock->sblock)) {
339          return false;
340       }
341       if (verbose) {
342          Jmsg(jcr, M_INFO, 0, _("Reposition from (file:block) %u:%u to %u:%u\n"),
343             dev->file, dev->block_num, bsr->volfile->sfile,
344             bsr->volblock->sblock);
345       }
346       Dmsg4(10, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
347             dev->file, dev->block_num, bsr->volfile->sfile,
348             bsr->volblock->sblock);
349       dev->reposition(dcr, bsr->volfile->sfile, bsr->volblock->sblock);
350       rec->Block = 0;
351    }
352    return false;
353 }
354
355 /*
356  * Position to the first file on this volume
357  */
358 static BSR *position_to_first_file(JCR *jcr, DCR *dcr)
359 {
360    BSR *bsr = NULL;
361    DEVICE *dev = dcr->dev;
362    /*
363     * Now find and position to first file and block
364     *   on this tape.
365     */
366    if (jcr->bsr) {
367       jcr->bsr->reposition = true;    /* force repositioning */
368       bsr = find_next_bsr(jcr->bsr, dev);
369       if (bsr && (bsr->volfile->sfile != 0 || bsr->volblock->sblock != 0)) {
370          Jmsg(jcr, M_INFO, 0, _("Forward spacing Volume \"%s\" to file:block %u:%u.\n"),
371             dev->VolHdr.VolumeName,
372             bsr->volfile->sfile, bsr->volblock->sblock);
373          dev->reposition(dcr, bsr->volfile->sfile, bsr->volblock->sblock);
374       }
375    }
376    return bsr;
377 }
378
379
380 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
381 {
382    const char *rtype;
383    char buf[100];
384
385    memset(sessrec, 0, sizeof(sessrec));
386    switch (rec->FileIndex) {
387    case PRE_LABEL:
388       rtype = _("Fresh Volume Label");
389       break;
390    case VOL_LABEL:
391       rtype = _("Volume Label");
392       unser_volume_label(dev, rec);
393       break;
394    case SOS_LABEL:
395       rtype = _("Begin Session");
396       unser_session_label(sessrec, rec);
397       break;
398    case EOS_LABEL:
399       rtype = _("End Session");
400       break;
401    case EOM_LABEL:
402       rtype = _("End of Media");
403       break;
404    default:
405       bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
406       rtype = buf;
407       break;
408    }
409    Dmsg5(dbglvl, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
410          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
411 }
412
413 #ifdef DEBUG
414 static char *rec_state_to_str(DEV_RECORD *rec)
415 {
416    static char buf[200];
417    buf[0] = 0;
418    if (rec->state & REC_NO_HEADER) {
419       bstrncat(buf, "Nohdr,", sizeof(buf));
420    }
421    if (is_partial_record(rec)) {
422       bstrncat(buf, "partial,", sizeof(buf));
423    }
424    if (rec->state & REC_BLOCK_EMPTY) {
425       bstrncat(buf, "empty,", sizeof(buf));
426    }
427    if (rec->state & REC_NO_MATCH) {
428       bstrncat(buf, "Nomatch,", sizeof(buf));
429    }
430    if (rec->state & REC_CONTINUATION) {
431       bstrncat(buf, "cont,", sizeof(buf));
432    }
433    if (buf[0]) {
434       buf[strlen(buf)-1] = 0;
435    }
436    return buf;
437 }
438 #endif