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