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