2 Bacula® - The Network Backup Solution
4 Copyright (C) 2001-2012 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 -- Volume (tape/disk) 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);
249 rec->state = st_none;
253 void empty_record(DEV_RECORD *rec)
255 rec->File = rec->Block = 0;
256 rec->VolSessionId = rec->VolSessionTime = 0;
257 rec->FileIndex = rec->Stream = 0;
258 rec->data_len = rec->remainder = 0;
259 rec->state_bits &= ~(REC_PARTIAL_RECORD|REC_BLOCK_EMPTY|REC_NO_MATCH|REC_CONTINUATION);
260 rec->state = st_none;
264 * Free the record entity
267 void free_record(DEV_RECORD *rec)
269 Dmsg0(950, "Enter free_record.\n");
271 free_pool_memory(rec->data);
273 Dmsg0(950, "Data buf is freed.\n");
274 free_pool_memory((POOLMEM *)rec);
275 Dmsg0(950, "Leave free_record.\n");
278 static bool write_header_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
282 rec->remlen = block->buf_len - block->binbuf;
283 /* Require enough room to write a full header */
284 if (rec->remlen >= WRITE_RECHDR_LENGTH) {
285 ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
286 if (BLOCK_VER == 1) {
287 ser_uint32(rec->VolSessionId);
288 ser_uint32(rec->VolSessionTime);
290 block->VolSessionId = rec->VolSessionId;
291 block->VolSessionTime = rec->VolSessionTime;
293 ser_int32(rec->FileIndex);
294 ser_int32(rec->Stream);
295 ser_uint32(rec->data_len);
297 block->bufp += WRITE_RECHDR_LENGTH;
298 block->binbuf += WRITE_RECHDR_LENGTH;
299 rec->remlen -= WRITE_RECHDR_LENGTH;
300 rec->remainder = rec->data_len;
301 if (rec->FileIndex > 0) {
302 /* If data record, update what we have in this block */
303 if (block->FirstIndex == 0) {
304 block->FirstIndex = rec->FileIndex;
306 block->LastIndex = rec->FileIndex;
309 rec->remainder = rec->data_len + WRITE_RECHDR_LENGTH;
315 static void write_continue_header_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
319 rec->remlen = block->buf_len - block->binbuf;
321 * We have unwritten bytes from a previous
322 * time. Presumably we have a new buffer (possibly
323 * containing a volume label), so the new header
324 * should be able to fit in the block -- otherwise we have
325 * an error. Note, we have to continue splitting the
326 * data record if it is longer than the block.
328 * First, write the header.
330 * Every time we write a header and it is a continuation
331 * of a previous partially written record, we store the
332 * Stream as -Stream in the record header.
334 ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
335 if (BLOCK_VER == 1) {
336 ser_uint32(rec->VolSessionId);
337 ser_uint32(rec->VolSessionTime);
339 block->VolSessionId = rec->VolSessionId;
340 block->VolSessionTime = rec->VolSessionTime;
342 ser_int32(rec->FileIndex);
343 if (rec->remainder > rec->data_len) {
344 ser_int32(rec->Stream); /* normal full header */
345 ser_uint32(rec->data_len);
346 rec->remainder = rec->data_len; /* must still do data record */
348 ser_int32(-rec->Stream); /* mark this as a continuation record */
349 ser_uint32(rec->remainder); /* bytes to do */
352 /* Require enough room to write a full header */
353 ASSERT(rec->remlen >= WRITE_RECHDR_LENGTH);
355 block->bufp += WRITE_RECHDR_LENGTH;
356 block->binbuf += WRITE_RECHDR_LENGTH;
357 rec->remlen -= WRITE_RECHDR_LENGTH;
358 if (rec->FileIndex > 0) {
359 /* If data record, update what we have in this block */
360 if (block->FirstIndex == 0) {
361 block->FirstIndex = rec->FileIndex;
363 block->LastIndex = rec->FileIndex;
367 static bool write_data_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
369 rec->remlen = block->buf_len - block->binbuf;
370 /* Write as much of data as possible */
371 if (rec->remlen >= rec->remainder) {
372 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
374 block->bufp += rec->remainder;
375 block->binbuf += rec->remainder;
377 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
380 if (!sm_check_rtn(__FILE__, __LINE__, False)) {
381 /* We damaged a buffer */
382 Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n"
383 "rem=%d remainder=%d\n",
384 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
385 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
386 rec->remlen, rec->remainder);
387 Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
388 block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
390 Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
391 block->buf, block->bufp-block->buf);
392 Emsg0(M_ABORT, 0, _("Damaged buffer\n"));
396 block->bufp += rec->remlen;
397 block->binbuf += rec->remlen;
398 rec->remainder -= rec->remlen;
399 return false; /* did partial transfer */
405 * Write a Record to the block
407 * Returns: false means the block could not be written to tape/disk.
408 * true on success (all bytes written to the block).
410 bool DCR::write_record(DEV_RECORD *rec)
412 while (!write_record_to_block(this, rec)) {
413 Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
415 if (!write_block_to_device()) {
416 Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
417 dev->print_name(), dev->bstrerror());
425 * Write a Record to the block
427 * Returns: false on failure (none or partially written)
428 * true on success (all bytes written)
430 * and remainder returned in packet.
432 * We require enough room for the header, and we deal with
433 * two special cases. 1. Only part of the record may have
434 * been transferred the last time (when remainder is
435 * non-zero), and 2. The remaining bytes to write may not
436 * all fit into the block.
438 bool write_record_to_block(DCR *dcr, DEV_RECORD *rec)
440 char buf1[100], buf2[100];
441 DEV_BLOCK *block = dcr->block;
444 ASSERT(block->binbuf == (uint32_t)(block->bufp - block->buf));
445 ASSERT(block->buf_len >= block->binbuf);
447 Dmsg6(890, "write_record_to_block() FI=%s SessId=%d Strm=%s len=%d\n"
448 "rem=%d remainder=%d\n",
449 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
450 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
451 rec->remlen, rec->remainder);
453 switch (rec->state) {
455 /* Figure out what to do */
456 rec->state = st_header;
457 continue; /* go to next state */
463 * If rec->remainder is non-zero, we have been called a
464 * second (or subsequent) time to finish writing a record
465 * that did not previously fit into the block.
467 if (!write_header_to_block(block, rec)) {
468 return false; /* write block then come back here */
470 rec->state = st_data; /* after header, now write data */
473 /* Write continuation header */
475 write_continue_header_to_block(block, rec);
476 rec->state = st_data;
477 if (rec->remlen == 0) {
478 return false; /* partial transfer */
482 /* Write normal data */
487 * Part of it may have already been transferred, and we
488 * may not have enough room to transfer the whole this time.
490 if (rec->remainder > 0) {
491 if (!write_data_to_block(block, rec)) {
492 rec->state = st_header_cont;
496 rec->remainder = 0; /* did whole transfer */
497 rec->state = st_none;
501 Dmsg0(000, "Something went wrong. Default state.\n");
502 rec->state = st_none;
510 * Test if we can write whole record to the block
512 * Returns: false on failure
513 * true on success (all bytes can be written)
515 bool can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
519 remlen = block->buf_len - block->binbuf;
520 if (rec->remainder == 0) {
521 if (remlen >= WRITE_RECHDR_LENGTH) {
522 remlen -= WRITE_RECHDR_LENGTH;
523 rec->remainder = rec->data_len;
530 if (rec->remainder > 0 && remlen < rec->remainder) {
536 uint64_t get_record_address(DEV_RECORD *rec)
538 return ((uint64_t)rec->File)<<32 | rec->Block;
542 * Read a Record from the block
543 * Returns: false if nothing read or if the continuation record does not match.
544 * In both of these cases, a block read must be done.
545 * true if at least the record header was read, this
546 * routine may have to be called again with a new
547 * block if the entire record was not read.
549 bool read_record_from_block(DCR *dcr, DEV_BLOCK *block, DEV_RECORD *rec)
553 uint32_t VolSessionId;
554 uint32_t VolSessionTime;
559 char buf1[100], buf2[100];
561 remlen = block->binbuf;
563 /* Clear state flags */
565 if (block->dev->is_tape()) {
566 rec->state_bits |= REC_ISTAPE;
568 rec->Block = ((DEVICE *)block->dev)->EndBlock;
569 rec->File = ((DEVICE *)block->dev)->EndFile;
572 * Get the header. There is always a full header,
573 * otherwise we find it in the next block.
575 Dmsg3(450, "Block=%d Ver=%d size=%u\n", block->BlockNumber, block->BlockVer,
577 if (block->BlockVer == 1) {
578 rhl = RECHDR1_LENGTH;
580 rhl = RECHDR2_LENGTH;
583 Dmsg4(450, "Enter read_record_block: remlen=%d data_len=%d rem=%d blkver=%d\n",
584 remlen, rec->data_len, rec->remainder, block->BlockVer);
586 unser_begin(block->bufp, WRITE_RECHDR_LENGTH);
587 if (block->BlockVer == 1) {
588 unser_uint32(VolSessionId);
589 unser_uint32(VolSessionTime);
591 VolSessionId = block->VolSessionId;
592 VolSessionTime = block->VolSessionTime;
594 unser_int32(FileIndex);
596 unser_uint32(data_bytes);
599 block->binbuf -= rhl;
602 /* If we are looking for more (remainder!=0), we reject anything
603 * where the VolSessionId and VolSessionTime don't agree
605 if (rec->remainder && (rec->VolSessionId != VolSessionId ||
606 rec->VolSessionTime != VolSessionTime)) {
607 rec->state_bits |= REC_NO_MATCH;
608 Dmsg0(450, "remainder and VolSession doesn't match\n");
609 return false; /* This is from some other Session */
612 /* if Stream is negative, it means that this is a continuation
613 * of a previous partially written record.
615 if (Stream < 0) { /* continuation record? */
616 Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n",
618 rec->state_bits |= REC_CONTINUATION;
619 if (!rec->remainder) { /* if we didn't read previously */
620 rec->data_len = 0; /* return data as if no continuation */
621 } else if (rec->Stream != -Stream) {
622 rec->state_bits |= REC_NO_MATCH;
623 return false; /* This is from some other Session */
625 rec->Stream = -Stream; /* set correct Stream */
626 rec->maskedStream = rec->Stream & STREAMMASK_TYPE;
627 } else { /* Regular record */
628 rec->Stream = Stream;
629 rec->maskedStream = rec->Stream & STREAMMASK_TYPE;
630 rec->data_len = 0; /* transfer to beginning of data */
632 rec->VolSessionId = VolSessionId;
633 rec->VolSessionTime = VolSessionTime;
634 rec->FileIndex = FileIndex;
636 if (block->FirstIndex == 0) {
637 block->FirstIndex = FileIndex;
639 block->LastIndex = FileIndex;
642 Dmsg6(450, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%u\n"
643 "remlen=%d data_len=%d\n",
644 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
645 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), data_bytes, remlen,
649 * No more records in this block because the number
650 * of remaining bytes are less than a record header
651 * length, so return empty handed, but indicate that
652 * he must read again. By returning, we allow the
653 * higher level routine to fetch the next block and
656 Dmsg0(450, "read_record_block: nothing\n");
657 rec->state_bits |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
658 empty_block(block); /* mark block empty */
663 if (data_bytes >= MAX_BLOCK_LENGTH) {
665 * Something is wrong, force read of next block, abort
666 * continuing with this block.
668 rec->state_bits |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
670 Jmsg2(dcr->jcr, M_WARNING, 0, _("Sanity check failed. maxlen=%d datalen=%d. Block discarded.\n"),
671 MAX_BLOCK_LENGTH, data_bytes);
675 rec->data = check_pool_memory_size(rec->data, rec->data_len+data_bytes);
678 * At this point, we have read the header, now we
679 * must transfer as much of the data record as
680 * possible taking into account: 1. A partial
681 * data record may have previously been transferred,
682 * 2. The current block may not contain the whole data
685 if (remlen >= data_bytes) {
686 /* Got whole record */
687 memcpy(rec->data+rec->data_len, block->bufp, data_bytes);
688 block->bufp += data_bytes;
689 block->binbuf -= data_bytes;
690 rec->data_len += data_bytes;
693 memcpy(rec->data+rec->data_len, block->bufp, remlen);
694 block->bufp += remlen;
695 block->binbuf -= remlen;
696 rec->data_len += remlen;
697 rec->remainder = 1; /* partial record transferred */
698 Dmsg1(450, "read_record_block: partial xfered=%d\n", rec->data_len);
699 rec->state_bits |= (REC_PARTIAL_RECORD | REC_BLOCK_EMPTY);
703 Dmsg4(450, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
704 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
705 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
706 return true; /* transferred full record */