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