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