2 Bacula® - The Network Backup Solution
4 Copyright (C) 2001-2008 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 two of the GNU 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 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
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_PLUGIN_NAME:
120 return "PLUGIN-NAME";
121 case STREAM_MACOS_FORK_DATA:
123 case STREAM_HFSPLUS_ATTRIBUTES:
124 return "HFSPLUS-ATTR";
125 case STREAM_SHA256_DIGEST:
127 case STREAM_SHA512_DIGEST:
129 case STREAM_SIGNED_DIGEST:
130 return "SIGNED-DIGEST";
131 case STREAM_ENCRYPTED_SESSION_DATA:
132 return "ENCRYPTED-SESSION-DATA";
133 case STREAM_ENCRYPTED_FILE_DATA:
134 return "ENCRYPTED-FILE";
135 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
136 return "ENCRYPTED-GZIP";
137 case STREAM_ENCRYPTED_WIN32_DATA:
138 return "ENCRYPTED-WIN32-DATA";
139 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
140 return "ENCRYPTED-WIN32-GZIP";
141 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
142 return "ENCRYPTED-MACOS-RSRC";
143 case -STREAM_UNIX_ATTRIBUTES:
145 case -STREAM_FILE_DATA:
147 case -STREAM_WIN32_DATA:
148 return "contWIN32-DATA";
149 case -STREAM_WIN32_GZIP_DATA:
150 return "contWIN32-GZIP";
151 case -STREAM_MD5_DIGEST:
153 case -STREAM_SHA1_DIGEST:
155 case -STREAM_GZIP_DATA:
157 case -STREAM_UNIX_ATTRIBUTES_EX:
158 return "contUNIX-ATTR-EX";
159 case -STREAM_SPARSE_DATA:
160 return "contSPARSE-DATA";
161 case -STREAM_SPARSE_GZIP_DATA:
162 return "contSPARSE-GZIP";
163 case -STREAM_PROGRAM_NAMES:
164 return "contPROG-NAMES";
165 case -STREAM_PROGRAM_DATA:
166 return "contPROG-DATA";
167 case -STREAM_MACOS_FORK_DATA:
168 return "contMACOS-RSRC";
169 case -STREAM_HFSPLUS_ATTRIBUTES:
170 return "contHFSPLUS-ATTR";
171 case -STREAM_SHA256_DIGEST:
173 case -STREAM_SHA512_DIGEST:
175 case -STREAM_SIGNED_DIGEST:
176 return "contSIGNED-DIGEST";
177 case -STREAM_ENCRYPTED_SESSION_DATA:
178 return "contENCRYPTED-SESSION-DATA";
179 case -STREAM_ENCRYPTED_FILE_DATA:
180 return "contENCRYPTED-FILE";
181 case -STREAM_ENCRYPTED_FILE_GZIP_DATA:
182 return "contENCRYPTED-GZIP";
183 case -STREAM_ENCRYPTED_WIN32_DATA:
184 return "contENCRYPTED-WIN32-DATA";
185 case -STREAM_ENCRYPTED_WIN32_GZIP_DATA:
186 return "contENCRYPTED-WIN32-GZIP";
187 case -STREAM_ENCRYPTED_MACOS_FORK_DATA:
188 return "contENCRYPTED-MACOS-RSRC";
189 case -STREAM_PLUGIN_NAME:
190 return "contPLUGIN-NAME";
193 sprintf(buf, "%d", stream);
199 * Return a new record entity
201 DEV_RECORD *new_record(void)
205 rec = (DEV_RECORD *)get_memory(sizeof(DEV_RECORD));
206 memset(rec, 0, sizeof(DEV_RECORD));
207 rec->data = get_pool_memory(PM_MESSAGE);
211 void empty_record(DEV_RECORD *rec)
213 rec->File = rec->Block = 0;
214 rec->VolSessionId = rec->VolSessionTime = 0;
215 rec->FileIndex = rec->Stream = 0;
216 rec->data_len = rec->remainder = 0;
217 rec->state &= ~(REC_PARTIAL_RECORD|REC_BLOCK_EMPTY|REC_NO_MATCH|REC_CONTINUATION);
221 * Free the record entity
224 void free_record(DEV_RECORD *rec)
226 Dmsg0(950, "Enter free_record.\n");
228 free_pool_memory(rec->data);
230 Dmsg0(950, "Data buf is freed.\n");
231 free_pool_memory((POOLMEM *)rec);
232 Dmsg0(950, "Leave free_record.\n");
237 * Write a Record to the block
239 * Returns: false on failure (none or partially written)
240 * true on success (all bytes written)
242 * and remainder returned in packet.
244 * We require enough room for the header, and we deal with
245 * two special cases. 1. Only part of the record may have
246 * been transferred the last time (when remainder is
247 * non-zero), and 2. The remaining bytes to write may not
248 * all fit into the block.
250 bool write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
254 char buf1[100], buf2[100];
256 remlen = block->buf_len - block->binbuf;
258 ASSERT(block->binbuf == (uint32_t) (block->bufp - block->buf));
259 ASSERT(block->buf_len >= block->binbuf);
261 Dmsg6(890, "write_record_to_block() FI=%s SessId=%d Strm=%s len=%d\n"
262 "rem=%d remainder=%d\n",
263 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
264 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
265 remlen, rec->remainder);
268 * If rec->remainder is non-zero, we have been called a
269 * second (or subsequent) time to finish writing a record
270 * that did not previously fit into the block.
272 if (rec->remainder == 0) {
273 /* Require enough room to write a full header */
274 if (remlen >= WRITE_RECHDR_LENGTH) {
275 ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
276 if (BLOCK_VER == 1) {
277 ser_uint32(rec->VolSessionId);
278 ser_uint32(rec->VolSessionTime);
280 block->VolSessionId = rec->VolSessionId;
281 block->VolSessionTime = rec->VolSessionTime;
283 ser_int32(rec->FileIndex);
284 ser_int32(rec->Stream);
285 ser_uint32(rec->data_len);
287 block->bufp += WRITE_RECHDR_LENGTH;
288 block->binbuf += WRITE_RECHDR_LENGTH;
289 remlen -= WRITE_RECHDR_LENGTH;
290 rec->remainder = rec->data_len;
291 if (rec->FileIndex > 0) {
292 /* If data record, update what we have in this block */
293 if (block->FirstIndex == 0) {
294 block->FirstIndex = rec->FileIndex;
296 block->LastIndex = rec->FileIndex;
299 rec->remainder = rec->data_len + WRITE_RECHDR_LENGTH;
304 * We are here to write unwritten bytes from a previous
305 * time. Presumably we have a new buffer (possibly
306 * containing a volume label), so the new header
307 * should be able to fit in the block -- otherwise we have
308 * an error. Note, we have to continue splitting the
309 * data record if it is longer than the block.
311 * First, write the header, then write as much as
312 * possible of the data record.
314 * Every time we write a header and it is a continuation
315 * of a previous partially written record, we store the
316 * Stream as -Stream in the record header.
318 ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
319 if (BLOCK_VER == 1) {
320 ser_uint32(rec->VolSessionId);
321 ser_uint32(rec->VolSessionTime);
323 block->VolSessionId = rec->VolSessionId;
324 block->VolSessionTime = rec->VolSessionTime;
326 ser_int32(rec->FileIndex);
327 if (rec->remainder > rec->data_len) {
328 ser_int32(rec->Stream); /* normal full header */
329 ser_uint32(rec->data_len);
330 rec->remainder = rec->data_len; /* must still do data record */
332 ser_int32(-rec->Stream); /* mark this as a continuation record */
333 ser_uint32(rec->remainder); /* bytes to do */
336 /* Require enough room to write a full header */
337 ASSERT(remlen >= WRITE_RECHDR_LENGTH);
339 block->bufp += WRITE_RECHDR_LENGTH;
340 block->binbuf += WRITE_RECHDR_LENGTH;
341 remlen -= WRITE_RECHDR_LENGTH;
342 if (rec->FileIndex > 0) {
343 /* If data record, update what we have in this block */
344 if (block->FirstIndex == 0) {
345 block->FirstIndex = rec->FileIndex;
347 block->LastIndex = rec->FileIndex;
351 return false; /* partial transfer */
355 * Now deal with data record.
356 * Part of it may have already been transferred, and we
357 * may not have enough room to transfer the whole this time.
359 if (rec->remainder > 0) {
360 /* Write as much of data as possible */
361 if (remlen >= rec->remainder) {
362 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
364 block->bufp += rec->remainder;
365 block->binbuf += rec->remainder;
367 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
370 if (!sm_check_rtn(__FILE__, __LINE__, False)) {
371 /* We damaged a buffer */
372 Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n"
373 "rem=%d remainder=%d\n",
374 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
375 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
376 remlen, rec->remainder);
377 Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
378 block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
380 Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
381 block->buf, block->bufp-block->buf);
383 Emsg0(M_ABORT, 0, _("Damaged buffer\n"));
387 block->bufp += remlen;
388 block->binbuf += remlen;
389 rec->remainder -= remlen;
390 return false; /* did partial transfer */
393 rec->remainder = 0; /* did whole transfer */
399 * Test if we can write whole record to the block
401 * Returns: false on failure
402 * true on success (all bytes can be written)
404 bool can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
408 remlen = block->buf_len - block->binbuf;
409 if (rec->remainder == 0) {
410 if (remlen >= WRITE_RECHDR_LENGTH) {
411 remlen -= WRITE_RECHDR_LENGTH;
412 rec->remainder = rec->data_len;
419 if (rec->remainder > 0 && remlen < rec->remainder) {
425 uint64_t get_record_address(DEV_RECORD *rec)
427 return ((uint64_t)rec->File)<<32 | rec->Block;
431 * Read a Record from the block
432 * Returns: false if nothing read or if the continuation record does not match.
433 * In both of these cases, a block read must be done.
434 * true if at least the record header was read, this
435 * routine may have to be called again with a new
436 * block if the entire record was not read.
438 bool read_record_from_block(DCR *dcr, DEV_BLOCK *block, DEV_RECORD *rec)
442 uint32_t VolSessionId;
443 uint32_t VolSessionTime;
448 char buf1[100], buf2[100];
450 remlen = block->binbuf;
452 /* Clear state flags */
454 if (block->dev->is_tape()) {
455 rec->state |= REC_ISTAPE;
457 rec->Block = ((DEVICE *)block->dev)->EndBlock;
458 rec->File = ((DEVICE *)block->dev)->EndFile;
461 * Get the header. There is always a full header,
462 * otherwise we find it in the next block.
464 Dmsg3(450, "Block=%d Ver=%d size=%u\n", block->BlockNumber, block->BlockVer,
466 if (block->BlockVer == 1) {
467 rhl = RECHDR1_LENGTH;
469 rhl = RECHDR2_LENGTH;
472 Dmsg4(450, "Enter read_record_block: remlen=%d data_len=%d rem=%d blkver=%d\n",
473 remlen, rec->data_len, rec->remainder, block->BlockVer);
475 unser_begin(block->bufp, WRITE_RECHDR_LENGTH);
476 if (block->BlockVer == 1) {
477 unser_uint32(VolSessionId);
478 unser_uint32(VolSessionTime);
480 VolSessionId = block->VolSessionId;
481 VolSessionTime = block->VolSessionTime;
483 unser_int32(FileIndex);
485 unser_uint32(data_bytes);
488 block->binbuf -= rhl;
491 /* If we are looking for more (remainder!=0), we reject anything
492 * where the VolSessionId and VolSessionTime don't agree
494 if (rec->remainder && (rec->VolSessionId != VolSessionId ||
495 rec->VolSessionTime != VolSessionTime)) {
496 rec->state |= REC_NO_MATCH;
497 Dmsg0(450, "remainder and VolSession doesn't match\n");
498 return false; /* This is from some other Session */
501 /* if Stream is negative, it means that this is a continuation
502 * of a previous partially written record.
504 if (Stream < 0) { /* continuation record? */
505 Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n",
507 rec->state |= REC_CONTINUATION;
508 if (!rec->remainder) { /* if we didn't read previously */
509 rec->data_len = 0; /* return data as if no continuation */
510 } else if (rec->Stream != -Stream) {
511 rec->state |= REC_NO_MATCH;
512 return false; /* This is from some other Session */
514 rec->Stream = -Stream; /* set correct Stream */
515 } else { /* Regular record */
516 rec->Stream = Stream;
517 rec->data_len = 0; /* transfer to beginning of data */
519 rec->VolSessionId = VolSessionId;
520 rec->VolSessionTime = VolSessionTime;
521 rec->FileIndex = FileIndex;
523 if (block->FirstIndex == 0) {
524 block->FirstIndex = FileIndex;
526 block->LastIndex = FileIndex;
529 Dmsg6(450, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%u\n"
530 "remlen=%d data_len=%d\n",
531 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
532 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), data_bytes, remlen,
536 * No more records in this block because the number
537 * of remaining bytes are less than a record header
538 * length, so return empty handed, but indicate that
539 * he must read again. By returning, we allow the
540 * higher level routine to fetch the next block and
543 Dmsg0(450, "read_record_block: nothing\n");
544 rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
545 empty_block(block); /* mark block empty */
550 if (data_bytes >= MAX_BLOCK_LENGTH) {
552 * Something is wrong, force read of next block, abort
553 * continuing with this block.
555 rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
557 Jmsg2(dcr->jcr, M_WARNING, 0, _("Sanity check failed. maxlen=%d datalen=%d. Block discarded.\n"),
558 MAX_BLOCK_LENGTH, data_bytes);
562 rec->data = check_pool_memory_size(rec->data, rec->data_len+data_bytes);
565 * At this point, we have read the header, now we
566 * must transfer as much of the data record as
567 * possible taking into account: 1. A partial
568 * data record may have previously been transferred,
569 * 2. The current block may not contain the whole data
572 if (remlen >= data_bytes) {
573 /* Got whole record */
574 memcpy(rec->data+rec->data_len, block->bufp, data_bytes);
575 block->bufp += data_bytes;
576 block->binbuf -= data_bytes;
577 rec->data_len += data_bytes;
580 memcpy(rec->data+rec->data_len, block->bufp, remlen);
581 block->bufp += remlen;
582 block->binbuf -= remlen;
583 rec->data_len += remlen;
584 rec->remainder = 1; /* partial record transferred */
585 Dmsg1(450, "read_record_block: partial xfered=%d\n", rec->data_len);
586 rec->state |= (REC_PARTIAL_RECORD | REC_BLOCK_EMPTY);
590 Dmsg4(450, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
591 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
592 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
593 return true; /* transferred full record */