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 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);
77 sprintf(buf, _("unknown: %d"), fi);
84 * Convert a Stream ID into a printable
85 * ASCII string. Not reentrant.
87 * A negative stream number represents
88 * stream data that is continued from a
89 * record in the previous block.
90 * If the FileIndex is negative, we are
91 * dealing with a Label, hence the
92 * stream is the JobId.
94 const char *stream_to_ascii(char *buf, int stream, int fi)
97 sprintf(buf, "%d", stream);
101 case STREAM_UNIX_ATTRIBUTES:
103 case STREAM_FILE_DATA:
105 case STREAM_WIN32_DATA:
107 case STREAM_WIN32_GZIP_DATA:
109 case STREAM_MD5_DIGEST:
111 case STREAM_SHA1_DIGEST:
113 case STREAM_GZIP_DATA:
115 case STREAM_UNIX_ATTRIBUTES_EX:
116 return "UNIX-ATTR-EX";
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_SPARSE_DATA:
166 return "contSPARSE-DATA";
167 case -STREAM_SPARSE_GZIP_DATA:
168 return "contSPARSE-GZIP";
169 case -STREAM_PROGRAM_NAMES:
170 return "contPROG-NAMES";
171 case -STREAM_PROGRAM_DATA:
172 return "contPROG-DATA";
173 case -STREAM_MACOS_FORK_DATA:
174 return "contMACOS-RSRC";
175 case -STREAM_HFSPLUS_ATTRIBUTES:
176 return "contHFSPLUS-ATTR";
177 case -STREAM_SHA256_DIGEST:
179 case -STREAM_SHA512_DIGEST:
181 case -STREAM_SIGNED_DIGEST:
182 return "contSIGNED-DIGEST";
183 case -STREAM_ENCRYPTED_SESSION_DATA:
184 return "contENCRYPTED-SESSION-DATA";
185 case -STREAM_ENCRYPTED_FILE_DATA:
186 return "contENCRYPTED-FILE";
187 case -STREAM_ENCRYPTED_FILE_GZIP_DATA:
188 return "contENCRYPTED-GZIP";
189 case -STREAM_ENCRYPTED_WIN32_DATA:
190 return "contENCRYPTED-WIN32-DATA";
191 case -STREAM_ENCRYPTED_WIN32_GZIP_DATA:
192 return "contENCRYPTED-WIN32-GZIP";
193 case -STREAM_ENCRYPTED_MACOS_FORK_DATA:
194 return "contENCRYPTED-MACOS-RSRC";
195 case -STREAM_PLUGIN_NAME:
196 return "contPLUGIN-NAME";
199 sprintf(buf, "%d", stream);
205 * Return a new record entity
207 DEV_RECORD *new_record(void)
211 rec = (DEV_RECORD *)get_memory(sizeof(DEV_RECORD));
212 memset(rec, 0, sizeof(DEV_RECORD));
213 rec->data = get_pool_memory(PM_MESSAGE);
217 void empty_record(DEV_RECORD *rec)
219 rec->File = rec->Block = 0;
220 rec->VolSessionId = rec->VolSessionTime = 0;
221 rec->FileIndex = rec->Stream = 0;
222 rec->data_len = rec->remainder = 0;
223 rec->state &= ~(REC_PARTIAL_RECORD|REC_BLOCK_EMPTY|REC_NO_MATCH|REC_CONTINUATION);
227 * Free the record entity
230 void free_record(DEV_RECORD *rec)
232 Dmsg0(950, "Enter free_record.\n");
234 free_pool_memory(rec->data);
236 Dmsg0(950, "Data buf is freed.\n");
237 free_pool_memory((POOLMEM *)rec);
238 Dmsg0(950, "Leave free_record.\n");
243 * Write a Record to the block
245 * Returns: false on failure (none or partially written)
246 * true on success (all bytes written)
248 * and remainder returned in packet.
250 * We require enough room for the header, and we deal with
251 * two special cases. 1. Only part of the record may have
252 * been transferred the last time (when remainder is
253 * non-zero), and 2. The remaining bytes to write may not
254 * all fit into the block.
256 bool write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
260 char buf1[100], buf2[100];
262 remlen = block->buf_len - block->binbuf;
264 ASSERT(block->binbuf == (uint32_t) (block->bufp - block->buf));
265 ASSERT(block->buf_len >= block->binbuf);
267 Dmsg6(890, "write_record_to_block() FI=%s SessId=%d Strm=%s len=%d\n"
268 "rem=%d remainder=%d\n",
269 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
270 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
271 remlen, rec->remainder);
274 * If rec->remainder is non-zero, we have been called a
275 * second (or subsequent) time to finish writing a record
276 * that did not previously fit into the block.
278 if (rec->remainder == 0) {
279 /* Require enough room to write a full header */
280 if (remlen >= WRITE_RECHDR_LENGTH) {
281 ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
282 if (BLOCK_VER == 1) {
283 ser_uint32(rec->VolSessionId);
284 ser_uint32(rec->VolSessionTime);
286 block->VolSessionId = rec->VolSessionId;
287 block->VolSessionTime = rec->VolSessionTime;
289 ser_int32(rec->FileIndex);
290 ser_int32(rec->Stream);
291 ser_uint32(rec->data_len);
293 block->bufp += WRITE_RECHDR_LENGTH;
294 block->binbuf += WRITE_RECHDR_LENGTH;
295 remlen -= WRITE_RECHDR_LENGTH;
296 rec->remainder = rec->data_len;
297 if (rec->FileIndex > 0) {
298 /* If data record, update what we have in this block */
299 if (block->FirstIndex == 0) {
300 block->FirstIndex = rec->FileIndex;
302 block->LastIndex = rec->FileIndex;
305 rec->remainder = rec->data_len + WRITE_RECHDR_LENGTH;
310 * We are here to write unwritten bytes from a previous
311 * time. Presumably we have a new buffer (possibly
312 * containing a volume label), so the new header
313 * should be able to fit in the block -- otherwise we have
314 * an error. Note, we have to continue splitting the
315 * data record if it is longer than the block.
317 * First, write the header, then write as much as
318 * possible of the data record.
320 * Every time we write a header and it is a continuation
321 * of a previous partially written record, we store the
322 * Stream as -Stream in the record header.
324 ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
325 if (BLOCK_VER == 1) {
326 ser_uint32(rec->VolSessionId);
327 ser_uint32(rec->VolSessionTime);
329 block->VolSessionId = rec->VolSessionId;
330 block->VolSessionTime = rec->VolSessionTime;
332 ser_int32(rec->FileIndex);
333 if (rec->remainder > rec->data_len) {
334 ser_int32(rec->Stream); /* normal full header */
335 ser_uint32(rec->data_len);
336 rec->remainder = rec->data_len; /* must still do data record */
338 ser_int32(-rec->Stream); /* mark this as a continuation record */
339 ser_uint32(rec->remainder); /* bytes to do */
342 /* Require enough room to write a full header */
343 ASSERT(remlen >= WRITE_RECHDR_LENGTH);
345 block->bufp += WRITE_RECHDR_LENGTH;
346 block->binbuf += WRITE_RECHDR_LENGTH;
347 remlen -= WRITE_RECHDR_LENGTH;
348 if (rec->FileIndex > 0) {
349 /* If data record, update what we have in this block */
350 if (block->FirstIndex == 0) {
351 block->FirstIndex = rec->FileIndex;
353 block->LastIndex = rec->FileIndex;
357 return false; /* partial transfer */
361 * Now deal with data record.
362 * Part of it may have already been transferred, and we
363 * may not have enough room to transfer the whole this time.
365 if (rec->remainder > 0) {
366 /* Write as much of data as possible */
367 if (remlen >= rec->remainder) {
368 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
370 block->bufp += rec->remainder;
371 block->binbuf += rec->remainder;
373 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
376 if (!sm_check_rtn(__FILE__, __LINE__, False)) {
377 /* We damaged a buffer */
378 Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n"
379 "rem=%d remainder=%d\n",
380 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
381 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
382 remlen, rec->remainder);
383 Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
384 block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
386 Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
387 block->buf, block->bufp-block->buf);
389 Emsg0(M_ABORT, 0, _("Damaged buffer\n"));
393 block->bufp += remlen;
394 block->binbuf += remlen;
395 rec->remainder -= remlen;
396 return false; /* did partial transfer */
399 rec->remainder = 0; /* did whole transfer */
405 * Test if we can write whole record to the block
407 * Returns: false on failure
408 * true on success (all bytes can be written)
410 bool can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
414 remlen = block->buf_len - block->binbuf;
415 if (rec->remainder == 0) {
416 if (remlen >= WRITE_RECHDR_LENGTH) {
417 remlen -= WRITE_RECHDR_LENGTH;
418 rec->remainder = rec->data_len;
425 if (rec->remainder > 0 && remlen < rec->remainder) {
431 uint64_t get_record_address(DEV_RECORD *rec)
433 return ((uint64_t)rec->File)<<32 | rec->Block;
437 * Read a Record from the block
438 * Returns: false if nothing read or if the continuation record does not match.
439 * In both of these cases, a block read must be done.
440 * true if at least the record header was read, this
441 * routine may have to be called again with a new
442 * block if the entire record was not read.
444 bool read_record_from_block(DCR *dcr, DEV_BLOCK *block, DEV_RECORD *rec)
448 uint32_t VolSessionId;
449 uint32_t VolSessionTime;
454 char buf1[100], buf2[100];
456 remlen = block->binbuf;
458 /* Clear state flags */
460 if (block->dev->is_tape()) {
461 rec->state |= REC_ISTAPE;
463 rec->Block = ((DEVICE *)block->dev)->EndBlock;
464 rec->File = ((DEVICE *)block->dev)->EndFile;
467 * Get the header. There is always a full header,
468 * otherwise we find it in the next block.
470 Dmsg3(450, "Block=%d Ver=%d size=%u\n", block->BlockNumber, block->BlockVer,
472 if (block->BlockVer == 1) {
473 rhl = RECHDR1_LENGTH;
475 rhl = RECHDR2_LENGTH;
478 Dmsg4(450, "Enter read_record_block: remlen=%d data_len=%d rem=%d blkver=%d\n",
479 remlen, rec->data_len, rec->remainder, block->BlockVer);
481 unser_begin(block->bufp, WRITE_RECHDR_LENGTH);
482 if (block->BlockVer == 1) {
483 unser_uint32(VolSessionId);
484 unser_uint32(VolSessionTime);
486 VolSessionId = block->VolSessionId;
487 VolSessionTime = block->VolSessionTime;
489 unser_int32(FileIndex);
491 unser_uint32(data_bytes);
494 block->binbuf -= rhl;
497 /* If we are looking for more (remainder!=0), we reject anything
498 * where the VolSessionId and VolSessionTime don't agree
500 if (rec->remainder && (rec->VolSessionId != VolSessionId ||
501 rec->VolSessionTime != VolSessionTime)) {
502 rec->state |= REC_NO_MATCH;
503 Dmsg0(450, "remainder and VolSession doesn't match\n");
504 return false; /* This is from some other Session */
507 /* if Stream is negative, it means that this is a continuation
508 * of a previous partially written record.
510 if (Stream < 0) { /* continuation record? */
511 Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n",
513 rec->state |= REC_CONTINUATION;
514 if (!rec->remainder) { /* if we didn't read previously */
515 rec->data_len = 0; /* return data as if no continuation */
516 } else if (rec->Stream != -Stream) {
517 rec->state |= REC_NO_MATCH;
518 return false; /* This is from some other Session */
520 rec->Stream = -Stream; /* set correct Stream */
521 } else { /* Regular record */
522 rec->Stream = Stream;
523 rec->data_len = 0; /* transfer to beginning of data */
525 rec->VolSessionId = VolSessionId;
526 rec->VolSessionTime = VolSessionTime;
527 rec->FileIndex = FileIndex;
529 if (block->FirstIndex == 0) {
530 block->FirstIndex = FileIndex;
532 block->LastIndex = FileIndex;
535 Dmsg6(450, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%u\n"
536 "remlen=%d data_len=%d\n",
537 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
538 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), data_bytes, remlen,
542 * No more records in this block because the number
543 * of remaining bytes are less than a record header
544 * length, so return empty handed, but indicate that
545 * he must read again. By returning, we allow the
546 * higher level routine to fetch the next block and
549 Dmsg0(450, "read_record_block: nothing\n");
550 rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
551 empty_block(block); /* mark block empty */
556 if (data_bytes >= MAX_BLOCK_LENGTH) {
558 * Something is wrong, force read of next block, abort
559 * continuing with this block.
561 rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
563 Jmsg2(dcr->jcr, M_WARNING, 0, _("Sanity check failed. maxlen=%d datalen=%d. Block discarded.\n"),
564 MAX_BLOCK_LENGTH, data_bytes);
568 rec->data = check_pool_memory_size(rec->data, rec->data_len+data_bytes);
571 * At this point, we have read the header, now we
572 * must transfer as much of the data record as
573 * possible taking into account: 1. A partial
574 * data record may have previously been transferred,
575 * 2. The current block may not contain the whole data
578 if (remlen >= data_bytes) {
579 /* Got whole record */
580 memcpy(rec->data+rec->data_len, block->bufp, data_bytes);
581 block->bufp += data_bytes;
582 block->binbuf -= data_bytes;
583 rec->data_len += data_bytes;
586 memcpy(rec->data+rec->data_len, block->bufp, remlen);
587 block->bufp += remlen;
588 block->binbuf -= remlen;
589 rec->data_len += remlen;
590 rec->remainder = 1; /* partial record transferred */
591 Dmsg1(450, "read_record_block: partial xfered=%d\n", rec->data_len);
592 rec->state |= (REC_PARTIAL_RECORD | REC_BLOCK_EMPTY);
596 Dmsg4(450, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
597 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
598 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
599 return true; /* transferred full record */