3 * record.c -- tape record handling functions
5 * Kern Sibbald, April MMI
6 * added BB02 format October MMII
12 Copyright (C) 2000-2004 Kern Sibbald and John Walker
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation; either version 2 of
17 the License, or (at your option) any later version.
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 General Public License for more details.
24 You should have received a copy of the GNU General Public
25 License along with this program; if not, write to the Free
26 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
35 extern int debug_level;
38 * Convert a FileIndex into a printable
39 * ASCII string. Not reentrant.
40 * If the FileIndex is negative, it flags the
41 * record as a Label, otherwise it is simply
42 * the FileIndex of the current file.
44 const char *FI_to_ascii(int fi)
48 sprintf(buf, "%d", fi);
66 sprintf(buf, "unknown: %d", fi);
73 * Convert a Stream ID into a printable
74 * ASCII string. Not reentrant.
76 * A negative stream number represents
77 * stream data that is continued from a
78 * record in the previous block.
79 * If the FileIndex is negative, we are
80 * dealing with a Label, hence the
81 * stream is the JobId.
83 const char *stream_to_ascii(int stream, int fi)
87 sprintf(buf, "%d", stream);
91 case STREAM_UNIX_ATTRIBUTES:
93 case STREAM_FILE_DATA:
95 case STREAM_WIN32_DATA:
97 case STREAM_WIN32_GZIP_DATA:
99 case STREAM_MD5_SIGNATURE:
101 case STREAM_SHA1_SIGNATURE:
103 case STREAM_GZIP_DATA:
105 case STREAM_UNIX_ATTRIBUTES_EX:
106 return "UNIX-ATTR-EX";
107 case STREAM_SPARSE_DATA:
108 return "SPARSE-DATA";
109 case STREAM_SPARSE_GZIP_DATA:
110 return "SPARSE-GZIP";
111 case STREAM_PROGRAM_NAMES:
113 case STREAM_PROGRAM_DATA:
115 case STREAM_MACOS_FORK_DATA:
117 case STREAM_HFSPLUS_ATTRIBUTES:
118 return "HFSPLUS-ATTR";
119 case -STREAM_UNIX_ATTRIBUTES:
121 case -STREAM_FILE_DATA:
123 case -STREAM_WIN32_DATA:
124 return "contWIN32-DATA";
125 case -STREAM_WIN32_GZIP_DATA:
126 return "contWIN32-GZIP";
127 case -STREAM_MD5_SIGNATURE:
129 case -STREAM_SHA1_SIGNATURE:
131 case -STREAM_GZIP_DATA:
133 case -STREAM_UNIX_ATTRIBUTES_EX:
134 return "contUNIX-ATTR-EX";
135 case -STREAM_SPARSE_DATA:
136 return "contSPARSE-DATA";
137 case -STREAM_SPARSE_GZIP_DATA:
138 return "contSPARSE-GZIP";
139 case -STREAM_PROGRAM_NAMES:
140 return "contPROG-NAMES";
141 case -STREAM_PROGRAM_DATA:
142 return "contPROG-DATA";
143 case -STREAM_MACOS_FORK_DATA:
144 return "contMACOS-RSRC";
145 case -STREAM_HFSPLUS_ATTRIBUTES:
146 return "contHFSPLUS-ATTR";
148 sprintf(buf, "%d", stream);
154 * Return a new record entity
156 DEV_RECORD *new_record(void)
160 rec = (DEV_RECORD *)get_memory(sizeof(DEV_RECORD));
161 memset(rec, 0, sizeof(DEV_RECORD));
162 rec->data = get_pool_memory(PM_MESSAGE);
166 void empty_record(DEV_RECORD *rec)
168 rec->File = rec->Block = 0;
169 rec->VolSessionId = rec->VolSessionTime = 0;
170 rec->FileIndex = rec->Stream = 0;
171 rec->data_len = rec->remainder = 0;
172 rec->state &= ~(REC_PARTIAL_RECORD|REC_BLOCK_EMPTY|REC_NO_MATCH|REC_CONTINUATION);
176 * Free the record entity
179 void free_record(DEV_RECORD *rec)
181 Dmsg0(350, "Enter free_record.\n");
183 free_pool_memory(rec->data);
185 Dmsg0(350, "Data buf is freed.\n");
186 free_pool_memory((POOLMEM *)rec);
187 Dmsg0(350, "Leave free_record.\n");
192 * Write a Record to the block
194 * Returns: false on failure (none or partially written)
195 * true on success (all bytes written)
197 * and remainder returned in packet.
199 * We require enough room for the header, and we deal with
200 * two special cases. 1. Only part of the record may have
201 * been transferred the last time (when remainder is
202 * non-zero), and 2. The remaining bytes to write may not
203 * all fit into the block.
205 bool write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
210 remlen = block->buf_len - block->binbuf;
212 ASSERT(block->binbuf == (uint32_t) (block->bufp - block->buf));
213 ASSERT(block->buf_len >= block->binbuf);
215 Dmsg6(890, "write_record_to_block() FI=%s SessId=%d Strm=%s len=%d\n"
216 "rem=%d remainder=%d\n",
217 FI_to_ascii(rec->FileIndex), rec->VolSessionId,
218 stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len,
219 remlen, rec->remainder);
222 * If rec->remainder is non-zero, we have been called a
223 * second (or subsequent) time to finish writing a record
224 * that did not previously fit into the block.
226 if (rec->remainder == 0) {
227 /* Require enough room to write a full header */
228 if (remlen >= WRITE_RECHDR_LENGTH) {
229 ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
230 if (BLOCK_VER == 1) {
231 ser_uint32(rec->VolSessionId);
232 ser_uint32(rec->VolSessionTime);
234 block->VolSessionId = rec->VolSessionId;
235 block->VolSessionTime = rec->VolSessionTime;
237 ser_int32(rec->FileIndex);
238 ser_int32(rec->Stream);
239 ser_uint32(rec->data_len);
241 block->bufp += WRITE_RECHDR_LENGTH;
242 block->binbuf += WRITE_RECHDR_LENGTH;
243 remlen -= WRITE_RECHDR_LENGTH;
244 rec->remainder = rec->data_len;
245 if (rec->FileIndex > 0) {
246 /* If data record, update what we have in this block */
247 if (block->FirstIndex == 0) {
248 block->FirstIndex = rec->FileIndex;
250 block->LastIndex = rec->FileIndex;
253 rec->remainder = rec->data_len + WRITE_RECHDR_LENGTH;
258 * We are here to write unwritten bytes from a previous
259 * time. Presumably we have a new buffer (possibly
260 * containing a volume label), so the new header
261 * should be able to fit in the block -- otherwise we have
262 * an error. Note, we have to continue splitting the
263 * data record if it is longer than the block.
265 * First, write the header, then write as much as
266 * possible of the data record.
268 * Every time we write a header and it is a continuation
269 * of a previous partially written record, we store the
270 * Stream as -Stream in the record header.
272 ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
273 if (BLOCK_VER == 1) {
274 ser_uint32(rec->VolSessionId);
275 ser_uint32(rec->VolSessionTime);
277 block->VolSessionId = rec->VolSessionId;
278 block->VolSessionTime = rec->VolSessionTime;
280 ser_int32(rec->FileIndex);
281 if (rec->remainder > rec->data_len) {
282 ser_int32(rec->Stream); /* normal full header */
283 ser_uint32(rec->data_len);
284 rec->remainder = rec->data_len; /* must still do data record */
286 ser_int32(-rec->Stream); /* mark this as a continuation record */
287 ser_uint32(rec->remainder); /* bytes to do */
290 /* Require enough room to write a full header */
291 ASSERT(remlen >= WRITE_RECHDR_LENGTH);
293 block->bufp += WRITE_RECHDR_LENGTH;
294 block->binbuf += WRITE_RECHDR_LENGTH;
295 remlen -= WRITE_RECHDR_LENGTH;
296 if (rec->FileIndex > 0) {
297 /* If data record, update what we have in this block */
298 if (block->FirstIndex == 0) {
299 block->FirstIndex = rec->FileIndex;
301 block->LastIndex = rec->FileIndex;
305 return false; /* partial transfer */
309 * Now deal with data record.
310 * Part of it may have already been transferred, and we
311 * may not have enough room to transfer the whole this time.
313 if (rec->remainder > 0) {
314 /* Write as much of data as possible */
315 if (remlen >= rec->remainder) {
316 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
318 block->bufp += rec->remainder;
319 block->binbuf += rec->remainder;
321 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
324 if (!sm_check_rtn(__FILE__, __LINE__, False)) {
325 /* We damaged a buffer */
326 Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n"
327 "rem=%d remainder=%d\n",
328 FI_to_ascii(rec->FileIndex), rec->VolSessionId,
329 stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len,
330 remlen, rec->remainder);
331 Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
332 block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
334 Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
335 block->buf, block->bufp-block->buf);
337 Emsg0(M_ABORT, 0, "Damaged buffer\n");
341 block->bufp += remlen;
342 block->binbuf += remlen;
343 rec->remainder -= remlen;
344 return false; /* did partial transfer */
347 rec->remainder = 0; /* did whole transfer */
353 * Test if we can write whole record to the block
355 * Returns: false on failure
356 * true on success (all bytes can be written)
358 bool can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
362 remlen = block->buf_len - block->binbuf;
363 if (rec->remainder == 0) {
364 if (remlen >= WRITE_RECHDR_LENGTH) {
365 remlen -= WRITE_RECHDR_LENGTH;
366 rec->remainder = rec->data_len;
373 if (rec->remainder > 0 && remlen < rec->remainder) {
381 * Read a Record from the block
382 * Returns: false if nothing read or if the continuation record does not match.
383 * In both of these cases, a block read must be done.
384 * true if at least the record header was read, this
385 * routine may have to be called again with a new
386 * block if the entire record was not read.
388 bool read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec)
392 uint32_t VolSessionId;
393 uint32_t VolSessionTime;
399 remlen = block->binbuf;
400 rec->Block = block->BlockNumber;
401 rec->File = ((DEVICE *)block->dev)->file;
403 /* Clear state flags */
405 if (((DEVICE *)block->dev)->state & ST_TAPE) {
406 rec->state |= REC_ISTAPE;
411 * Get the header. There is always a full header,
412 * otherwise we find it in the next block.
414 Dmsg3(450, "Block=%d Ver=%d size=%u\n", block->BlockNumber, block->BlockVer,
416 if (block->BlockVer == 1) {
417 rhl = RECHDR1_LENGTH;
419 rhl = RECHDR2_LENGTH;
422 Dmsg4(450, "Enter read_record_block: remlen=%d data_len=%d rem=%d blkver=%d\n",
423 remlen, rec->data_len, rec->remainder, block->BlockVer);
425 unser_begin(block->bufp, WRITE_RECHDR_LENGTH);
426 if (block->BlockVer == 1) {
427 unser_uint32(VolSessionId);
428 unser_uint32(VolSessionTime);
430 VolSessionId = block->VolSessionId;
431 VolSessionTime = block->VolSessionTime;
433 unser_int32(FileIndex);
435 unser_uint32(data_bytes);
438 block->binbuf -= rhl;
441 /* If we are looking for more (remainder!=0), we reject anything
442 * where the VolSessionId and VolSessionTime don't agree
444 if (rec->remainder && (rec->VolSessionId != VolSessionId ||
445 rec->VolSessionTime != VolSessionTime)) {
446 rec->state |= REC_NO_MATCH;
447 Dmsg0(450, "remainder and VolSession doesn't match\n");
448 return false; /* This is from some other Session */
451 /* if Stream is negative, it means that this is a continuation
452 * of a previous partially written record.
454 if (Stream < 0) { /* continuation record? */
455 Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n",
457 rec->state |= REC_CONTINUATION;
458 if (!rec->remainder) { /* if we didn't read previously */
459 rec->data_len = 0; /* return data as if no continuation */
460 } else if (rec->Stream != -Stream) {
461 rec->state |= REC_NO_MATCH;
462 return false; /* This is from some other Session */
464 rec->Stream = -Stream; /* set correct Stream */
465 } else { /* Regular record */
466 rec->Stream = Stream;
467 rec->data_len = 0; /* transfer to beginning of data */
469 rec->VolSessionId = VolSessionId;
470 rec->VolSessionTime = VolSessionTime;
471 rec->FileIndex = FileIndex;
473 if (block->FirstIndex == 0) {
474 block->FirstIndex = FileIndex;
476 block->LastIndex = FileIndex;
479 Dmsg6(450, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%u\n"
480 "remlen=%d data_len=%d\n",
481 FI_to_ascii(rec->FileIndex), rec->VolSessionId,
482 stream_to_ascii(rec->Stream, rec->FileIndex), data_bytes, remlen,
486 * No more records in this block because the number
487 * of remaining bytes are less than a record header
488 * length, so return empty handed, but indicate that
489 * he must read again. By returning, we allow the
490 * higher level routine to fetch the next block and
493 Dmsg0(450, "read_record_block: nothing\n");
495 if (!rec->remainder) {
496 rec->remainder = 1; /* set to expect continuation */
497 rec->data_len = 0; /* no data transferred */
500 rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
501 empty_block(block); /* mark block empty */
505 ASSERT(data_bytes < MAX_BLOCK_LENGTH); /* temp sanity check */
507 rec->data = check_pool_memory_size(rec->data, rec->data_len+data_bytes);
510 * At this point, we have read the header, now we
511 * must transfer as much of the data record as
512 * possible taking into account: 1. A partial
513 * data record may have previously been transferred,
514 * 2. The current block may not contain the whole data
517 if (remlen >= data_bytes) {
518 /* Got whole record */
519 memcpy(rec->data+rec->data_len, block->bufp, data_bytes);
520 block->bufp += data_bytes;
521 block->binbuf -= data_bytes;
522 rec->data_len += data_bytes;
525 memcpy(rec->data+rec->data_len, block->bufp, remlen);
526 block->bufp += remlen;
527 block->binbuf -= remlen;
528 rec->data_len += remlen;
529 rec->remainder = 1; /* partial record transferred */
530 Dmsg1(450, "read_record_block: partial xfered=%d\n", rec->data_len);
531 rec->state |= (REC_PARTIAL_RECORD | REC_BLOCK_EMPTY);
535 Dmsg4(450, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
536 FI_to_ascii(rec->FileIndex), rec->VolSessionId,
537 stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len);
538 return true; /* transferred full record */