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