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