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_MD5_DIGEST:
114 case STREAM_SHA1_DIGEST:
116 case STREAM_GZIP_DATA:
118 case STREAM_UNIX_ATTRIBUTES_EX:
119 return "contUNIX-ATTR-EX";
120 case STREAM_RESTORE_OBJECT:
121 return "contRESTORE-OBJECT";
122 case STREAM_SPARSE_DATA:
123 return "contSPARSE-DATA";
124 case STREAM_SPARSE_GZIP_DATA:
125 return "contSPARSE-GZIP";
126 case STREAM_PROGRAM_NAMES:
127 return "contPROG-NAMES";
128 case STREAM_PROGRAM_DATA:
129 return "contPROG-DATA";
130 case STREAM_MACOS_FORK_DATA:
131 return "contMACOS-RSRC";
132 case STREAM_HFSPLUS_ATTRIBUTES:
133 return "contHFSPLUS-ATTR";
134 case STREAM_SHA256_DIGEST:
136 case STREAM_SHA512_DIGEST:
138 case STREAM_SIGNED_DIGEST:
139 return "contSIGNED-DIGEST";
140 case STREAM_ENCRYPTED_SESSION_DATA:
141 return "contENCRYPTED-SESSION-DATA";
142 case STREAM_ENCRYPTED_FILE_DATA:
143 return "contENCRYPTED-FILE";
144 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
145 return "contENCRYPTED-GZIP";
146 case STREAM_ENCRYPTED_WIN32_DATA:
147 return "contENCRYPTED-WIN32-DATA";
148 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
149 return "contENCRYPTED-WIN32-GZIP";
150 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
151 return "contENCRYPTED-MACOS-RSRC";
152 case STREAM_PLUGIN_NAME:
153 return "contPLUGIN-NAME";
156 sprintf(buf, "%d", -stream);
161 switch (stream & STREAMMASK_TYPE) {
162 case STREAM_UNIX_ATTRIBUTES:
164 case STREAM_FILE_DATA:
166 case STREAM_WIN32_DATA:
168 case STREAM_WIN32_GZIP_DATA:
170 case STREAM_MD5_DIGEST:
172 case STREAM_SHA1_DIGEST:
174 case STREAM_GZIP_DATA:
176 case STREAM_UNIX_ATTRIBUTES_EX:
177 return "UNIX-ATTR-EX";
178 case STREAM_RESTORE_OBJECT:
179 return "RESTORE-OBJECT";
180 case STREAM_SPARSE_DATA:
181 return "SPARSE-DATA";
182 case STREAM_SPARSE_GZIP_DATA:
183 return "SPARSE-GZIP";
184 case STREAM_PROGRAM_NAMES:
186 case STREAM_PROGRAM_DATA:
188 case STREAM_PLUGIN_NAME:
189 return "PLUGIN-NAME";
190 case STREAM_MACOS_FORK_DATA:
192 case STREAM_HFSPLUS_ATTRIBUTES:
193 return "HFSPLUS-ATTR";
194 case STREAM_SHA256_DIGEST:
196 case STREAM_SHA512_DIGEST:
198 case STREAM_SIGNED_DIGEST:
199 return "SIGNED-DIGEST";
200 case STREAM_ENCRYPTED_SESSION_DATA:
201 return "ENCRYPTED-SESSION-DATA";
202 case STREAM_ENCRYPTED_FILE_DATA:
203 return "ENCRYPTED-FILE";
204 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
205 return "ENCRYPTED-GZIP";
206 case STREAM_ENCRYPTED_WIN32_DATA:
207 return "ENCRYPTED-WIN32-DATA";
208 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
209 return "ENCRYPTED-WIN32-GZIP";
210 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
211 return "ENCRYPTED-MACOS-RSRC";
214 sprintf(buf, "%d", stream);
220 * Return a new record entity
222 DEV_RECORD *new_record(void)
226 rec = (DEV_RECORD *)get_memory(sizeof(DEV_RECORD));
227 memset(rec, 0, sizeof(DEV_RECORD));
228 rec->data = get_pool_memory(PM_MESSAGE);
232 void empty_record(DEV_RECORD *rec)
234 rec->File = rec->Block = 0;
235 rec->VolSessionId = rec->VolSessionTime = 0;
236 rec->FileIndex = rec->Stream = 0;
237 rec->data_len = rec->remainder = 0;
238 rec->state &= ~(REC_PARTIAL_RECORD|REC_BLOCK_EMPTY|REC_NO_MATCH|REC_CONTINUATION);
242 * Free the record entity
245 void free_record(DEV_RECORD *rec)
247 Dmsg0(950, "Enter free_record.\n");
249 free_pool_memory(rec->data);
251 Dmsg0(950, "Data buf is freed.\n");
252 free_pool_memory((POOLMEM *)rec);
253 Dmsg0(950, "Leave free_record.\n");
258 * Write a Record to the block
260 * Returns: false on failure (none or partially written)
261 * true on success (all bytes written)
263 * and remainder returned in packet.
265 * We require enough room for the header, and we deal with
266 * two special cases. 1. Only part of the record may have
267 * been transferred the last time (when remainder is
268 * non-zero), and 2. The remaining bytes to write may not
269 * all fit into the block.
271 bool write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
275 char buf1[100], buf2[100];
277 remlen = block->buf_len - block->binbuf;
279 ASSERT(block->binbuf == (uint32_t) (block->bufp - block->buf));
280 ASSERT(block->buf_len >= block->binbuf);
282 Dmsg6(890, "write_record_to_block() FI=%s SessId=%d Strm=%s len=%d\n"
283 "rem=%d remainder=%d\n",
284 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
285 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
286 remlen, rec->remainder);
289 * If rec->remainder is non-zero, we have been called a
290 * second (or subsequent) time to finish writing a record
291 * that did not previously fit into the block.
293 if (rec->remainder == 0) {
294 /* Require enough room to write a full header */
295 if (remlen >= WRITE_RECHDR_LENGTH) {
296 ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
297 if (BLOCK_VER == 1) {
298 ser_uint32(rec->VolSessionId);
299 ser_uint32(rec->VolSessionTime);
301 block->VolSessionId = rec->VolSessionId;
302 block->VolSessionTime = rec->VolSessionTime;
304 ser_int32(rec->FileIndex);
305 ser_int32(rec->Stream);
306 ser_uint32(rec->data_len);
308 block->bufp += WRITE_RECHDR_LENGTH;
309 block->binbuf += WRITE_RECHDR_LENGTH;
310 remlen -= WRITE_RECHDR_LENGTH;
311 rec->remainder = rec->data_len;
312 if (rec->FileIndex > 0) {
313 /* If data record, update what we have in this block */
314 if (block->FirstIndex == 0) {
315 block->FirstIndex = rec->FileIndex;
317 block->LastIndex = rec->FileIndex;
320 rec->remainder = rec->data_len + WRITE_RECHDR_LENGTH;
325 * We are here to write unwritten bytes from a previous
326 * time. Presumably we have a new buffer (possibly
327 * containing a volume label), so the new header
328 * should be able to fit in the block -- otherwise we have
329 * an error. Note, we have to continue splitting the
330 * data record if it is longer than the block.
332 * First, write the header, then write as much as
333 * possible of the data record.
335 * Every time we write a header and it is a continuation
336 * of a previous partially written record, we store the
337 * Stream as -Stream in the record header.
339 ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
340 if (BLOCK_VER == 1) {
341 ser_uint32(rec->VolSessionId);
342 ser_uint32(rec->VolSessionTime);
344 block->VolSessionId = rec->VolSessionId;
345 block->VolSessionTime = rec->VolSessionTime;
347 ser_int32(rec->FileIndex);
348 if (rec->remainder > rec->data_len) {
349 ser_int32(rec->Stream); /* normal full header */
350 ser_uint32(rec->data_len);
351 rec->remainder = rec->data_len; /* must still do data record */
353 ser_int32(-rec->Stream); /* mark this as a continuation record */
354 ser_uint32(rec->remainder); /* bytes to do */
357 /* Require enough room to write a full header */
358 ASSERT(remlen >= WRITE_RECHDR_LENGTH);
360 block->bufp += WRITE_RECHDR_LENGTH;
361 block->binbuf += WRITE_RECHDR_LENGTH;
362 remlen -= WRITE_RECHDR_LENGTH;
363 if (rec->FileIndex > 0) {
364 /* If data record, update what we have in this block */
365 if (block->FirstIndex == 0) {
366 block->FirstIndex = rec->FileIndex;
368 block->LastIndex = rec->FileIndex;
372 return false; /* partial transfer */
376 * Now deal with data record.
377 * Part of it may have already been transferred, and we
378 * may not have enough room to transfer the whole this time.
380 if (rec->remainder > 0) {
381 /* Write as much of data as possible */
382 if (remlen >= rec->remainder) {
383 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
385 block->bufp += rec->remainder;
386 block->binbuf += rec->remainder;
388 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
391 if (!sm_check_rtn(__FILE__, __LINE__, False)) {
392 /* We damaged a buffer */
393 Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n"
394 "rem=%d remainder=%d\n",
395 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
396 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
397 remlen, rec->remainder);
398 Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
399 block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
401 Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
402 block->buf, block->bufp-block->buf);
404 Emsg0(M_ABORT, 0, _("Damaged buffer\n"));
408 block->bufp += remlen;
409 block->binbuf += remlen;
410 rec->remainder -= remlen;
411 return false; /* did partial transfer */
414 rec->remainder = 0; /* did whole transfer */
420 * Test if we can write whole record to the block
422 * Returns: false on failure
423 * true on success (all bytes can be written)
425 bool can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
429 remlen = block->buf_len - block->binbuf;
430 if (rec->remainder == 0) {
431 if (remlen >= WRITE_RECHDR_LENGTH) {
432 remlen -= WRITE_RECHDR_LENGTH;
433 rec->remainder = rec->data_len;
440 if (rec->remainder > 0 && remlen < rec->remainder) {
446 uint64_t get_record_address(DEV_RECORD *rec)
448 return ((uint64_t)rec->File)<<32 | rec->Block;
452 * Read a Record from the block
453 * Returns: false if nothing read or if the continuation record does not match.
454 * In both of these cases, a block read must be done.
455 * true if at least the record header was read, this
456 * routine may have to be called again with a new
457 * block if the entire record was not read.
459 bool read_record_from_block(DCR *dcr, DEV_BLOCK *block, DEV_RECORD *rec)
463 uint32_t VolSessionId;
464 uint32_t VolSessionTime;
469 char buf1[100], buf2[100];
471 remlen = block->binbuf;
473 /* Clear state flags */
475 if (block->dev->is_tape()) {
476 rec->state |= REC_ISTAPE;
478 rec->Block = ((DEVICE *)block->dev)->EndBlock;
479 rec->File = ((DEVICE *)block->dev)->EndFile;
482 * Get the header. There is always a full header,
483 * otherwise we find it in the next block.
485 Dmsg3(450, "Block=%d Ver=%d size=%u\n", block->BlockNumber, block->BlockVer,
487 if (block->BlockVer == 1) {
488 rhl = RECHDR1_LENGTH;
490 rhl = RECHDR2_LENGTH;
493 Dmsg4(450, "Enter read_record_block: remlen=%d data_len=%d rem=%d blkver=%d\n",
494 remlen, rec->data_len, rec->remainder, block->BlockVer);
496 unser_begin(block->bufp, WRITE_RECHDR_LENGTH);
497 if (block->BlockVer == 1) {
498 unser_uint32(VolSessionId);
499 unser_uint32(VolSessionTime);
501 VolSessionId = block->VolSessionId;
502 VolSessionTime = block->VolSessionTime;
504 unser_int32(FileIndex);
506 unser_uint32(data_bytes);
509 block->binbuf -= rhl;
512 /* If we are looking for more (remainder!=0), we reject anything
513 * where the VolSessionId and VolSessionTime don't agree
515 if (rec->remainder && (rec->VolSessionId != VolSessionId ||
516 rec->VolSessionTime != VolSessionTime)) {
517 rec->state |= REC_NO_MATCH;
518 Dmsg0(450, "remainder and VolSession doesn't match\n");
519 return false; /* This is from some other Session */
522 /* if Stream is negative, it means that this is a continuation
523 * of a previous partially written record.
525 if (Stream < 0) { /* continuation record? */
526 Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n",
528 rec->state |= REC_CONTINUATION;
529 if (!rec->remainder) { /* if we didn't read previously */
530 rec->data_len = 0; /* return data as if no continuation */
531 } else if (rec->Stream != -Stream) {
532 rec->state |= REC_NO_MATCH;
533 return false; /* This is from some other Session */
535 rec->Stream = -Stream; /* set correct Stream */
536 rec->maskedStream = rec->Stream & STREAMMASK_TYPE;
537 } else { /* Regular record */
538 rec->Stream = Stream;
539 rec->maskedStream = rec->Stream & STREAMMASK_TYPE;
540 rec->data_len = 0; /* transfer to beginning of data */
542 rec->VolSessionId = VolSessionId;
543 rec->VolSessionTime = VolSessionTime;
544 rec->FileIndex = FileIndex;
546 if (block->FirstIndex == 0) {
547 block->FirstIndex = FileIndex;
549 block->LastIndex = FileIndex;
552 Dmsg6(450, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%u\n"
553 "remlen=%d data_len=%d\n",
554 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
555 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), data_bytes, remlen,
559 * No more records in this block because the number
560 * of remaining bytes are less than a record header
561 * length, so return empty handed, but indicate that
562 * he must read again. By returning, we allow the
563 * higher level routine to fetch the next block and
566 Dmsg0(450, "read_record_block: nothing\n");
567 rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
568 empty_block(block); /* mark block empty */
573 if (data_bytes >= MAX_BLOCK_LENGTH) {
575 * Something is wrong, force read of next block, abort
576 * continuing with this block.
578 rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
580 Jmsg2(dcr->jcr, M_WARNING, 0, _("Sanity check failed. maxlen=%d datalen=%d. Block discarded.\n"),
581 MAX_BLOCK_LENGTH, data_bytes);
585 rec->data = check_pool_memory_size(rec->data, rec->data_len+data_bytes);
588 * At this point, we have read the header, now we
589 * must transfer as much of the data record as
590 * possible taking into account: 1. A partial
591 * data record may have previously been transferred,
592 * 2. The current block may not contain the whole data
595 if (remlen >= data_bytes) {
596 /* Got whole record */
597 memcpy(rec->data+rec->data_len, block->bufp, data_bytes);
598 block->bufp += data_bytes;
599 block->binbuf -= data_bytes;
600 rec->data_len += data_bytes;
603 memcpy(rec->data+rec->data_len, block->bufp, remlen);
604 block->bufp += remlen;
605 block->binbuf -= remlen;
606 rec->data_len += remlen;
607 rec->remainder = 1; /* partial record transferred */
608 Dmsg1(450, "read_record_block: partial xfered=%d\n", rec->data_len);
609 rec->state |= (REC_PARTIAL_RECORD | REC_BLOCK_EMPTY);
613 Dmsg4(450, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
614 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
615 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
616 return true; /* transferred full record */