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)
95 sprintf(buf, "%d", stream);
99 case STREAM_UNIX_ATTRIBUTES:
101 case STREAM_FILE_DATA:
103 case STREAM_WIN32_DATA:
105 case STREAM_WIN32_GZIP_DATA:
107 case STREAM_MD5_DIGEST:
109 case STREAM_SHA1_DIGEST:
111 case STREAM_GZIP_DATA:
113 case STREAM_UNIX_ATTRIBUTES_EX:
114 return "UNIX-ATTR-EX";
115 case STREAM_RESTORE_OBJECT:
116 return "RESTORE-OBJECT";
117 case STREAM_SPARSE_DATA:
118 return "SPARSE-DATA";
119 case STREAM_SPARSE_GZIP_DATA:
120 return "SPARSE-GZIP";
121 case STREAM_PROGRAM_NAMES:
123 case STREAM_PROGRAM_DATA:
125 case STREAM_PLUGIN_NAME:
126 return "PLUGIN-NAME";
127 case STREAM_MACOS_FORK_DATA:
129 case STREAM_HFSPLUS_ATTRIBUTES:
130 return "HFSPLUS-ATTR";
131 case STREAM_SHA256_DIGEST:
133 case STREAM_SHA512_DIGEST:
135 case STREAM_SIGNED_DIGEST:
136 return "SIGNED-DIGEST";
137 case STREAM_ENCRYPTED_SESSION_DATA:
138 return "ENCRYPTED-SESSION-DATA";
139 case STREAM_ENCRYPTED_FILE_DATA:
140 return "ENCRYPTED-FILE";
141 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
142 return "ENCRYPTED-GZIP";
143 case STREAM_ENCRYPTED_WIN32_DATA:
144 return "ENCRYPTED-WIN32-DATA";
145 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
146 return "ENCRYPTED-WIN32-GZIP";
147 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
148 return "ENCRYPTED-MACOS-RSRC";
149 case -STREAM_UNIX_ATTRIBUTES:
151 case -STREAM_FILE_DATA:
153 case -STREAM_WIN32_DATA:
154 return "contWIN32-DATA";
155 case -STREAM_WIN32_GZIP_DATA:
156 return "contWIN32-GZIP";
157 case -STREAM_MD5_DIGEST:
159 case -STREAM_SHA1_DIGEST:
161 case -STREAM_GZIP_DATA:
163 case -STREAM_UNIX_ATTRIBUTES_EX:
164 return "contUNIX-ATTR-EX";
165 case -STREAM_RESTORE_OBJECT:
166 return "contRESTORE-OBJECT";
167 case -STREAM_SPARSE_DATA:
168 return "contSPARSE-DATA";
169 case -STREAM_SPARSE_GZIP_DATA:
170 return "contSPARSE-GZIP";
171 case -STREAM_PROGRAM_NAMES:
172 return "contPROG-NAMES";
173 case -STREAM_PROGRAM_DATA:
174 return "contPROG-DATA";
175 case -STREAM_MACOS_FORK_DATA:
176 return "contMACOS-RSRC";
177 case -STREAM_HFSPLUS_ATTRIBUTES:
178 return "contHFSPLUS-ATTR";
179 case -STREAM_SHA256_DIGEST:
181 case -STREAM_SHA512_DIGEST:
183 case -STREAM_SIGNED_DIGEST:
184 return "contSIGNED-DIGEST";
185 case -STREAM_ENCRYPTED_SESSION_DATA:
186 return "contENCRYPTED-SESSION-DATA";
187 case -STREAM_ENCRYPTED_FILE_DATA:
188 return "contENCRYPTED-FILE";
189 case -STREAM_ENCRYPTED_FILE_GZIP_DATA:
190 return "contENCRYPTED-GZIP";
191 case -STREAM_ENCRYPTED_WIN32_DATA:
192 return "contENCRYPTED-WIN32-DATA";
193 case -STREAM_ENCRYPTED_WIN32_GZIP_DATA:
194 return "contENCRYPTED-WIN32-GZIP";
195 case -STREAM_ENCRYPTED_MACOS_FORK_DATA:
196 return "contENCRYPTED-MACOS-RSRC";
197 case -STREAM_PLUGIN_NAME:
198 return "contPLUGIN-NAME";
201 sprintf(buf, "%d", stream);
207 * Return a new record entity
209 DEV_RECORD *new_record(void)
213 rec = (DEV_RECORD *)get_memory(sizeof(DEV_RECORD));
214 memset(rec, 0, sizeof(DEV_RECORD));
215 rec->data = get_pool_memory(PM_MESSAGE);
219 void empty_record(DEV_RECORD *rec)
221 rec->File = rec->Block = 0;
222 rec->VolSessionId = rec->VolSessionTime = 0;
223 rec->FileIndex = rec->Stream = 0;
224 rec->data_len = rec->remainder = 0;
225 rec->state &= ~(REC_PARTIAL_RECORD|REC_BLOCK_EMPTY|REC_NO_MATCH|REC_CONTINUATION);
229 * Free the record entity
232 void free_record(DEV_RECORD *rec)
234 Dmsg0(950, "Enter free_record.\n");
236 free_pool_memory(rec->data);
238 Dmsg0(950, "Data buf is freed.\n");
239 free_pool_memory((POOLMEM *)rec);
240 Dmsg0(950, "Leave free_record.\n");
245 * Write a Record to the block
247 * Returns: false on failure (none or partially written)
248 * true on success (all bytes written)
250 * and remainder returned in packet.
252 * We require enough room for the header, and we deal with
253 * two special cases. 1. Only part of the record may have
254 * been transferred the last time (when remainder is
255 * non-zero), and 2. The remaining bytes to write may not
256 * all fit into the block.
258 bool write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
262 char buf1[100], buf2[100];
264 remlen = block->buf_len - block->binbuf;
266 ASSERT(block->binbuf == (uint32_t) (block->bufp - block->buf));
267 ASSERT(block->buf_len >= block->binbuf);
269 Dmsg6(890, "write_record_to_block() FI=%s SessId=%d Strm=%s len=%d\n"
270 "rem=%d remainder=%d\n",
271 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
272 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
273 remlen, rec->remainder);
276 * If rec->remainder is non-zero, we have been called a
277 * second (or subsequent) time to finish writing a record
278 * that did not previously fit into the block.
280 if (rec->remainder == 0) {
281 /* Require enough room to write a full header */
282 if (remlen >= WRITE_RECHDR_LENGTH) {
283 ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
284 if (BLOCK_VER == 1) {
285 ser_uint32(rec->VolSessionId);
286 ser_uint32(rec->VolSessionTime);
288 block->VolSessionId = rec->VolSessionId;
289 block->VolSessionTime = rec->VolSessionTime;
291 ser_int32(rec->FileIndex);
292 ser_int32(rec->Stream);
293 ser_uint32(rec->data_len);
295 block->bufp += WRITE_RECHDR_LENGTH;
296 block->binbuf += WRITE_RECHDR_LENGTH;
297 remlen -= WRITE_RECHDR_LENGTH;
298 rec->remainder = rec->data_len;
299 if (rec->FileIndex > 0) {
300 /* If data record, update what we have in this block */
301 if (block->FirstIndex == 0) {
302 block->FirstIndex = rec->FileIndex;
304 block->LastIndex = rec->FileIndex;
307 rec->remainder = rec->data_len + WRITE_RECHDR_LENGTH;
312 * We are here to write unwritten bytes from a previous
313 * time. Presumably we have a new buffer (possibly
314 * containing a volume label), so the new header
315 * should be able to fit in the block -- otherwise we have
316 * an error. Note, we have to continue splitting the
317 * data record if it is longer than the block.
319 * First, write the header, then write as much as
320 * possible of the data record.
322 * Every time we write a header and it is a continuation
323 * of a previous partially written record, we store the
324 * Stream as -Stream in the record header.
326 ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
327 if (BLOCK_VER == 1) {
328 ser_uint32(rec->VolSessionId);
329 ser_uint32(rec->VolSessionTime);
331 block->VolSessionId = rec->VolSessionId;
332 block->VolSessionTime = rec->VolSessionTime;
334 ser_int32(rec->FileIndex);
335 if (rec->remainder > rec->data_len) {
336 ser_int32(rec->Stream); /* normal full header */
337 ser_uint32(rec->data_len);
338 rec->remainder = rec->data_len; /* must still do data record */
340 ser_int32(-rec->Stream); /* mark this as a continuation record */
341 ser_uint32(rec->remainder); /* bytes to do */
344 /* Require enough room to write a full header */
345 ASSERT(remlen >= WRITE_RECHDR_LENGTH);
347 block->bufp += WRITE_RECHDR_LENGTH;
348 block->binbuf += WRITE_RECHDR_LENGTH;
349 remlen -= WRITE_RECHDR_LENGTH;
350 if (rec->FileIndex > 0) {
351 /* If data record, update what we have in this block */
352 if (block->FirstIndex == 0) {
353 block->FirstIndex = rec->FileIndex;
355 block->LastIndex = rec->FileIndex;
359 return false; /* partial transfer */
363 * Now deal with data record.
364 * Part of it may have already been transferred, and we
365 * may not have enough room to transfer the whole this time.
367 if (rec->remainder > 0) {
368 /* Write as much of data as possible */
369 if (remlen >= rec->remainder) {
370 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
372 block->bufp += rec->remainder;
373 block->binbuf += rec->remainder;
375 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
378 if (!sm_check_rtn(__FILE__, __LINE__, False)) {
379 /* We damaged a buffer */
380 Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n"
381 "rem=%d remainder=%d\n",
382 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
383 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
384 remlen, rec->remainder);
385 Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
386 block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
388 Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
389 block->buf, block->bufp-block->buf);
391 Emsg0(M_ABORT, 0, _("Damaged buffer\n"));
395 block->bufp += remlen;
396 block->binbuf += remlen;
397 rec->remainder -= remlen;
398 return false; /* did partial transfer */
401 rec->remainder = 0; /* did whole transfer */
407 * Test if we can write whole record to the block
409 * Returns: false on failure
410 * true on success (all bytes can be written)
412 bool can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
416 remlen = block->buf_len - block->binbuf;
417 if (rec->remainder == 0) {
418 if (remlen >= WRITE_RECHDR_LENGTH) {
419 remlen -= WRITE_RECHDR_LENGTH;
420 rec->remainder = rec->data_len;
427 if (rec->remainder > 0 && remlen < rec->remainder) {
433 uint64_t get_record_address(DEV_RECORD *rec)
435 return ((uint64_t)rec->File)<<32 | rec->Block;
439 * Read a Record from the block
440 * Returns: false if nothing read or if the continuation record does not match.
441 * In both of these cases, a block read must be done.
442 * true if at least the record header was read, this
443 * routine may have to be called again with a new
444 * block if the entire record was not read.
446 bool read_record_from_block(DCR *dcr, DEV_BLOCK *block, DEV_RECORD *rec)
450 uint32_t VolSessionId;
451 uint32_t VolSessionTime;
456 char buf1[100], buf2[100];
458 remlen = block->binbuf;
460 /* Clear state flags */
462 if (block->dev->is_tape()) {
463 rec->state |= REC_ISTAPE;
465 rec->Block = ((DEVICE *)block->dev)->EndBlock;
466 rec->File = ((DEVICE *)block->dev)->EndFile;
469 * Get the header. There is always a full header,
470 * otherwise we find it in the next block.
472 Dmsg3(450, "Block=%d Ver=%d size=%u\n", block->BlockNumber, block->BlockVer,
474 if (block->BlockVer == 1) {
475 rhl = RECHDR1_LENGTH;
477 rhl = RECHDR2_LENGTH;
480 Dmsg4(450, "Enter read_record_block: remlen=%d data_len=%d rem=%d blkver=%d\n",
481 remlen, rec->data_len, rec->remainder, block->BlockVer);
483 unser_begin(block->bufp, WRITE_RECHDR_LENGTH);
484 if (block->BlockVer == 1) {
485 unser_uint32(VolSessionId);
486 unser_uint32(VolSessionTime);
488 VolSessionId = block->VolSessionId;
489 VolSessionTime = block->VolSessionTime;
491 unser_int32(FileIndex);
493 unser_uint32(data_bytes);
496 block->binbuf -= rhl;
499 /* If we are looking for more (remainder!=0), we reject anything
500 * where the VolSessionId and VolSessionTime don't agree
502 if (rec->remainder && (rec->VolSessionId != VolSessionId ||
503 rec->VolSessionTime != VolSessionTime)) {
504 rec->state |= REC_NO_MATCH;
505 Dmsg0(450, "remainder and VolSession doesn't match\n");
506 return false; /* This is from some other Session */
509 /* if Stream is negative, it means that this is a continuation
510 * of a previous partially written record.
512 if (Stream < 0) { /* continuation record? */
513 Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n",
515 rec->state |= REC_CONTINUATION;
516 if (!rec->remainder) { /* if we didn't read previously */
517 rec->data_len = 0; /* return data as if no continuation */
518 } else if (rec->Stream != -Stream) {
519 rec->state |= REC_NO_MATCH;
520 return false; /* This is from some other Session */
522 rec->Stream = -Stream; /* set correct Stream */
523 } else { /* Regular record */
524 rec->Stream = Stream;
525 rec->data_len = 0; /* transfer to beginning of data */
527 rec->VolSessionId = VolSessionId;
528 rec->VolSessionTime = VolSessionTime;
529 rec->FileIndex = FileIndex;
531 if (block->FirstIndex == 0) {
532 block->FirstIndex = FileIndex;
534 block->LastIndex = FileIndex;
537 Dmsg6(450, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%u\n"
538 "remlen=%d data_len=%d\n",
539 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
540 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), data_bytes, remlen,
544 * No more records in this block because the number
545 * of remaining bytes are less than a record header
546 * length, so return empty handed, but indicate that
547 * he must read again. By returning, we allow the
548 * higher level routine to fetch the next block and
551 Dmsg0(450, "read_record_block: nothing\n");
552 rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
553 empty_block(block); /* mark block empty */
558 if (data_bytes >= MAX_BLOCK_LENGTH) {
560 * Something is wrong, force read of next block, abort
561 * continuing with this block.
563 rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
565 Jmsg2(dcr->jcr, M_WARNING, 0, _("Sanity check failed. maxlen=%d datalen=%d. Block discarded.\n"),
566 MAX_BLOCK_LENGTH, data_bytes);
570 rec->data = check_pool_memory_size(rec->data, rec->data_len+data_bytes);
573 * At this point, we have read the header, now we
574 * must transfer as much of the data record as
575 * possible taking into account: 1. A partial
576 * data record may have previously been transferred,
577 * 2. The current block may not contain the whole data
580 if (remlen >= data_bytes) {
581 /* Got whole record */
582 memcpy(rec->data+rec->data_len, block->bufp, data_bytes);
583 block->bufp += data_bytes;
584 block->binbuf -= data_bytes;
585 rec->data_len += data_bytes;
588 memcpy(rec->data+rec->data_len, block->bufp, remlen);
589 block->bufp += remlen;
590 block->binbuf -= remlen;
591 rec->data_len += remlen;
592 rec->remainder = 1; /* partial record transferred */
593 Dmsg1(450, "read_record_block: partial xfered=%d\n", rec->data_len);
594 rec->state |= (REC_PARTIAL_RECORD | REC_BLOCK_EMPTY);
598 Dmsg4(450, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
599 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
600 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
601 return true; /* transferred full record */