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