]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/read_record.c
This commit was manufactured by cvs2svn to create tag
[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  *
9  *    Kern E. Sibbald, August MMII
10  *
11  *   Version $Id$
12  */
13 /*
14    Copyright (C) 2000-2003 Kern Sibbald and John Walker
15
16    This program is free software; you can redistribute it and/or
17    modify it under the terms of the GNU General Public License as
18    published by the Free Software Foundation; either version 2 of
19    the License, or (at your option) any later version.
20
21    This program is distributed in the hope that it will be useful,
22    but WITHOUT ANY WARRANTY; without even the implied warranty of
23    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24    General Public License for more details.
25
26    You should have received a copy of the GNU General Public
27    License along with this program; if not, write to the Free
28    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
29    MA 02111-1307, USA.
30
31  */
32
33 #include "bacula.h"
34 #include "stored.h"
35
36 /* Forward referenced functions */
37 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
38 static BSR *position_to_first_file(JCR *jcr, DEVICE *dev);
39 static int try_repositioning(JCR *jcr, DEV_RECORD *rec, DEVICE *dev);
40 #ifdef DEBUG
41 static char *rec_state_to_str(DEV_RECORD *rec);
42 #endif
43
44 int read_records(JCR *jcr,  DEVICE *dev, 
45        int record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec),
46        int mount_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block))
47 {
48    DEV_BLOCK *block;
49    DEV_RECORD *rec = NULL;
50    uint32_t record;
51    int ok = TRUE;
52    bool done = false;
53    SESSION_LABEL sessrec;
54    dlist *recs;                         /* linked list of rec packets open */
55
56    block = new_block(dev);
57    recs = new dlist(rec, &rec->link);
58    position_to_first_file(jcr, dev);
59
60    for ( ; ok && !done; ) {
61       if (job_canceled(jcr)) {
62          ok = FALSE;
63          break;
64       }
65       if (!read_block_from_device(jcr, dev, block, CHECK_BLOCK_NUMBERS)) {
66          if (dev_state(dev, ST_EOT)) {
67             DEV_RECORD *trec = new_record();
68
69             Jmsg(jcr, M_INFO, 0, "End of Volume at file %u  on device %s, Volume \"%s\"\n", 
70                  dev->file, dev_name(dev), jcr->VolumeName);
71             if (!mount_cb(jcr, dev, block)) {
72                Jmsg(jcr, M_INFO, 0, "End of all volumes.\n");
73                ok = FALSE;
74                /*
75                 * Create EOT Label so that Media record may
76                 *  be properly updated because this is the last
77                 *  tape.
78                 */
79                trec->FileIndex = EOT_LABEL;
80                trec->File = dev->file;
81                ok = record_cb(jcr, dev, block, trec);
82                free_record(trec);
83                break;
84             }
85             /*
86              * We just have a new tape up, now read the label (first record)
87              *  and pass it off to the callback routine, then continue
88              *  most likely reading the previous record.
89              */
90             read_block_from_device(jcr, dev, block, NO_BLOCK_NUMBER_CHECK);
91             read_record_from_block(block, trec);
92             handle_session_record(dev, trec, &sessrec);
93             ok = record_cb(jcr, dev, block, trec);
94             free_record(trec);
95             position_to_first_file(jcr, dev);
96             /* After reading label, we must read first data block */
97             continue;
98
99          } else if (dev_state(dev, ST_EOF)) {
100             if (verbose) {
101             Jmsg(jcr, M_INFO, 0, "Got EOF at file %u  on device %s, Volume \"%s\"\n", 
102                   dev->file, dev_name(dev), jcr->VolumeName);
103             }
104             Dmsg3(100, "Got EOF at file %u  on device %s, Volume \"%s\"\n", 
105                   dev->file, dev_name(dev), jcr->VolumeName);
106             continue;
107          } else if (dev_state(dev, ST_SHORT)) {
108             Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
109             continue;
110          } else {
111             /* I/O error or strange end of tape */
112             display_tape_error_status(jcr, dev);
113             ok = FALSE;
114             break;
115          }
116       }
117       Dmsg2(100, "New block at position=(file:block) %d:%d\n", dev->file, dev->block_num);
118 #ifdef if_and_when_FAST_BLOCK_REJECTION_is_working
119       /* this does not stop when file/block are too big */
120       if (!match_bsr_block(jcr->bsr, block)) {
121          if (try_repositioning(jcr, rec, dev)) {
122             break;                    /* get next volume */
123          }
124          continue;                    /* skip this record */
125       }
126 #endif
127
128       /*
129        * Get a new record for each Job as defined by
130        *   VolSessionId and VolSessionTime 
131        */
132       bool found = false;
133       for (rec=(DEV_RECORD *)recs->first(); rec; rec=(DEV_RECORD *)recs->next(rec)) {
134          if (rec->VolSessionId == block->VolSessionId &&
135              rec->VolSessionTime == block->VolSessionTime) {
136             found = true;
137             break;
138           }
139       }
140       if (!found) {
141          rec = new_record();
142          recs->prepend(rec);
143          Dmsg2(100, "New record for SI=%d ST=%d\n",
144              block->VolSessionId, block->VolSessionTime);
145       } else {
146 #ifdef xxx
147          if (rec->Block != 0 && (rec->Block+1) != block->BlockNumber) {
148             Jmsg(jcr, M_ERROR, 0, _("Invalid block number. Expected %u, got %u\n"),
149                  rec->Block+1, block->BlockNumber);
150          }
151 #endif 
152       }
153       Dmsg3(100, "After mount next vol. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec), 
154             block->BlockNumber, rec->remainder);
155       record = 0;
156       rec->state = 0;
157       Dmsg1(100, "Block empty %d\n", is_block_empty(rec));
158       for (rec->state=0; !is_block_empty(rec); ) {
159          if (!read_record_from_block(block, rec)) {
160             Dmsg3(100, "!read-break. state=%s blk=%d rem=%d\n", rec_state_to_str(rec), 
161                   block->BlockNumber, rec->remainder);
162             break;
163          }
164          Dmsg5(100, "read-OK. state=%s blk=%d rem=%d file:block=%d:%d\n", 
165                  rec_state_to_str(rec), block->BlockNumber, rec->remainder,
166                  dev->file, dev->block_num);
167          /*
168           * At this point, we have at least a record header.
169           *  Now decide if we want this record or not, but remember
170           *  before accessing the record, we may need to read again to
171           *  get all the data.
172           */
173          record++;
174          Dmsg6(100, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
175             rec_state_to_str(rec), block->BlockNumber,
176             rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
177
178          if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
179             Dmsg0(40, "Get EOM LABEL\n");
180             break;                         /* yes, get out */
181          }
182
183          /* Some sort of label? */ 
184          if (rec->FileIndex < 0) {
185             handle_session_record(dev, rec, &sessrec);
186             ok = record_cb(jcr, dev, block, rec);
187             if (rec->FileIndex == EOS_LABEL) {
188                Dmsg2(100, "Remove rec. SI=%d ST=%d\n", rec->VolSessionId,
189                   rec->VolSessionTime);
190                recs->remove(rec);
191                free_record(rec);
192             }
193             continue;
194          } /* end if label record */
195
196          /* 
197           * Apply BSR filter
198           */
199          if (jcr->bsr) {
200             int stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec);
201             if (stat == -1) { /* no more possible matches */
202                done = true;   /* all items found, stop */
203                Dmsg2(100, "All done=(file:block) %d:%d\n", dev->file, dev->block_num);
204                break;
205             } else if (stat == 0) {  /* no match */
206                Dmsg4(100, "Clear rem=%d FI=%d before set_eof pos %d:%d\n", 
207                   rec->remainder, rec->FileIndex, dev->file, dev->block_num);
208                rec->remainder = 0;
209                rec->state &= ~REC_PARTIAL_RECORD;
210                if (try_repositioning(jcr, rec, dev)) {
211                   break;
212                }     
213                continue;              /* we don't want record, read next one */
214             }
215          }
216          if (is_partial_record(rec)) {
217             Dmsg6(100, "Partial, break. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
218                rec_state_to_str(rec), block->BlockNumber,
219                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
220             break;                    /* read second part of record */
221          }
222          ok = record_cb(jcr, dev, block, rec);
223          if (rec->Stream == STREAM_MD5_SIGNATURE || rec->Stream == STREAM_SHA1_SIGNATURE) {
224             Dmsg3(100, "Done FI=%d before set_eof pos %d:%d\n", rec->FileIndex,
225                   dev->file, dev->block_num);
226             if (match_set_eof(jcr->bsr, rec) && try_repositioning(jcr, rec, dev)) {
227                Dmsg2(100, "Break after match_set_eof pos %d:%d\n",
228                      dev->file, dev->block_num);
229                break;
230             }
231             Dmsg2(100, "After set_eof pos %d:%d\n", dev->file, dev->block_num);
232          }
233       } /* end for loop over records */
234       Dmsg2(100, "After end records position=(file:block) %d:%d\n", dev->file, dev->block_num);
235    } /* end for loop over blocks */
236 // Dmsg2(100, "Position=(file:block) %d:%d\n", dev->file, dev->block_num);
237
238    /* Walk down list and free all remaining allocated recs */
239    for (rec=(DEV_RECORD *)recs->first(); rec; ) {
240       DEV_RECORD *nrec = (DEV_RECORD *)recs->next(rec);
241       recs->remove(rec);
242       free_record(rec);
243       rec = nrec;
244    }
245    delete recs;
246    print_block_read_errors(jcr, block);
247    free_block(block);
248    return ok;
249 }
250
251 /*
252  * See if we can reposition.
253  *   Returns:  1 if at end of volume
254  *             0 otherwise
255  */
256 static int try_repositioning(JCR *jcr, DEV_RECORD *rec, DEVICE *dev)
257 {
258    BSR *bsr;
259    bsr = find_next_bsr(jcr->bsr, dev);
260    if (bsr == NULL && jcr->bsr->mount_next_volume) {
261       Dmsg0(100, "Would mount next volume here\n");
262       Dmsg2(100, "Current postion (file:block) %d:%d\n",
263          dev->file, dev->block_num);
264       jcr->bsr->mount_next_volume = false;
265       dev->state |= ST_EOT;
266       rec->Block = 0;
267       return 1;
268    }     
269    if (bsr) {
270       if (verbose > 1) {
271          Jmsg(jcr, M_INFO, 0, "Reposition from (file:block) %d:%d to %d:%d\n",
272             dev->file, dev->block_num, bsr->volfile->sfile,
273             bsr->volblock->sblock);
274       }
275       Dmsg4(100, "Try_Reposition from (file:block) %d:%d to %d:%d\n",
276             dev->file, dev->block_num, bsr->volfile->sfile,
277             bsr->volblock->sblock);
278       reposition_dev(dev, bsr->volfile->sfile, bsr->volblock->sblock);
279       rec->Block = 0;
280    }
281    return 0;
282 }
283
284 /*
285  * Position to the first file on this volume
286  */
287 static BSR *position_to_first_file(JCR *jcr, DEVICE *dev)
288 {
289    BSR *bsr = NULL;
290    /*
291     * Now find and position to first file and block 
292     *   on this tape.
293     */
294    if (jcr->bsr) {
295       jcr->bsr->reposition = true;    /* force repositioning */
296       bsr = find_next_bsr(jcr->bsr, dev);
297       if (bsr && (bsr->volfile->sfile != 0 || bsr->volblock->sblock != 0)) {
298          Jmsg(jcr, M_INFO, 0, _("Forward spacing to file:block %u:%u.\n"), 
299             bsr->volfile->sfile, bsr->volblock->sblock);
300          Dmsg2(100, "Forward spacing to file:block %u:%u.\n", 
301             bsr->volfile->sfile, bsr->volblock->sblock);
302          reposition_dev(dev, bsr->volfile->sfile, bsr->volblock->sblock);
303       }
304    }
305    return bsr;
306 }
307
308
309 static void handle_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
310 {
311    char *rtype;
312    char buf[100];
313    memset(sessrec, 0, sizeof(sessrec));
314    switch (rec->FileIndex) {
315    case PRE_LABEL:
316       rtype = "Fresh Volume Label";   
317       break;
318    case VOL_LABEL:
319       rtype = "Volume Label";
320       unser_volume_label(dev, rec);
321       break;
322    case SOS_LABEL:
323       rtype = "Begin Session";
324       unser_session_label(sessrec, rec);
325       break;
326    case EOS_LABEL:
327       rtype = "End Session";
328       break;
329    case EOM_LABEL:
330       rtype = "End of Media";
331       break;
332    default:
333       bsnprintf(buf, sizeof(buf), "Unknown code %d\n", rec->FileIndex);
334       rtype = buf;
335       break;
336    }
337    Dmsg5(100, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
338          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
339 }
340
341 #ifdef DEBUG
342 static char *rec_state_to_str(DEV_RECORD *rec)
343 {
344    static char buf[200]; 
345    buf[0] = 0;
346    if (rec->state & REC_NO_HEADER) {
347       strcat(buf, "Nohdr,");
348    }
349    if (is_partial_record(rec)) {
350       strcat(buf, "partial,");
351    }
352    if (rec->state & REC_BLOCK_EMPTY) {
353       strcat(buf, "empty,");
354    }
355    if (rec->state & REC_NO_MATCH) {
356       strcat(buf, "Nomatch,");
357    }
358    if (rec->state & REC_CONTINUATION) {
359       strcat(buf, "cont,");
360    }
361    if (buf[0]) {
362       buf[strlen(buf)-1] = 0;
363    }
364    return buf;
365 }
366 #endif