3 * record.c -- tape record handling functions
5 * Kern Sibbald, April MMI
6 * added BB02 format October MMII
12 Bacula® - The Network Backup Solution
14 Copyright (C) 2001-2006 Free Software Foundation Europe e.V.
16 The main author of Bacula is Kern Sibbald, with contributions from
17 many others, a complete list can be found in the file AUTHORS.
18 This program is Free Software; you can redistribute it and/or
19 modify it under the terms of version two of the GNU General Public
20 License as published by the Free Software Foundation and included
23 This program is distributed in the hope that it will be useful, but
24 WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26 General Public License for more details.
28 You should have received a copy of the GNU General Public License
29 along with this program; if not, write to the Free Software
30 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
33 Bacula® is a registered trademark of John Walker.
34 The licensor of Bacula is the Free Software Foundation Europe
35 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
36 Switzerland, email:ftf@fsfeurope.org.
44 * Convert a FileIndex into a printable
45 * ASCII string. Not reentrant.
46 * If the FileIndex is negative, it flags the
47 * record as a Label, otherwise it is simply
48 * the FileIndex of the current file.
50 const char *FI_to_ascii(char *buf, int fi)
53 sprintf(buf, "%d", fi);
71 sprintf(buf, _("unknown: %d"), fi);
78 * Convert a Stream ID into a printable
79 * ASCII string. Not reentrant.
81 * A negative stream number represents
82 * stream data that is continued from a
83 * record in the previous block.
84 * If the FileIndex is negative, we are
85 * dealing with a Label, hence the
86 * stream is the JobId.
88 const char *stream_to_ascii(char *buf, int stream, int fi)
91 sprintf(buf, "%d", stream);
95 case STREAM_UNIX_ATTRIBUTES:
97 case STREAM_FILE_DATA:
99 case STREAM_WIN32_DATA:
101 case STREAM_WIN32_GZIP_DATA:
103 case STREAM_MD5_DIGEST:
105 case STREAM_SHA1_DIGEST:
107 case STREAM_GZIP_DATA:
109 case STREAM_UNIX_ATTRIBUTES_EX:
110 return "UNIX-ATTR-EX";
111 case STREAM_SPARSE_DATA:
112 return "SPARSE-DATA";
113 case STREAM_SPARSE_GZIP_DATA:
114 return "SPARSE-GZIP";
115 case STREAM_PROGRAM_NAMES:
117 case STREAM_PROGRAM_DATA:
119 case STREAM_MACOS_FORK_DATA:
121 case STREAM_HFSPLUS_ATTRIBUTES:
122 return "HFSPLUS-ATTR";
123 case STREAM_SHA256_DIGEST:
125 case STREAM_SHA512_DIGEST:
127 case STREAM_SIGNED_DIGEST:
128 return "SIGNED-DIGEST";
129 case STREAM_ENCRYPTED_SESSION_DATA:
130 return "ENCRYPTED-SESSION-DATA";
131 case STREAM_ENCRYPTED_FILE_DATA:
132 return "ENCRYPTED-FILE";
133 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
134 return "ENCRYPTED-GZIP";
135 case STREAM_ENCRYPTED_WIN32_DATA:
136 return "ENCRYPTED-WIN32-DATA";
137 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
138 return "ENCRYPTED-WIN32-GZIP";
139 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
140 return "ENCRYPTED-MACOS-RSRC";
141 case -STREAM_UNIX_ATTRIBUTES:
143 case -STREAM_FILE_DATA:
145 case -STREAM_WIN32_DATA:
146 return "contWIN32-DATA";
147 case -STREAM_WIN32_GZIP_DATA:
148 return "contWIN32-GZIP";
149 case -STREAM_MD5_DIGEST:
151 case -STREAM_SHA1_DIGEST:
153 case -STREAM_GZIP_DATA:
155 case -STREAM_UNIX_ATTRIBUTES_EX:
156 return "contUNIX-ATTR-EX";
157 case -STREAM_SPARSE_DATA:
158 return "contSPARSE-DATA";
159 case -STREAM_SPARSE_GZIP_DATA:
160 return "contSPARSE-GZIP";
161 case -STREAM_PROGRAM_NAMES:
162 return "contPROG-NAMES";
163 case -STREAM_PROGRAM_DATA:
164 return "contPROG-DATA";
165 case -STREAM_MACOS_FORK_DATA:
166 return "contMACOS-RSRC";
167 case -STREAM_HFSPLUS_ATTRIBUTES:
168 return "contHFSPLUS-ATTR";
169 case -STREAM_SHA256_DIGEST:
171 case -STREAM_SHA512_DIGEST:
173 case -STREAM_SIGNED_DIGEST:
174 return "contSIGNED-DIGEST";
175 case -STREAM_ENCRYPTED_SESSION_DATA:
176 return "contENCRYPTED-SESSION-DATA";
177 case -STREAM_ENCRYPTED_FILE_DATA:
178 return "contENCRYPTED-FILE";
179 case -STREAM_ENCRYPTED_FILE_GZIP_DATA:
180 return "contENCRYPTED-GZIP";
181 case -STREAM_ENCRYPTED_WIN32_DATA:
182 return "contENCRYPTED-WIN32-DATA";
183 case -STREAM_ENCRYPTED_WIN32_GZIP_DATA:
184 return "contENCRYPTED-WIN32-GZIP";
185 case -STREAM_ENCRYPTED_MACOS_FORK_DATA:
186 return "contENCRYPTED-MACOS-RSRC";
188 sprintf(buf, "%d", stream);
194 * Return a new record entity
196 DEV_RECORD *new_record(void)
200 rec = (DEV_RECORD *)get_memory(sizeof(DEV_RECORD));
201 memset(rec, 0, sizeof(DEV_RECORD));
202 rec->data = get_pool_memory(PM_MESSAGE);
206 void empty_record(DEV_RECORD *rec)
208 rec->File = rec->Block = 0;
209 rec->VolSessionId = rec->VolSessionTime = 0;
210 rec->FileIndex = rec->Stream = 0;
211 rec->data_len = rec->remainder = 0;
212 rec->state &= ~(REC_PARTIAL_RECORD|REC_BLOCK_EMPTY|REC_NO_MATCH|REC_CONTINUATION);
216 * Free the record entity
219 void free_record(DEV_RECORD *rec)
221 Dmsg0(950, "Enter free_record.\n");
223 free_pool_memory(rec->data);
225 Dmsg0(950, "Data buf is freed.\n");
226 free_pool_memory((POOLMEM *)rec);
227 Dmsg0(950, "Leave free_record.\n");
232 * Write a Record to the block
234 * Returns: false on failure (none or partially written)
235 * true on success (all bytes written)
237 * and remainder returned in packet.
239 * We require enough room for the header, and we deal with
240 * two special cases. 1. Only part of the record may have
241 * been transferred the last time (when remainder is
242 * non-zero), and 2. The remaining bytes to write may not
243 * all fit into the block.
245 bool write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
249 char buf1[100], buf2[100];
251 remlen = block->buf_len - block->binbuf;
253 ASSERT(block->binbuf == (uint32_t) (block->bufp - block->buf));
254 ASSERT(block->buf_len >= block->binbuf);
256 Dmsg6(890, "write_record_to_block() FI=%s SessId=%d Strm=%s len=%d\n"
257 "rem=%d remainder=%d\n",
258 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
259 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
260 remlen, rec->remainder);
263 * If rec->remainder is non-zero, we have been called a
264 * second (or subsequent) time to finish writing a record
265 * that did not previously fit into the block.
267 if (rec->remainder == 0) {
268 /* Require enough room to write a full header */
269 if (remlen >= WRITE_RECHDR_LENGTH) {
270 ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
271 if (BLOCK_VER == 1) {
272 ser_uint32(rec->VolSessionId);
273 ser_uint32(rec->VolSessionTime);
275 block->VolSessionId = rec->VolSessionId;
276 block->VolSessionTime = rec->VolSessionTime;
278 ser_int32(rec->FileIndex);
279 ser_int32(rec->Stream);
280 ser_uint32(rec->data_len);
282 block->bufp += WRITE_RECHDR_LENGTH;
283 block->binbuf += WRITE_RECHDR_LENGTH;
284 remlen -= WRITE_RECHDR_LENGTH;
285 rec->remainder = rec->data_len;
286 if (rec->FileIndex > 0) {
287 /* If data record, update what we have in this block */
288 if (block->FirstIndex == 0) {
289 block->FirstIndex = rec->FileIndex;
291 block->LastIndex = rec->FileIndex;
294 rec->remainder = rec->data_len + WRITE_RECHDR_LENGTH;
299 * We are here to write unwritten bytes from a previous
300 * time. Presumably we have a new buffer (possibly
301 * containing a volume label), so the new header
302 * should be able to fit in the block -- otherwise we have
303 * an error. Note, we have to continue splitting the
304 * data record if it is longer than the block.
306 * First, write the header, then write as much as
307 * possible of the data record.
309 * Every time we write a header and it is a continuation
310 * of a previous partially written record, we store the
311 * Stream as -Stream in the record header.
313 ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
314 if (BLOCK_VER == 1) {
315 ser_uint32(rec->VolSessionId);
316 ser_uint32(rec->VolSessionTime);
318 block->VolSessionId = rec->VolSessionId;
319 block->VolSessionTime = rec->VolSessionTime;
321 ser_int32(rec->FileIndex);
322 if (rec->remainder > rec->data_len) {
323 ser_int32(rec->Stream); /* normal full header */
324 ser_uint32(rec->data_len);
325 rec->remainder = rec->data_len; /* must still do data record */
327 ser_int32(-rec->Stream); /* mark this as a continuation record */
328 ser_uint32(rec->remainder); /* bytes to do */
331 /* Require enough room to write a full header */
332 ASSERT(remlen >= WRITE_RECHDR_LENGTH);
334 block->bufp += WRITE_RECHDR_LENGTH;
335 block->binbuf += WRITE_RECHDR_LENGTH;
336 remlen -= WRITE_RECHDR_LENGTH;
337 if (rec->FileIndex > 0) {
338 /* If data record, update what we have in this block */
339 if (block->FirstIndex == 0) {
340 block->FirstIndex = rec->FileIndex;
342 block->LastIndex = rec->FileIndex;
346 return false; /* partial transfer */
350 * Now deal with data record.
351 * Part of it may have already been transferred, and we
352 * may not have enough room to transfer the whole this time.
354 if (rec->remainder > 0) {
355 /* Write as much of data as possible */
356 if (remlen >= rec->remainder) {
357 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
359 block->bufp += rec->remainder;
360 block->binbuf += rec->remainder;
362 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
365 if (!sm_check_rtn(__FILE__, __LINE__, False)) {
366 /* We damaged a buffer */
367 Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n"
368 "rem=%d remainder=%d\n",
369 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
370 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
371 remlen, rec->remainder);
372 Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
373 block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
375 Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
376 block->buf, block->bufp-block->buf);
378 Emsg0(M_ABORT, 0, _("Damaged buffer\n"));
382 block->bufp += remlen;
383 block->binbuf += remlen;
384 rec->remainder -= remlen;
385 return false; /* did partial transfer */
388 rec->remainder = 0; /* did whole transfer */
394 * Test if we can write whole record to the block
396 * Returns: false on failure
397 * true on success (all bytes can be written)
399 bool can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
403 remlen = block->buf_len - block->binbuf;
404 if (rec->remainder == 0) {
405 if (remlen >= WRITE_RECHDR_LENGTH) {
406 remlen -= WRITE_RECHDR_LENGTH;
407 rec->remainder = rec->data_len;
414 if (rec->remainder > 0 && remlen < rec->remainder) {
422 * Read a Record from the block
423 * Returns: false if nothing read or if the continuation record does not match.
424 * In both of these cases, a block read must be done.
425 * true if at least the record header was read, this
426 * routine may have to be called again with a new
427 * block if the entire record was not read.
429 bool read_record_from_block(DCR *dcr, DEV_BLOCK *block, DEV_RECORD *rec)
433 uint32_t VolSessionId;
434 uint32_t VolSessionTime;
439 char buf1[100], buf2[100];
441 remlen = block->binbuf;
442 rec->Block = block->BlockNumber;
443 rec->File = ((DEVICE *)block->dev)->file;
445 /* Clear state flags */
447 if (block->dev->is_tape()) {
448 rec->state |= REC_ISTAPE;
453 * Get the header. There is always a full header,
454 * otherwise we find it in the next block.
456 Dmsg3(450, "Block=%d Ver=%d size=%u\n", block->BlockNumber, block->BlockVer,
458 if (block->BlockVer == 1) {
459 rhl = RECHDR1_LENGTH;
461 rhl = RECHDR2_LENGTH;
464 Dmsg4(450, "Enter read_record_block: remlen=%d data_len=%d rem=%d blkver=%d\n",
465 remlen, rec->data_len, rec->remainder, block->BlockVer);
467 unser_begin(block->bufp, WRITE_RECHDR_LENGTH);
468 if (block->BlockVer == 1) {
469 unser_uint32(VolSessionId);
470 unser_uint32(VolSessionTime);
472 VolSessionId = block->VolSessionId;
473 VolSessionTime = block->VolSessionTime;
475 unser_int32(FileIndex);
477 unser_uint32(data_bytes);
480 block->binbuf -= rhl;
483 /* If we are looking for more (remainder!=0), we reject anything
484 * where the VolSessionId and VolSessionTime don't agree
486 if (rec->remainder && (rec->VolSessionId != VolSessionId ||
487 rec->VolSessionTime != VolSessionTime)) {
488 rec->state |= REC_NO_MATCH;
489 Dmsg0(450, "remainder and VolSession doesn't match\n");
490 return false; /* This is from some other Session */
493 /* if Stream is negative, it means that this is a continuation
494 * of a previous partially written record.
496 if (Stream < 0) { /* continuation record? */
497 Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n",
499 rec->state |= REC_CONTINUATION;
500 if (!rec->remainder) { /* if we didn't read previously */
501 rec->data_len = 0; /* return data as if no continuation */
502 } else if (rec->Stream != -Stream) {
503 rec->state |= REC_NO_MATCH;
504 return false; /* This is from some other Session */
506 rec->Stream = -Stream; /* set correct Stream */
507 } else { /* Regular record */
508 rec->Stream = Stream;
509 rec->data_len = 0; /* transfer to beginning of data */
511 rec->VolSessionId = VolSessionId;
512 rec->VolSessionTime = VolSessionTime;
513 rec->FileIndex = FileIndex;
515 if (block->FirstIndex == 0) {
516 block->FirstIndex = FileIndex;
518 block->LastIndex = FileIndex;
521 Dmsg6(450, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%u\n"
522 "remlen=%d data_len=%d\n",
523 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
524 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), data_bytes, remlen,
528 * No more records in this block because the number
529 * of remaining bytes are less than a record header
530 * length, so return empty handed, but indicate that
531 * he must read again. By returning, we allow the
532 * higher level routine to fetch the next block and
535 Dmsg0(450, "read_record_block: nothing\n");
536 rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
537 empty_block(block); /* mark block empty */
542 if (data_bytes >= MAX_BLOCK_LENGTH) {
544 * Something is wrong, force read of next block, abort
545 * continuing with this block.
547 rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
549 Jmsg2(dcr->jcr, M_WARNING, 0, _("Sanity check failed. maxlen=%d datalen=%d. Block discarded.\n"),
550 MAX_BLOCK_LENGTH, data_bytes);
554 rec->data = check_pool_memory_size(rec->data, rec->data_len+data_bytes);
557 * At this point, we have read the header, now we
558 * must transfer as much of the data record as
559 * possible taking into account: 1. A partial
560 * data record may have previously been transferred,
561 * 2. The current block may not contain the whole data
564 if (remlen >= data_bytes) {
565 /* Got whole record */
566 memcpy(rec->data+rec->data_len, block->bufp, data_bytes);
567 block->bufp += data_bytes;
568 block->binbuf -= data_bytes;
569 rec->data_len += data_bytes;
572 memcpy(rec->data+rec->data_len, block->bufp, remlen);
573 block->bufp += remlen;
574 block->binbuf -= remlen;
575 rec->data_len += remlen;
576 rec->remainder = 1; /* partial record transferred */
577 Dmsg1(450, "read_record_block: partial xfered=%d\n", rec->data_len);
578 rec->state |= (REC_PARTIAL_RECORD | REC_BLOCK_EMPTY);
582 Dmsg4(450, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
583 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
584 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
585 return true; /* transferred full record */