]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/read_records.c
b42b1810b5c81c2f3d67f84f2441795a35aafa16
[bacula/bacula] / bacula / src / stored / read_records.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2016 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is 
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *
21  *  This routine provides a routine that will handle all
22  *    the gory little details of reading a record from a Bacula
23  *    archive. It uses a callback to pass you each record in turn,
24  *    as well as a callback for mounting the next tape.  It takes
25  *    care of reading blocks, applying the bsr, ...
26  *    Note, this routine is really the heart of the restore routines,
27  *    and we are *really* bit pushing here so be careful about making
28  *    any modifications.
29  *
30  *    Kern E. Sibbald, August MMII
31  *
32  */
33
34 #include "bacula.h"
35 #include "stored.h"
36
37 /* Forward referenced functions */
38 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
39 static BSR *position_to_first_file(JCR *jcr, DCR *dcr);
40 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr);
41 #ifdef DEBUG
42 static char *rec_state_bits_to_str(DEV_RECORD *rec);
43 #endif
44
45 static const int dbglvl = 190;
46 static const int no_FileIndex = -999999;
47
48 /*
49  * This subroutine reads all the records and passes them back to your
50  *  callback routine (also mount routine at EOM).
51  * You must not change any values in the DEV_RECORD packet
52  */
53 bool read_records(DCR *dcr,
54        bool record_cb(DCR *dcr, DEV_RECORD *rec),
55        bool mount_cb(DCR *dcr))
56 {
57    JCR *jcr = dcr->jcr;
58    DEVICE *dev = dcr->dev;
59    DEV_BLOCK *block = dcr->block;
60    DEV_RECORD *rec = NULL;
61    uint32_t record;
62    int32_t lastFileIndex;
63    bool ok = true;
64    bool done = false;
65    SESSION_LABEL sessrec;
66    dlist *recs;                         /* linked list of rec packets open */
67
68    recs = New(dlist(rec, &rec->link));
69    position_to_first_file(jcr, dcr);
70    jcr->mount_next_volume = false;
71
72    for ( ; ok && !done; ) {
73       if (job_canceled(jcr)) {
74          ok = false;
75          break;
76       }
77       if (!dcr->read_block_from_device(CHECK_BLOCK_NUMBERS)) {
78          if (dev->at_eot()) {
79             DEV_RECORD *trec = new_record();
80             Jmsg(jcr, M_INFO, 0, _("End of Volume at file %u on device %s, Volume \"%s\"\n"),
81                  dev->file, dev->print_name(), dcr->VolumeName);
82             volume_unused(dcr);       /* mark volume unused */
83             if (!mount_cb(dcr)) {
84                Jmsg(jcr, M_INFO, 0, _("End of all volumes.\n"));
85                ok = false;            /* Stop everything */
86                /*
87                 * Create EOT Label so that Media record may
88                 *  be properly updated because this is the last
89                 *  tape.
90                 */
91                trec->FileIndex = EOT_LABEL;
92                trec->File = dev->file;
93                ok = record_cb(dcr, trec);
94                free_record(trec);
95                if (jcr->mount_next_volume) {
96                   jcr->mount_next_volume = false;
97                   dev->clear_eot();
98                }
99                break;
100             }
101             jcr->mount_next_volume = false;
102             /*
103              * The Device can change at the end of a tape, so refresh it
104              *   and the block from the dcr.
105              */
106             dev = dcr->dev;
107             block = dcr->block;
108             /*
109              * We just have a new tape up, now read the label (first record)
110              *  and pass it off to the callback routine, then continue
111              *  most likely reading the previous record.
112              */
113             dcr->read_block_from_device(NO_BLOCK_NUMBER_CHECK);
114             read_record_from_block(dcr, trec);
115             handle_session_record(dev, trec, &sessrec);
116             ok = record_cb(dcr, trec);
117             free_record(trec);
118             position_to_first_file(jcr, dcr);
119             /* After reading label, we must read first data block */
120             continue;
121
122          } else if (dev->at_eof()) {
123             Dmsg3(200, "End of file %u  on device %s, Volume \"%s\"\n",
124                   dev->file, dev->print_name(), dcr->VolumeName);
125             continue;
126          } else if (dev->is_short_block()) {
127             Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
128             continue;
129          } else {
130             /* I/O error or strange end of tape */
131             display_tape_error_status(jcr, dev);
132             if (forge_on || jcr->ignore_label_errors) {
133                dev->fsr(1);       /* try skipping bad record */
134                Pmsg0(000, _("Did fsr in attemp to skip bad record.\n"));
135                continue;              /* try to continue */
136             }
137             ok = false;               /* stop everything */
138             break;
139          }
140       }
141       Dmsg2(dbglvl, "Read new block at pos=%u:%u\n", dev->file, dev->block_num);
142 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
143       /* this does not stop when file/block are too big */
144       if (!match_bsr_block(jcr->bsr, block)) {
145          if (try_repositioning(jcr, rec, dcr)) {
146             break;                    /* get next volume */
147          }
148          continue;                    /* skip this record */
149       }
150 #endif
151
152       /*
153        * Get a new record for each Job as defined by
154        *   VolSessionId and VolSessionTime
155        */
156       bool found = false;
157       foreach_dlist(rec, recs) {
158          if (rec->VolSessionId == block->VolSessionId &&
159              rec->VolSessionTime == block->VolSessionTime) {
160             found = true;
161             break;
162           }
163       }
164       if (!found) {
165          rec = new_record();
166          recs->prepend(rec);
167          Dmsg3(dbglvl, "New record for state=%s SI=%d ST=%d\n",
168              rec_state_bits_to_str(rec),
169              block->VolSessionId, block->VolSessionTime);
170       }
171       Dmsg3(dbglvl, "Before read rec loop. stat=%s blk=%d rem=%d\n", rec_state_bits_to_str(rec),
172             block->BlockNumber, rec->remainder);
173       record = 0;
174       rec->state_bits = 0;
175       lastFileIndex = no_FileIndex;
176       Dmsg1(dbglvl, "Block %s empty\n", is_block_marked_empty(rec)?"is":"NOT");
177       for (rec->state_bits=0; ok && !is_block_marked_empty(rec); ) {
178          if (!read_record_from_block(dcr, rec)) {
179             Dmsg3(200, "!read-break. state_bits=%s blk=%d rem=%d\n", rec_state_bits_to_str(rec),
180                   block->BlockNumber, rec->remainder);
181             break;
182          }
183          Dmsg5(dbglvl, "read-OK. state_bits=%s blk=%d rem=%d file:block=%u:%u\n",
184                  rec_state_bits_to_str(rec), block->BlockNumber, rec->remainder,
185                  dev->file, dev->block_num);
186          /*
187           * At this point, we have at least a record header.
188           *  Now decide if we want this record or not, but remember
189           *  before accessing the record, we may need to read again to
190           *  get all the data.
191           */
192          record++;
193          Dmsg6(dbglvl, "recno=%d state_bits=%s blk=%d SI=%d ST=%d FI=%d\n", record,
194             rec_state_bits_to_str(rec), block->BlockNumber,
195             rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
196
197          if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
198             Dmsg0(40, "Get EOM LABEL\n");
199             break;                         /* yes, get out */
200          }
201
202          /* Some sort of label? */
203          if (rec->FileIndex < 0) {
204             handle_session_record(dev, rec, &sessrec);
205             if (jcr->bsr) {
206                /* We just check block FI and FT not FileIndex */
207                rec->match_stat = match_bsr_block(jcr->bsr, block);
208             } else {
209                rec->match_stat = 0;
210             }
211             /*
212              * Note, we pass *all* labels to the callback routine. If
213              *  he wants to know if they matched the bsr, then he must
214              *  check the match_stat in the record */
215             ok = record_cb(dcr, rec);
216 #ifdef xxx
217             /*
218              * If this is the end of the Session (EOS) for this record
219              *  we can remove the record.  Note, there is a separate
220              *  record to read each session. If a new session is seen
221              *  a new record will be created at approx line 157 above.
222              * However, it seg faults in the for line at lineno 196.
223              */
224             if (rec->FileIndex == EOS_LABEL) {
225                Dmsg2(dbglvl, "Remove EOS rec. SI=%d ST=%d\n", rec->VolSessionId,
226                   rec->VolSessionTime);
227                recs->remove(rec);
228                free_record(rec);
229             }
230 #endif
231             continue;
232          } /* end if label record */
233
234          /*
235           * Apply BSR filter
236           */
237          if (jcr->bsr) {
238             rec->match_stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec, jcr);
239             Dmsg2(dbglvl, "match_bsr=%d bsr->reposition=%d\n", rec->match_stat,
240                jcr->bsr->reposition);
241             if (rec->match_stat == -1) { /* no more possible matches */
242                done = true;   /* all items found, stop */
243                Dmsg2(dbglvl, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
244                break;
245             } else if (rec->match_stat == 0) {  /* no match */
246                Dmsg4(dbglvl, "BSR no match: clear rem=%d FI=%d before set_eof pos %u:%u\n",
247                   rec->remainder, rec->FileIndex, dev->file, dev->block_num);
248                rec->remainder = 0;
249                rec->state_bits &= ~REC_PARTIAL_RECORD;
250                if (try_repositioning(jcr, rec, dcr)) {
251                   break;
252                }
253                continue;              /* we don't want record, read next one */
254             }
255          }
256          dcr->VolLastIndex = rec->FileIndex;  /* let caller know where we are */
257          if (is_partial_record(rec)) {
258             Dmsg6(dbglvl, "Partial, break. recno=%d state_bits=%s blk=%d SI=%d ST=%d FI=%d\n", record,
259                rec_state_bits_to_str(rec), block->BlockNumber,
260                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
261             break;                    /* read second part of record */
262          }
263
264          Dmsg6(dbglvl, "OK callback. recno=%d state_bits=%s blk=%d SI=%d ST=%d FI=%d\n", record,
265                rec_state_bits_to_str(rec), block->BlockNumber,
266                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
267          if (lastFileIndex != no_FileIndex && lastFileIndex != rec->FileIndex) {
268             if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
269                Dmsg2(dbglvl, "This bsr done, break pos %u:%u\n",
270                      dev->file, dev->block_num);
271                break;
272             }
273             Dmsg2(dbglvl, "==== inside LastIndex=%d FileIndex=%d\n", lastFileIndex, rec->FileIndex);
274          }
275          Dmsg2(dbglvl, "==== LastIndex=%d FileIndex=%d\n", lastFileIndex, rec->FileIndex);
276          lastFileIndex = rec->FileIndex;
277          ok = record_cb(dcr, rec);
278 #if 0
279          /*
280           * If we have a digest stream, we check to see if we have
281           *  finished the current bsr, and if so, repositioning will
282           *  be turned on.
283           */
284          if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
285             Dmsg3(dbglvl, "=== Have digest FI=%u before bsr check pos %u:%u\n", rec->FileIndex,
286                   dev->file, dev->block_num);
287             if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
288                Dmsg1(dbglvl, "==== BSR done at FI=%d\n", rec->FileIndex);
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 #endif
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    Dmsg2(dbglvl, "nextbsr=%p mount_next_volume=%d\n", bsr, jcr->bsr->mount_next_volume);
324    if (bsr == NULL && jcr->bsr->mount_next_volume) {
325       Dmsg0(dbglvl, "Would mount next volume here\n");
326       Dmsg2(dbglvl, "Current postion (file:block) %u:%u\n",
327          dev->file, dev->block_num);
328       jcr->bsr->mount_next_volume = false;
329       if (!dev->at_eot()) {
330          /* Set EOT flag to force mount of next Volume */
331          jcr->mount_next_volume = true;
332          dev->set_eot();
333       }
334       rec->Block = 0;
335       return true;
336    }
337    if (bsr) {
338       /*
339        * ***FIXME*** gross kludge to make disk seeking work.  Remove
340        *   when find_next_bsr() is fixed not to return a bsr already
341        *   completed.
342        */
343       uint32_t block, file;
344       /* TODO: use dev->file_addr ? */
345       uint64_t dev_addr = (((uint64_t) dev->file)<<32) | dev->block_num;
346       uint64_t bsr_addr = get_bsr_start_addr(bsr, &file, &block);
347
348       if (dev_addr > bsr_addr) {
349          return false;
350       }
351       Dmsg4(dbglvl, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
352             dev->file, dev->block_num, file, block);
353       dev->reposition(dcr, file, block);
354       rec->Block = 0;
355       return true;
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    uint32_t file, block;
368    /*
369     * Now find and position to first file and block
370     *   on this tape.
371     */
372    if (jcr->bsr) {
373       jcr->bsr->reposition = true;    /* force repositioning */
374       bsr = find_next_bsr(jcr->bsr, dev);
375
376       if (get_bsr_start_addr(bsr, &file, &block) > 0) {
377          Jmsg(jcr, M_INFO, 0, _("Forward spacing Volume \"%s\" to file:block %u:%u.\n"),
378               dev->VolHdr.VolumeName, file, block);
379          dev->reposition(dcr, file, block);
380       }
381    }
382    return bsr;
383 }
384
385
386 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
387 {
388    const char *rtype;
389    char buf[100];
390
391    memset(sessrec, 0, sizeof(SESSION_LABEL));
392    switch (rec->FileIndex) {
393    case PRE_LABEL:
394       rtype = _("Fresh Volume Label");
395       break;
396    case VOL_LABEL:
397       rtype = _("Volume Label");
398       unser_volume_label(dev, rec);
399       break;
400    case SOS_LABEL:
401       rtype = _("Begin Session");
402       unser_session_label(sessrec, rec);
403       break;
404    case EOS_LABEL:
405       rtype = _("End Session");
406       break;
407    case EOM_LABEL:
408       rtype = _("End of Media");
409       break;
410    default:
411       bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
412       rtype = buf;
413       break;
414    }
415    Dmsg5(dbglvl, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
416          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
417 }
418
419 #ifdef DEBUG
420 static char *rec_state_bits_to_str(DEV_RECORD *rec)
421 {
422    static char buf[200];
423    buf[0] = 0;
424    if (rec->state_bits & REC_NO_HEADER) {
425       bstrncat(buf, "Nohdr,", sizeof(buf));
426    }
427    if (is_partial_record(rec)) {
428       bstrncat(buf, "partial,", sizeof(buf));
429    }
430    if (rec->state_bits & REC_BLOCK_EMPTY) {
431       bstrncat(buf, "empty,", sizeof(buf));
432    }
433    if (rec->state_bits & REC_NO_MATCH) {
434       bstrncat(buf, "Nomatch,", sizeof(buf));
435    }
436    if (rec->state_bits & REC_CONTINUATION) {
437       bstrncat(buf, "cont,", sizeof(buf));
438    }
439    if (buf[0]) {
440       buf[strlen(buf)-1] = 0;
441    }
442    return buf;
443 }
444 #endif