2 Bacula® - The Network Backup Solution
4 Copyright (C) 2001-2010 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * record.c -- tape record handling functions
32 * Kern Sibbald, April MMI
33 * added BB02 format October MMII
42 * Convert a FileIndex into a printable
43 * ASCII string. Not reentrant.
44 * If the FileIndex is negative, it flags the
45 * record as a Label, otherwise it is simply
46 * the FileIndex of the current file.
48 const char *FI_to_ascii(char *buf, int fi)
51 sprintf(buf, "%d", fi);
75 sprintf(buf, _("unknown: %d"), fi);
82 * Convert a Stream ID into a printable
83 * ASCII string. Not reentrant.
85 * A negative stream number represents
86 * stream data that is continued from a
87 * record in the previous block.
88 * If the FileIndex is negative, we are
89 * dealing with a Label, hence the
90 * stream is the JobId.
92 const char *stream_to_ascii(char *buf, int stream, int fi)
96 sprintf(buf, "%d", stream);
101 stream &= STREAMMASK_TYPE;
102 /* Stream was negative => all are continuation items */
104 case STREAM_UNIX_ATTRIBUTES:
106 case STREAM_FILE_DATA:
108 case STREAM_WIN32_DATA:
109 return "contWIN32-DATA";
110 case STREAM_WIN32_GZIP_DATA:
111 return "contWIN32-GZIP";
112 case STREAM_WIN32_COMPRESSED_DATA:
113 return "contWIN32-COMPRESSED";
114 case STREAM_MD5_DIGEST:
116 case STREAM_SHA1_DIGEST:
118 case STREAM_GZIP_DATA:
120 case STREAM_COMPRESSED_DATA:
121 return "contCOMPRESSED";
122 case STREAM_UNIX_ATTRIBUTES_EX:
123 return "contUNIX-ATTR-EX";
124 case STREAM_RESTORE_OBJECT:
125 return "contRESTORE-OBJECT";
126 case STREAM_SPARSE_DATA:
127 return "contSPARSE-DATA";
128 case STREAM_SPARSE_GZIP_DATA:
129 return "contSPARSE-GZIP";
130 case STREAM_SPARSE_COMPRESSED_DATA:
131 return "contSPARSE-COMPRESSED";
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";
140 case STREAM_SHA256_DIGEST:
142 case STREAM_SHA512_DIGEST:
144 case STREAM_SIGNED_DIGEST:
145 return "contSIGNED-DIGEST";
146 case STREAM_ENCRYPTED_SESSION_DATA:
147 return "contENCRYPTED-SESSION-DATA";
148 case STREAM_ENCRYPTED_FILE_DATA:
149 return "contENCRYPTED-FILE";
150 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
151 return "contENCRYPTED-GZIP";
152 case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
153 return "contENCRYPTED-COMPRESSED";
154 case STREAM_ENCRYPTED_WIN32_DATA:
155 return "contENCRYPTED-WIN32-DATA";
156 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
157 return "contENCRYPTED-WIN32-GZIP";
158 case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
159 return "contENCRYPTED-WIN32-COMPRESSED";
160 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
161 return "contENCRYPTED-MACOS-RSRC";
162 case STREAM_PLUGIN_NAME:
163 return "contPLUGIN-NAME";
166 sprintf(buf, "%d", -stream);
171 switch (stream & STREAMMASK_TYPE) {
172 case STREAM_UNIX_ATTRIBUTES:
174 case STREAM_FILE_DATA:
176 case STREAM_WIN32_DATA:
178 case STREAM_WIN32_GZIP_DATA:
180 case STREAM_WIN32_COMPRESSED_DATA:
181 return "WIN32-COMPRESSED";
182 case STREAM_MD5_DIGEST:
184 case STREAM_SHA1_DIGEST:
186 case STREAM_GZIP_DATA:
188 case STREAM_COMPRESSED_DATA:
190 case STREAM_UNIX_ATTRIBUTES_EX:
191 return "UNIX-ATTR-EX";
192 case STREAM_RESTORE_OBJECT:
193 return "RESTORE-OBJECT";
194 case STREAM_SPARSE_DATA:
195 return "SPARSE-DATA";
196 case STREAM_SPARSE_GZIP_DATA:
197 return "SPARSE-GZIP";
198 case STREAM_SPARSE_COMPRESSED_DATA:
199 return "SPARSE-COMPRESSED";
200 case STREAM_PROGRAM_NAMES:
202 case STREAM_PROGRAM_DATA:
204 case STREAM_PLUGIN_NAME:
205 return "PLUGIN-NAME";
206 case STREAM_MACOS_FORK_DATA:
208 case STREAM_HFSPLUS_ATTRIBUTES:
209 return "HFSPLUS-ATTR";
210 case STREAM_SHA256_DIGEST:
212 case STREAM_SHA512_DIGEST:
214 case STREAM_SIGNED_DIGEST:
215 return "SIGNED-DIGEST";
216 case STREAM_ENCRYPTED_SESSION_DATA:
217 return "ENCRYPTED-SESSION-DATA";
218 case STREAM_ENCRYPTED_FILE_DATA:
219 return "ENCRYPTED-FILE";
220 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
221 return "ENCRYPTED-GZIP";
222 case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
223 return "ENCRYPTED-COMPRESSED";
224 case STREAM_ENCRYPTED_WIN32_DATA:
225 return "ENCRYPTED-WIN32-DATA";
226 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
227 return "ENCRYPTED-WIN32-GZIP";
228 case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
229 return "ENCRYPTED-WIN32-COMPRESSED";
230 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
231 return "ENCRYPTED-MACOS-RSRC";
234 sprintf(buf, "%d", stream);
240 * Return a new record entity
242 DEV_RECORD *new_record(void)
246 rec = (DEV_RECORD *)get_memory(sizeof(DEV_RECORD));
247 memset(rec, 0, sizeof(DEV_RECORD));
248 rec->data = get_pool_memory(PM_MESSAGE);
252 void empty_record(DEV_RECORD *rec)
254 rec->File = rec->Block = 0;
255 rec->VolSessionId = rec->VolSessionTime = 0;
256 rec->FileIndex = rec->Stream = 0;
257 rec->data_len = rec->remainder = 0;
258 rec->state &= ~(REC_PARTIAL_RECORD|REC_BLOCK_EMPTY|REC_NO_MATCH|REC_CONTINUATION);
262 * Free the record entity
265 void free_record(DEV_RECORD *rec)
267 Dmsg0(950, "Enter free_record.\n");
269 free_pool_memory(rec->data);
271 Dmsg0(950, "Data buf is freed.\n");
272 free_pool_memory((POOLMEM *)rec);
273 Dmsg0(950, "Leave free_record.\n");
278 * Write a Record to the block
280 * Returns: false on failure (none or partially written)
281 * true on success (all bytes written)
283 * and remainder returned in packet.
285 * We require enough room for the header, and we deal with
286 * two special cases. 1. Only part of the record may have
287 * been transferred the last time (when remainder is
288 * non-zero), and 2. The remaining bytes to write may not
289 * all fit into the block.
291 bool write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
295 char buf1[100], buf2[100];
297 remlen = block->buf_len - block->binbuf;
299 ASSERT(block->binbuf == (uint32_t) (block->bufp - block->buf));
300 ASSERT(block->buf_len >= block->binbuf);
302 Dmsg6(890, "write_record_to_block() FI=%s SessId=%d Strm=%s len=%d\n"
303 "rem=%d remainder=%d\n",
304 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
305 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
306 remlen, rec->remainder);
309 * If rec->remainder is non-zero, we have been called a
310 * second (or subsequent) time to finish writing a record
311 * that did not previously fit into the block.
313 if (rec->remainder == 0) {
314 /* Require enough room to write a full header */
315 if (remlen >= WRITE_RECHDR_LENGTH) {
316 ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
317 if (BLOCK_VER == 1) {
318 ser_uint32(rec->VolSessionId);
319 ser_uint32(rec->VolSessionTime);
321 block->VolSessionId = rec->VolSessionId;
322 block->VolSessionTime = rec->VolSessionTime;
324 ser_int32(rec->FileIndex);
325 ser_int32(rec->Stream);
326 ser_uint32(rec->data_len);
328 block->bufp += WRITE_RECHDR_LENGTH;
329 block->binbuf += WRITE_RECHDR_LENGTH;
330 remlen -= WRITE_RECHDR_LENGTH;
331 rec->remainder = rec->data_len;
332 if (rec->FileIndex > 0) {
333 /* If data record, update what we have in this block */
334 if (block->FirstIndex == 0) {
335 block->FirstIndex = rec->FileIndex;
337 block->LastIndex = rec->FileIndex;
340 rec->remainder = rec->data_len + WRITE_RECHDR_LENGTH;
345 * We are here to write unwritten bytes from a previous
346 * time. Presumably we have a new buffer (possibly
347 * containing a volume label), so the new header
348 * should be able to fit in the block -- otherwise we have
349 * an error. Note, we have to continue splitting the
350 * data record if it is longer than the block.
352 * First, write the header, then write as much as
353 * possible of the data record.
355 * Every time we write a header and it is a continuation
356 * of a previous partially written record, we store the
357 * Stream as -Stream in the record header.
359 ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
360 if (BLOCK_VER == 1) {
361 ser_uint32(rec->VolSessionId);
362 ser_uint32(rec->VolSessionTime);
364 block->VolSessionId = rec->VolSessionId;
365 block->VolSessionTime = rec->VolSessionTime;
367 ser_int32(rec->FileIndex);
368 if (rec->remainder > rec->data_len) {
369 ser_int32(rec->Stream); /* normal full header */
370 ser_uint32(rec->data_len);
371 rec->remainder = rec->data_len; /* must still do data record */
373 ser_int32(-rec->Stream); /* mark this as a continuation record */
374 ser_uint32(rec->remainder); /* bytes to do */
377 /* Require enough room to write a full header */
378 ASSERT(remlen >= WRITE_RECHDR_LENGTH);
380 block->bufp += WRITE_RECHDR_LENGTH;
381 block->binbuf += WRITE_RECHDR_LENGTH;
382 remlen -= WRITE_RECHDR_LENGTH;
383 if (rec->FileIndex > 0) {
384 /* If data record, update what we have in this block */
385 if (block->FirstIndex == 0) {
386 block->FirstIndex = rec->FileIndex;
388 block->LastIndex = rec->FileIndex;
392 return false; /* partial transfer */
396 * Now deal with data record.
397 * Part of it may have already been transferred, and we
398 * may not have enough room to transfer the whole this time.
400 if (rec->remainder > 0) {
401 /* Write as much of data as possible */
402 if (remlen >= rec->remainder) {
403 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
405 block->bufp += rec->remainder;
406 block->binbuf += rec->remainder;
408 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
411 if (!sm_check_rtn(__FILE__, __LINE__, False)) {
412 /* We damaged a buffer */
413 Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n"
414 "rem=%d remainder=%d\n",
415 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
416 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
417 remlen, rec->remainder);
418 Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
419 block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
421 Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
422 block->buf, block->bufp-block->buf);
424 Emsg0(M_ABORT, 0, _("Damaged buffer\n"));
428 block->bufp += remlen;
429 block->binbuf += remlen;
430 rec->remainder -= remlen;
431 return false; /* did partial transfer */
434 rec->remainder = 0; /* did whole transfer */
440 * Test if we can write whole record to the block
442 * Returns: false on failure
443 * true on success (all bytes can be written)
445 bool can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
449 remlen = block->buf_len - block->binbuf;
450 if (rec->remainder == 0) {
451 if (remlen >= WRITE_RECHDR_LENGTH) {
452 remlen -= WRITE_RECHDR_LENGTH;
453 rec->remainder = rec->data_len;
460 if (rec->remainder > 0 && remlen < rec->remainder) {
466 uint64_t get_record_address(DEV_RECORD *rec)
468 return ((uint64_t)rec->File)<<32 | rec->Block;
472 * Read a Record from the block
473 * Returns: false if nothing read or if the continuation record does not match.
474 * In both of these cases, a block read must be done.
475 * true if at least the record header was read, this
476 * routine may have to be called again with a new
477 * block if the entire record was not read.
479 bool read_record_from_block(DCR *dcr, DEV_BLOCK *block, DEV_RECORD *rec)
483 uint32_t VolSessionId;
484 uint32_t VolSessionTime;
489 char buf1[100], buf2[100];
491 remlen = block->binbuf;
493 /* Clear state flags */
495 if (block->dev->is_tape()) {
496 rec->state |= REC_ISTAPE;
498 rec->Block = ((DEVICE *)block->dev)->EndBlock;
499 rec->File = ((DEVICE *)block->dev)->EndFile;
502 * Get the header. There is always a full header,
503 * otherwise we find it in the next block.
505 Dmsg3(450, "Block=%d Ver=%d size=%u\n", block->BlockNumber, block->BlockVer,
507 if (block->BlockVer == 1) {
508 rhl = RECHDR1_LENGTH;
510 rhl = RECHDR2_LENGTH;
513 Dmsg4(450, "Enter read_record_block: remlen=%d data_len=%d rem=%d blkver=%d\n",
514 remlen, rec->data_len, rec->remainder, block->BlockVer);
516 unser_begin(block->bufp, WRITE_RECHDR_LENGTH);
517 if (block->BlockVer == 1) {
518 unser_uint32(VolSessionId);
519 unser_uint32(VolSessionTime);
521 VolSessionId = block->VolSessionId;
522 VolSessionTime = block->VolSessionTime;
524 unser_int32(FileIndex);
526 unser_uint32(data_bytes);
529 block->binbuf -= rhl;
532 /* If we are looking for more (remainder!=0), we reject anything
533 * where the VolSessionId and VolSessionTime don't agree
535 if (rec->remainder && (rec->VolSessionId != VolSessionId ||
536 rec->VolSessionTime != VolSessionTime)) {
537 rec->state |= REC_NO_MATCH;
538 Dmsg0(450, "remainder and VolSession doesn't match\n");
539 return false; /* This is from some other Session */
542 /* if Stream is negative, it means that this is a continuation
543 * of a previous partially written record.
545 if (Stream < 0) { /* continuation record? */
546 Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n",
548 rec->state |= REC_CONTINUATION;
549 if (!rec->remainder) { /* if we didn't read previously */
550 rec->data_len = 0; /* return data as if no continuation */
551 } else if (rec->Stream != -Stream) {
552 rec->state |= REC_NO_MATCH;
553 return false; /* This is from some other Session */
555 rec->Stream = -Stream; /* set correct Stream */
556 rec->maskedStream = rec->Stream & STREAMMASK_TYPE;
557 } else { /* Regular record */
558 rec->Stream = Stream;
559 rec->maskedStream = rec->Stream & STREAMMASK_TYPE;
560 rec->data_len = 0; /* transfer to beginning of data */
562 rec->VolSessionId = VolSessionId;
563 rec->VolSessionTime = VolSessionTime;
564 rec->FileIndex = FileIndex;
566 if (block->FirstIndex == 0) {
567 block->FirstIndex = FileIndex;
569 block->LastIndex = FileIndex;
572 Dmsg6(450, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%u\n"
573 "remlen=%d data_len=%d\n",
574 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
575 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), data_bytes, remlen,
579 * No more records in this block because the number
580 * of remaining bytes are less than a record header
581 * length, so return empty handed, but indicate that
582 * he must read again. By returning, we allow the
583 * higher level routine to fetch the next block and
586 Dmsg0(450, "read_record_block: nothing\n");
587 rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
588 empty_block(block); /* mark block empty */
593 if (data_bytes >= MAX_BLOCK_LENGTH) {
595 * Something is wrong, force read of next block, abort
596 * continuing with this block.
598 rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
600 Jmsg2(dcr->jcr, M_WARNING, 0, _("Sanity check failed. maxlen=%d datalen=%d. Block discarded.\n"),
601 MAX_BLOCK_LENGTH, data_bytes);
605 rec->data = check_pool_memory_size(rec->data, rec->data_len+data_bytes);
608 * At this point, we have read the header, now we
609 * must transfer as much of the data record as
610 * possible taking into account: 1. A partial
611 * data record may have previously been transferred,
612 * 2. The current block may not contain the whole data
615 if (remlen >= data_bytes) {
616 /* Got whole record */
617 memcpy(rec->data+rec->data_len, block->bufp, data_bytes);
618 block->bufp += data_bytes;
619 block->binbuf -= data_bytes;
620 rec->data_len += data_bytes;
623 memcpy(rec->data+rec->data_len, block->bufp, remlen);
624 block->bufp += remlen;
625 block->binbuf -= remlen;
626 rec->data_len += remlen;
627 rec->remainder = 1; /* partial record transferred */
628 Dmsg1(450, "read_record_block: partial xfered=%d\n", rec->data_len);
629 rec->state |= (REC_PARTIAL_RECORD | REC_BLOCK_EMPTY);
633 Dmsg4(450, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
634 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
635 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
636 return true; /* transferred full record */