2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 Kern Sibbald
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.
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.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
21 * record_read.c -- Volume (tape/disk) record read functions
23 * Kern Sibbald, April MMI
24 * added BB02 format October MMII
32 /* Imported subroutines */
34 static const int read_dbglvl = 200;
35 static const int dbgep = 200; /* debug execution path */
38 * Read the header record
40 static bool read_header(DCR *dcr, DEV_BLOCK *block, DEV_RECORD *rec)
43 uint32_t VolSessionId;
44 uint32_t VolSessionTime;
48 char buf1[100], buf2[100];
50 Dmsg0(dbgep, "=== rpath 1 read_header\n");
51 /* Clear state flags */
53 if (block->dev->is_tape()) {
54 rec->state_bits |= REC_ISTAPE;
56 rec->Block = ((DEVICE *)block->dev)->EndBlock;
57 rec->File = ((DEVICE *)block->dev)->EndFile;
60 * Get the header. There is always a full header,
61 * otherwise we find it in the next block.
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) {
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);
75 unser_begin(block->bufp, WRITE_RECHDR_LENGTH);
76 if (block->BlockVer == 1) {
77 unser_uint32(VolSessionId);
78 unser_uint32(VolSessionTime);
80 VolSessionId = block->VolSessionId;
81 VolSessionTime = block->VolSessionTime;
83 unser_int32(FileIndex);
85 unser_uint32(rec->data_bytes);
91 /* If we are looking for more (remainder!=0), we reject anything
92 * where the VolSessionId and VolSessionTime don't agree
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 */
102 /* if Stream is negative, it means that this is a continuation
103 * of a previous partially written record.
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",
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 */
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 */
126 rec->VolSessionId = VolSessionId;
127 rec->VolSessionTime = VolSessionTime;
128 rec->FileIndex = FileIndex;
130 Dmsg0(dbgep, "=== rpath 9 FileIndex>0\n");
131 if (block->FirstIndex == 0) {
132 Dmsg0(dbgep, "=== rpath 10 FirstIndex\n");
133 block->FirstIndex = FileIndex;
135 block->LastIndex = rec->FileIndex;
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,
143 Dmsg0(dbgep, "=== rpath 11a block out of records\n");
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
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 */
159 if (rec->data_bytes >= MAX_BLOCK_LENGTH) {
160 Dmsg0(dbgep, "=== rpath 11b maxlen too big\n");
162 * Something is wrong, force read of next block, abort
163 * continuing with this block.
165 rec->state_bits |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
167 Jmsg2(dcr->jcr, M_WARNING, 0, _("Sanity check failed. maxlen=%d datalen=%d. Block discarded.\n"),
168 MAX_BLOCK_LENGTH, rec->data_bytes);
172 rec->data = check_pool_memory_size(rec->data, rec->data_len+rec->data_bytes);
173 rec->rstate = st_data;
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.
183 static void read_data(DEV_BLOCK *block, DEV_RECORD *rec)
185 char buf1[100], buf2[100];
187 Dmsg0(dbgep, "=== rpath 22 read_data\n");
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
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;
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,
209 Dmsg0(dbgep, "=== rpath 24 partial record\n");
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);
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.
230 bool read_record_from_block(DCR *dcr, DEV_RECORD *rec)
234 Dmsg0(dbgep, "=== rpath 1 Enter read_record_from block\n");
236 switch (rec->rstate) {
238 dump_block(dcr->ameta_block, "st_none");
240 Dmsg0(dbgep, "=== rpath 33 st_header\n");
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");
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 */
258 Dmsg0(dbgep, "=== rpath 50 default\n");
259 Dmsg0(0, "======= In default !!!!!\n");
260 Pmsg1(190, "Read: unknown state=%d\n", rec->rstate);
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);
273 rec->rstate = st_none;