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