]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/read_record.c
Implement repositioning code + misc
[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 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
37 #ifdef DEBUG
38 static char *rec_state_to_str(DEV_RECORD *rec);
39 #endif
40
41 int read_records(JCR *jcr,  DEVICE *dev, 
42        int record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec),
43        int mount_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block))
44 {
45    DEV_BLOCK *block;
46    DEV_RECORD *rec = NULL;
47    uint32_t record;
48    int ok = TRUE;
49    bool done = false;
50    SESSION_LABEL sessrec;
51    dlist *recs;                         /* linked list of rec packets open */
52
53    block = new_block(dev);
54    recs = new dlist(rec, &rec->link);
55
56    for ( ; ok && !done; ) {
57       if (job_canceled(jcr)) {
58          ok = FALSE;
59          break;
60       }
61       if (!read_block_from_device(jcr, dev, block, CHECK_BLOCK_NUMBERS)) {
62          Dmsg0(20, "!read_record()\n");
63          if (dev_state(dev, ST_EOT)) {
64             DEV_RECORD *trec = new_record();
65
66             Dmsg3(100, "EOT. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec), 
67                   block->BlockNumber, rec->remainder);
68             Jmsg(jcr, M_INFO, 0, "End of Volume at file %u  on device %s, Volume \"%s\"\n", 
69                  dev->file, dev_name(dev), jcr->VolumeName);
70             if (!mount_cb(jcr, dev, block)) {
71                Jmsg(jcr, M_INFO, 0, "End of all volumes.\n");
72                Dmsg3(100, "After mount next vol. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec), 
73                   block->BlockNumber, rec->remainder);
74                ok = FALSE;
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                trec->Block = rec->Block; /* return block last read */
83                ok = record_cb(jcr, dev, block, trec);
84                free_record(trec);
85                break;
86             }
87             Dmsg3(100, "After mount next vol. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec), 
88                   block->BlockNumber, rec->remainder);
89             /*
90              * We just have a new tape up, now read the label (first record)
91              *  and pass it off to the callback routine, then continue
92              *  most likely reading the previous record.
93              */
94             read_block_from_device(jcr, dev, block, NO_BLOCK_NUMBER_CHECK);
95             read_record_from_block(block, trec);
96             get_session_record(dev, trec, &sessrec);
97             ok = record_cb(jcr, dev, block, trec);
98             free_record(trec);
99             /*
100              * Now find and position to first file and block 
101              *   on this tape.
102              */
103             BSR *bsr = find_next_bsr(jcr->bsr, dev);
104             if (bsr == NULL && jcr->bsr->mount_next_volume) {
105                Dmsg0(100, "Would mount next volume here\n");
106             }     
107             if (bsr) {
108                Dmsg4(100, "Reposition new tape from (file:block) %d:%d to %d:%d\n",
109                   dev->file, dev->block_num, bsr->volfile->sfile,
110                   bsr->volblock->sblock);
111                reposition_dev(dev, bsr->volfile->sfile, bsr->volblock->sblock);
112                Dmsg2(100, "Now at (file:block) %d:%d\n",
113                   dev->file, dev->block_num);
114             }
115          } else if (dev_state(dev, ST_EOF)) {
116             Jmsg(jcr, M_INFO, 0, "Got EOF at file %u  on device %s, Volume \"%s\"\n", 
117                   dev->file, dev_name(dev), jcr->VolumeName);
118             Dmsg0(20, "read_record got eof. try again\n");
119             continue;
120          } else if (dev_state(dev, ST_SHORT)) {
121             Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
122             continue;
123          } else {
124             /* I/O error or strange end of tape */
125             display_tape_error_status(jcr, dev);
126             ok = FALSE;
127             break;
128          }
129       }
130       Dmsg5(100, "Read block: dev=%d blk=%d VI=%u VT=%u blen=%d\n", dev->block_num, block->BlockNumber, 
131          block->VolSessionId, block->VolSessionTime, block->block_len);
132       if (!match_bsr_block(jcr->bsr, block)) {
133          Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
134             block->BlockNumber, block->block_len, block->BlockVer,
135             block->VolSessionId, block->VolSessionTime);
136          continue;
137       }
138       Dmsg4(100, "Block: %d VI=%u VT=%u blen=%d\n", block->BlockNumber, 
139          block->VolSessionId, block->VolSessionTime, block->block_len);
140
141       /*
142        * Get a new record for each Job as defined by
143        *   VolSessionId and VolSessionTime 
144        */
145       bool found = false;
146       for (rec=(DEV_RECORD *)recs->first(); rec; rec=(DEV_RECORD *)recs->next(rec)) {
147          if (rec->VolSessionId == block->VolSessionId &&
148              rec->VolSessionTime == block->VolSessionTime) {
149             found = true;
150             break;
151           }
152       }
153       if (!found) {
154          rec = new_record();
155          recs->prepend(rec);
156          Dmsg2(100, "New record for SI=%d ST=%d\n",
157              block->VolSessionId, block->VolSessionTime);
158       } else {
159          if (rec->Block != 0 && (rec->Block+1) != block->BlockNumber) {
160             Jmsg(jcr, M_ERROR, 0, _("Invalid block number. Expected %u, got %u\n"),
161                  rec->Block+1, block->BlockNumber);
162          }
163       }
164       record = 0;
165       for (rec->state=0; !is_block_empty(rec); ) {
166          if (!read_record_from_block(block, rec)) {
167             Dmsg3(10, "!read-break. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec), 
168                   block->BlockNumber, rec->remainder);
169             break;
170          }
171          Dmsg5(100, "read-OK. stat=%s blk=%d rem=%d file:block=%d:%d\n", 
172                  rec_state_to_str(rec), block->BlockNumber, rec->remainder,
173                  dev->file, dev->block_num);
174          /*
175           * At this point, we have at least a record header.
176           *  Now decide if we want this record or not, but remember
177           *  before accessing the record, we may need to read again to
178           *  get all the data.
179           */
180          record++;
181          Dmsg6(100, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
182             rec_state_to_str(rec), block->BlockNumber,
183             rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
184          Dmsg4(30, "VolSId=%ld FI=%s Strm=%s Size=%ld\n", rec->VolSessionId,
185                FI_to_ascii(rec->FileIndex), 
186                stream_to_ascii(rec->Stream, rec->FileIndex), 
187                rec->data_len);
188
189          if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
190             Dmsg0(40, "Get EOM LABEL\n");
191             break;                         /* yes, get out */
192          }
193
194          /* Some sort of label? */ 
195          if (rec->FileIndex < 0) {
196             get_session_record(dev, rec, &sessrec);
197             ok = record_cb(jcr, dev, block, rec);
198             if (rec->FileIndex == EOS_LABEL) {
199                Dmsg2(100, "Remove rec. SI=%d ST=%d\n", rec->VolSessionId,
200                   rec->VolSessionTime);
201                recs->remove(rec);
202                free_record(rec);
203             }
204             continue;
205          } /* end if label record */
206
207          /* 
208           * Apply BSR filter
209           */
210          if (jcr->bsr) {
211             int stat = match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec);
212             if (stat == -1) { /* no more possible matches */
213                done = true;   /* all items found, stop */
214                break;
215             } else if (stat == 0) {  /* no match */
216                BSR *bsr;
217                bsr = find_next_bsr(jcr->bsr, dev);
218                if (bsr == NULL && jcr->bsr->mount_next_volume) {
219                   Dmsg0(100, "Would mount next volume here\n");
220                   Dmsg2(100, "Current postion (file:block) %d:%d\n",
221                      dev->file, dev->block_num);
222                   dev->state |= ST_EOT;
223                   rec->Block = 0;
224                   break;
225                }     
226                if (bsr) {
227                   Dmsg4(100, "Reposition from (file:block) %d:%d to %d:%d\n",
228                      dev->file, dev->block_num, bsr->volfile->sfile,
229                      bsr->volblock->sblock);
230                   reposition_dev(dev, bsr->volfile->sfile, bsr->volblock->sblock);
231                   rec->Block = 0;
232                   Dmsg2(100, "Now at (file:block) %d:%d\n",
233                      dev->file, dev->block_num);
234                }
235                Dmsg5(10, "BSR no match rec=%d block=%d SessId=%d SessTime=%d FI=%d\n",
236                   record, block->BlockNumber, rec->VolSessionId, rec->VolSessionTime, 
237                   rec->FileIndex);
238                continue;              /* we don't want record, read next one */
239             }
240          }
241          if (is_partial_record(rec)) {
242             Dmsg6(10, "Partial, break. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
243                rec_state_to_str(rec), block->BlockNumber,
244                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
245             break;                    /* read second part of record */
246          }
247          ok = record_cb(jcr, dev, block, rec);
248       } /* end for loop over records */
249    } /* end for loop over blocks */
250
251    /* Walk down list and free all remaining allocated recs */
252    for (rec=(DEV_RECORD *)recs->first(); rec; ) {
253       DEV_RECORD *nrec = (DEV_RECORD *)recs->next(rec);
254       recs->remove(rec);
255       free_record(rec);
256       rec = nrec;
257    }
258    delete recs;
259    free_block(block);
260    return ok;
261 }
262
263
264 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
265 {
266    char *rtype;
267    memset(sessrec, 0, sizeof(sessrec));
268    switch (rec->FileIndex) {
269    case PRE_LABEL:
270       rtype = "Fresh Volume Label";   
271       break;
272    case VOL_LABEL:
273       rtype = "Volume Label";
274       unser_volume_label(dev, rec);
275       break;
276    case SOS_LABEL:
277       rtype = "Begin Session";
278       unser_session_label(sessrec, rec);
279       break;
280    case EOS_LABEL:
281       rtype = "End Session";
282       break;
283    case EOM_LABEL:
284       rtype = "End of Media";
285       break;
286    default:
287       rtype = "Unknown";
288       break;
289    }
290    Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
291          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
292 }
293
294 #ifdef DEBUG
295 static char *rec_state_to_str(DEV_RECORD *rec)
296 {
297    static char buf[200]; 
298    buf[0] = 0;
299    if (rec->state & REC_NO_HEADER) {
300       strcat(buf, "Nohdr,");
301    }
302    if (is_partial_record(rec)) {
303       strcat(buf, "partial,");
304    }
305    if (rec->state & REC_BLOCK_EMPTY) {
306       strcat(buf, "empty,");
307    }
308    if (rec->state & REC_NO_MATCH) {
309       strcat(buf, "Nomatch,");
310    }
311    if (rec->state & REC_CONTINUATION) {
312       strcat(buf, "cont,");
313    }
314    if (buf[0]) {
315       buf[strlen(buf)-1] = 0;
316    }
317    return buf;
318 }
319 #endif