]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/read_record.c
Update the Microsoft Visual Studio build to match the MinGW32 build.
[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          Dmsg6(dbglvl, "OK callback. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
250                rec_state_to_str(rec), block->BlockNumber,
251                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
252          ok = record_cb(dcr, rec);
253          /*
254           * If we have a digest stream, we check to see if we have 
255           *  finished the current bsr, and if so, repositioning will
256           *  be truned on.
257           */
258          if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) {
259             Dmsg3(dbglvl, "Have digest FI=%u before bsr check pos %u:%u\n", rec->FileIndex,
260                   dev->file, dev->block_num);
261             if (is_this_bsr_done(jcr->bsr, rec) && try_repositioning(jcr, rec, dev)) {
262                Dmsg2(dbglvl, "This bsr done, break pos %u:%u\n",
263                      dev->file, dev->block_num);
264                break;
265             }
266             Dmsg2(900, "After is_bsr_done pos %u:%u\n", dev->file, dev->block_num);
267          }
268       } /* end for loop over records */
269       Dmsg2(dbglvl, "After end recs in block. pos=%u:%u\n", dev->file, dev->block_num);
270    } /* end for loop over blocks */
271 // Dmsg2(dbglvl, "Position=(file:block) %u:%u\n", dev->file, dev->block_num);
272
273    /* Walk down list and free all remaining allocated recs */
274    while (!recs->empty()) {
275       rec = (DEV_RECORD *)recs->first();
276       recs->remove(rec);
277       free_record(rec);
278    }
279    delete recs;
280    print_block_read_errors(jcr, block);
281    return ok;
282 }
283
284 /*
285  * See if we can reposition.
286  *   Returns:  true  if at end of volume
287  *             false otherwise
288  */
289 static bool try_repositioning(JCR *jcr, DEV_RECORD *rec, DEVICE *dev)
290 {
291    BSR *bsr;
292    bsr = find_next_bsr(jcr->bsr, dev);
293    if (bsr == NULL && jcr->bsr->mount_next_volume) {
294       Dmsg0(dbglvl, "Would mount next volume here\n");
295       Dmsg2(dbglvl, "Current postion (file:block) %u:%u\n",
296          dev->file, dev->block_num);
297       jcr->bsr->mount_next_volume = false;
298       if (!dev->at_eot()) {
299          /* Set EOT flag to force mount of next Volume */
300          jcr->mount_next_volume = true;
301          dev->set_eot();
302       }
303       rec->Block = 0;
304       return true;
305    }
306    if (bsr) {
307       if (verbose) {
308          Jmsg(jcr, M_INFO, 0, _("Reposition from (file:block) %u:%u to %u:%u\n"),
309             dev->file, dev->block_num, bsr->volfile->sfile,
310             bsr->volblock->sblock);
311       }
312       Dmsg4(dbglvl, "Try_Reposition from (file:block) %u:%u to %u:%u\n",
313             dev->file, dev->block_num, bsr->volfile->sfile,
314             bsr->volblock->sblock);
315       dev->reposition(bsr->volfile->sfile, bsr->volblock->sblock);
316       rec->Block = 0;
317    }
318    return false;
319 }
320
321 /*
322  * Position to the first file on this volume
323  */
324 static BSR *position_to_first_file(JCR *jcr, DEVICE *dev)
325 {
326    BSR *bsr = NULL;
327    /*
328     * Now find and position to first file and block
329     *   on this tape.
330     */
331    if (jcr->bsr) {
332       jcr->bsr->reposition = true;    /* force repositioning */
333       bsr = find_next_bsr(jcr->bsr, dev);
334       if (bsr && (bsr->volfile->sfile != 0 || bsr->volblock->sblock != 0)) {
335          Jmsg(jcr, M_INFO, 0, _("Forward spacing to file:block %u:%u.\n"),
336             bsr->volfile->sfile, bsr->volblock->sblock);
337          Dmsg2(dbglvl, "Forward spacing to file:block %u:%u.\n",
338             bsr->volfile->sfile, bsr->volblock->sblock);
339          dev->reposition(bsr->volfile->sfile, bsr->volblock->sblock);
340       }
341    }
342    return bsr;
343 }
344
345
346 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
347 {
348    const char *rtype;
349    char buf[100];
350
351    memset(sessrec, 0, sizeof(sessrec));
352    switch (rec->FileIndex) {
353    case PRE_LABEL:
354       rtype = _("Fresh Volume Label");
355       break;
356    case VOL_LABEL:
357       rtype = _("Volume Label");
358       unser_volume_label(dev, rec);
359       break;
360    case SOS_LABEL:
361       rtype = _("Begin Session");
362       unser_session_label(sessrec, rec);
363       break;
364    case EOS_LABEL:
365       rtype = _("End Session");
366       break;
367    case EOM_LABEL:
368       rtype = _("End of Media");
369       break;
370    default:
371       bsnprintf(buf, sizeof(buf), _("Unknown code %d\n"), rec->FileIndex);
372       rtype = buf;
373       break;
374    }
375    Dmsg5(dbglvl, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
376          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
377 }
378
379 #ifdef DEBUG
380 static char *rec_state_to_str(DEV_RECORD *rec)
381 {
382    static char buf[200];
383    buf[0] = 0;
384    if (rec->state & REC_NO_HEADER) {
385       bstrncat(buf, "Nohdr,", sizeof(buf));
386    }
387    if (is_partial_record(rec)) {
388       bstrncat(buf, "partial,", sizeof(buf));
389    }
390    if (rec->state & REC_BLOCK_EMPTY) {
391       bstrncat(buf, "empty,", sizeof(buf));
392    }
393    if (rec->state & REC_NO_MATCH) {
394       bstrncat(buf, "Nomatch,", sizeof(buf));
395    }
396    if (rec->state & REC_CONTINUATION) {
397       bstrncat(buf, "cont,", sizeof(buf));
398    }
399    if (buf[0]) {
400       buf[strlen(buf)-1] = 0;
401    }
402    return buf;
403 }
404 #endif