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