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