3 * record.c -- tape record handling functions
5 * Kern Sibbald, April MMI
6 * added BB02 format October MMII
12 Copyright (C) 2001-2005 Kern Sibbald
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License
16 version 2 as amended with additional clauses defined in the
17 file LICENSE in the main source directory.
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
22 the file LICENSE for additional details.
30 extern int debug_level;
33 * Convert a FileIndex into a printable
34 * ASCII string. Not reentrant.
35 * If the FileIndex is negative, it flags the
36 * record as a Label, otherwise it is simply
37 * the FileIndex of the current file.
39 const char *FI_to_ascii(char *buf, int fi)
42 sprintf(buf, "%d", fi);
60 sprintf(buf, _("unknown: %d"), fi);
67 * Convert a Stream ID into a printable
68 * ASCII string. Not reentrant.
70 * A negative stream number represents
71 * stream data that is continued from a
72 * record in the previous block.
73 * If the FileIndex is negative, we are
74 * dealing with a Label, hence the
75 * stream is the JobId.
77 const char *stream_to_ascii(char *buf, int stream, int fi)
80 sprintf(buf, "%d", stream);
84 case STREAM_UNIX_ATTRIBUTES:
86 case STREAM_FILE_DATA:
88 case STREAM_WIN32_DATA:
90 case STREAM_WIN32_GZIP_DATA:
92 case STREAM_MD5_SIGNATURE:
94 case STREAM_SHA1_SIGNATURE:
96 case STREAM_GZIP_DATA:
98 case STREAM_UNIX_ATTRIBUTES_EX:
99 return "UNIX-ATTR-EX";
100 case STREAM_SPARSE_DATA:
101 return "SPARSE-DATA";
102 case STREAM_SPARSE_GZIP_DATA:
103 return "SPARSE-GZIP";
104 case STREAM_PROGRAM_NAMES:
106 case STREAM_PROGRAM_DATA:
108 case STREAM_MACOS_FORK_DATA:
110 case STREAM_HFSPLUS_ATTRIBUTES:
111 return "HFSPLUS-ATTR";
112 case -STREAM_UNIX_ATTRIBUTES:
114 case -STREAM_FILE_DATA:
116 case -STREAM_WIN32_DATA:
117 return "contWIN32-DATA";
118 case -STREAM_WIN32_GZIP_DATA:
119 return "contWIN32-GZIP";
120 case -STREAM_MD5_SIGNATURE:
122 case -STREAM_SHA1_SIGNATURE:
124 case -STREAM_GZIP_DATA:
126 case -STREAM_UNIX_ATTRIBUTES_EX:
127 return "contUNIX-ATTR-EX";
128 case -STREAM_SPARSE_DATA:
129 return "contSPARSE-DATA";
130 case -STREAM_SPARSE_GZIP_DATA:
131 return "contSPARSE-GZIP";
132 case -STREAM_PROGRAM_NAMES:
133 return "contPROG-NAMES";
134 case -STREAM_PROGRAM_DATA:
135 return "contPROG-DATA";
136 case -STREAM_MACOS_FORK_DATA:
137 return "contMACOS-RSRC";
138 case -STREAM_HFSPLUS_ATTRIBUTES:
139 return "contHFSPLUS-ATTR";
141 sprintf(buf, "%d", stream);
147 * Return a new record entity
149 DEV_RECORD *new_record(void)
153 rec = (DEV_RECORD *)get_memory(sizeof(DEV_RECORD));
154 memset(rec, 0, sizeof(DEV_RECORD));
155 rec->data = get_pool_memory(PM_MESSAGE);
159 void empty_record(DEV_RECORD *rec)
161 rec->File = rec->Block = 0;
162 rec->VolSessionId = rec->VolSessionTime = 0;
163 rec->FileIndex = rec->Stream = 0;
164 rec->data_len = rec->remainder = 0;
165 rec->state &= ~(REC_PARTIAL_RECORD|REC_BLOCK_EMPTY|REC_NO_MATCH|REC_CONTINUATION);
169 * Free the record entity
172 void free_record(DEV_RECORD *rec)
174 Dmsg0(950, "Enter free_record.\n");
176 free_pool_memory(rec->data);
178 Dmsg0(950, "Data buf is freed.\n");
179 free_pool_memory((POOLMEM *)rec);
180 Dmsg0(950, "Leave free_record.\n");
185 * Write a Record to the block
187 * Returns: false on failure (none or partially written)
188 * true on success (all bytes written)
190 * and remainder returned in packet.
192 * We require enough room for the header, and we deal with
193 * two special cases. 1. Only part of the record may have
194 * been transferred the last time (when remainder is
195 * non-zero), and 2. The remaining bytes to write may not
196 * all fit into the block.
198 bool write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
202 char buf1[100], buf2[100];
204 remlen = block->buf_len - block->binbuf;
206 ASSERT(block->binbuf == (uint32_t) (block->bufp - block->buf));
207 ASSERT(block->buf_len >= block->binbuf);
209 Dmsg6(890, "write_record_to_block() FI=%s SessId=%d Strm=%s len=%d\n"
210 "rem=%d remainder=%d\n",
211 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
212 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
213 remlen, rec->remainder);
216 * If rec->remainder is non-zero, we have been called a
217 * second (or subsequent) time to finish writing a record
218 * that did not previously fit into the block.
220 if (rec->remainder == 0) {
221 /* Require enough room to write a full header */
222 if (remlen >= WRITE_RECHDR_LENGTH) {
223 ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
224 if (BLOCK_VER == 1) {
225 ser_uint32(rec->VolSessionId);
226 ser_uint32(rec->VolSessionTime);
228 block->VolSessionId = rec->VolSessionId;
229 block->VolSessionTime = rec->VolSessionTime;
231 ser_int32(rec->FileIndex);
232 ser_int32(rec->Stream);
233 ser_uint32(rec->data_len);
235 block->bufp += WRITE_RECHDR_LENGTH;
236 block->binbuf += WRITE_RECHDR_LENGTH;
237 remlen -= WRITE_RECHDR_LENGTH;
238 rec->remainder = rec->data_len;
239 if (rec->FileIndex > 0) {
240 /* If data record, update what we have in this block */
241 if (block->FirstIndex == 0) {
242 block->FirstIndex = rec->FileIndex;
244 block->LastIndex = rec->FileIndex;
247 rec->remainder = rec->data_len + WRITE_RECHDR_LENGTH;
252 * We are here to write unwritten bytes from a previous
253 * time. Presumably we have a new buffer (possibly
254 * containing a volume label), so the new header
255 * should be able to fit in the block -- otherwise we have
256 * an error. Note, we have to continue splitting the
257 * data record if it is longer than the block.
259 * First, write the header, then write as much as
260 * possible of the data record.
262 * Every time we write a header and it is a continuation
263 * of a previous partially written record, we store the
264 * Stream as -Stream in the record header.
266 ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
267 if (BLOCK_VER == 1) {
268 ser_uint32(rec->VolSessionId);
269 ser_uint32(rec->VolSessionTime);
271 block->VolSessionId = rec->VolSessionId;
272 block->VolSessionTime = rec->VolSessionTime;
274 ser_int32(rec->FileIndex);
275 if (rec->remainder > rec->data_len) {
276 ser_int32(rec->Stream); /* normal full header */
277 ser_uint32(rec->data_len);
278 rec->remainder = rec->data_len; /* must still do data record */
280 ser_int32(-rec->Stream); /* mark this as a continuation record */
281 ser_uint32(rec->remainder); /* bytes to do */
284 /* Require enough room to write a full header */
285 ASSERT(remlen >= WRITE_RECHDR_LENGTH);
287 block->bufp += WRITE_RECHDR_LENGTH;
288 block->binbuf += WRITE_RECHDR_LENGTH;
289 remlen -= WRITE_RECHDR_LENGTH;
290 if (rec->FileIndex > 0) {
291 /* If data record, update what we have in this block */
292 if (block->FirstIndex == 0) {
293 block->FirstIndex = rec->FileIndex;
295 block->LastIndex = rec->FileIndex;
299 return false; /* partial transfer */
303 * Now deal with data record.
304 * Part of it may have already been transferred, and we
305 * may not have enough room to transfer the whole this time.
307 if (rec->remainder > 0) {
308 /* Write as much of data as possible */
309 if (remlen >= rec->remainder) {
310 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
312 block->bufp += rec->remainder;
313 block->binbuf += rec->remainder;
315 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
318 if (!sm_check_rtn(__FILE__, __LINE__, False)) {
319 /* We damaged a buffer */
320 Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n"
321 "rem=%d remainder=%d\n",
322 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
323 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
324 remlen, rec->remainder);
325 Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
326 block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
328 Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
329 block->buf, block->bufp-block->buf);
331 Emsg0(M_ABORT, 0, _("Damaged buffer\n"));
335 block->bufp += remlen;
336 block->binbuf += remlen;
337 rec->remainder -= remlen;
338 return false; /* did partial transfer */
341 rec->remainder = 0; /* did whole transfer */
347 * Test if we can write whole record to the block
349 * Returns: false on failure
350 * true on success (all bytes can be written)
352 bool can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
356 remlen = block->buf_len - block->binbuf;
357 if (rec->remainder == 0) {
358 if (remlen >= WRITE_RECHDR_LENGTH) {
359 remlen -= WRITE_RECHDR_LENGTH;
360 rec->remainder = rec->data_len;
367 if (rec->remainder > 0 && remlen < rec->remainder) {
375 * Read a Record from the block
376 * Returns: false if nothing read or if the continuation record does not match.
377 * In both of these cases, a block read must be done.
378 * true if at least the record header was read, this
379 * routine may have to be called again with a new
380 * block if the entire record was not read.
382 bool read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec)
386 uint32_t VolSessionId;
387 uint32_t VolSessionTime;
392 char buf1[100], buf2[100];
394 remlen = block->binbuf;
395 rec->Block = block->BlockNumber;
396 rec->File = ((DEVICE *)block->dev)->file;
398 /* Clear state flags */
400 if (block->dev->is_tape()) {
401 rec->state |= REC_ISTAPE;
406 * Get the header. There is always a full header,
407 * otherwise we find it in the next block.
409 Dmsg3(450, "Block=%d Ver=%d size=%u\n", block->BlockNumber, block->BlockVer,
411 if (block->BlockVer == 1) {
412 rhl = RECHDR1_LENGTH;
414 rhl = RECHDR2_LENGTH;
417 Dmsg4(450, "Enter read_record_block: remlen=%d data_len=%d rem=%d blkver=%d\n",
418 remlen, rec->data_len, rec->remainder, block->BlockVer);
420 unser_begin(block->bufp, WRITE_RECHDR_LENGTH);
421 if (block->BlockVer == 1) {
422 unser_uint32(VolSessionId);
423 unser_uint32(VolSessionTime);
425 VolSessionId = block->VolSessionId;
426 VolSessionTime = block->VolSessionTime;
428 unser_int32(FileIndex);
430 unser_uint32(data_bytes);
433 block->binbuf -= rhl;
436 /* If we are looking for more (remainder!=0), we reject anything
437 * where the VolSessionId and VolSessionTime don't agree
439 if (rec->remainder && (rec->VolSessionId != VolSessionId ||
440 rec->VolSessionTime != VolSessionTime)) {
441 rec->state |= REC_NO_MATCH;
442 Dmsg0(450, "remainder and VolSession doesn't match\n");
443 return false; /* This is from some other Session */
446 /* if Stream is negative, it means that this is a continuation
447 * of a previous partially written record.
449 if (Stream < 0) { /* continuation record? */
450 Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n",
452 rec->state |= REC_CONTINUATION;
453 if (!rec->remainder) { /* if we didn't read previously */
454 rec->data_len = 0; /* return data as if no continuation */
455 } else if (rec->Stream != -Stream) {
456 rec->state |= REC_NO_MATCH;
457 return false; /* This is from some other Session */
459 rec->Stream = -Stream; /* set correct Stream */
460 } else { /* Regular record */
461 rec->Stream = Stream;
462 rec->data_len = 0; /* transfer to beginning of data */
464 rec->VolSessionId = VolSessionId;
465 rec->VolSessionTime = VolSessionTime;
466 rec->FileIndex = FileIndex;
468 if (block->FirstIndex == 0) {
469 block->FirstIndex = FileIndex;
471 block->LastIndex = FileIndex;
474 Dmsg6(450, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%u\n"
475 "remlen=%d data_len=%d\n",
476 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
477 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), data_bytes, remlen,
481 * No more records in this block because the number
482 * of remaining bytes are less than a record header
483 * length, so return empty handed, but indicate that
484 * he must read again. By returning, we allow the
485 * higher level routine to fetch the next block and
488 Dmsg0(450, "read_record_block: nothing\n");
489 rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
490 empty_block(block); /* mark block empty */
494 ASSERT(data_bytes < MAX_BLOCK_LENGTH); /* temp sanity check */
496 rec->data = check_pool_memory_size(rec->data, rec->data_len+data_bytes);
499 * At this point, we have read the header, now we
500 * must transfer as much of the data record as
501 * possible taking into account: 1. A partial
502 * data record may have previously been transferred,
503 * 2. The current block may not contain the whole data
506 if (remlen >= data_bytes) {
507 /* Got whole record */
508 memcpy(rec->data+rec->data_len, block->bufp, data_bytes);
509 block->bufp += data_bytes;
510 block->binbuf -= data_bytes;
511 rec->data_len += data_bytes;
514 memcpy(rec->data+rec->data_len, block->bufp, remlen);
515 block->bufp += remlen;
516 block->binbuf -= remlen;
517 rec->data_len += remlen;
518 rec->remainder = 1; /* partial record transferred */
519 Dmsg1(450, "read_record_block: partial xfered=%d\n", rec->data_len);
520 rec->state |= (REC_PARTIAL_RECORD | REC_BLOCK_EMPTY);
524 Dmsg4(450, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
525 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
526 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
527 return true; /* transferred full record */