]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/read_record.c
Apply Joao's patch to SQLite tables to make chars work.
[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 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  *   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 = 500;
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    bool ok = true;
72    bool done = false;
73    SESSION_LABEL sessrec;
74    dlist *recs;                         /* linked list of rec packets open */
75
76    recs = New(dlist(rec, &rec->link));
77    position_to_first_file(jcr, dcr);
78    jcr->mount_next_volume = false;
79
80    for ( ; ok && !done; ) {
81       if (job_canceled(jcr)) {
82          ok = false;
83          break;
84       }
85       if (!read_block_from_device(dcr, CHECK_BLOCK_NUMBERS)) {
86          if (dev->at_eot()) {
87             DEV_RECORD *trec = new_record();
88             Jmsg(jcr, M_INFO, 0, _("End of Volume at file %u on device %s, Volume \"%s\"\n"),
89                  dev->file, dev->print_name(), dcr->VolumeName);
90             volume_unused(dcr);       /* mark volume unused */
91             if (!mount_cb(dcr)) {
92                Jmsg(jcr, M_INFO, 0, _("End of all volumes.\n"));
93                ok = false;            /* Stop everything */
94                /*
95                 * Create EOT Label so that Media record may
96                 *  be properly updated because this is the last
97                 *  tape.
98                 */
99                trec->FileIndex = EOT_LABEL;
100                trec->File = dev->file;
101                ok = record_cb(dcr, trec);
102                free_record(trec);
103                if (jcr->mount_next_volume) {
104                   jcr->mount_next_volume = false;
105                   dev->clear_eot();
106                }
107                break;
108             }
109             jcr->mount_next_volume = false;
110             /*  
111              * The Device can change at the end of a tape, so refresh it
112              *   and the block from the dcr.
113              */
114             dev = dcr->dev;
115             block = dcr->block;
116             /*
117              * We just have a new tape up, now read the label (first record)
118              *  and pass it off to the callback routine, then continue
119              *  most likely reading the previous record.
120              */
121             read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
122             read_record_from_block(dcr, block, trec);
123             handle_session_record(dev, trec, &sessrec);
124             ok = record_cb(dcr, trec);
125             free_record(trec);
126             position_to_first_file(jcr, dcr);
127             /* After reading label, we must read first data block */
128             continue;
129
130          } else if (dev->at_eof()) {
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             Dmsg3(200, "End of file %u  on device %s, Volume \"%s\"\n",
145                   dev->file, dev->print_name(), dcr->VolumeName);
146             continue;
147          } else if (dev->is_short_block()) {
148             Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
149             continue;
150          } else {
151             /* I/O error or strange end of tape */
152             display_tape_error_status(jcr, dev);
153             if (forge_on || jcr->ignore_label_errors) {
154                dev->fsr(1);       /* try skipping bad record */
155                Pmsg0(000, _("Did fsr in attemp to skip bad record.\n"));
156                continue;              /* try to continue */
157             }
158             ok = false;               /* stop everything */
159             break;
160          }
161       }
162       Dmsg2(dbglvl, "Read new block at pos=%u:%u\n", dev->file, dev->block_num);
163 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
164       /* this does not stop when file/block are too big */
165       if (!match_bsr_block(jcr->bsr, block)) {
166          if (try_repositioning(jcr, rec, dcr)) {
167             break;                    /* get next volume */
168          }
169          continue;                    /* skip this record */
170       }
171 #endif
172
173       /*
174        * Get a new record for each Job as defined by
175        *   VolSessionId and VolSessionTime
176        */
177       bool found = false;
178       foreach_dlist(rec, recs) {
179          if (rec->VolSessionId == block->VolSessionId &&
180              rec->VolSessionTime == block->VolSessionTime) {
181             found = true;
182             break;
183           }
184       }
185       if (!found) {
186          rec = new_record();
187          recs->prepend(rec);
188          Dmsg3(dbglvl, "New record for state=%s SI=%d ST=%d\n",
189              rec_state_to_str(rec),
190              block->VolSessionId, block->VolSessionTime);
191       }
192       Dmsg3(dbglvl, "Before read rec loop. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
193             block->BlockNumber, rec->remainder);
194       record = 0;
195       rec->state = 0;
196       Dmsg1(dbglvl, "Block %s empty\n", is_block_empty(rec)?"is":"NOT");
197       for (rec->state=0; ok && !is_block_empty(rec); ) {
198          if (!read_record_from_block(dcr, block, rec)) {
199             Dmsg3(400, "!read-break. state=%s blk=%d rem=%d\n", rec_state_to_str(rec),
200                   block->BlockNumber, rec->remainder);
201             break;
202          }
203          Dmsg5(dbglvl, "read-OK. state=%s blk=%d rem=%d file:block=%u:%u\n",
204                  rec_state_to_str(rec), block->BlockNumber, rec->remainder,
205                  dev->file, dev->block_num);
206          /*
207           * At this point, we have at least a record header.
208           *  Now decide if we want this record or not, but remember
209           *  before accessing the record, we may need to read again to
210           *  get all the data.
211           */
212          record++;
213          Dmsg6(dbglvl, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
214             rec_state_to_str(rec), block->BlockNumber,
215             rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
216
217          if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
218             Dmsg0(40, "Get EOM LABEL\n");
219             break;                         /* yes, get out */
220          }
221
222          /* Some sort of label? */
223          if (rec->FileIndex < 0) {
224             handle_session_record(dev, rec, &sessrec);
225             if (jcr->bsr) {
226                /* We just check block FI and FT not FileIndex */
227                rec->match_stat = match_bsr_block(jcr->bsr, block);
228             } else {
229                rec->match_stat = 0;
230             }
231             /*
232              * Note, we pass *all* labels to the callback routine. If
233              *  he wants to know if they matched the bsr, then he must
234              *  check the match_stat in the record */
235             ok = record_cb(dcr, rec);
236 #ifdef xxx
237             /*
238              * If this is the end of the Session (EOS) for this record
239              *  we can remove the record.  Note, there is a separate
240              *  record to read each session. If a new session is seen
241              *  a new record will be created at approx line 157 above.
242              * However, it seg faults in the for line at lineno 196.
243              */
244             if (rec->FileIndex == EOS_LABEL) {
245                Dmsg2(dbglvl, "Remove EOS rec. SI=%d ST=%d\n", rec->VolSessionId,
246                   rec->VolSessionTime);
247                recs->remove(rec);
248                free_record(rec);
249             }
250 #endif
251             continue;
252          } /* end if label record */
253
254          /*
255           * Apply BSR filter
256           */
257          if (jcr->bsr) {
258             rec->match_stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec, jcr);
259             if (rec->match_stat == -1) { /* no more possible matches */
260                done = true;   /* all items found, stop */
261                Dmsg2(dbglvl, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
262                break;
263             } else if (rec->match_stat == 0) {  /* no match */
264                Dmsg4(dbglvl, "BSR no match: clear rem=%d FI=%d before set_eof pos %u:%u\n",
265                   rec->remainder, rec->FileIndex, dev->file, dev->block_num);
266                rec->remainder = 0;
267                rec->state &= ~REC_PARTIAL_RECORD;
268                if (try_repositioning(jcr, rec, dcr)) {
269                   break;
270                }
271                continue;              /* we don't want record, read next one */
272             }
273          }
274          dcr->VolLastIndex = rec->FileIndex;  /* let caller know where we are */
275          if (is_partial_record(rec)) {
276             Dmsg6(dbglvl, "Partial, break. 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             break;                    /* read second part of record */
280          }
281
282          Dmsg6(dbglvl, "OK callback. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
283                rec_state_to_str(rec), block->BlockNumber,
284                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
285          ok = record_cb(dcr, rec);
286          /*
287           * If we have a digest stream, we check to see if we have 
288           *  finished the current bsr, and if so, repositioning will
289           *  be turned on.
290           */
291          if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
292             Dmsg3(dbglvl, "Have digest FI=%u before bsr check pos %u:%u\n", rec->FileIndex,
293                   dev->file, dev->block_num);
294             if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dcr)) {
295                Dmsg2(dbglvl, "This bsr done, break pos %u:%u\n",
296                      dev->file, dev->block_num);
297                break;
298             }
299             Dmsg2(900, "After is_bsr_done pos %u:%u\n", dev->file, dev->block_num);
300          }
301       } /* end for loop over records */
302       Dmsg2(dbglvl, "After end recs in block. pos=%u:%u\n", dev->file, dev->block_num);
303    } /* end for loop over blocks */
304 // Dmsg2(dbglvl, "Position=(file:block) %u:%u\n", dev->file, dev->block_num);
305
306    /* Walk down list and free all remaining allocated recs */
307    while (!recs->empty()) {
308       rec = (DEV_RECORD *)recs->first();
309       recs->remove(rec);
310       free_record(rec);
311    }
312    delete recs;
313    print_block_read_errors(jcr, block);
314    return ok;
315 }
316
317 /*
318  * See if we can reposition.
319  *   Returns:  true  if at end of volume
320  *             false otherwise
321  */
322 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DCR *dcr)
323 {
324    BSR *bsr;
325    DEVICE *dev = dcr->dev;
326
327    bsr = find_next_bsr(jcr->bsr, dev);
328    if (bsr == NULL && jcr->bsr->mount_next_volume) {
329       Dmsg0(dbglvl, "Would mount next volume here\n");
330       Dmsg2(dbglvl, "Current postion (file:block) %u:%u\n",
331          dev->file, dev->block_num);
332       jcr->bsr->mount_next_volume = false;
333       if (!dev->at_eot()) {
334          /* Set EOT flag to force mount of next Volume */
335          jcr->mount_next_volume = true;
336          dev->set_eot();
337       }
338       rec->Block = 0;
339       return true;
340    }
341    if (bsr) {
342       /*
343        * ***FIXME*** gross kludge to make disk seeking work.  Remove
344        *   when find_next_bsr() is fixed not to return a bsr already
345        *   completed.
346        */
347       if (dev->file > bsr->volfile->sfile ||             
348          (dev->file == bsr->volfile->sfile && dev->block_num > bsr->volblock->sblock)) {
349          return false;
350       }
351       if (verbose) {
352          Jmsg(jcr, M_INFO, 0, _("Reposition from (file:block) %u:%u to %u:%u\n"),
353             dev->file, dev->block_num, bsr->volfile->sfile,
354             bsr->volblock->sblock);
355       }
356       Dmsg4(10, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
357             dev->file, dev->block_num, bsr->volfile->sfile,
358             bsr->volblock->sblock);
359       dev->reposition(dcr, bsr->volfile->sfile, bsr->volblock->sblock);
360       rec->Block = 0;
361    }
362    return false;
363 }
364
365 /*
366  * Position to the first file on this volume
367  */
368 static BSR *position_to_first_file(JCR *jcr, DCR *dcr)
369 {
370    BSR *bsr = NULL;
371    DEVICE *dev = dcr->dev;
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       if (bsr && (bsr->volfile->sfile != 0 || bsr->volblock->sblock != 0)) {
380          Jmsg(jcr, M_INFO, 0, _("Forward spacing Volume \"%s\" to file:block %u:%u.\n"),
381             dev->VolHdr.VolumeName,
382             bsr->volfile->sfile, bsr->volblock->sblock);
383          dev->reposition(dcr, bsr->volfile->sfile, bsr->volblock->sblock);
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