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