3 * record.c -- tape record handling functions
5 * Kern Sibbald, April MMI
11 Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation; either version 2 of
16 the License, or (at your option) any later version.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
23 You should have received a copy of the GNU General Public
24 License along with this program; if not, write to the Free
25 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
34 extern int debug_level;
37 * Convert a FileIndex into a printable
38 * ASCII string. Not reentrant.
39 * If the FileIndex is negative, it flags the
40 * record as a Label, otherwise it is simply
41 * the FileIndex of the current file.
43 char *FI_to_ascii(int fi)
47 sprintf(buf, "%d", fi);
62 sprintf(buf, "unknown: %d", fi);
69 * Convert a Stream ID into a printable
70 * ASCII string. Not reentrant.
72 char *stream_to_ascii(int stream)
76 case STREAM_UNIX_ATTRIBUTES:
78 case STREAM_FILE_DATA:
80 case STREAM_MD5_SIGNATURE:
82 case STREAM_GZIP_DATA:
85 sprintf(buf, "%d", stream);
91 * Return a new record entity
93 DEV_RECORD *new_record(void)
97 rec = (DEV_RECORD *) get_memory(sizeof(DEV_RECORD));
98 memset(rec, 0, sizeof(DEV_RECORD));
99 rec->data = get_pool_memory(PM_MESSAGE);
104 * Free the record entity
107 void free_record(DEV_RECORD *rec)
109 Dmsg0(150, "Enter free_record.\n");
111 free_pool_memory(rec->data);
113 Dmsg0(150, "Data buf is freed.\n");
114 free_pool_memory((POOLMEM *)rec);
115 Dmsg0(150, "Leave free_record.\n");
120 * Write a Record to the block
122 * Returns: 0 on failure (none or partially written)
123 * 1 on success (all bytes written)
125 * and remainder returned in packet.
127 * We require enough room for the header, and we deal with
128 * two special cases. 1. Only part of the record may have
129 * been transferred the last time (when remainder is
130 * non-zero), and 2. The remaining bytes to write may not
131 * all fit into the block.
133 int write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
138 sm_check(__FILE__, __LINE__, False);
139 remlen = block->buf_len - block->binbuf;
141 ASSERT(block->binbuf == (uint32_t) (block->bufp - block->buf));
144 Dmsg6(190, "write_record_to_block() FI=%s SessId=%d Strm=%s len=%d\n\
145 rem=%d remainder=%d\n",
146 FI_to_ascii(rec->FileIndex), rec->VolSessionId,
147 stream_to_ascii(rec->Stream), rec->data_len,
148 remlen, rec->remainder);
151 * If rec->remainder is non-zero, we have been called a
152 * second (or subsequent) time to finish writing a record
153 * that did not previously fit into the block.
155 if (rec->remainder == 0) {
156 /* Require enough room to write a full header */
157 if (remlen >= WRITE_RECHDR_LENGTH) {
158 ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
159 if (BLOCK_VER == 1) {
160 ser_uint32(rec->VolSessionId);
161 ser_uint32(rec->VolSessionTime);
163 block->VolSessionId = rec->VolSessionId;
164 block->VolSessionTime = rec->VolSessionTime;
166 ser_int32(rec->FileIndex);
167 ser_int32(rec->Stream);
168 ser_uint32(rec->data_len);
170 block->bufp += WRITE_RECHDR_LENGTH;
171 block->binbuf += WRITE_RECHDR_LENGTH;
172 remlen -= WRITE_RECHDR_LENGTH;
173 rec->remainder = rec->data_len;
175 rec->remainder = rec->data_len + WRITE_RECHDR_LENGTH;
176 sm_check(__FILE__, __LINE__, False);
181 * We are here to write unwritten bytes from a previous
182 * time. Presumably we have a new buffer (possibly
183 * containing a volume label), so the new header
184 * should be able to fit in the block -- otherwise we have
185 * an error. Note, we may have to continue splitting the
186 * data record though.
188 * First, write the header, then write as much as
189 * possible of the data record.
191 * Every time we write a header and it is a continuation
192 * of a previous partially written record, we store the
193 * Stream as -Stream in the record header.
195 ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
196 if (BLOCK_VER == 1) {
197 ser_uint32(rec->VolSessionId);
198 ser_uint32(rec->VolSessionTime);
200 block->VolSessionId = rec->VolSessionId;
201 block->VolSessionTime = rec->VolSessionTime;
203 ser_int32(rec->FileIndex);
204 if (rec->remainder > rec->data_len) {
205 ser_int32(rec->Stream); /* normal full header */
206 ser_uint32(rec->data_len);
207 rec->remainder = rec->data_len; /* must still do data record */
209 ser_int32(-rec->Stream); /* mark this as a continuation record */
210 ser_uint32(rec->remainder); /* bytes to do */
213 /* Require enough room to write a full header */
214 ASSERT(remlen >= WRITE_RECHDR_LENGTH);
216 block->bufp += WRITE_RECHDR_LENGTH;
217 block->binbuf += WRITE_RECHDR_LENGTH;
218 remlen -= WRITE_RECHDR_LENGTH;
221 sm_check(__FILE__, __LINE__, False);
222 return 0; /* partial transfer */
226 * Now deal with data record.
227 * Part of it may have already been transferred, and we
228 * may not have enough room to transfer the whole this time.
230 if (rec->remainder > 0) {
231 /* Write as much of data as possible */
232 if (remlen >= rec->remainder) {
233 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
235 block->bufp += rec->remainder;
236 block->binbuf += rec->remainder;
238 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
240 if (!sm_check_rtn(__FILE__, __LINE__, False)) {
241 /* We damaged a buffer */
242 Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n\
243 rem=%d remainder=%d\n",
244 FI_to_ascii(rec->FileIndex), rec->VolSessionId,
245 stream_to_ascii(rec->Stream), rec->data_len,
246 remlen, rec->remainder);
247 Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
248 block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
250 Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
251 block->buf, block->bufp-block->buf);
253 Emsg0(M_ABORT, 0, "Damaged buffer\n");
256 block->bufp += remlen;
257 block->binbuf += remlen;
258 rec->remainder -= remlen;
259 return 0; /* did partial transfer */
262 rec->remainder = 0; /* did whole transfer */
263 sm_check(__FILE__, __LINE__, False);
269 * Test if we can write whole record to the block
271 * Returns: 0 on failure
272 * 1 on success (all bytes can be written)
274 int can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
278 remlen = block->buf_len - block->binbuf;
279 if (rec->remainder == 0) {
280 if (remlen >= WRITE_RECHDR_LENGTH) {
281 remlen -= WRITE_RECHDR_LENGTH;
282 rec->remainder = rec->data_len;
289 if (rec->remainder > 0 && remlen < rec->remainder) {
297 * Read a Record from the block
298 * Returns: 0 if nothing read or if the continuation record does not match.
299 * In both of these cases, a block read must be done.
300 * 1 if at least the record header was read, this
301 * routine may have to be called again with a new
302 * block if the entire record was not read.
304 int read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec)
308 uint32_t VolSessionId;
309 uint32_t VolSessionTime;
315 remlen = block->binbuf;
316 rec->Block = block->BlockNumber;
318 /* Clear state flags */
322 * Get the header. There is always a full header,
323 * otherwise we find it in the next block.
325 Dmsg3(100, "Block=%d Ver=%d size=%u\n", block->BlockNumber, block->BlockVer,
327 if (block->BlockVer == 1) {
328 rhl = RECHDR1_LENGTH;
330 rhl = RECHDR2_LENGTH;
333 Dmsg4(90, "Enter read_record_block: remlen=%d data_len=%d rem=%d blkver=%d\n",
334 remlen, rec->data_len, rec->remainder, block->BlockVer);
336 unser_begin(block->bufp, WRITE_RECHDR_LENGTH);
337 if (block->BlockVer == 1) {
338 unser_uint32(VolSessionId);
339 unser_uint32(VolSessionTime);
341 VolSessionId = block->VolSessionId;
342 VolSessionTime = block->VolSessionTime;
344 unser_int32(FileIndex);
346 unser_uint32(data_bytes);
349 block->binbuf -= rhl;
352 /* If we are looking for more (remainder!=0), we reject anything
353 * where the VolSessionId and VolSessionTime don't agree
355 if (rec->remainder && (rec->VolSessionId != VolSessionId ||
356 rec->VolSessionTime != VolSessionTime)) {
357 rec->state |= REC_NO_MATCH;
358 return 0; /* This is from some other Session */
361 /* if Stream is negative, it means that this is a continuation
362 * of a previous partially written record.
364 if (Stream < 0) { /* continuation record? */
365 Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n",
367 rec->state |= REC_CONTINUATION;
368 if (!rec->remainder) { /* if we didn't read previously */
369 rec->data_len = 0; /* return data as if no continuation */
370 } else if (rec->Stream != -Stream) {
371 rec->state |= REC_NO_MATCH;
372 return 0; /* This is from some other Session */
374 rec->Stream = -Stream; /* set correct Stream */
375 } else { /* Regular record */
376 rec->Stream = Stream;
377 rec->data_len = 0; /* transfer to beginning of data */
379 rec->VolSessionId = VolSessionId;
380 rec->VolSessionTime = VolSessionTime;
381 rec->FileIndex = FileIndex;
383 Dmsg6(100, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%u\n\
384 remlen=%d data_len=%d\n",
385 FI_to_ascii(rec->FileIndex), rec->VolSessionId,
386 stream_to_ascii(rec->Stream), data_bytes, remlen, rec->data_len);
389 * No more records in this block because the number
390 * of remaining bytes are less than a record header
391 * length, so return empty handed, but indicate that
392 * he must read again. By returning, we allow the
393 * higher level routine to fetch the next block and
396 Dmsg0(90, "read_record_block: nothing\n");
398 if (!rec->remainder) {
399 rec->remainder = 1; /* set to expect continuation */
400 rec->data_len = 0; /* no data transferred */
403 rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
407 ASSERT(data_bytes < MAX_BLOCK_LENGTH); /* temp sanity check */
409 rec->data = check_pool_memory_size(rec->data, rec->data_len+data_bytes);
412 * At this point, we have read the header, now we
413 * must transfer as much of the data record as
414 * possible taking into account: 1. A partial
415 * data record may have previously been transferred,
416 * 2. The current block may not contain the whole data
419 if (remlen >= data_bytes) {
420 /* Got whole record */
421 memcpy(rec->data+rec->data_len, block->bufp, data_bytes);
422 block->bufp += data_bytes;
423 block->binbuf -= data_bytes;
424 rec->data_len += data_bytes;
427 memcpy(rec->data+rec->data_len, block->bufp, remlen);
428 block->bufp += remlen;
429 block->binbuf -= remlen;
430 rec->data_len += remlen;
431 rec->remainder = 1; /* partial record transferred */
432 Dmsg1(90, "read_record_block: partial xfered=%d\n", rec->data_len);
433 rec->state |= (REC_PARTIAL_RECORD | REC_BLOCK_EMPTY);
437 Dmsg4(90, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
438 FI_to_ascii(rec->FileIndex), rec->VolSessionId,
439 stream_to_ascii(rec->Stream), rec->data_len);
440 return 1; /* transferred full record */