]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/read_record.c
Strip pathname portion off all message routines that print filename:line.
[bacula/bacula] / bacula / src / stored / read_record.c
1 /*
2  *
3  *  This routine provides a routine that will handle all
4  *    the gory little details of reading a record from a Bacula
5  *    archive. It uses a callback to pass you each record in turn,
6  *    as well as a callback for mounting the next tape.  It takes
7  *    care of reading blocks, applying the bsr, ...
8  *    Note, this routine is really the heart of the restore routines,
9  *    and we are *really* bit pushing here so be careful about making
10  *    any modifications.
11  *
12  *    Kern E. Sibbald, August MMII
13  *
14  *   Version $Id$
15  */
16 /*
17    Copyright (C) 2000-2006 Kern Sibbald
18
19    This program is free software; you can redistribute it and/or
20    modify it under the terms of the GNU General Public License
21    version 2 as amended with additional clauses defined in the
22    file LICENSE in the main source directory.
23
24    This program is distributed in the hope that it will be useful,
25    but WITHOUT ANY WARRANTY; without even the implied warranty of
26    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
27    the file LICENSE for additional details.
28
29  */
30
31 #include "bacula.h"
32 #include "stored.h"
33
34 /* Forward referenced functions */
35 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
36 static BSR *position_to_first_file(JCR *jcr, DEVICE *dev);
37 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DEVICE *dev);
38 #ifdef DEBUG
39 static char *rec_state_to_str(DEV_RECORD *rec);
40 #endif
41
42 static const int dbglvl = 1000;
43
44 bool read_records(DCR *dcr,
45        bool record_cb(DCR *dcr, DEV_RECORD *rec),
46        bool mount_cb(DCR *dcr))
47 {
48    JCR *jcr = dcr->jcr;
49    DEVICE *dev = dcr->dev;
50    DEV_BLOCK *block = dcr->block;
51    DEV_RECORD *rec = NULL;
52    uint32_t record;
53    bool ok = true;
54    bool done = false;
55    SESSION_LABEL sessrec;
56    dlist *recs;                         /* linked list of rec packets open */
57
58    recs = New(dlist(rec, &rec->link));
59    position_to_first_file(jcr, dev);
60    jcr->mount_next_volume = false;
61
62    for ( ; ok && !done; ) {
63       if (job_canceled(jcr)) {
64          ok = false;
65          break;
66       }
67       if (!read_block_from_device(dcr, CHECK_BLOCK_NUMBERS)) {
68          if (dev->at_eot()) {
69             DEV_RECORD *trec = new_record();
70             Jmsg(jcr, M_INFO, 0, _("End of Volume at file %u on device %s, Volume \"%s\"\n"),
71                  dev->file, dev->print_name(), dcr->VolumeName);
72             if (!mount_cb(dcr)) {
73                Jmsg(jcr, M_INFO, 0, _("End of all volumes.\n"));
74                ok = false;            /* Stop everything */
75                /*
76                 * Create EOT Label so that Media record may
77                 *  be properly updated because this is the last
78                 *  tape.
79                 */
80                trec->FileIndex = EOT_LABEL;
81                trec->File = dev->file;
82                ok = record_cb(dcr, trec);
83                free_record(trec);
84                if (jcr->mount_next_volume) {
85                   jcr->mount_next_volume = false;
86                   dev->clear_eot();
87                }
88                break;
89             }
90             jcr->mount_next_volume = false;
91             /*  
92              * The Device can change at the end of a tape, so refresh it
93              *   from the dcr.
94              */
95             dev = dcr->dev;
96             /*
97              * We just have a new tape up, now read the label (first record)
98              *  and pass it off to the callback routine, then continue
99              *  most likely reading the previous record.
100              */
101             read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
102             read_record_from_block(block, trec);
103             handle_session_record(dev, trec, &sessrec);
104             ok = record_cb(dcr, trec);
105             free_record(trec);
106             position_to_first_file(jcr, dev);
107             /* After reading label, we must read first data block */
108             continue;
109
110          } else if (dev->at_eof()) {
111             if (verbose) {
112                Jmsg(jcr, M_INFO, 0, _("End of file %u  on device %s, Volume \"%s\"\n"),
113                   dev->file, dev->print_name(), dcr->VolumeName);
114             }
115             Dmsg3(200, "End of file %u  on device %s, Volume \"%s\"\n",
116                   dev->file, dev->print_name(), dcr->VolumeName);
117             continue;
118          } else if (dev->is_short_block()) {
119             Jmsg1(jcr, M_ERROR, 0, "%s", dev->errmsg);
120             continue;
121          } else {
122             /* I/O error or strange end of tape */
123             display_tape_error_status(jcr, dev);
124             if (forge_on || jcr->ignore_label_errors) {
125                dev->fsr(1);       /* try skipping bad record */
126                Pmsg0(000, _("Did fsr\n"));
127                continue;              /* try to continue */
128             }
129             ok = false;               /* stop everything */
130             break;
131          }
132       }
133       Dmsg2(dbglvl, "Read new block at pos=%u:%u\n", dev->file, dev->block_num);
134 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
135       /* this does not stop when file/block are too big */
136       if (!match_bsr_block(jcr->bsr, block)) {
137          if (try_repositioning(jcr, rec, dev)) {
138             break;                    /* get next volume */
139          }
140          continue;                    /* skip this record */
141       }
142 #endif
143
144       /*
145        * Get a new record for each Job as defined by
146        *   VolSessionId and VolSessionTime
147        */
148       bool found = false;
149       foreach_dlist(rec, recs) {
150          if (rec->VolSessionId == block->VolSessionId &&
151              rec->VolSessionTime == block->VolSessionTime) {
152             found = true;
153             break;
154           }
155       }
156       if (!found) {
157          rec = new_record();
158          recs->prepend(rec);
159          Dmsg3(dbglvl, "New record for state=%s SI=%d ST=%d\n",
160              rec_state_to_str(rec),
161              block->VolSessionId, block->VolSessionTime);
162       }
163       Dmsg3(dbglvl, "Before read rec loop. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
164             block->BlockNumber, rec->remainder);
165       record = 0;
166       rec->state = 0;
167       Dmsg1(dbglvl, "Block %s empty\n", is_block_empty(rec)?"is":"NOT");
168       for (rec->state=0; ok && !is_block_empty(rec); ) {
169          if (!read_record_from_block(block, rec)) {
170             Dmsg3(400, "!read-break. state=%s blk=%d rem=%d\n", rec_state_to_str(rec),
171                   block->BlockNumber, rec->remainder);
172             break;
173          }
174          Dmsg5(dbglvl, "read-OK. state=%s blk=%d rem=%d file:block=%u:%u\n",
175                  rec_state_to_str(rec), block->BlockNumber, rec->remainder,
176                  dev->file, dev->block_num);
177          /*
178           * At this point, we have at least a record header.
179           *  Now decide if we want this record or not, but remember
180           *  before accessing the record, we may need to read again to
181           *  get all the data.
182           */
183          record++;
184          Dmsg6(dbglvl, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
185             rec_state_to_str(rec), block->BlockNumber,
186             rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
187
188          if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
189             Dmsg0(40, "Get EOM LABEL\n");
190             break;                         /* yes, get out */
191          }
192
193          /* Some sort of label? */
194          if (rec->FileIndex < 0) {
195             handle_session_record(dev, rec, &sessrec);
196             if (jcr->bsr) {
197                /* We just check block FI and FT not FileIndex */
198                rec->match_stat = match_bsr_block(jcr->bsr, block);
199             } else {
200                rec->match_stat = 0;
201             }
202             /*
203              * Note, we pass *all* labels to the callback routine. If
204              *  he wants to know if they matched the bsr, then he must
205              *  check the match_stat in the record */
206             ok = record_cb(dcr, rec);
207             /*
208              * If this is the end of the Session (EOS) for this record
209              *  we can remove the record.  Note, there is a separate
210              *  record to read each session. If a new session is seen
211              *  a new record will be created at approx line 157 above.
212              */
213             if (rec->FileIndex == EOS_LABEL) {
214                Dmsg2(dbglvl, "Remove EOS rec. SI=%d ST=%d\n", rec->VolSessionId,
215                   rec->VolSessionTime);
216                recs->remove(rec);
217                free_record(rec);
218             }
219             continue;
220          } /* end if label record */
221
222          /*
223           * Apply BSR filter
224           */
225          if (jcr->bsr) {
226             rec->match_stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec);
227             if (rec->match_stat == -1) { /* no more possible matches */
228                done = true;   /* all items found, stop */
229                Dmsg2(dbglvl, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
230                break;
231             } else if (rec->match_stat == 0) {  /* no match */
232                Dmsg4(dbglvl, "BSR no match: clear rem=%d FI=%d before set_eof pos %u:%u\n",
233                   rec->remainder, rec->FileIndex, dev->file, dev->block_num);
234                rec->remainder = 0;
235                rec->state &= ~REC_PARTIAL_RECORD;
236                if (try_repositioning(jcr, rec, dev)) {
237                   break;
238                }
239                continue;              /* we don't want record, read next one */
240             }
241          }
242          dcr->VolLastIndex = rec->FileIndex;  /* let caller know where we are */
243          if (is_partial_record(rec)) {
244             Dmsg6(dbglvl, "Partial, break. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
245                rec_state_to_str(rec), block->BlockNumber,
246                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
247             break;                    /* read second part of record */
248          }
249          ok = record_cb(dcr, rec);
250          /*
251           * If we have a digest stream, we check to see if we have 
252           *  finished the current bsr, and if so, repositioning will
253           *  be truned on.
254           */
255          if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
256             Dmsg3(dbglvl, "Have digest FI=%u before bsr check pos %u:%u\n", rec->FileIndex,
257                   dev->file, dev->block_num);
258             if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dev)) {
259                Dmsg2(dbglvl, "This bsr done, break pos %u:%u\n",
260                      dev->file, dev->block_num);
261                break;
262             }
263             Dmsg2(900, "After is_bsr_done pos %u:%u\n", dev->file, dev->block_num);
264          }
265       } /* end for loop over records */
266       Dmsg2(dbglvl, "After end recs in block. pos=%u:%u\n", dev->file, dev->block_num);
267    } /* end for loop over blocks */
268 // Dmsg2(dbglvl, "Position=(file:block) %u:%u\n", dev->file, dev->block_num);
269
270    /* Walk down list and free all remaining allocated recs */
271    while (!recs->empty()) {
272       rec = (DEV_RECORD *)recs->first();
273       recs->remove(rec);
274       free_record(rec);
275    }
276    delete recs;
277    print_block_read_errors(jcr, block);
278    return ok;
279 }
280
281 /*
282  * See if we can reposition.
283  *   Returns:  true  if at end of volume
284  *             false otherwise
285  */
286 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DEVICE *dev)
287 {
288    BSR *bsr;
289    bsr = find_next_bsr(jcr->bsr, dev);
290    if (bsr == NULL && jcr->bsr->mount_next_volume) {
291       Dmsg0(dbglvl, "Would mount next volume here\n");
292       Dmsg2(dbglvl, "Current postion (file:block) %u:%u\n",
293          dev->file, dev->block_num);
294       jcr->bsr->mount_next_volume = false;
295       if (!dev->at_eot()) {
296          /* Set EOT flag to force mount of next Volume */
297          jcr->mount_next_volume = true;
298          dev->set_eot();
299       }
300       rec->Block = 0;
301       return true;
302    }
303    if (bsr) {
304       if (verbose) {
305          Jmsg(jcr, M_INFO, 0, _("Reposition from (file:block) %u:%u to %u:%u\n"),
306             dev->file, dev->block_num, bsr->volfile->sfile,
307             bsr->volblock->sblock);
308       }
309       Dmsg4(dbglvl, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
310             dev->file, dev->block_num, bsr->volfile->sfile,
311             bsr->volblock->sblock);
312       dev->reposition(bsr->volfile->sfile, bsr->volblock->sblock);
313       rec->Block = 0;
314    }
315    return false;
316 }
317
318 /*
319  * Position to the first file on this volume
320  */
321 static BSR *position_to_first_file(JCR *jcr, DEVICE *dev)
322 {
323    BSR *bsr = NULL;
324    /*
325     * Now find and position to first file and block
326     *   on this tape.
327     */
328    if (jcr->bsr) {
329       jcr->bsr->reposition = true;    /* force repositioning */
330       bsr = find_next_bsr(jcr->bsr, dev);
331       if (bsr && (bsr->volfile->sfile != 0 || bsr->volblock->sblock != 0)) {
332          Jmsg(jcr, M_INFO, 0, _("Forward spacing to file:block %u:%u.\n"),
333             bsr->volfile->sfile, bsr->volblock->sblock);
334          Dmsg2(dbglvl, "Forward spacing to file:block %u:%u.\n",
335             bsr->volfile->sfile, bsr->volblock->sblock);
336          dev->reposition(bsr->volfile->sfile, bsr->volblock->sblock);
337       }
338    }
339    return bsr;
340 }
341
342
343 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
344 {
345    const char *rtype;
346    char buf[100];
347
348    memset(sessrec, 0, sizeof(sessrec));
349    switch (rec->FileIndex) {
350    case PRE_LABEL:
351       rtype = _("Fresh Volume Label");
352       break;
353    case VOL_LABEL:
354       rtype = _("Volume Label");
355       unser_volume_label(dev, rec);
356       break;
357    case SOS_LABEL:
358       rtype = _("Begin Session");
359       unser_session_label(sessrec, rec);
360       break;
361    case EOS_LABEL:
362       rtype = _("End Session");
363       break;
364    case EOM_LABEL:
365       rtype = _("End of Media");
366       break;
367    default:
368       bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
369       rtype = buf;
370       break;
371    }
372    Dmsg5(dbglvl, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
373          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
374 }
375
376 #ifdef DEBUG
377 static char *rec_state_to_str(DEV_RECORD *rec)
378 {
379    static char buf[200];
380    buf[0] = 0;
381    if (rec->state & REC_NO_HEADER) {
382       bstrncat(buf, "Nohdr,", sizeof(buf));
383    }
384    if (is_partial_record(rec)) {
385       bstrncat(buf, "partial,", sizeof(buf));
386    }
387    if (rec->state & REC_BLOCK_EMPTY) {
388       bstrncat(buf, "empty,", sizeof(buf));
389    }
390    if (rec->state & REC_NO_MATCH) {
391       bstrncat(buf, "Nomatch,", sizeof(buf));
392    }
393    if (rec->state & REC_CONTINUATION) {
394       bstrncat(buf, "cont,", sizeof(buf));
395    }
396    if (buf[0]) {
397       buf[strlen(buf)-1] = 0;
398    }
399    return buf;
400 }
401 #endif