]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/read_record.c
kes Fix logic error in handling error return from mtx-changer
[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 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, dev);
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, dev);
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, dev)) {
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, dev)) {
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, dev)) {
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, DEVICE *dev)
296 {
297    BSR *bsr;
298    bsr = find_next_bsr(jcr->bsr, dev);
299    if (bsr == NULL && jcr->bsr->mount_next_volume) {
300       Dmsg0(dbglvl, "Would mount next volume here\n");
301       Dmsg2(dbglvl, "Current postion (file:block) %u:%u\n",
302          dev->file, dev->block_num);
303       jcr->bsr->mount_next_volume = false;
304       if (!dev->at_eot()) {
305          /* Set EOT flag to force mount of next Volume */
306          jcr->mount_next_volume = true;
307          dev->set_eot();
308       }
309       rec->Block = 0;
310       return true;
311    }
312    if (bsr) {
313       if (verbose) {
314          Jmsg(jcr, M_INFO, 0, _("Reposition from (file:block) %u:%u to %u:%u\n"),
315             dev->file, dev->block_num, bsr->volfile->sfile,
316             bsr->volblock->sblock);
317       }
318       Dmsg4(dbglvl, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
319             dev->file, dev->block_num, bsr->volfile->sfile,
320             bsr->volblock->sblock);
321       dev->reposition(bsr->volfile->sfile, bsr->volblock->sblock);
322       rec->Block = 0;
323    }
324    return false;
325 }
326
327 /*
328  * Position to the first file on this volume
329  */
330 static BSR *position_to_first_file(JCR *jcr, DEVICE *dev)
331 {
332    BSR *bsr = NULL;
333    /*
334     * Now find and position to first file and block
335     *   on this tape.
336     */
337    if (jcr->bsr) {
338       jcr->bsr->reposition = true;    /* force repositioning */
339       bsr = find_next_bsr(jcr->bsr, dev);
340       if (bsr && (bsr->volfile->sfile != 0 || bsr->volblock->sblock != 0)) {
341          Jmsg(jcr, M_INFO, 0, _("Forward spacing to file:block %u:%u.\n"),
342             bsr->volfile->sfile, bsr->volblock->sblock);
343          Dmsg2(dbglvl, "Forward spacing to file:block %u:%u.\n",
344             bsr->volfile->sfile, bsr->volblock->sblock);
345          dev->reposition(bsr->volfile->sfile, bsr->volblock->sblock);
346       }
347    }
348    return bsr;
349 }
350
351
352 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
353 {
354    const char *rtype;
355    char buf[100];
356
357    memset(sessrec, 0, sizeof(sessrec));
358    switch (rec->FileIndex) {
359    case PRE_LABEL:
360       rtype = _("Fresh Volume Label");
361       break;
362    case VOL_LABEL:
363       rtype = _("Volume Label");
364       unser_volume_label(dev, rec);
365       break;
366    case SOS_LABEL:
367       rtype = _("Begin Session");
368       unser_session_label(sessrec, rec);
369       break;
370    case EOS_LABEL:
371       rtype = _("End Session");
372       break;
373    case EOM_LABEL:
374       rtype = _("End of Media");
375       break;
376    default:
377       bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
378       rtype = buf;
379       break;
380    }
381    Dmsg5(dbglvl, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
382          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
383 }
384
385 #ifdef DEBUG
386 static char *rec_state_to_str(DEV_RECORD *rec)
387 {
388    static char buf[200];
389    buf[0] = 0;
390    if (rec->state & REC_NO_HEADER) {
391       bstrncat(buf, "Nohdr,", sizeof(buf));
392    }
393    if (is_partial_record(rec)) {
394       bstrncat(buf, "partial,", sizeof(buf));
395    }
396    if (rec->state & REC_BLOCK_EMPTY) {
397       bstrncat(buf, "empty,", sizeof(buf));
398    }
399    if (rec->state & REC_NO_MATCH) {
400       bstrncat(buf, "Nomatch,", sizeof(buf));
401    }
402    if (rec->state & REC_CONTINUATION) {
403       bstrncat(buf, "cont,", sizeof(buf));
404    }
405    if (buf[0]) {
406       buf[strlen(buf)-1] = 0;
407    }
408    return buf;
409 }
410 #endif