]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/read_record.c
ebl Remove Reposition info message
[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(dbglvl, "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(dbglvl, "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       Dmsg4(10, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
356             dev->file, dev->block_num, file, block);
357       dev->reposition(dcr, file, block);
358       rec->Block = 0;
359    }
360    return false;
361 }
362
363 /*
364  * Position to the first file on this volume
365  */
366 static BSR *position_to_first_file(JCR *jcr, DCR *dcr)
367 {
368    BSR *bsr = NULL;
369    DEVICE *dev = dcr->dev;
370    uint32_t file, block;
371    /*
372     * Now find and position to first file and block
373     *   on this tape.
374     */
375    if (jcr->bsr) {
376       jcr->bsr->reposition = true;    /* force repositioning */
377       bsr = find_next_bsr(jcr->bsr, dev);
378       
379       if (get_bsr_start_addr(bsr, &file, &block) > 0) {
380          Jmsg(jcr, M_INFO, 0, _("Forward spacing Volume \"%s\" to file:block %u:%u.\n"),
381               dev->VolHdr.VolumeName, file, block);
382          dev->reposition(dcr, file, block);
383       }
384    }
385    return bsr;
386 }
387
388
389 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
390 {
391    const char *rtype;
392    char buf[100];
393
394    memset(sessrec, 0, sizeof(sessrec));
395    switch (rec->FileIndex) {
396    case PRE_LABEL:
397       rtype = _("Fresh Volume Label");
398       break;
399    case VOL_LABEL:
400       rtype = _("Volume Label");
401       unser_volume_label(dev, rec);
402       break;
403    case SOS_LABEL:
404       rtype = _("Begin Session");
405       unser_session_label(sessrec, rec);
406       break;
407    case EOS_LABEL:
408       rtype = _("End Session");
409       break;
410    case EOM_LABEL:
411       rtype = _("End of Media");
412       break;
413    default:
414       bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
415       rtype = buf;
416       break;
417    }
418    Dmsg5(dbglvl, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
419          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
420 }
421
422 #ifdef DEBUG
423 static char *rec_state_to_str(DEV_RECORD *rec)
424 {
425    static char buf[200];
426    buf[0] = 0;
427    if (rec->state & REC_NO_HEADER) {
428       bstrncat(buf, "Nohdr,", sizeof(buf));
429    }
430    if (is_partial_record(rec)) {
431       bstrncat(buf, "partial,", sizeof(buf));
432    }
433    if (rec->state & REC_BLOCK_EMPTY) {
434       bstrncat(buf, "empty,", sizeof(buf));
435    }
436    if (rec->state & REC_NO_MATCH) {
437       bstrncat(buf, "Nomatch,", sizeof(buf));
438    }
439    if (rec->state & REC_CONTINUATION) {
440       bstrncat(buf, "cont,", sizeof(buf));
441    }
442    if (buf[0]) {
443       buf[strlen(buf)-1] = 0;
444    }
445    return buf;
446 }
447 #endif