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