1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2001-2012 Free Software Foundation Europe e.V.
5 Copyright (C) 2016-2020 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 "stored/device_control_record.h"
35 #include "lib/attribs.h"
36 #include "lib/util.h"
37 #include "include/jcr.h"
38
39 namespace storagedaemon {
40
41 /**
42 * Convert a FileIndex into a printable
43 * ASCII string. Not reentrant.
44 *
45 * If the FileIndex is negative, it flags the
46 * record as a Label, otherwise it is simply
47 * the FileIndex of the current file.
48 */
FI_to_ascii(char * buf,int fi)49 const char* FI_to_ascii(char* buf, int fi)
50 {
51 if (fi >= 0) {
52 sprintf(buf, "%d", fi);
53 return buf;
54 }
55 switch (fi) {
56 case PRE_LABEL:
57 return "PRE_LABEL";
58 case VOL_LABEL:
59 return "VOL_LABEL";
60 case EOM_LABEL:
61 return "EOM_LABEL";
62 case SOS_LABEL:
63 return "SOS_LABEL";
64 case EOS_LABEL:
65 return "EOS_LABEL";
66 case EOT_LABEL:
67 return "EOT_LABEL";
68 break;
69 case SOB_LABEL:
70 return "SOB_LABEL";
71 break;
72 case EOB_LABEL:
73 return "EOB_LABEL";
74 break;
75 default:
76 sprintf(buf, _("unknown: %d"), fi);
77 return buf;
78 }
79 }
80
81
compression_to_str(PoolMem & resultbuffer,const char * compression_algorithm,uint32_t data_length,uint16_t compression_level,uint16_t compression_algorithm_version)82 static const char* compression_to_str(PoolMem& resultbuffer,
83 const char* compression_algorithm,
84 uint32_t data_length,
85 uint16_t compression_level,
86 uint16_t compression_algorithm_version)
87 {
88 PoolMem tmp(PM_MESSAGE);
89 tmp.bsprintf("%s, level=%u, version=%u, length=%u", compression_algorithm,
90 compression_level, compression_algorithm_version, data_length);
91 resultbuffer.strcat(tmp);
92 return resultbuffer.c_str();
93 }
94
record_compression_to_str(PoolMem & resultbuffer,const DeviceRecord * rec)95 static const char* record_compression_to_str(PoolMem& resultbuffer,
96 const DeviceRecord* rec)
97 {
98 int32_t maskedStream = rec->maskedStream;
99 POOLMEM* buf = rec->data;
100 PoolMem tmp(PM_MESSAGE);
101 unser_declare;
102
103 if (maskedStream == STREAM_SPARSE_GZIP_DATA
104 || maskedStream == STREAM_SPARSE_COMPRESSED_DATA) {
105 uint64_t faddr = 0;
106
107 SerBegin(buf, sizeof(uint64_t));
108 unser_uint64(faddr);
109 SerEnd(buf, sizeof(uint64_t));
110
111 buf += sizeof(uint64_t);
112
113 Dmsg1(400, "Sparse data stream found: start address=%llu\n", faddr);
114 tmp.bsprintf("Sparse: StartAddress=%llu. ", faddr);
115 resultbuffer.strcat(tmp);
116 }
117
118 Dmsg1(400, "Stream found in DecompressData(): %d\n", maskedStream);
119 switch (maskedStream) {
120 case STREAM_COMPRESSED_DATA:
121 case STREAM_SPARSE_COMPRESSED_DATA:
122 case STREAM_WIN32_COMPRESSED_DATA:
123 case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
124 case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA: {
125 uint32_t comp_magic, comp_len;
126 uint16_t comp_level, comp_version;
127
128 /*
129 * Read compress header
130 */
131 UnserBegin(buf, sizeof(comp_stream_header));
132 unser_uint32(comp_magic);
133 unser_uint32(comp_len);
134 unser_uint16(comp_level);
135 unser_uint16(comp_version);
136 UnserEnd(buf, sizeof(comp_stream_header));
137
138 Dmsg4(400,
139 "Compressed data stream found: magic=0x%x, len=%d, level=%d, "
140 "ver=0x%x\n",
141 comp_magic, comp_len, comp_level, comp_version);
142
143 switch (comp_magic) {
144 case COMPRESS_GZIP:
145 compression_to_str(resultbuffer, "GZIP", comp_len, comp_level,
146 comp_version);
147 break;
148 case COMPRESS_LZO1X:
149 compression_to_str(resultbuffer, "LZO1X", comp_len, comp_level,
150 comp_version);
151 break;
152 case COMPRESS_FZFZ:
153 compression_to_str(resultbuffer, "FZFZ", comp_len, comp_level,
154 comp_version);
155 break;
156 case COMPRESS_FZ4L:
157 compression_to_str(resultbuffer, "FZ4L", comp_len, comp_level,
158 comp_version);
159 break;
160 case COMPRESS_FZ4H:
161 compression_to_str(resultbuffer, "FZ4H", comp_len, comp_level,
162 comp_version);
163 break;
164 default:
165 tmp.bsprintf(
166 _("Compression algorithm 0x%x found, but not supported!\n"),
167 comp_magic);
168 resultbuffer.strcat(tmp);
169 break;
170 }
171 break;
172 }
173 case STREAM_GZIP_DATA:
174 case STREAM_SPARSE_GZIP_DATA:
175 /* deprecated */
176 compression_to_str(resultbuffer, "GZIP", 0, 0, 0);
177 break;
178 default:
179 break;
180 }
181
182 return resultbuffer.c_str();
183 }
184
record_digest_to_str(PoolMem & resultbuffer,const DeviceRecord * rec)185 static const char* record_digest_to_str(PoolMem& resultbuffer,
186 const DeviceRecord* rec)
187 {
188 char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
189
190 switch (rec->maskedStream) {
191 case STREAM_MD5_DIGEST:
192 BinToBase64(digest, sizeof(digest), (char*)rec->data,
193 CRYPTO_DIGEST_MD5_SIZE, true);
194 break;
195 case STREAM_SHA1_DIGEST:
196 BinToBase64(digest, sizeof(digest), (char*)rec->data,
197 CRYPTO_DIGEST_SHA1_SIZE, true);
198 break;
199 case STREAM_SHA256_DIGEST:
200 BinToBase64(digest, sizeof(digest), (char*)rec->data,
201 CRYPTO_DIGEST_SHA256_SIZE, true);
202 break;
203 case STREAM_SHA512_DIGEST:
204 BinToBase64(digest, sizeof(digest), (char*)rec->data,
205 CRYPTO_DIGEST_SHA512_SIZE, true);
206 break;
207 default:
208 return "";
209 }
210
211 resultbuffer.bsprintf("%s (base64)", digest);
212
213 return resultbuffer.c_str();
214 }
215
216 /**
217 * Convert a Stream ID into a printable
218 * ASCII string. Not reentrant.
219 *
220 * A negative stream number represents
221 * stream data that is continued from a
222 * record in the previous block.
223 *
224 * If the FileIndex is negative, we are
225 * dealing with a Label, hence the
226 * stream is the JobId.
227 */
stream_to_ascii(char * buf,int stream,int fi)228 const char* stream_to_ascii(char* buf, int stream, int fi)
229 {
230 if (fi < 0) {
231 sprintf(buf, "%d", stream);
232 return buf;
233 }
234
235 if (stream < 0) {
236 stream = -stream;
237 stream &= STREAMMASK_TYPE;
238 /*
239 * Stream was negative => all are continuation items
240 */
241 switch (stream) {
242 case STREAM_UNIX_ATTRIBUTES:
243 return "contUATTR";
244 case STREAM_FILE_DATA:
245 return "contDATA";
246 case STREAM_WIN32_DATA:
247 return "contWIN32-DATA";
248 case STREAM_WIN32_GZIP_DATA:
249 return "contWIN32-GZIP";
250 case STREAM_WIN32_COMPRESSED_DATA:
251 return "contWIN32-COMPRESSED";
252 case STREAM_MD5_DIGEST:
253 return "contMD5";
254 case STREAM_SHA1_DIGEST:
255 return "contSHA1";
256 case STREAM_GZIP_DATA:
257 return "contGZIP";
258 case STREAM_COMPRESSED_DATA:
259 return "contCOMPRESSED";
260 case STREAM_UNIX_ATTRIBUTES_EX:
261 return "contUNIX-Attributes-EX";
262 case STREAM_RESTORE_OBJECT:
263 return "contRESTORE-OBJECT";
264 case STREAM_SPARSE_DATA:
265 return "contSPARSE-DATA";
266 case STREAM_SPARSE_GZIP_DATA:
267 return "contSPARSE-GZIP";
268 case STREAM_SPARSE_COMPRESSED_DATA:
269 return "contSPARSE-COMPRESSED";
270 case STREAM_PROGRAM_NAMES:
271 return "contPROG-NAMES";
272 case STREAM_PROGRAM_DATA:
273 return "contPROG-DATA";
274 case STREAM_MACOS_FORK_DATA:
275 return "contMACOS-RSRC";
276 case STREAM_HFSPLUS_ATTRIBUTES:
277 return "contHFSPLUS-Attributes";
278 case STREAM_SHA256_DIGEST:
279 return "contSHA256";
280 case STREAM_SHA512_DIGEST:
281 return "contSHA512";
282 case STREAM_SIGNED_DIGEST:
283 return "contSIGNED-DIGEST";
284 case STREAM_ENCRYPTED_SESSION_DATA:
285 return "contENCRYPTED-SESSION-DATA";
286 case STREAM_ENCRYPTED_FILE_DATA:
287 return "contENCRYPTED-FILE";
288 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
289 return "contENCRYPTED-GZIP";
290 case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
291 return "contENCRYPTED-COMPRESSED";
292 case STREAM_ENCRYPTED_WIN32_DATA:
293 return "contENCRYPTED-WIN32-DATA";
294 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
295 return "contENCRYPTED-WIN32-GZIP";
296 case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
297 return "contENCRYPTED-WIN32-COMPRESSED";
298 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
299 return "contENCRYPTED-MACOS-RSRC";
300 case STREAM_PLUGIN_NAME:
301 return "contPLUGIN-NAME";
302 default:
303 sprintf(buf, "%d", -stream);
304 return buf;
305 }
306 }
307
308 switch (stream & STREAMMASK_TYPE) {
309 case STREAM_UNIX_ATTRIBUTES:
310 return "UATTR";
311 case STREAM_FILE_DATA:
312 return "DATA";
313 case STREAM_WIN32_DATA:
314 return "WIN32-DATA";
315 case STREAM_WIN32_GZIP_DATA:
316 return "WIN32-GZIP";
317 case STREAM_WIN32_COMPRESSED_DATA:
318 return "WIN32-COMPRESSED";
319 case STREAM_MD5_DIGEST:
320 return "MD5";
321 case STREAM_SHA1_DIGEST:
322 return "SHA1";
323 case STREAM_GZIP_DATA:
324 return "GZIP";
325 case STREAM_COMPRESSED_DATA:
326 return "COMPRESSED";
327 case STREAM_UNIX_ATTRIBUTES_EX:
328 return "UNIX-Attributes-EX";
329 case STREAM_RESTORE_OBJECT:
330 return "RESTORE-OBJECT";
331 case STREAM_SPARSE_DATA:
332 return "SPARSE-DATA";
333 case STREAM_SPARSE_GZIP_DATA:
334 return "SPARSE-GZIP";
335 case STREAM_SPARSE_COMPRESSED_DATA:
336 return "SPARSE-COMPRESSED";
337 case STREAM_PROGRAM_NAMES:
338 return "PROG-NAMES";
339 case STREAM_PROGRAM_DATA:
340 return "PROG-DATA";
341 case STREAM_PLUGIN_NAME:
342 return "PLUGIN-NAME";
343 case STREAM_MACOS_FORK_DATA:
344 return "MACOS-RSRC";
345 case STREAM_HFSPLUS_ATTRIBUTES:
346 return "HFSPLUS-Attributes";
347 case STREAM_SHA256_DIGEST:
348 return "SHA256";
349 case STREAM_SHA512_DIGEST:
350 return "SHA512";
351 case STREAM_SIGNED_DIGEST:
352 return "SIGNED-DIGEST";
353 case STREAM_ENCRYPTED_SESSION_DATA:
354 return "ENCRYPTED-SESSION-DATA";
355 case STREAM_ENCRYPTED_FILE_DATA:
356 return "ENCRYPTED-FILE";
357 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
358 return "ENCRYPTED-GZIP";
359 case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
360 return "ENCRYPTED-COMPRESSED";
361 case STREAM_ENCRYPTED_WIN32_DATA:
362 return "ENCRYPTED-WIN32-DATA";
363 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
364 return "ENCRYPTED-WIN32-GZIP";
365 case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
366 return "ENCRYPTED-WIN32-COMPRESSED";
367 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
368 return "ENCRYPTED-MACOS-RSRC";
369 default:
370 sprintf(buf, "%d", stream);
371 return buf;
372 }
373 }
374
record_state_to_ascii(rec_state state)375 static const char* record_state_to_ascii(rec_state state)
376 {
377 switch (state) {
378 case st_none:
379 return "st_none";
380 case st_header:
381 return "st_header";
382 case st_header_cont:
383 return "st_header_cont";
384 case st_data:
385 return "st_data";
386 default:
387 return "<unknown>";
388 }
389 }
390
findex_to_str(int32_t index,char * buf,size_t bufsz)391 static const char* findex_to_str(int32_t index, char* buf, size_t bufsz)
392 {
393 if (index >= 0) {
394 Bsnprintf(buf, bufsz, "<User> %d", index);
395 return buf;
396 }
397
398 FI_to_ascii(buf, index);
399
400 return buf;
401 }
402
403
record_unix_attributes_to_str(PoolMem & resultbuffer,JobControlRecord * jcr,const DeviceRecord * rec)404 static const char* record_unix_attributes_to_str(PoolMem& resultbuffer,
405 JobControlRecord* jcr,
406 const DeviceRecord* rec)
407 {
408 Attributes* attr = new_attr(NULL);
409
410 if (!UnpackAttributesRecord(jcr, rec->Stream, rec->data, rec->data_len,
411 attr)) {
412 resultbuffer.bsprintf("ERROR");
413 return NULL;
414 }
415
416 attr->data_stream = DecodeStat(attr->attr, &attr->statp, sizeof(attr->statp),
417 &attr->LinkFI);
418 BuildAttrOutputFnames(jcr, attr);
419 attr_to_str(resultbuffer, jcr, attr);
420
421 FreeAttr(attr);
422
423 return resultbuffer.c_str();
424 }
425
426
get_record_short_info(PoolMem & resultbuffer,JobControlRecord * jcr,const DeviceRecord * rec)427 static const char* get_record_short_info(PoolMem& resultbuffer,
428 JobControlRecord* jcr,
429 const DeviceRecord* rec)
430 {
431 switch (rec->maskedStream) {
432 case STREAM_UNIX_ATTRIBUTES:
433 case STREAM_UNIX_ATTRIBUTES_EX:
434 record_unix_attributes_to_str(resultbuffer, jcr, rec);
435 break;
436 case STREAM_MD5_DIGEST:
437 case STREAM_SHA1_DIGEST:
438 case STREAM_SHA256_DIGEST:
439 case STREAM_SHA512_DIGEST:
440 record_digest_to_str(resultbuffer, rec);
441 break;
442 case STREAM_PLUGIN_NAME: {
443 char data[100];
444 int len = MIN(rec->data_len + 1, sizeof(data));
445 bstrncpy(data, rec->data, len);
446 resultbuffer.bsprintf("data: %s\n", data);
447 break;
448 }
449 case STREAM_RESTORE_OBJECT:
450 resultbuffer.bsprintf("Restore Object record");
451 break;
452 case STREAM_COMPRESSED_DATA:
453 case STREAM_SPARSE_COMPRESSED_DATA:
454 case STREAM_WIN32_COMPRESSED_DATA:
455 case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
456 case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
457 case STREAM_GZIP_DATA: /* deprecated */
458 case STREAM_SPARSE_GZIP_DATA: /* deprecated */
459 record_compression_to_str(resultbuffer, rec);
460 break;
461 default:
462 break;
463 }
464 return resultbuffer.c_str();
465 }
466
record_to_str(PoolMem & resultbuffer,JobControlRecord * jcr,const DeviceRecord * rec)467 const char* record_to_str(PoolMem& resultbuffer,
468 JobControlRecord* jcr,
469 const DeviceRecord* rec)
470 {
471 PoolMem record_info_buf(PM_MESSAGE);
472 char stream_buf[100];
473
474 resultbuffer.bsprintf(
475 "FileIndex=%-5d Stream=%-2d %-25s DataLen=%-5d", rec->FileIndex,
476 rec->Stream, stream_to_ascii(stream_buf, rec->Stream, rec->FileIndex),
477 rec->data_len);
478 IndentMultilineString(
479 resultbuffer, get_record_short_info(record_info_buf, jcr, rec), " | ");
480
481 return resultbuffer.c_str();
482 }
483
DumpRecord(const char * tag,const DeviceRecord * rec)484 void DumpRecord(const char* tag, const DeviceRecord* rec)
485 {
486 char stream[128];
487 char findex[128];
488
489 Dmsg2(100, "%s: rec %p\n", tag, rec);
490
491 Dmsg3(100, "%-14s next %p prev %p\n", "link", rec->link.next, rec->link.prev);
492 Dmsg2(100, "%-14s %u\n", "File", rec->File);
493 Dmsg2(100, "%-14s %u\n", "Block", rec->Block);
494 Dmsg2(100, "%-14s %u\n", "VolSessionId", rec->VolSessionId);
495 Dmsg2(100, "%-14s %u\n", "VolSessionTime", rec->VolSessionTime);
496 Dmsg2(100, "%-14s %s\n", "FileIndex",
497 findex_to_str(rec->FileIndex, findex, sizeof(findex)));
498 Dmsg2(100, "%-14s %s\n", "Stream",
499 stream_to_ascii(stream, rec->Stream, rec->FileIndex));
500 Dmsg2(100, "%-14s %d\n", "maskedStream", rec->maskedStream);
501 Dmsg2(100, "%-14s %u\n", "data_len", rec->data_len);
502 Dmsg2(100, "%-14s %u\n", "remainder", rec->remainder);
503 for (unsigned int i = 0;
504 i < (sizeof(rec->state_bits) / sizeof(rec->state_bits[0])); i++) {
505 Dmsg3(100, "%-11s[%d] %2.2x\n", "state_bits", i,
506 (uint8_t)rec->state_bits[i]);
507 }
508 Dmsg3(100, "%-14s %u (%s)\n", "state", rec->state,
509 record_state_to_ascii(rec->state));
510 Dmsg2(100, "%-14s %p\n", "bsr", rec->bsr);
511 Dmsg2(100, "%-14s %p\n", "data", rec->data);
512 Dmsg2(100, "%-14s %d\n", "match_stat", rec->match_stat);
513 Dmsg2(100, "%-14s %u\n", "last_VolSessionId", rec->last_VolSessionId);
514 Dmsg2(100, "%-14s %u\n", "last_VolSessionTime", rec->last_VolSessionTime);
515 Dmsg2(100, "%-14s %d\n", "last_FileIndex", rec->last_FileIndex);
516 Dmsg2(100, "%-14s %d\n", "last_Stream", rec->last_Stream);
517 Dmsg2(100, "%-14s %s\n", "own_mempool", rec->own_mempool ? "true" : "false");
518 }
519
520 /**
521 * Return a new record entity
522 */
new_record(bool with_data)523 DeviceRecord* new_record(bool with_data)
524 {
525 DeviceRecord* rec;
526
527 rec = (DeviceRecord*)GetPoolMemory(PM_RECORD);
528 *rec = DeviceRecord{};
529 if (with_data) {
530 rec->data = GetPoolMemory(PM_MESSAGE);
531 rec->own_mempool = true;
532 }
533 rec->state = st_none;
534
535 return rec;
536 }
537
EmptyRecord(DeviceRecord * rec)538 void EmptyRecord(DeviceRecord* rec)
539 {
540 rec->File = rec->Block = 0;
541 rec->VolSessionId = rec->VolSessionTime = 0;
542 rec->FileIndex = rec->Stream = 0;
543 rec->data_len = rec->remainder = 0;
544
545 ClearBit(REC_PARTIAL_RECORD, rec->state_bits);
546 ClearBit(REC_BLOCK_EMPTY, rec->state_bits);
547 ClearBit(REC_NO_MATCH, rec->state_bits);
548 ClearBit(REC_CONTINUATION, rec->state_bits);
549
550 rec->state = st_none;
551 }
552
CopyRecordState(DeviceRecord * dst,DeviceRecord * src)553 void CopyRecordState(DeviceRecord* dst, DeviceRecord* src)
554 {
555 bool own_mempool;
556 int32_t Stream, maskedStream;
557 uint32_t data_len;
558 POOLMEM* data;
559
560 /*
561 * Preserve some important fields all other can be overwritten.
562 */
563 Stream = dst->Stream;
564 maskedStream = dst->maskedStream;
565 data = dst->data;
566 data_len = dst->data_len;
567 own_mempool = dst->own_mempool;
568
569 memcpy(dst, src, sizeof(DeviceRecord));
570
571 dst->Stream = Stream;
572 dst->maskedStream = maskedStream;
573 dst->data = data;
574 dst->data_len = data_len;
575 dst->own_mempool = own_mempool;
576 }
577
578 /**
579 * Free the record entity
580 */
FreeRecord(DeviceRecord * rec)581 void FreeRecord(DeviceRecord* rec)
582 {
583 Dmsg0(950, "Enter FreeRecord.\n");
584 if (rec->data && rec->own_mempool) { FreePoolMemory(rec->data); }
585 Dmsg0(950, "Data buf is freed.\n");
586 FreePoolMemory((POOLMEM*)rec);
587 Dmsg0(950, "Leave FreeRecord.\n");
588 }
589
WriteHeaderToBlock(DeviceBlock * block,const DeviceRecord * rec,int32_t Stream)590 static inline ssize_t WriteHeaderToBlock(DeviceBlock* block,
591 const DeviceRecord* rec,
592 int32_t Stream)
593 {
594 ser_declare;
595
596 /*
597 * Require enough room to write a full header
598 */
599 if (BlockWriteNavail(block) < WRITE_RECHDR_LENGTH) return -1;
600
601 SerBegin(block->bufp, WRITE_RECHDR_LENGTH);
602
603 if (BLOCK_VER == 1) {
604 ser_uint32(rec->VolSessionId);
605 ser_uint32(rec->VolSessionTime);
606 } else {
607 block->VolSessionId = rec->VolSessionId;
608 block->VolSessionTime = rec->VolSessionTime;
609 }
610
611 ser_int32(rec->FileIndex);
612 ser_int32(Stream);
613
614 ser_uint32(
615 rec->remainder); /* each header tracks remaining user bytes to write */
616
617 block->bufp += WRITE_RECHDR_LENGTH;
618 block->binbuf += WRITE_RECHDR_LENGTH;
619
620 if (rec->FileIndex > 0) {
621 /*
622 * If data record, update what we have in this block
623 */
624 if (block->FirstIndex == 0) { block->FirstIndex = rec->FileIndex; }
625 block->LastIndex = rec->FileIndex;
626 }
627
628 return WRITE_RECHDR_LENGTH;
629 }
630
WriteDataToBlock(DeviceBlock * block,const DeviceRecord * rec)631 static inline ssize_t WriteDataToBlock(DeviceBlock* block,
632 const DeviceRecord* rec)
633 {
634 uint32_t len;
635
636 len = MIN(rec->remainder, BlockWriteNavail(block));
637 memcpy(block->bufp,
638 ((unsigned char*)rec->data) + (rec->data_len - rec->remainder), len);
639 block->bufp += len;
640 block->binbuf += len;
641 return len;
642 }
643
644 /**
645 * Write a Record to the block
646 *
647 * Returns: false means the block could not be written to tape/disk.
648 * true on success (all bytes written to the block).
649 */
WriteRecord()650 bool DeviceControlRecord::WriteRecord()
651 {
652 bool retval = false;
653 bool translated_record = false;
654 char buf1[100], buf2[100];
655
656 /*
657 * Perform record translations.
658 */
659 before_rec = rec;
660 after_rec = NULL;
661 if (GeneratePluginEvent(jcr, bSdEventWriteRecordTranslation, this)
662 != bRC_OK) {
663 goto bail_out;
664 }
665
666 /*
667 * The record got translated when we got an after_rec pointer after calling
668 * the bSdEventWriteRecordTranslation plugin event. If no translation has
669 * taken place we just point the after_rec pointer to same DeviceRecord as in
670 * the before_rec pointer.
671 */
672 if (!after_rec) {
673 after_rec = before_rec;
674 } else {
675 translated_record = true;
676 }
677
678 while (!WriteRecordToBlock(this, after_rec)) {
679 Dmsg2(850, "!WriteRecordToBlock data_len=%d rem=%d\n", after_rec->data_len,
680 after_rec->remainder);
681 if (!WriteBlockToDevice()) {
682 Dmsg2(90, "Got WriteBlockToDev error on device %s. %s\n",
683 dev->print_name(), dev->bstrerror());
684 goto bail_out;
685 }
686 }
687
688 jcr->JobBytes += after_rec->data_len; /* increment bytes this job */
689 if (jcr->impl->RemainingQuota && 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
951 && (rec->VolSessionId != VolSessionId
952 || rec->VolSessionTime != VolSessionTime)) {
953 SetBit(REC_NO_MATCH, rec->state_bits);
954 Dmsg0(450, "remainder and VolSession doesn't match\n");
955 return false; /* This is from some other Session */
956 }
957
958 /*
959 * If Stream is negative, it means that this is a continuation
960 * of a previous partially written record.
961 */
962 if (Stream < 0) { /* continuation record? */
963 Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n",
964 rec->remainder);
965 SetBit(REC_CONTINUATION, rec->state_bits);
966 if (!rec->remainder) { /* if we didn't read previously */
967 rec->data_len = 0; /* return data as if no continuation */
968 } else if (rec->Stream != -Stream) {
969 SetBit(REC_NO_MATCH, rec->state_bits);
970 return false; /* This is from some other Session */
971 }
972 rec->Stream = -Stream; /* set correct Stream */
973 rec->maskedStream = rec->Stream & STREAMMASK_TYPE;
974 } else { /* Regular record */
975 rec->Stream = Stream;
976 rec->maskedStream = rec->Stream & STREAMMASK_TYPE;
977 rec->data_len = 0; /* transfer to beginning of data */
978 }
979 rec->VolSessionId = VolSessionId;
980 rec->VolSessionTime = VolSessionTime;
981 rec->FileIndex = FileIndex;
982 if (FileIndex > 0) {
983 if (dcr->block->FirstIndex == 0) { dcr->block->FirstIndex = FileIndex; }
984 dcr->block->LastIndex = FileIndex;
985 }
986
987 Dmsg6(450,
988 "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%u\n"
989 "remlen=%d data_len=%d\n",
990 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
991 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), data_bytes,
992 remlen, rec->data_len);
993 } else {
994 /*
995 * No more records in this block because the number
996 * of remaining bytes are less than a record header
997 * length, so return empty handed, but indicate that
998 * he must read again. By returning, we allow the
999 * higher level routine to fetch the next block and
1000 * then reread.
1001 */
1002 Dmsg0(450, "read_record_block: nothing\n");
1003 SetBit(REC_NO_HEADER, rec->state_bits);
1004 SetBit(REC_BLOCK_EMPTY, rec->state_bits);
1005 EmptyBlock(dcr->block); /* mark block empty */
1006 return false;
1007 }
1008
1009 /* Sanity check */
1010 if (data_bytes >= MAX_BLOCK_LENGTH) {
1011 /*
1012 * Something is wrong, force read of next block, abort
1013 * continuing with this block.
1014 */
1015 SetBit(REC_NO_HEADER, rec->state_bits);
1016 SetBit(REC_BLOCK_EMPTY, rec->state_bits);
1017 EmptyBlock(dcr->block);
1018 Jmsg2(dcr->jcr, M_WARNING, 0,
1019 _("Sanity check failed. maxlen=%d datalen=%d. Block discarded.\n"),
1020 MAX_BLOCK_LENGTH, data_bytes);
1021 return false;
1022 }
1023
1024 rec->data = CheckPoolMemorySize(rec->data, rec->data_len + data_bytes);
1025
1026 /*
1027 * At this point, we have read the header, now we
1028 * must transfer as much of the data record as
1029 * possible taking into account: 1. A partial
1030 * data record may have previously been transferred,
1031 * 2. The current block may not contain the whole data
1032 * record.
1033 */
1034 if (remlen >= data_bytes) {
1035 /*
1036 * Got whole record
1037 */
1038 memcpy(rec->data + rec->data_len, dcr->block->bufp, data_bytes);
1039 dcr->block->bufp += data_bytes;
1040 dcr->block->binbuf -= data_bytes;
1041 rec->data_len += data_bytes;
1042 } else {
1043 /*
1044 * Partial record
1045 */
1046 memcpy(rec->data + rec->data_len, dcr->block->bufp, remlen);
1047 dcr->block->bufp += remlen;
1048 dcr->block->binbuf -= remlen;
1049 rec->data_len += remlen;
1050 rec->remainder = 1; /* partial record transferred */
1051 Dmsg1(450, "read_record_block: partial xfered=%d\n", rec->data_len);
1052 SetBit(REC_PARTIAL_RECORD, rec->state_bits);
1053 SetBit(REC_BLOCK_EMPTY, rec->state_bits);
1054 return true;
1055 }
1056 rec->remainder = 0;
1057
1058 Dmsg4(450, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
1059 FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
1060 stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
1061
1062 return true; /* transferred full record */
1063 }
1064
1065 } /* namespace storagedaemon */
1066