1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2001-2012 Free Software Foundation Europe e.V.
5 Copyright (C) 2016-2019 Bareos GmbH & Co. KG
6
7 This program is Free Software; you can redistribute it and/or
8 modify it under the terms of version three of the GNU Affero General Public
9 License as published by the Free Software Foundation and included
10 in the file LICENSE.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Affero General Public License for more details.
16
17 You should have received a copy of the GNU Affero General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301, USA.
21 */
22 /*
23 * Kern Sibbald, April MMI
24 * added BB02 format October MMII
25 */
26 /**
27 * @file
28 * Volume (tape/disk) record handling functions
29 */
30
31 #include "include/bareos.h"
32 #include "stored/jcr_private.h"
33 #include "stored/stored.h"
34 #include "lib/attribs.h"
35 #include "lib/util.h"
36 #include "include/jcr.h"
37
38 namespace storagedaemon {
39
40 /**
41 * Convert a FileIndex into a printable
42 * ASCII string. Not reentrant.
43 *
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.
47 */
FI_to_ascii(char * buf,int fi)48 const char* FI_to_ascii(char* buf, int fi)
49 {
50 if (fi >= 0) {
51 sprintf(buf, "%d", fi);
52 return buf;
53 }
54 switch (fi) {
55 case PRE_LABEL:
56 return "PRE_LABEL";
57 case VOL_LABEL:
58 return "VOL_LABEL";
59 case EOM_LABEL:
60 return "EOM_LABEL";
61 case SOS_LABEL:
62 return "SOS_LABEL";
63 case EOS_LABEL:
64 return "EOS_LABEL";
65 case EOT_LABEL:
66 return "EOT_LABEL";
67 break;
68 case SOB_LABEL:
69 return "SOB_LABEL";
70 break;
71 case EOB_LABEL:
72 return "EOB_LABEL";
73 break;
74 default:
75 sprintf(buf, _("unknown: %d"), fi);
76 return buf;
77 }
78 }
79
80
compression_to_str(PoolMem & resultbuffer,const char * compression_algorithm,uint32_t data_length,uint16_t compression_level,uint16_t compression_algorithm_version)81 static const char* compression_to_str(PoolMem& resultbuffer,
82 const char* compression_algorithm,
83 uint32_t data_length,
84 uint16_t compression_level,
85 uint16_t compression_algorithm_version)
86 {
87 PoolMem tmp(PM_MESSAGE);
88 tmp.bsprintf("%s, level=%u, version=%u, length=%u", compression_algorithm,
89 compression_level, compression_algorithm_version, data_length);
90 resultbuffer.strcat(tmp);
91 return resultbuffer.c_str();
92 }
93
record_compression_to_str(PoolMem & resultbuffer,const DeviceRecord * rec)94 static const char* record_compression_to_str(PoolMem& resultbuffer,
95 const DeviceRecord* rec)
96 {
97 int32_t maskedStream = rec->maskedStream;
98 POOLMEM* buf = rec->data;
99 PoolMem tmp(PM_MESSAGE);
100 unser_declare;
101
102 if (maskedStream == STREAM_SPARSE_GZIP_DATA ||
103 maskedStream == STREAM_SPARSE_COMPRESSED_DATA) {
104 uint64_t faddr = 0;
105
106 SerBegin(buf, sizeof(uint64_t));
107 unser_uint64(faddr);
108 SerEnd(buf, sizeof(uint64_t));
109
110 buf += sizeof(uint64_t);
111
112 Dmsg1(400, "Sparse data stream found: start address=%llu\n", faddr);
113 tmp.bsprintf("Sparse: StartAddress=%llu. ", faddr);
114 resultbuffer.strcat(tmp);
115 }
116
117 Dmsg1(400, "Stream found in DecompressData(): %d\n", maskedStream);
118 switch (maskedStream) {
119 case STREAM_COMPRESSED_DATA:
120 case STREAM_SPARSE_COMPRESSED_DATA:
121 case STREAM_WIN32_COMPRESSED_DATA:
122 case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
123 case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA: {
124 uint32_t comp_magic, comp_len;
125 uint16_t comp_level, comp_version;
126
127 /*
128 * Read compress header
129 */
130 UnserBegin(buf, sizeof(comp_stream_header));
131 unser_uint32(comp_magic);
132 unser_uint32(comp_len);
133 unser_uint16(comp_level);
134 unser_uint16(comp_version);
135 UnserEnd(buf, sizeof(comp_stream_header));
136
137 Dmsg4(400,
138 "Compressed data stream found: magic=0x%x, len=%d, level=%d, "
139 "ver=0x%x\n",
140 comp_magic, comp_len, comp_level, comp_version);
141
142 switch (comp_magic) {
143 case COMPRESS_GZIP:
144 compression_to_str(resultbuffer, "GZIP", comp_len, comp_level,
145 comp_version);
146 break;
147 case COMPRESS_LZO1X:
148 compression_to_str(resultbuffer, "LZO1X", comp_len, comp_level,
149 comp_version);
150 break;
151 case COMPRESS_FZFZ:
152 compression_to_str(resultbuffer, "FZFZ", comp_len, comp_level,
153 comp_version);
154 break;
155 case COMPRESS_FZ4L:
156 compression_to_str(resultbuffer, "FZ4L", comp_len, comp_level,
157 comp_version);
158 break;
159 case COMPRESS_FZ4H:
160 compression_to_str(resultbuffer, "FZ4H", comp_len, comp_level,
161 comp_version);
162 break;
163 default:
164 tmp.bsprintf(
165 _("Compression algorithm 0x%x found, but not supported!\n"),
166 comp_magic);
167 resultbuffer.strcat(tmp);
168 break;
169 }
170 break;
171 }
172 case STREAM_GZIP_DATA:
173 case STREAM_SPARSE_GZIP_DATA:
174 /* deprecated */
175 compression_to_str(resultbuffer, "GZIP", 0, 0, 0);
176 break;
177 default:
178 break;
179 }
180
181 return resultbuffer.c_str();
182 }
183
record_digest_to_str(PoolMem & resultbuffer,const DeviceRecord * rec)184 static const char* record_digest_to_str(PoolMem& resultbuffer,
185 const DeviceRecord* rec)
186 {
187 char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
188
189 switch (rec->maskedStream) {
190 case STREAM_MD5_DIGEST:
191 BinToBase64(digest, sizeof(digest), (char*)rec->data,
192 CRYPTO_DIGEST_MD5_SIZE, true);
193 break;
194 case STREAM_SHA1_DIGEST:
195 BinToBase64(digest, sizeof(digest), (char*)rec->data,
196 CRYPTO_DIGEST_SHA1_SIZE, true);
197 break;
198 case STREAM_SHA256_DIGEST:
199 BinToBase64(digest, sizeof(digest), (char*)rec->data,
200 CRYPTO_DIGEST_SHA256_SIZE, true);
201 break;
202 case STREAM_SHA512_DIGEST:
203 BinToBase64(digest, sizeof(digest), (char*)rec->data,
204 CRYPTO_DIGEST_SHA512_SIZE, true);
205 break;
206 default:
207 return "";
208 }
209
210 resultbuffer.bsprintf("%s (base64)", digest);
211
212 return resultbuffer.c_str();
213 }
214
215 /**
216 * Convert a Stream ID into a printable
217 * ASCII string. Not reentrant.
218 *
219 * A negative stream number represents
220 * stream data that is continued from a
221 * record in the previous block.
222 *
223 * If the FileIndex is negative, we are
224 * dealing with a Label, hence the
225 * stream is the JobId.
226 */
stream_to_ascii(char * buf,int stream,int fi)227 const char* stream_to_ascii(char* buf, int stream, int fi)
228 {
229 if (fi < 0) {
230 sprintf(buf, "%d", stream);
231 return buf;
232 }
233
234 if (stream < 0) {
235 stream = -stream;
236 stream &= STREAMMASK_TYPE;
237 /*
238 * Stream was negative => all are continuation items
239 */
240 switch (stream) {
241 case STREAM_UNIX_ATTRIBUTES:
242 return "contUATTR";
243 case STREAM_FILE_DATA:
244 return "contDATA";
245 case STREAM_WIN32_DATA:
246 return "contWIN32-DATA";
247 case STREAM_WIN32_GZIP_DATA:
248 return "contWIN32-GZIP";
249 case STREAM_WIN32_COMPRESSED_DATA:
250 return "contWIN32-COMPRESSED";
251 case STREAM_MD5_DIGEST:
252 return "contMD5";
253 case STREAM_SHA1_DIGEST:
254 return "contSHA1";
255 case STREAM_GZIP_DATA:
256 return "contGZIP";
257 case STREAM_COMPRESSED_DATA:
258 return "contCOMPRESSED";
259 case STREAM_UNIX_ATTRIBUTES_EX:
260 return "contUNIX-Attributes-EX";
261 case STREAM_RESTORE_OBJECT:
262 return "contRESTORE-OBJECT";
263 case STREAM_SPARSE_DATA:
264 return "contSPARSE-DATA";
265 case STREAM_SPARSE_GZIP_DATA:
266 return "contSPARSE-GZIP";
267 case STREAM_SPARSE_COMPRESSED_DATA:
268 return "contSPARSE-COMPRESSED";
269 case STREAM_PROGRAM_NAMES:
270 return "contPROG-NAMES";
271 case STREAM_PROGRAM_DATA:
272 return "contPROG-DATA";
273 case STREAM_MACOS_FORK_DATA:
274 return "contMACOS-RSRC";
275 case STREAM_HFSPLUS_ATTRIBUTES:
276 return "contHFSPLUS-Attributes";
277 case STREAM_SHA256_DIGEST:
278 return "contSHA256";
279 case STREAM_SHA512_DIGEST:
280 return "contSHA512";
281 case STREAM_SIGNED_DIGEST:
282 return "contSIGNED-DIGEST";
283 case STREAM_ENCRYPTED_SESSION_DATA:
284 return "contENCRYPTED-SESSION-DATA";
285 case STREAM_ENCRYPTED_FILE_DATA:
286 return "contENCRYPTED-FILE";
287 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
288 return "contENCRYPTED-GZIP";
289 case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
290 return "contENCRYPTED-COMPRESSED";
291 case STREAM_ENCRYPTED_WIN32_DATA:
292 return "contENCRYPTED-WIN32-DATA";
293 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
294 return "contENCRYPTED-WIN32-GZIP";
295 case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
296 return "contENCRYPTED-WIN32-COMPRESSED";
297 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
298 return "contENCRYPTED-MACOS-RSRC";
299 case STREAM_PLUGIN_NAME:
300 return "contPLUGIN-NAME";
301 default:
302 sprintf(buf, "%d", -stream);
303 return buf;
304 }
305 }
306
307 switch (stream & STREAMMASK_TYPE) {
308 case STREAM_UNIX_ATTRIBUTES:
309 return "UATTR";
310 case STREAM_FILE_DATA:
311 return "DATA";
312 case STREAM_WIN32_DATA:
313 return "WIN32-DATA";
314 case STREAM_WIN32_GZIP_DATA:
315 return "WIN32-GZIP";
316 case STREAM_WIN32_COMPRESSED_DATA:
317 return "WIN32-COMPRESSED";
318 case STREAM_MD5_DIGEST:
319 return "MD5";
320 case STREAM_SHA1_DIGEST:
321 return "SHA1";
322 case STREAM_GZIP_DATA:
323 return "GZIP";
324 case STREAM_COMPRESSED_DATA:
325 return "COMPRESSED";
326 case STREAM_UNIX_ATTRIBUTES_EX:
327 return "UNIX-Attributes-EX";
328 case STREAM_RESTORE_OBJECT:
329 return "RESTORE-OBJECT";
330 case STREAM_SPARSE_DATA:
331 return "SPARSE-DATA";
332 case STREAM_SPARSE_GZIP_DATA:
333 return "SPARSE-GZIP";
334 case STREAM_SPARSE_COMPRESSED_DATA:
335 return "SPARSE-COMPRESSED";
336 case STREAM_PROGRAM_NAMES:
337 return "PROG-NAMES";
338 case STREAM_PROGRAM_DATA:
339 return "PROG-DATA";
340 case STREAM_PLUGIN_NAME:
341 return "PLUGIN-NAME";
342 case STREAM_MACOS_FORK_DATA:
343 return "MACOS-RSRC";
344 case STREAM_HFSPLUS_ATTRIBUTES:
345 return "HFSPLUS-Attributes";
346 case STREAM_SHA256_DIGEST:
347 return "SHA256";
348 case STREAM_SHA512_DIGEST:
349 return "SHA512";
350 case STREAM_SIGNED_DIGEST:
351 return "SIGNED-DIGEST";
352 case STREAM_ENCRYPTED_SESSION_DATA:
353 return "ENCRYPTED-SESSION-DATA";
354 case STREAM_ENCRYPTED_FILE_DATA:
355 return "ENCRYPTED-FILE";
356 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
357 return "ENCRYPTED-GZIP";
358 case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
359 return "ENCRYPTED-COMPRESSED";
360 case STREAM_ENCRYPTED_WIN32_DATA:
361 return "ENCRYPTED-WIN32-DATA";
362 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
363 return "ENCRYPTED-WIN32-GZIP";
364 case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
365 return "ENCRYPTED-WIN32-COMPRESSED";
366 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
367 return "ENCRYPTED-MACOS-RSRC";
368 default:
369 sprintf(buf, "%d", stream);
370 return buf;
371 }
372 }
373
record_state_to_ascii(rec_state state)374 static const char* record_state_to_ascii(rec_state state)
375 {
376 switch (state) {
377 case st_none:
378 return "st_none";
379 case st_header:
380 return "st_header";
381 case st_header_cont:
382 return "st_header_cont";
383 case st_data:
384 return "st_data";
385 default:
386 return "<unknown>";
387 }
388 }
389
findex_to_str(int32_t index,char * buf,size_t bufsz)390 static const char* findex_to_str(int32_t index, char* buf, size_t bufsz)
391 {
392 if (index >= 0) {
393 Bsnprintf(buf, bufsz, "<User> %d", index);
394 return buf;
395 }
396
397 FI_to_ascii(buf, index);
398
399 return buf;
400 }
401
402
record_unix_attributes_to_str(PoolMem & resultbuffer,JobControlRecord * jcr,const DeviceRecord * rec)403 static const char* record_unix_attributes_to_str(PoolMem& resultbuffer,
404 JobControlRecord* jcr,
405 const DeviceRecord* rec)
406 {
407 Attributes* attr = new_attr(NULL);
408
409 if (!UnpackAttributesRecord(jcr, rec->Stream, rec->data, rec->data_len,
410 attr)) {
411 resultbuffer.bsprintf("ERROR");
412 return NULL;
413 }
414
415 attr->data_stream =
416 DecodeStat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
417 BuildAttrOutputFnames(jcr, attr);
418 attr_to_str(resultbuffer, jcr, attr);
419
420 FreeAttr(attr);
421
422 return resultbuffer.c_str();
423 }
424
425
get_record_short_info(PoolMem & resultbuffer,JobControlRecord * jcr,const DeviceRecord * rec)426 static const char* get_record_short_info(PoolMem& resultbuffer,
427 JobControlRecord* jcr,
428 const DeviceRecord* rec)
429 {
430 switch (rec->maskedStream) {
431 case STREAM_UNIX_ATTRIBUTES:
432 case STREAM_UNIX_ATTRIBUTES_EX:
433 record_unix_attributes_to_str(resultbuffer, jcr, rec);
434 break;
435 case STREAM_MD5_DIGEST:
436 case STREAM_SHA1_DIGEST:
437 case STREAM_SHA256_DIGEST:
438 case STREAM_SHA512_DIGEST:
439 record_digest_to_str(resultbuffer, rec);
440 break;
441 case STREAM_PLUGIN_NAME: {
442 char data[100];
443 int len = MIN(rec->data_len + 1, sizeof(data));
444 bstrncpy(data, rec->data, len);
445 resultbuffer.bsprintf("data: %s\n", data);
446 break;
447 }
448 case STREAM_RESTORE_OBJECT:
449 resultbuffer.bsprintf("Restore Object record");
450 break;
451 case STREAM_COMPRESSED_DATA:
452 case STREAM_SPARSE_COMPRESSED_DATA:
453 case STREAM_WIN32_COMPRESSED_DATA:
454 case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
455 case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
456 case STREAM_GZIP_DATA: /* deprecated */
457 case STREAM_SPARSE_GZIP_DATA: /* deprecated */
458 record_compression_to_str(resultbuffer, rec);
459 break;
460 default:
461 break;
462 }
463 return resultbuffer.c_str();
464 }
465
record_to_str(PoolMem & resultbuffer,JobControlRecord * jcr,const DeviceRecord * rec)466 const char* record_to_str(PoolMem& resultbuffer,
467 JobControlRecord* jcr,
468 const DeviceRecord* rec)
469 {
470 PoolMem record_info_buf(PM_MESSAGE);
471 char stream_buf[100];
472
473 resultbuffer.bsprintf(
474 "FileIndex=%-5d Stream=%-2d %-25s DataLen=%-5d", rec->FileIndex,
475 rec->Stream, stream_to_ascii(stream_buf, rec->Stream, rec->FileIndex),
476 rec->data_len);
477 IndentMultilineString(
478 resultbuffer, get_record_short_info(record_info_buf, jcr, rec), " | ");
479
480 return resultbuffer.c_str();
481 }
482
DumpRecord(const char * tag,const DeviceRecord * rec)483 void DumpRecord(const char* tag, const DeviceRecord* rec)
484 {
485 char stream[128];
486 char findex[128];
487
488 Dmsg2(100, "%s: rec %p\n", tag, rec);
489
490 Dmsg3(100, "%-14s next %p prev %p\n", "link", rec->link.next, rec->link.prev);
491 Dmsg2(100, "%-14s %u\n", "File", rec->File);
492 Dmsg2(100, "%-14s %u\n", "Block", rec->Block);
493 Dmsg2(100, "%-14s %u\n", "VolSessionId", rec->VolSessionId);
494 Dmsg2(100, "%-14s %u\n", "VolSessionTime", rec->VolSessionTime);
495 Dmsg2(100, "%-14s %s\n", "FileIndex",
496 findex_to_str(rec->FileIndex, findex, sizeof(findex)));
497 Dmsg2(100, "%-14s %s\n", "Stream",
498 stream_to_ascii(stream, rec->Stream, rec->FileIndex));
499 Dmsg2(100, "%-14s %d\n", "maskedStream", rec->maskedStream);
500 Dmsg2(100, "%-14s %u\n", "data_len", rec->data_len);
501 Dmsg2(100, "%-14s %u\n", "remainder", rec->remainder);
502 for (unsigned int i = 0;
503 i < (sizeof(rec->state_bits) / sizeof(rec->state_bits[0])); i++) {
504 Dmsg3(100, "%-11s[%d] %2.2x\n", "state_bits", i,
505 (uint8_t)rec->state_bits[i]);
506 }
507 Dmsg3(100, "%-14s %u (%s)\n", "state", rec->state,
508 record_state_to_ascii(rec->state));
509 Dmsg2(100, "%-14s %p\n", "bsr", rec->bsr);
510 Dmsg2(100, "%-14s %p\n", "data", rec->data);
511 Dmsg2(100, "%-14s %d\n", "match_stat", rec->match_stat);
512 Dmsg2(100, "%-14s %u\n", "last_VolSessionId", rec->last_VolSessionId);
513 Dmsg2(100, "%-14s %u\n", "last_VolSessionTime", rec->last_VolSessionTime);
514 Dmsg2(100, "%-14s %d\n", "last_FileIndex", rec->last_FileIndex);
515 Dmsg2(100, "%-14s %d\n", "last_Stream", rec->last_Stream);
516 Dmsg2(100, "%-14s %s\n", "own_mempool", rec->own_mempool ? "true" : "false");
517 }
518
519 /**
520 * Return a new record entity
521 */
new_record(bool with_data)522 DeviceRecord* new_record(bool with_data)
523 {
524 DeviceRecord* rec;
525
526 rec = (DeviceRecord*)GetPoolMemory(PM_RECORD);
527 *rec = DeviceRecord{};
528 if (with_data) {
529 rec->data = GetPoolMemory(PM_MESSAGE);
530 rec->own_mempool = true;
531 }
532 rec->state = st_none;
533
534 return rec;
535 }
536
EmptyRecord(DeviceRecord * rec)537 void EmptyRecord(DeviceRecord* rec)
538 {
539 rec->File = rec->Block = 0;
540 rec->VolSessionId = rec->VolSessionTime = 0;
541 rec->FileIndex = rec->Stream = 0;
542 rec->data_len = rec->remainder = 0;
543
544 ClearBit(REC_PARTIAL_RECORD, rec->state_bits);
545 ClearBit(REC_BLOCK_EMPTY, rec->state_bits);
546 ClearBit(REC_NO_MATCH, rec->state_bits);
547 ClearBit(REC_CONTINUATION, rec->state_bits);
548
549 rec->state = st_none;
550 }
551
CopyRecordState(DeviceRecord * dst,DeviceRecord * src)552 void CopyRecordState(DeviceRecord* dst, DeviceRecord* src)
553 {
554 bool own_mempool;
555 int32_t Stream, maskedStream;
556 uint32_t data_len;
557 POOLMEM* data;
558
559 /*
560 * Preserve some important fields all other can be overwritten.
561 */
562 Stream = dst->Stream;
563 maskedStream = dst->maskedStream;
564 data = dst->data;
565 data_len = dst->data_len;
566 own_mempool = dst->own_mempool;
567
568 memcpy(dst, src, sizeof(DeviceRecord));
569
570 dst->Stream = Stream;
571 dst->maskedStream = maskedStream;
572 dst->data = data;
573 dst->data_len = data_len;
574 dst->own_mempool = own_mempool;
575 }
576
577 /**
578 * Free the record entity
579 */
FreeRecord(DeviceRecord * rec)580 void FreeRecord(DeviceRecord* rec)
581 {
582 Dmsg0(950, "Enter FreeRecord.\n");
583 if (rec->data && rec->own_mempool) { FreePoolMemory(rec->data); }
584 Dmsg0(950, "Data buf is freed.\n");
585 FreePoolMemory((POOLMEM*)rec);
586 Dmsg0(950, "Leave FreeRecord.\n");
587 }
588
WriteHeaderToBlock(DeviceBlock * block,const DeviceRecord * rec,int32_t Stream)589 static inline ssize_t WriteHeaderToBlock(DeviceBlock* block,
590 const DeviceRecord* rec,
591 int32_t Stream)
592 {
593 ser_declare;
594
595 /*
596 * Require enough room to write a full header
597 */
598 if (BlockWriteNavail(block) < WRITE_RECHDR_LENGTH) return -1;
599
600 SerBegin(block->bufp, WRITE_RECHDR_LENGTH);
601
602 if (BLOCK_VER == 1) {
603 ser_uint32(rec->VolSessionId);
604 ser_uint32(rec->VolSessionTime);
605 } else {
606 block->VolSessionId = rec->VolSessionId;
607 block->VolSessionTime = rec->VolSessionTime;
608 }
609
610 ser_int32(rec->FileIndex);
611 ser_int32(Stream);
612
613 ser_uint32(
614 rec->remainder); /* each header tracks remaining user bytes to write */
615
616 block->bufp += WRITE_RECHDR_LENGTH;
617 block->binbuf += WRITE_RECHDR_LENGTH;
618
619 if (rec->FileIndex > 0) {
620 /*
621 * If data record, update what we have in this block
622 */
623 if (block->FirstIndex == 0) { block->FirstIndex = rec->FileIndex; }
624 block->LastIndex = rec->FileIndex;
625 }
626
627 return WRITE_RECHDR_LENGTH;
628 }
629
WriteDataToBlock(DeviceBlock * block,const DeviceRecord * rec)630 static inline ssize_t WriteDataToBlock(DeviceBlock* block,
631 const DeviceRecord* rec)
632 {
633 uint32_t len;
634
635 len = MIN(rec->remainder, BlockWriteNavail(block));
636 memcpy(block->bufp,
637 ((unsigned char*)rec->data) + (rec->data_len - rec->remainder), len);
638 block->bufp += len;
639 block->binbuf += len;
640 return len;
641 }
642
643 /**
644 * Write a Record to the block
645 *
646 * Returns: false means the block could not be written to tape/disk.
647 * true on success (all bytes written to the block).
648 */
WriteRecord()649 bool DeviceControlRecord::WriteRecord()
650 {
651 bool retval = false;
652 bool translated_record = false;
653 char buf1[100], buf2[100];
654
655 /*
656 * Perform record translations.
657 */
658 before_rec = rec;
659 after_rec = NULL;
660 if (GeneratePluginEvent(jcr, bsdEventWriteRecordTranslation, this) !=
661 bRC_OK) {
662 goto bail_out;
663 }
664
665 /*
666 * The record got translated when we got an after_rec pointer after calling
667 * the bsdEventWriteRecordTranslation plugin event. If no translation has
668 * taken place we just point the after_rec pointer to same DeviceRecord as in
669 * the before_rec pointer.
670 */
671 if (!after_rec) {
672 after_rec = before_rec;
673 } else {
674 translated_record = true;
675 }
676
677 while (!WriteRecordToBlock(this, after_rec)) {
678 Dmsg2(850, "!WriteRecordToBlock data_len=%d rem=%d\n", after_rec->data_len,
679 after_rec->remainder);
680 if (!WriteBlockToDevice()) {
681 Dmsg2(90, "Got WriteBlockToDev error on device %s. %s\n",
682 dev->print_name(), dev->bstrerror());
683 goto bail_out;
684 }
685 }
686
687 jcr->JobBytes += after_rec->data_len; /* increment bytes this job */
688 if (jcr->impl->RemainingQuota &&
689 jcr->JobBytes > jcr->impl->RemainingQuota) {
690 Jmsg0(jcr, M_FATAL, 0, _("Quota Exceeded. Job Terminated.\n"));
691 goto bail_out;
692 }
693
694 Dmsg4(850, "WriteRecord FI=%s SessId=%d Strm=%s len=%d\n",
695 FI_to_ascii(buf1, after_rec->FileIndex), after_rec->VolSessionId,
696 stream_to_ascii(buf2, after_rec->Stream, after_rec->FileIndex),
697 after_rec->data_len);
698
699 retval = true;
700
701 bail_out:
702 if (translated_record) {
703 CopyRecordState(before_rec, after_rec);
704 FreeRecord(after_rec);
705 after_rec = NULL;
706 }
707
708 return retval;
709 }
710
711 /**
712 * Write a Record to the block
713 *
714 * Returns: false on failure (none or partially written)
715 * true on success (all bytes written)
716 *
717 * and remainder returned in packet.
718 *
719 * We require enough room for the header, and we deal with
720 * two special cases. 1. Only part of the record may have
721 * been transferred the last time (when remainder is
722 * non-zero), and 2. The remaining bytes to write may not
723 * all fit into the block.
724 */
WriteRecordToBlock(DeviceControlRecord * dcr,DeviceRecord * rec)725 bool WriteRecordToBlock(DeviceControlRecord* dcr, DeviceRecord* rec)
726 {
727 ssize_t n;
728 bool retval = false;
729 char buf1[100], buf2[100];
730 DeviceBlock* block = dcr->block;
731
732 /*
733 * After this point the record is in nrec not rec e.g. its either converted
734 * or is just a pointer to the same as the rec pointer being passed in.
735 */
736
737 while (1) {
738 ASSERT(block->binbuf == (uint32_t)(block->bufp - block->buf));
739 ASSERT(block->buf_len >= block->binbuf);
740
741 Dmsg9(890,
742 "%s() state=%d (%s) FI=%s SessId=%d Strm=%s len=%d "
743 "block_navail=%d remainder=%d\n",
744 __PRETTY_FUNCTION__, rec->state, record_state_to_ascii(rec->state),
745 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
746 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
747 BlockWriteNavail(block), rec->remainder);
748
749 switch (rec->state) {
750 case st_none:
751 /*
752 * Figure out what to do
753 */
754 rec->state = st_header;
755 rec->remainder = rec->data_len; /* length of data remaining to write */
756 continue; /* goto st_header */
757
758 case st_header:
759 /*
760 * Write header
761 */
762 n = WriteHeaderToBlock(block, rec, rec->Stream);
763 if (n < 0) {
764 /*
765 * the header did not fit into the block, so flush the current
766 * block and come back to st_header and try again on the next block.
767 */
768 goto bail_out;
769 }
770
771 if (BlockWriteNavail(block) == 0) {
772 /*
773 * The header fit, but no bytes of data will fit,
774 * so flush this block and start the next block with a
775 * continuation header.
776 */
777 rec->state = st_header_cont;
778 goto bail_out;
779 }
780
781 /*
782 * The header fit, and at least one byte of data will fit,
783 * so move to the st_data state and start filling the block
784 * with data bytes
785 */
786 rec->state = st_data;
787 continue;
788
789 case st_header_cont:
790 /*
791 * Write continuation header
792 */
793 n = WriteHeaderToBlock(block, rec, -rec->Stream);
794 if (n < 0) {
795 /*
796 * The continuation header wouldn't fit, which is impossible
797 * unless something is broken
798 */
799 Emsg0(M_ABORT, 0, _("couldn't write continuation header\n"));
800 }
801
802 /*
803 * After successfully writing a continuation header, we always start
804 * writing data, even if none will fit into this block.
805 */
806 rec->state = st_data;
807
808 if (BlockWriteNavail(block) == 0) {
809 /*
810 * The header fit, but no bytes of data will fit,
811 * so flush the block and start the next block with
812 * data bytes
813 */
814 goto bail_out; /* Partial transfer */
815 }
816
817 continue;
818
819 case st_data:
820 /*
821 * Write normal data
822 *
823 * Part of it may have already been transferred, and we
824 * may not have enough room to transfer the whole this time.
825 */
826 if (rec->remainder > 0) {
827 n = WriteDataToBlock(block, rec);
828 if (n < 0) {
829 /*
830 * error appending data to block should be impossible
831 * unless something is broken
832 */
833 Emsg0(M_ABORT, 0, _("data write error\n"));
834 }
835
836 rec->remainder -= n;
837
838 if (rec->remainder > 0) {
839 /*
840 * Could not fit all of the data bytes into this block, so
841 * flush the current block, and start the next block with a
842 * continuation header
843 */
844 rec->state = st_header_cont;
845 goto bail_out;
846 }
847 }
848
849 rec->remainder = 0; /* did whole transfer */
850 rec->state = st_none;
851 retval = true;
852 goto bail_out;
853
854 default:
855 Emsg1(M_ABORT, 0, _("Something went wrong. Unknown state %d.\n"),
856 rec->state);
857 rec->state = st_none;
858 retval = true;
859 goto bail_out;
860 }
861 }
862
863 bail_out:
864 return retval;
865 }
866
867 /**
868 * Test if we can write whole record to the block
869 *
870 * Returns: false on failure
871 * true on success (all bytes can be written)
872 */
CanWriteRecordToBlock(DeviceBlock * block,const DeviceRecord * rec)873 bool CanWriteRecordToBlock(DeviceBlock* block, const DeviceRecord* rec)
874 {
875 return BlockWriteNavail(block) >= WRITE_RECHDR_LENGTH + rec->remainder;
876 }
877
GetRecordAddress(const DeviceRecord * rec)878 uint64_t GetRecordAddress(const DeviceRecord* rec)
879 {
880 return ((uint64_t)rec->File) << 32 | rec->Block;
881 }
882
883 /**
884 * Read a Record from the block
885 *
886 * Returns: false if nothing read or if the continuation record does not match.
887 * In both of these cases, a block read must be done.
888 * true if at least the record header was read, this
889 * routine may have to be called again with a new
890 * block if the entire record was not read.
891 */
ReadRecordFromBlock(DeviceControlRecord * dcr,DeviceRecord * rec)892 bool ReadRecordFromBlock(DeviceControlRecord* dcr, DeviceRecord* rec)
893 {
894 ser_declare;
895 uint32_t remlen;
896 uint32_t VolSessionId;
897 uint32_t VolSessionTime;
898 int32_t FileIndex;
899 int32_t Stream;
900 uint32_t data_bytes;
901 uint32_t rhl;
902 char buf1[100], buf2[100];
903
904 remlen = dcr->block->binbuf;
905
906 /*
907 * Clear state flags
908 */
909 ClearAllBits(REC_STATE_MAX, rec->state_bits);
910 if (dcr->block->dev->IsTape()) { SetBit(REC_ISTAPE, rec->state_bits); }
911 rec->Block = ((Device*)(dcr->block->dev))->EndBlock;
912 rec->File = ((Device*)(dcr->block->dev))->EndFile;
913
914 /*
915 * Get the header. There is always a full header, otherwise we find it in the
916 * next block.
917 */
918 Dmsg3(450, "Block=%d Ver=%d size=%u\n", dcr->block->BlockNumber,
919 dcr->block->BlockVer, dcr->block->block_len);
920 if (dcr->block->BlockVer == 1) {
921 rhl = RECHDR1_LENGTH;
922 } else {
923 rhl = RECHDR2_LENGTH;
924 }
925 if (remlen >= rhl) {
926 Dmsg4(450,
927 "Enter read_record_block: remlen=%d data_len=%d rem=%d blkver=%d\n",
928 remlen, rec->data_len, rec->remainder, dcr->block->BlockVer);
929
930 UnserBegin(dcr->block->bufp, WRITE_RECHDR_LENGTH);
931 if (dcr->block->BlockVer == 1) {
932 unser_uint32(VolSessionId);
933 unser_uint32(VolSessionTime);
934 } else {
935 VolSessionId = dcr->block->VolSessionId;
936 VolSessionTime = dcr->block->VolSessionTime;
937 }
938 unser_int32(FileIndex);
939 unser_int32(Stream);
940 unser_uint32(data_bytes);
941
942 dcr->block->bufp += rhl;
943 dcr->block->binbuf -= rhl;
944 remlen -= rhl;
945
946 /*
947 * If we are looking for more (remainder!=0), we reject anything
948 * where the VolSessionId and VolSessionTime don't agree
949 */
950 if (rec->remainder && (rec->VolSessionId != VolSessionId ||
951 rec->VolSessionTime != VolSessionTime)) {
952 SetBit(REC_NO_MATCH, rec->state_bits);
953 Dmsg0(450, "remainder and VolSession doesn't match\n");
954 return false; /* This is from some other Session */
955 }
956
957 /*
958 * If Stream is negative, it means that this is a continuation
959 * of a previous partially written record.
960 */
961 if (Stream < 0) { /* continuation record? */
962 Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n",
963 rec->remainder);
964 SetBit(REC_CONTINUATION, rec->state_bits);
965 if (!rec->remainder) { /* if we didn't read previously */
966 rec->data_len = 0; /* return data as if no continuation */
967 } else if (rec->Stream != -Stream) {
968 SetBit(REC_NO_MATCH, rec->state_bits);
969 return false; /* This is from some other Session */
970 }
971 rec->Stream = -Stream; /* set correct Stream */
972 rec->maskedStream = rec->Stream & STREAMMASK_TYPE;
973 } else { /* Regular record */
974 rec->Stream = Stream;
975 rec->maskedStream = rec->Stream & STREAMMASK_TYPE;
976 rec->data_len = 0; /* transfer to beginning of data */
977 }
978 rec->VolSessionId = VolSessionId;
979 rec->VolSessionTime = VolSessionTime;
980 rec->FileIndex = FileIndex;
981 if (FileIndex > 0) {
982 if (dcr->block->FirstIndex == 0) { dcr->block->FirstIndex = FileIndex; }
983 dcr->block->LastIndex = FileIndex;
984 }
985
986 Dmsg6(450,
987 "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%u\n"
988 "remlen=%d data_len=%d\n",
989 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
990 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), data_bytes,
991 remlen, rec->data_len);
992 } else {
993 /*
994 * No more records in this block because the number
995 * of remaining bytes are less than a record header
996 * length, so return empty handed, but indicate that
997 * he must read again. By returning, we allow the
998 * higher level routine to fetch the next block and
999 * then reread.
1000 */
1001 Dmsg0(450, "read_record_block: nothing\n");
1002 SetBit(REC_NO_HEADER, rec->state_bits);
1003 SetBit(REC_BLOCK_EMPTY, rec->state_bits);
1004 EmptyBlock(dcr->block); /* mark block empty */
1005 return false;
1006 }
1007
1008 /* Sanity check */
1009 if (data_bytes >= MAX_BLOCK_LENGTH) {
1010 /*
1011 * Something is wrong, force read of next block, abort
1012 * continuing with this block.
1013 */
1014 SetBit(REC_NO_HEADER, rec->state_bits);
1015 SetBit(REC_BLOCK_EMPTY, rec->state_bits);
1016 EmptyBlock(dcr->block);
1017 Jmsg2(dcr->jcr, M_WARNING, 0,
1018 _("Sanity check failed. maxlen=%d datalen=%d. Block discarded.\n"),
1019 MAX_BLOCK_LENGTH, data_bytes);
1020 return false;
1021 }
1022
1023 rec->data = CheckPoolMemorySize(rec->data, rec->data_len + data_bytes);
1024
1025 /*
1026 * At this point, we have read the header, now we
1027 * must transfer as much of the data record as
1028 * possible taking into account: 1. A partial
1029 * data record may have previously been transferred,
1030 * 2. The current block may not contain the whole data
1031 * record.
1032 */
1033 if (remlen >= data_bytes) {
1034 /*
1035 * Got whole record
1036 */
1037 memcpy(rec->data + rec->data_len, dcr->block->bufp, data_bytes);
1038 dcr->block->bufp += data_bytes;
1039 dcr->block->binbuf -= data_bytes;
1040 rec->data_len += data_bytes;
1041 } else {
1042 /*
1043 * Partial record
1044 */
1045 memcpy(rec->data + rec->data_len, dcr->block->bufp, remlen);
1046 dcr->block->bufp += remlen;
1047 dcr->block->binbuf -= remlen;
1048 rec->data_len += remlen;
1049 rec->remainder = 1; /* partial record transferred */
1050 Dmsg1(450, "read_record_block: partial xfered=%d\n", rec->data_len);
1051 SetBit(REC_PARTIAL_RECORD, rec->state_bits);
1052 SetBit(REC_BLOCK_EMPTY, rec->state_bits);
1053 return true;
1054 }
1055 rec->remainder = 0;
1056
1057 Dmsg4(450, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
1058 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
1059 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
1060
1061 return true; /* transferred full record */
1062 }
1063
1064 } /* namespace storagedaemon */
1065