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