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