]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/read_record.c
Get correct slot when auto unloading a device.
[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              * However, it seg faults in the for line at lineno 196.
238              */
239             if (rec->FileIndex == EOS_LABEL) {
240                Dmsg2(dbglvl, "Remove EOS rec. SI=%d ST=%d\n", rec->VolSessionId,
241                   rec->VolSessionTime);
242                recs->remove(rec);
243                free_record(rec);
244             }
245 #endif
246             continue;
247          } /* end if label record */
248
249          /*
250           * Apply BSR filter
251           */
252          if (jcr->bsr) {
253             rec->match_stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec);
254             if (rec->match_stat == -1) { /* no more possible matches */
255                done = true;   /* all items found, stop */
256                Dmsg2(dbglvl, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
257                break;
258             } else if (rec->match_stat == 0) {  /* no match */
259                Dmsg4(dbglvl, "BSR no match: clear rem=%d FI=%d before set_eof pos %u:%u\n",
260                   rec->remainder, rec->FileIndex, dev->file, dev->block_num);
261                rec->remainder = 0;
262                rec->state &= ~REC_PARTIAL_RECORD;
263                if (try_repositioning(jcr, rec, dcr)) {
264                   break;
265                }
266                continue;              /* we don't want record, read next one */
267             }
268          }
269          dcr->VolLastIndex = rec->FileIndex;  /* let caller know where we are */
270          if (is_partial_record(rec)) {
271             Dmsg6(dbglvl, "Partial, break. 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             break;                    /* read second part of record */
275          }
276          Dmsg6(dbglvl, "OK callback. 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          ok = record_cb(dcr, rec);
280          /*
281           * If we have a digest stream, we check to see if we have 
282           *  finished the current bsr, and if so, repositioning will
283           *  be turned on.
284           */
285          if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
286             Dmsg3(dbglvl, "Have digest FI=%u before bsr check pos %u:%u\n", rec->FileIndex,
287                   dev->file, dev->block_num);
288             if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
289                Dmsg2(dbglvl, "This bsr done, break pos %u:%u\n",
290                      dev->file, dev->block_num);
291                break;
292             }
293             Dmsg2(900, "After is_bsr_done pos %u:%u\n", dev->file, dev->block_num);
294          }
295       } /* end for loop over records */
296       Dmsg2(dbglvl, "After end recs in block. pos=%u:%u\n", dev->file, dev->block_num);
297    } /* end for loop over blocks */
298 // Dmsg2(dbglvl, "Position=(file:block) %u:%u\n", dev->file, dev->block_num);
299
300    /* Walk down list and free all remaining allocated recs */
301    while (!recs->empty()) {
302       rec = (DEV_RECORD *)recs->first();
303       recs->remove(rec);
304       free_record(rec);
305    }
306    delete recs;
307    print_block_read_errors(jcr, block);
308    return ok;
309 }
310
311 /*
312  * See if we can reposition.
313  *   Returns:  true  if at end of volume
314  *             false otherwise
315  */
316 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr)
317 {
318    BSR *bsr;
319    DEVICE *dev = dcr->dev;
320
321    bsr = find_next_bsr(jcr->bsr, dev);
322    if (bsr == NULL && jcr->bsr->mount_next_volume) {
323       Dmsg0(dbglvl, "Would mount next volume here\n");
324       Dmsg2(dbglvl, "Current postion (file:block) %u:%u\n",
325          dev->file, dev->block_num);
326       jcr->bsr->mount_next_volume = false;
327       if (!dev->at_eot()) {
328          /* Set EOT flag to force mount of next Volume */
329          jcr->mount_next_volume = true;
330          dev->set_eot();
331       }
332       rec->Block = 0;
333       return true;
334    }
335    if (bsr) {
336       /*
337        * ***FIXME*** gross kludge to make disk seeking work.  Remove
338        *   when find_next_bsr() is fixed not to return a bsr already
339        *   completed.
340        */
341       if (dev->file > bsr->volfile->sfile ||             
342          (dev->file == bsr->volfile->sfile && dev->block_num > bsr->volblock->sblock)) {
343          return false;
344       }
345       if (verbose) {
346          Jmsg(jcr, M_INFO, 0, _("Reposition from (file:block) %u:%u to %u:%u\n"),
347             dev->file, dev->block_num, bsr->volfile->sfile,
348             bsr->volblock->sblock);
349       }
350       Dmsg4(10, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
351             dev->file, dev->block_num, bsr->volfile->sfile,
352             bsr->volblock->sblock);
353       dev->reposition(dcr, bsr->volfile->sfile, bsr->volblock->sblock);
354       rec->Block = 0;
355    }
356    return false;
357 }
358
359 /*
360  * Position to the first file on this volume
361  */
362 static BSR *position_to_first_file(JCR *jcr, DCR *dcr)
363 {
364    BSR *bsr = NULL;
365    DEVICE *dev = dcr->dev;
366    /*
367     * Now find and position to first file and block
368     *   on this tape.
369     */
370    if (jcr->bsr) {
371       jcr->bsr->reposition = true;    /* force repositioning */
372       bsr = find_next_bsr(jcr->bsr, dev);
373       if (bsr && (bsr->volfile->sfile != 0 || bsr->volblock->sblock != 0)) {
374          Jmsg(jcr, M_INFO, 0, _("Forward spacing Volume \"%s\" to file:block %u:%u.\n"),
375             dev->VolHdr.VolumeName,
376             bsr->volfile->sfile, bsr->volblock->sblock);
377          dev->reposition(dcr, bsr->volfile->sfile, bsr->volblock->sblock);
378       }
379    }
380    return bsr;
381 }
382
383
384 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
385 {
386    const char *rtype;
387    char buf[100];
388
389    memset(sessrec, 0, sizeof(sessrec));
390    switch (rec->FileIndex) {
391    case PRE_LABEL:
392       rtype = _("Fresh Volume Label");
393       break;
394    case VOL_LABEL:
395       rtype = _("Volume Label");
396       unser_volume_label(dev, rec);
397       break;
398    case SOS_LABEL:
399       rtype = _("Begin Session");
400       unser_session_label(sessrec, rec);
401       break;
402    case EOS_LABEL:
403       rtype = _("End Session");
404       break;
405    case EOM_LABEL:
406       rtype = _("End of Media");
407       break;
408    default:
409       bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
410       rtype = buf;
411       break;
412    }
413    Dmsg5(dbglvl, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
414          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
415 }
416
417 #ifdef DEBUG
418 static char *rec_state_to_str(DEV_RECORD *rec)
419 {
420    static char buf[200];
421    buf[0] = 0;
422    if (rec->state & REC_NO_HEADER) {
423       bstrncat(buf, "Nohdr,", sizeof(buf));
424    }
425    if (is_partial_record(rec)) {
426       bstrncat(buf, "partial,", sizeof(buf));
427    }
428    if (rec->state & REC_BLOCK_EMPTY) {
429       bstrncat(buf, "empty,", sizeof(buf));
430    }
431    if (rec->state & REC_NO_MATCH) {
432       bstrncat(buf, "Nomatch,", sizeof(buf));
433    }
434    if (rec->state & REC_CONTINUATION) {
435       bstrncat(buf, "cont,", sizeof(buf));
436    }
437    if (buf[0]) {
438       buf[strlen(buf)-1] = 0;
439    }
440    return buf;
441 }
442 #endif