]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/record_read.c
Fix bug #180 ERR=success in btape when tape error
[bacula/bacula] / bacula / src / stored / record_read.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2001-2014 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from many
7    others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    Bacula® is a registered trademark of Kern Sibbald.
15 */
16 /*
17  *
18  *   record_read.c -- Volume (tape/disk) record read functions
19  *
20  *            Kern Sibbald, April MMI
21  *              added BB02 format October MMII
22  */
23
24
25 #include "bacula.h"
26 #include "stored.h"
27
28 /* Imported subroutines */
29
30
31 static const int read_dbglvl = 200;
32 static const int dbgep = 250;         /* debug execution path */
33
34 /*
35  * Read the header record
36  */
37 static bool read_header(DCR *dcr, DEV_BLOCK *block, DEV_RECORD *rec)
38 {
39    ser_declare;
40    uint32_t VolSessionId;
41    uint32_t VolSessionTime;
42    int32_t  FileIndex;
43    int32_t  Stream;
44    uint32_t rhl;
45    char buf1[100], buf2[100];
46
47    Dmsg0(dbgep, "=== rpath 1 read_header\n");
48    /* Clear state flags */
49    rec->state_bits = 0;
50    if (block->dev->is_tape()) {
51       rec->state_bits |= REC_ISTAPE;
52    }
53    rec->Block = ((DEVICE *)block->dev)->EndBlock;
54    rec->File = ((DEVICE *)block->dev)->EndFile;
55
56    /*
57     * Get the header. There is always a full header,
58     * otherwise we find it in the next block.
59     */
60    Dmsg3(read_dbglvl, "Block=%d Ver=%d block_len=%u\n",
61          block->BlockNumber, block->BlockVer, block->block_len);
62    if (block->BlockVer == 1) {
63       rhl = RECHDR1_LENGTH;
64    } else {
65       rhl = RECHDR2_LENGTH;
66    }
67    if (rec->remlen >= rhl) {
68       Dmsg0(dbgep, "=== rpath 2 begin unserial header\n");
69       Dmsg4(read_dbglvl, "read_header: remlen=%d data_len=%d rem=%d blkver=%d\n",
70             rec->remlen, rec->data_len, rec->remainder, block->BlockVer);
71
72       unser_begin(block->bufp, WRITE_RECHDR_LENGTH);
73       if (block->BlockVer == 1) {
74          unser_uint32(VolSessionId);
75          unser_uint32(VolSessionTime);
76       } else {
77          VolSessionId = block->VolSessionId;
78          VolSessionTime = block->VolSessionTime;
79       }
80       unser_int32(FileIndex);
81       unser_int32(Stream);
82       unser_uint32(rec->data_bytes);
83
84       block->bufp += rhl;
85       block->binbuf -= rhl;
86       rec->remlen -= rhl;
87
88       /* If we are looking for more (remainder!=0), we reject anything
89        *  where the VolSessionId and VolSessionTime don't agree
90        */
91       if (rec->remainder && (rec->VolSessionId != VolSessionId ||
92                              rec->VolSessionTime != VolSessionTime)) {
93          rec->state_bits |= REC_NO_MATCH;
94          Dmsg0(read_dbglvl, "remainder and VolSession doesn't match\n");
95          Dmsg0(dbgep, "=== rpath 4 VolSession no match\n");
96          return false;             /* This is from some other Session */
97       }
98
99       /* if Stream is negative, it means that this is a continuation
100        * of a previous partially written record.
101        */
102       if (Stream < 0) {               /* continuation record? */
103          Dmsg0(dbgep, "=== rpath 5 negative stream\n");
104          Dmsg1(read_dbglvl, "Got negative Stream => continuation. remainder=%d\n",
105             rec->remainder);
106          rec->state_bits |= REC_CONTINUATION;
107          if (!rec->remainder) {       /* if we didn't read previously */
108             Dmsg0(dbgep, "=== rpath 6 no remainder\n");
109             rec->data_len = 0;        /* return data as if no continuation */
110          } else if (rec->Stream != -Stream) {
111             Dmsg0(dbgep, "=== rpath 7 wrong cont stream\n");
112             rec->state_bits |= REC_NO_MATCH;
113             return false;             /* This is from some other Session */
114          }
115          rec->Stream = -Stream;       /* set correct Stream */
116          rec->maskedStream = rec->Stream & STREAMMASK_TYPE;
117       } else {                        /* Regular record */
118          Dmsg0(dbgep, "=== rpath 8 normal stream\n");
119          rec->Stream = Stream;
120          rec->maskedStream = rec->Stream & STREAMMASK_TYPE;
121          rec->data_len = 0;           /* transfer to beginning of data */
122       }
123       rec->VolSessionId = VolSessionId;
124       rec->VolSessionTime = VolSessionTime;
125       rec->FileIndex = FileIndex;
126       if (FileIndex > 0) {
127          Dmsg0(dbgep, "=== rpath 9 FileIndex>0\n");
128          if (block->FirstIndex == 0) {
129             Dmsg0(dbgep, "=== rpath 10 FirstIndex\n");
130             block->FirstIndex = FileIndex;
131          }
132          block->LastIndex = rec->FileIndex;
133       }
134
135       Dmsg6(read_dbglvl, "read_header: FI=%s SessId=%d Strm=%s len=%u rec->remlen=%d data_len=%d\n",
136          FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
137          stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_bytes, rec->remlen,
138          rec->data_len);
139    } else {
140       Dmsg0(dbgep, "=== rpath 11a block out of records\n");
141       /*
142        * No more records in this block because the number
143        * of remaining bytes are less than a record header
144        * length, so return empty handed, but indicate that
145        * he must read again. By returning, we allow the
146        * higher level routine to fetch the next block and
147        * then reread.
148        */
149       Dmsg0(read_dbglvl, "read_header: End of block\n");
150       rec->state_bits |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
151       empty_block(block);                      /* mark block empty */
152       return false;
153    }
154
155    /* Sanity check */
156    if (rec->data_bytes >= MAX_BLOCK_LENGTH) {
157       Dmsg0(dbgep, "=== rpath 11b maxlen too big\n");
158       /*
159        * Something is wrong, force read of next block, abort
160        *   continuing with this block.
161        */
162       rec->state_bits |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
163       empty_block(block);
164       Jmsg2(dcr->jcr, M_WARNING, 0, _("Sanity check failed. maxlen=%d datalen=%d. Block discarded.\n"),
165          MAX_BLOCK_LENGTH, rec->data_bytes);
166       return false;
167    }
168
169    rec->data = check_pool_memory_size(rec->data, rec->data_len+rec->data_bytes);
170    rec->rstate = st_data;
171    return true;
172 }
173
174 /*
175  * We have just read a header, now read the data into the record.
176  *  Note, if we do not read the full record, we will return to
177  *  read the next header, which will then come back here later
178  *  to finish reading the full record.
179  */
180 static void read_data(DEV_BLOCK *block, DEV_RECORD *rec)
181 {
182    char buf1[100], buf2[100];
183
184    Dmsg0(dbgep, "=== rpath 22 read_data\n");
185    /*
186     * At this point, we have read the header, now we
187     * must transfer as much of the data record as
188     * possible taking into account: 1. A partial
189     * data record may have previously been transferred,
190     * 2. The current block may not contain the whole data
191     * record.
192     */
193    if (rec->remlen >= rec->data_bytes) {
194       Dmsg0(dbgep, "=== rpath 23 full record\n");
195       /* Got whole record */
196       memcpy(rec->data+rec->data_len, block->bufp, rec->data_bytes);
197       block->bufp += rec->data_bytes;
198       block->binbuf -= rec->data_bytes;
199       rec->data_len += rec->data_bytes;
200       rec->remainder = 0;
201       Dmsg5(190, "Rdata full FI=%s SessId=%d Strm=%s len=%d block=%p\n",
202          FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
203          stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
204          block);
205    } else {
206       Dmsg0(dbgep, "=== rpath 24 partial record\n");
207       /* Partial record */
208       memcpy(rec->data+rec->data_len, block->bufp, rec->remlen);
209       block->bufp += rec->remlen;
210       block->binbuf -= rec->remlen;
211       rec->data_len += rec->remlen;
212       rec->remainder = 1;             /* partial record transferred */
213       Dmsg1(read_dbglvl, "read_data: partial xfered=%d\n", rec->data_len);
214       rec->state_bits |= (REC_PARTIAL_RECORD | REC_BLOCK_EMPTY);
215    }
216 }
217
218
219 /*
220  * Read a Record from the block
221  *  Returns: false if nothing read or if the continuation record does not match.
222  *                 In both of these cases, a block read must be done.
223  *           true  if at least the record header was read, this
224  *                 routine may have to be called again with a new
225  *                 block if the entire record was not read.
226  */
227 bool read_record_from_block(DCR *dcr,  DEV_RECORD *rec)
228 {
229    bool rtn;
230
231    Dmsg0(dbgep, "=== rpath 1 Enter read_record_from block\n");
232    for ( ;; ) {
233       switch (rec->rstate) {
234       case st_none:
235          dump_block(dcr->block, "st_none");
236       case st_header:
237          Dmsg0(dbgep, "=== rpath 33 st_header\n");
238          rec->remlen = dcr->block->binbuf;
239          /* Note read_header sets rec->rstate on return true */
240          if (!read_header(dcr, dcr->block, rec)) {  /* sets state */
241             Dmsg0(dbgep, "=== rpath 34 failed read header\n");
242             Dmsg0(read_dbglvl, "read_header returned EOF.\n");
243             goto fail_out;
244          }
245          continue;
246
247       case st_data:
248          Dmsg0(dbgep, "=== rpath 37 st_data\n");
249          read_data(dcr->block, rec);
250          rec->rstate = st_header;         /* next pass look for a header */
251          goto get_out;
252
253       default:
254          Dmsg0(dbgep, "=== rpath 50 default\n");
255          Dmsg0(0, "======= In default !!!!!\n");
256          Pmsg1(190, "Read: unknown state=%d\n", rec->rstate);
257          goto fail_out;
258       }
259    }
260 get_out:
261    char buf1[100], buf2[100];
262    Dmsg5(read_dbglvl, "read_rec return: FI=%s Strm=%s len=%d rem=%d remainder=%d\n",
263          FI_to_ascii(buf1, rec->FileIndex),
264          stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
265          rec->remlen, rec->remainder);
266    rtn = true;
267    goto out;
268 fail_out:
269    rec->rstate = st_none;
270    rtn = false;
271 out:
272    return rtn;
273 }