1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2013-2014 Planets Communications B.V.
5    Copyright (C) 2013-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, which is
10    listed 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  * Marco van Wieringen, June 2013
24  */
25 /**
26  * @file
27  * Storage Daemon plugin that handles automatic deflation/inflation of data.
28  */
29 #include "include/bareos.h"
30 #include "stored/stored.h"
31 #include "stored/device_control_record.h"
32 
33 #if defined(HAVE_LIBZ)
34 #  include <zlib.h>
35 #endif
36 
37 #include "fastlz/fastlzlib.h"
38 
39 using namespace storagedaemon;
40 
41 #define PLUGIN_LICENSE "Bareos AGPLv3"
42 #define PLUGIN_AUTHOR "Marco van Wieringen"
43 #define PLUGIN_DATE "June 2013"
44 #define PLUGIN_VERSION "1"
45 #define PLUGIN_DESCRIPTION "Auto Xflation Storage Daemon Plugin"
46 #define PLUGIN_USAGE "(No usage yet)"
47 
48 #define Dmsg(context, level, ...)                                         \
49   bareos_core_functions->DebugMessage(context, __FILE__, __LINE__, level, \
50                                       __VA_ARGS__)
51 #define Jmsg(context, type, ...)                                          \
52   bareos_core_functions->JobMessage(context, __FILE__, __LINE__, type, 0, \
53                                     __VA_ARGS__)
54 
55 #define SETTING_YES (char*)"yes"
56 #define SETTING_NO (char*)"no"
57 #define SETTING_UNSET (char*)"unknown"
58 
59 #define COMPRESSOR_NAME_GZIP (char*)"GZIP"
60 #define COMPRESSOR_NAME_LZO (char*)"LZO"
61 #define COMPRESSOR_NAME_FZLZ (char*)"FASTLZ"
62 #define COMPRESSOR_NAME_FZ4L (char*)"LZ4"
63 #define COMPRESSOR_NAME_FZ4H (char*)"LZ4HC"
64 #define COMPRESSOR_NAME_UNSET (char*)"unknown"
65 
66 /**
67  * Forward referenced functions
68  */
69 static bRC newPlugin(PluginContext* ctx);
70 static bRC freePlugin(PluginContext* ctx);
71 static bRC getPluginValue(PluginContext* ctx, pVariable var, void* value);
72 static bRC setPluginValue(PluginContext* ctx, pVariable var, void* value);
73 static bRC handlePluginEvent(PluginContext* ctx, bSdEvent* event, void* value);
74 static bRC handleJobEnd(PluginContext* ctx);
75 static bRC setup_record_translation(PluginContext* ctx, void* value);
76 static bRC handle_read_translation(PluginContext* ctx, void* value);
77 static bRC handle_write_translation(PluginContext* ctx, void* value);
78 
79 static bool SetupAutoDeflation(PluginContext* ctx, DeviceControlRecord* dcr);
80 static bool SetupAutoInflation(PluginContext* ctx, DeviceControlRecord* dcr);
81 static bool AutoDeflateRecord(PluginContext* ctx, DeviceControlRecord* dcr);
82 static bool AutoInflateRecord(PluginContext* ctx, DeviceControlRecord* dcr);
83 
84 /**
85  * Is the SD in compatible mode or not.
86  */
87 static bool sd_enabled_compatible = false;
88 
89 /**
90  * Pointers to Bareos functions
91  */
92 static CoreFunctions* bareos_core_functions = NULL;
93 static PluginApiDefinition* bareos_plugin_interface_version = NULL;
94 
95 static PluginInformation pluginInfo
96     = {sizeof(pluginInfo), SD_PLUGIN_INTERFACE_VERSION,
97        SD_PLUGIN_MAGIC,    PLUGIN_LICENSE,
98        PLUGIN_AUTHOR,      PLUGIN_DATE,
99        PLUGIN_VERSION,     PLUGIN_DESCRIPTION,
100        PLUGIN_USAGE};
101 
102 static PluginFunctions pluginFuncs
103     = {sizeof(pluginFuncs), SD_PLUGIN_INTERFACE_VERSION,
104 
105        /*
106         * Entry points into plugin
107         */
108        newPlugin,  /* new plugin instance */
109        freePlugin, /* free plugin instance */
110        getPluginValue, setPluginValue, handlePluginEvent};
111 
112 /**
113  * Plugin private context
114  */
115 struct plugin_ctx {
116   /*
117    * Counters for compression/decompression ratio
118    */
119   uint64_t deflate_bytes_in;
120   uint64_t deflate_bytes_out;
121   uint64_t inflate_bytes_in;
122   uint64_t inflate_bytes_out;
123 };
124 
125 static int const debuglevel = 200;
126 
127 #ifdef __cplusplus
128 extern "C" {
129 #endif
130 
131 /**
132  * loadPlugin() and unloadPlugin() are entry points that are
133  *  exported, so Bareos can directly call these two entry points
134  *  they are common to all Bareos plugins.
135  *
136  * External entry point called by Bareos to "load the plugin
137  */
loadPlugin(PluginApiDefinition * lbareos_plugin_interface_version,CoreFunctions * lbareos_core_functions,PluginInformation ** plugin_information,PluginFunctions ** plugin_functions)138 bRC loadPlugin(PluginApiDefinition* lbareos_plugin_interface_version,
139                CoreFunctions* lbareos_core_functions,
140                PluginInformation** plugin_information,
141                PluginFunctions** plugin_functions)
142 {
143   bareos_core_functions
144       = lbareos_core_functions; /* set Bareos funct pointers */
145   bareos_plugin_interface_version = lbareos_plugin_interface_version;
146   *plugin_information = &pluginInfo; /* return pointer to our info */
147   *plugin_functions = &pluginFuncs;  /* return pointer to our functions */
148 
149   /*
150    * Get the current setting of the compatible flag.
151    */
152   bareos_core_functions->getBareosValue(NULL, bsdVarCompatible,
153                                         (void*)&sd_enabled_compatible);
154 
155   return bRC_OK;
156 }
157 
158 /**
159  * External entry point to unload the plugin
160  */
unloadPlugin()161 bRC unloadPlugin() { return bRC_OK; }
162 
163 #ifdef __cplusplus
164 }
165 #endif
166 
167 /**
168  * The following entry points are accessed through the function
169  * pointers we supplied to Bareos. Each plugin type (dir, fd, sd)
170  * has its own set of entry points that the plugin must define.
171  *
172  * Create a new instance of the plugin i.e. allocate our private storage
173  */
newPlugin(PluginContext * ctx)174 static bRC newPlugin(PluginContext* ctx)
175 {
176   int JobId = 0;
177   struct plugin_ctx* p_ctx;
178 
179   bareos_core_functions->getBareosValue(ctx, bsdVarJobId, (void*)&JobId);
180   Dmsg(ctx, debuglevel, "autoxflate-sd: newPlugin JobId=%d\n", JobId);
181 
182   p_ctx = (struct plugin_ctx*)malloc(sizeof(struct plugin_ctx));
183   if (!p_ctx) { return bRC_Error; }
184 
185   memset(p_ctx, 0, sizeof(struct plugin_ctx));
186   ctx->plugin_private_context = (void*)p_ctx; /* set our context pointer */
187 
188   /*
189    * Only register plugin events we are interested in.
190    *
191    * bSdEventJobEnd - SD Job finished.
192    * bSdEventSetupRecordTranslation - Setup the buffers for doing record
193    * translation. bSdEventReadRecordTranslation - Perform read-side record
194    * translation. bSdEventWriteRecordTranslation - Perform write-side record
195    * translantion.
196    */
197   bareos_core_functions->registerBareosEvents(
198       ctx, 4, bSdEventJobEnd, bSdEventSetupRecordTranslation,
199       bSdEventReadRecordTranslation, bSdEventWriteRecordTranslation);
200 
201   return bRC_OK;
202 }
203 
204 /**
205  * Free a plugin instance, i.e. release our private storage
206  */
freePlugin(PluginContext * ctx)207 static bRC freePlugin(PluginContext* ctx)
208 {
209   int JobId = 0;
210   struct plugin_ctx* p_ctx = (struct plugin_ctx*)ctx->plugin_private_context;
211 
212   bareos_core_functions->getBareosValue(ctx, bsdVarJobId, (void*)&JobId);
213   Dmsg(ctx, debuglevel, "autoxflate-sd: freePlugin JobId=%d\n", JobId);
214 
215   if (!p_ctx) {
216     Dmsg(ctx, debuglevel, "autoxflate-sd: freePlugin JobId=%d\n", JobId);
217     return bRC_Error;
218   }
219 
220   if (p_ctx) { free(p_ctx); }
221   ctx->plugin_private_context = NULL;
222 
223   return bRC_OK;
224 }
225 
226 /**
227  * Return some plugin value (none defined)
228  */
getPluginValue(PluginContext * ctx,pVariable var,void * value)229 static bRC getPluginValue(PluginContext* ctx, pVariable var, void* value)
230 {
231   Dmsg(ctx, debuglevel, "autoxflate-sd: getPluginValue var=%d\n", var);
232 
233   return bRC_OK;
234 }
235 
236 /**
237  * Set a plugin value (none defined)
238  */
setPluginValue(PluginContext * ctx,pVariable var,void * value)239 static bRC setPluginValue(PluginContext* ctx, pVariable var, void* value)
240 {
241   Dmsg(ctx, debuglevel, "autoxflate-sd: setPluginValue var=%d\n", var);
242 
243   return bRC_OK;
244 }
245 
246 /**
247  * Handle an event that was generated in Bareos
248  */
handlePluginEvent(PluginContext * ctx,bSdEvent * event,void * value)249 static bRC handlePluginEvent(PluginContext* ctx, bSdEvent* event, void* value)
250 {
251   switch (event->eventType) {
252     case bSdEventSetupRecordTranslation:
253       return setup_record_translation(ctx, value);
254     case bSdEventReadRecordTranslation:
255       return handle_read_translation(ctx, value);
256     case bSdEventWriteRecordTranslation:
257       return handle_write_translation(ctx, value);
258     case bSdEventJobEnd:
259       return handleJobEnd(ctx);
260     default:
261       Dmsg(ctx, debuglevel, "autoxflate-sd: Unknown event %d\n",
262            event->eventType);
263       return bRC_Error;
264   }
265 
266   return bRC_OK;
267 }
268 
269 /**
270  * At end of job report how inflate/deflate ratio was.
271  */
handleJobEnd(PluginContext * ctx)272 static bRC handleJobEnd(PluginContext* ctx)
273 {
274   struct plugin_ctx* p_ctx = (struct plugin_ctx*)ctx->plugin_private_context;
275 
276   if (!p_ctx) { goto bail_out; }
277 
278   if (p_ctx->inflate_bytes_in) {
279     Dmsg(ctx, debuglevel, "autoxflate-sd: inflate ratio: %lld/%lld = %0.2f%%\n",
280          p_ctx->inflate_bytes_out, p_ctx->inflate_bytes_in,
281          (p_ctx->inflate_bytes_out * 100.0 / p_ctx->inflate_bytes_in));
282     Jmsg(ctx, M_INFO, _("autoxflate-sd: inflate ratio: %0.2f%%\n"),
283          (p_ctx->inflate_bytes_out * 100.0 / p_ctx->inflate_bytes_in));
284   }
285 
286   if (p_ctx->deflate_bytes_in) {
287     Dmsg(ctx, debuglevel,
288          "autoxflate-sd: deflate ratio: %lld/%lld =  %0.2f%%\n",
289          p_ctx->deflate_bytes_out, p_ctx->deflate_bytes_in,
290          (p_ctx->deflate_bytes_out * 100.0 / p_ctx->deflate_bytes_in));
291     Jmsg(ctx, M_INFO, _("autoxflate-sd: deflate ratio: %0.2f%%\n"),
292          (p_ctx->deflate_bytes_out * 100.0 / p_ctx->deflate_bytes_in));
293   }
294 
295 bail_out:
296   return bRC_OK;
297 }
298 
setup_record_translation(PluginContext * ctx,void * value)299 static bRC setup_record_translation(PluginContext* ctx, void* value)
300 {
301   DeviceControlRecord* dcr;
302   bool did_setup = false;
303   const char* inflate_in = SETTING_UNSET;
304   const char* inflate_out = SETTING_UNSET;
305   const char* deflate_in = SETTING_UNSET;
306   const char* deflate_out = SETTING_UNSET;
307 
308   /*
309    * Unpack the arguments passed in.
310    */
311   dcr = (DeviceControlRecord*)value;
312   if (!dcr) { return bRC_Error; }
313 
314   /*
315    * Give jobmessage info what is configured
316    */
317   switch (dcr->autodeflate) {
318     case AutoXflateMode::IO_DIRECTION_NONE:
319       deflate_in = SETTING_NO;
320       deflate_out = SETTING_NO;
321       break;
322     case AutoXflateMode::IO_DIRECTION_IN:
323       deflate_in = SETTING_YES;
324       deflate_out = SETTING_NO;
325       break;
326     case AutoXflateMode::IO_DIRECTION_OUT:
327       deflate_in = SETTING_NO;
328       deflate_out = SETTING_YES;
329       break;
330     case AutoXflateMode::IO_DIRECTION_INOUT:
331       deflate_in = SETTING_YES;
332       deflate_out = SETTING_YES;
333       break;
334     default:
335       Jmsg(ctx, M_ERROR,
336            _("autoxflate-sd: Unexpected autodeflate setting on %s"),
337            dcr->dev_name);
338       break;
339   }
340 
341   switch (dcr->autoinflate) {
342     case AutoXflateMode::IO_DIRECTION_NONE:
343       inflate_in = SETTING_NO;
344       inflate_out = SETTING_NO;
345       break;
346     case AutoXflateMode::IO_DIRECTION_IN:
347       inflate_in = SETTING_YES;
348       inflate_out = SETTING_NO;
349       break;
350     case AutoXflateMode::IO_DIRECTION_OUT:
351       inflate_in = SETTING_NO;
352       inflate_out = SETTING_YES;
353       break;
354     case AutoXflateMode::IO_DIRECTION_INOUT:
355       inflate_in = SETTING_YES;
356       inflate_out = SETTING_YES;
357       break;
358     default:
359       Jmsg(ctx, M_ERROR,
360            _("autoxflate-sd: Unexpected autoinflate setting on %s"),
361            dcr->dev_name);
362       break;
363   }
364 
365   /*
366    * Setup auto deflation/inflation of streams when enabled for this device.
367    */
368   switch (dcr->autodeflate) {
369     case AutoXflateMode::IO_DIRECTION_NONE:
370       break;
371     case AutoXflateMode::IO_DIRECTION_OUT:
372     case AutoXflateMode::IO_DIRECTION_INOUT:
373       if (!SetupAutoDeflation(ctx, dcr)) { return bRC_Error; }
374       did_setup = true;
375       break;
376     default:
377       break;
378   }
379 
380   switch (dcr->autoinflate) {
381     case AutoXflateMode::IO_DIRECTION_NONE:
382       break;
383     case AutoXflateMode::IO_DIRECTION_OUT:
384     case AutoXflateMode::IO_DIRECTION_INOUT:
385       if (!SetupAutoInflation(ctx, dcr)) { return bRC_Error; }
386       did_setup = true;
387       break;
388     default:
389       break;
390   }
391 
392   if (did_setup) {
393     Jmsg(ctx, M_INFO,
394          _("autoxflate-sd: %s OUT:[SD->inflate=%s->deflate=%s->DEV] "
395            "IN:[DEV->inflate=%s->deflate=%s->SD]\n"),
396          dcr->dev_name, inflate_out, deflate_out, inflate_in, deflate_in);
397   }
398 
399   return bRC_OK;
400 }
401 
handle_read_translation(PluginContext * ctx,void * value)402 static bRC handle_read_translation(PluginContext* ctx, void* value)
403 {
404   DeviceControlRecord* dcr;
405   bool swap_record = false;
406 
407   /*
408    * Unpack the arguments passed in.
409    */
410   dcr = (DeviceControlRecord*)value;
411   if (!dcr) { return bRC_Error; }
412 
413   /*
414    * See if we need to perform auto deflation/inflation of streams.
415    */
416   switch (dcr->autoinflate) {
417     case AutoXflateMode::IO_DIRECTION_IN:
418     case AutoXflateMode::IO_DIRECTION_INOUT:
419       swap_record = AutoInflateRecord(ctx, dcr);
420       break;
421     default:
422       break;
423   }
424 
425   if (!swap_record) {
426     switch (dcr->autodeflate) {
427       case AutoXflateMode::IO_DIRECTION_IN:
428       case AutoXflateMode::IO_DIRECTION_INOUT:
429         swap_record = AutoDeflateRecord(ctx, dcr);
430         break;
431       default:
432         break;
433     }
434   }
435 
436   return bRC_OK;
437 }
438 
handle_write_translation(PluginContext * ctx,void * value)439 static bRC handle_write_translation(PluginContext* ctx, void* value)
440 {
441   DeviceControlRecord* dcr;
442   bool swap_record = false;
443 
444   /*
445    * Unpack the arguments passed in.
446    */
447   dcr = (DeviceControlRecord*)value;
448   if (!dcr) { return bRC_Error; }
449 
450   /*
451    * See if we need to perform auto deflation/inflation of streams.
452    */
453   switch (dcr->autoinflate) {
454     case AutoXflateMode::IO_DIRECTION_OUT:
455     case AutoXflateMode::IO_DIRECTION_INOUT:
456       swap_record = AutoInflateRecord(ctx, dcr);
457       break;
458     default:
459       break;
460   }
461 
462   if (!swap_record) {
463     switch (dcr->autodeflate) {
464       case AutoXflateMode::IO_DIRECTION_OUT:
465       case AutoXflateMode::IO_DIRECTION_INOUT:
466         swap_record = AutoDeflateRecord(ctx, dcr);
467         break;
468       default:
469         break;
470     }
471   }
472 
473   return bRC_OK;
474 }
475 
476 /**
477  * Setup deflate for auto deflate of data streams.
478  */
SetupAutoDeflation(PluginContext * ctx,DeviceControlRecord * dcr)479 static bool SetupAutoDeflation(PluginContext* ctx, DeviceControlRecord* dcr)
480 {
481   JobControlRecord* jcr = dcr->jcr;
482   bool retval = false;
483   uint32_t compress_buf_size = 0;
484   const char* compressorname = COMPRESSOR_NAME_UNSET;
485 
486   if (jcr->buf_size == 0) { jcr->buf_size = DEFAULT_NETWORK_BUFFER_SIZE; }
487 
488   if (!SetupCompressionBuffers(jcr, sd_enabled_compatible,
489                                dcr->device_resource->autodeflate_algorithm,
490                                &compress_buf_size)) {
491     goto bail_out;
492   }
493 
494   /*
495    * See if we need to create a new compression buffer or make sure the existing
496    * is big enough.
497    */
498   if (!jcr->compress.deflate_buffer) {
499     jcr->compress.deflate_buffer = GetMemory(compress_buf_size);
500     jcr->compress.deflate_buffer_size = compress_buf_size;
501   } else {
502     if (compress_buf_size > jcr->compress.deflate_buffer_size) {
503       jcr->compress.deflate_buffer
504           = ReallocPoolMemory(jcr->compress.deflate_buffer, compress_buf_size);
505       jcr->compress.deflate_buffer_size = compress_buf_size;
506     }
507   }
508 
509   switch (dcr->device_resource->autodeflate_algorithm) {
510 #if defined(HAVE_LIBZ)
511     case COMPRESS_GZIP: {
512       compressorname = COMPRESSOR_NAME_GZIP;
513       int zstat;
514       z_stream* pZlibStream;
515 
516       pZlibStream = (z_stream*)jcr->compress.workset.pZLIB;
517       if ((zstat
518            = deflateParams(pZlibStream, dcr->device_resource->autodeflate_level,
519                            Z_DEFAULT_STRATEGY))
520           != Z_OK) {
521         Jmsg(ctx, M_FATAL,
522              _("autoxflate-sd: Compression deflateParams error: %d\n"), zstat);
523         jcr->setJobStatus(JS_ErrorTerminated);
524         goto bail_out;
525       }
526       break;
527     }
528 #endif
529 #if defined(HAVE_LZO)
530     case COMPRESS_LZO1X:
531       compressorname = COMPRESSOR_NAME_LZO;
532       break;
533 #endif
534     case COMPRESS_FZFZ:
535       compressorname = COMPRESSOR_NAME_FZLZ;
536     case COMPRESS_FZ4L:
537       compressorname = COMPRESSOR_NAME_FZ4L;
538     case COMPRESS_FZ4H: {
539       compressorname = COMPRESSOR_NAME_FZ4H;
540       int zstat;
541       zfast_stream* pZfastStream;
542       zfast_stream_compressor compressor = COMPRESSOR_FASTLZ;
543 
544       switch (dcr->device_resource->autodeflate_algorithm) {
545         case COMPRESS_FZ4L:
546         case COMPRESS_FZ4H:
547           compressor = COMPRESSOR_LZ4;
548           break;
549       }
550 
551       pZfastStream = (zfast_stream*)jcr->compress.workset.pZFAST;
552       if ((zstat = fastlzlibSetCompressor(pZfastStream, compressor)) != Z_OK) {
553         Jmsg(ctx, M_FATAL,
554              _("autoxflate-sd: Compression fastlzlibSetCompressor error: %d\n"),
555              zstat);
556         jcr->setJobStatus(JS_ErrorTerminated);
557         goto bail_out;
558       }
559       break;
560     }
561     default:
562       break;
563   }
564 
565   Jmsg(ctx, M_INFO, _("autoxflate-sd: Compressor on device %s is %s\n"),
566        dcr->dev_name, compressorname);
567   retval = true;
568 
569 bail_out:
570   return retval;
571 }
572 
573 /**
574  * Setup inflation for auto inflation of data streams.
575  */
SetupAutoInflation(PluginContext * ctx,DeviceControlRecord * dcr)576 static bool SetupAutoInflation(PluginContext* ctx, DeviceControlRecord* dcr)
577 {
578   JobControlRecord* jcr = dcr->jcr;
579   uint32_t decompress_buf_size;
580 
581   if (jcr->buf_size == 0) { jcr->buf_size = DEFAULT_NETWORK_BUFFER_SIZE; }
582 
583   SetupDecompressionBuffers(jcr, &decompress_buf_size);
584   if (decompress_buf_size > 0) {
585     /*
586      * See if we need to create a new compression buffer or make sure the
587      * existing is big enough.
588      */
589     if (!jcr->compress.inflate_buffer) {
590       jcr->compress.inflate_buffer = GetMemory(decompress_buf_size);
591       jcr->compress.inflate_buffer_size = decompress_buf_size;
592     } else {
593       if (decompress_buf_size > jcr->compress.inflate_buffer_size) {
594         jcr->compress.inflate_buffer = ReallocPoolMemory(
595             jcr->compress.inflate_buffer, decompress_buf_size);
596         jcr->compress.inflate_buffer_size = decompress_buf_size;
597       }
598     }
599   } else {
600     return false;
601   }
602 
603   return true;
604 }
605 
606 /**
607  * Perform automatic compression of certain stream types when enabled in the
608  * config.
609  */
AutoDeflateRecord(PluginContext * ctx,DeviceControlRecord * dcr)610 static bool AutoDeflateRecord(PluginContext* ctx, DeviceControlRecord* dcr)
611 {
612   ser_declare;
613   bool retval = false;
614   comp_stream_header ch;
615   DeviceRecord *rec, *nrec;
616   struct plugin_ctx* p_ctx;
617   unsigned char* data = NULL;
618   bool intermediate_value = false;
619   unsigned int max_compression_length = 0;
620 
621   p_ctx = (struct plugin_ctx*)ctx->plugin_private_context;
622   if (!p_ctx) { goto bail_out; }
623 
624   /*
625    * See what our starting point is. When dcr->after_rec is set we already have
626    * a translated record by another SD plugin. Then we use that translated
627    * record as the starting point otherwise we start at dcr->before_rec. When an
628    * earlier translation already happened we can free that record when we have a
629    * success full translation here as that record is of no use anymore.
630    */
631   if (dcr->after_rec) {
632     rec = dcr->after_rec;
633     intermediate_value = true;
634   } else {
635     rec = dcr->before_rec;
636   }
637 
638   /*
639    * We only do autocompression for the following stream types:
640    *
641    * - STREAM_FILE_DATA
642    * - STREAM_WIN32_DATA
643    * - STREAM_SPARSE_DATA
644    */
645   switch (rec->maskedStream) {
646     case STREAM_FILE_DATA:
647     case STREAM_WIN32_DATA:
648     case STREAM_SPARSE_DATA:
649       break;
650     default:
651       goto bail_out;
652   }
653 
654   /*
655    * Clone the data from the original DeviceRecord to the converted one.
656    * As we use the compression buffers for the data we need a new
657    * DeviceRecord without a new memory buffer so we call new_record here
658    * with the with_data boolean set explicitly to false.
659    */
660   nrec = bareos_core_functions->new_record(false);
661   bareos_core_functions->CopyRecordState(nrec, rec);
662 
663   /*
664    * Setup the converted DeviceRecord to point with its data buffer to the
665    * compression buffer.
666    */
667   nrec->data = dcr->jcr->compress.deflate_buffer;
668   switch (rec->maskedStream) {
669     case STREAM_FILE_DATA:
670     case STREAM_WIN32_DATA:
671       data = (unsigned char*)nrec->data + sizeof(comp_stream_header);
672       max_compression_length
673           = dcr->jcr->compress.deflate_buffer_size - sizeof(comp_stream_header);
674       break;
675     case STREAM_SPARSE_DATA:
676       data = (unsigned char*)nrec->data + OFFSET_FADDR_SIZE
677              + sizeof(comp_stream_header);
678       max_compression_length = dcr->jcr->compress.deflate_buffer_size
679                                - OFFSET_FADDR_SIZE - sizeof(comp_stream_header);
680       break;
681   }
682 
683   /*
684    * Compress the data using the configured compression algorithm.
685    */
686   if (!CompressData(dcr->jcr, dcr->device_resource->autodeflate_algorithm,
687                     rec->data, rec->data_len, data, max_compression_length,
688                     &nrec->data_len)) {
689     bareos_core_functions->FreeRecord(nrec);
690     goto bail_out;
691   }
692 
693   /*
694    * Map the streams.
695    */
696   switch (rec->maskedStream) {
697     case STREAM_FILE_DATA:
698       nrec->Stream = STREAM_COMPRESSED_DATA;
699       nrec->maskedStream = STREAM_COMPRESSED_DATA;
700       break;
701     case STREAM_WIN32_DATA:
702       nrec->Stream = STREAM_WIN32_COMPRESSED_DATA;
703       nrec->maskedStream = STREAM_WIN32_COMPRESSED_DATA;
704       break;
705     case STREAM_SPARSE_DATA:
706       nrec->Stream = STREAM_SPARSE_COMPRESSED_DATA;
707       nrec->maskedStream = STREAM_SPARSE_COMPRESSED_DATA;
708       break;
709     default:
710       break;
711   }
712 
713   /*
714    * Generate a compression header.
715    */
716   ch.magic = dcr->device_resource->autodeflate_algorithm;
717   ch.level = dcr->device_resource->autodeflate_level;
718   ch.version = COMP_HEAD_VERSION;
719   ch.size = nrec->data_len;
720 
721   switch (nrec->maskedStream) {
722     case STREAM_COMPRESSED_DATA:
723     case STREAM_WIN32_COMPRESSED_DATA:
724       SerBegin(nrec->data, sizeof(comp_stream_header));
725       ser_uint32(ch.magic);
726       ser_uint32(ch.size);
727       ser_uint16(ch.level);
728       ser_uint16(ch.version);
729       SerEnd(nrec->data, sizeof(comp_stream_header));
730       nrec->data_len += sizeof(comp_stream_header);
731       break;
732     case STREAM_SPARSE_COMPRESSED_DATA:
733       /*
734        * Copy the sparse offset from the original.
735        */
736       memcpy(nrec->data, rec->data, OFFSET_FADDR_SIZE);
737       SerBegin(nrec->data + OFFSET_FADDR_SIZE, sizeof(comp_stream_header));
738       ser_uint32(ch.magic);
739       ser_uint32(ch.size);
740       ser_uint16(ch.level);
741       ser_uint16(ch.version);
742       SerEnd(nrec->data + OFFSET_FADDR_SIZE, sizeof(comp_stream_header));
743       nrec->data_len += OFFSET_FADDR_SIZE + sizeof(comp_stream_header);
744       break;
745   }
746 
747   Dmsg(ctx, 400,
748        "AutoDeflateRecord: From datastream %d to %d from original size %ld to "
749        "%ld\n",
750        rec->maskedStream, nrec->maskedStream, rec->data_len, nrec->data_len);
751 
752   p_ctx->deflate_bytes_in += rec->data_len;
753   p_ctx->deflate_bytes_out += nrec->data_len;
754 
755   /*
756    * If the input is just an intermediate value free it now.
757    */
758   if (intermediate_value) { bareos_core_functions->FreeRecord(dcr->after_rec); }
759   dcr->after_rec = nrec;
760   retval = true;
761 
762 bail_out:
763   return retval;
764 }
765 
766 /**
767  * Inflate (uncompress) the content of a read record and return the data as an
768  * alternative datastream.
769  */
AutoInflateRecord(PluginContext * ctx,DeviceControlRecord * dcr)770 static bool AutoInflateRecord(PluginContext* ctx, DeviceControlRecord* dcr)
771 {
772   DeviceRecord *rec, *nrec;
773   bool retval = false;
774   struct plugin_ctx* p_ctx;
775   bool intermediate_value = false;
776 
777   p_ctx = (struct plugin_ctx*)ctx->plugin_private_context;
778   if (!p_ctx) { goto bail_out; }
779 
780   /*
781    * See what our starting point is. When dcr->after_rec is set we already have
782    * a translated record by another SD plugin. Then we use that translated
783    * record as the starting point otherwise we start at dcr->before_rec. When an
784    * earlier translation already happened we can free that record when we have a
785    * success full translation here as that record is of no use anymore.
786    */
787   if (dcr->after_rec) {
788     rec = dcr->after_rec;
789     intermediate_value = true;
790   } else {
791     rec = dcr->before_rec;
792   }
793 
794   /*
795    * We only do auto inflation for the following stream types:
796    *
797    * - STREAM_COMPRESSED_DATA
798    * - STREAM_WIN32_COMPRESSED_DATA
799    * - STREAM_SPARSE_COMPRESSED_DATA
800    */
801   switch (rec->maskedStream) {
802     case STREAM_COMPRESSED_DATA:
803     case STREAM_WIN32_COMPRESSED_DATA:
804     case STREAM_SPARSE_COMPRESSED_DATA:
805       break;
806     default:
807       goto bail_out;
808   }
809 
810   /*
811    * Clone the data from the original DeviceRecord to the converted one.
812    * As we use the compression buffers for the data we need a new
813    * DeviceRecord without a new memory buffer so we call new_record here
814    * with the with_data boolean set explicitly to false.
815    */
816   nrec = bareos_core_functions->new_record(false);
817   bareos_core_functions->CopyRecordState(nrec, rec);
818 
819   /*
820    * Setup the converted record to point to the original data.
821    * The DecompressData function will decompress that data and
822    * then update the pointers with the data in the compression buffer
823    * and with the length of the decompressed data.
824    */
825   nrec->data = rec->data;
826   nrec->data_len = rec->data_len;
827 
828   if (!DecompressData(dcr->jcr, "Unknown", rec->maskedStream, &nrec->data,
829                       &nrec->data_len, true)) {
830     bareos_core_functions->FreeRecord(nrec);
831     goto bail_out;
832   }
833 
834   /*
835    * If we succeeded in decompressing the data update the stream type.
836    */
837   switch (rec->maskedStream) {
838     case STREAM_COMPRESSED_DATA:
839       nrec->Stream = STREAM_FILE_DATA;
840       nrec->maskedStream = STREAM_FILE_DATA;
841       break;
842     case STREAM_WIN32_COMPRESSED_DATA:
843       nrec->Stream = STREAM_WIN32_DATA;
844       nrec->maskedStream = STREAM_WIN32_DATA;
845       break;
846     case STREAM_SPARSE_COMPRESSED_DATA:
847       nrec->Stream = STREAM_SPARSE_DATA;
848       nrec->maskedStream = STREAM_SPARSE_DATA;
849       break;
850     default:
851       break;
852   }
853 
854   Dmsg(ctx, 400,
855        "AutoInflateRecord: From datastream %d to %d from original size %ld to "
856        "%ld\n",
857        rec->maskedStream, nrec->maskedStream, rec->data_len, nrec->data_len);
858 
859   p_ctx->inflate_bytes_in += rec->data_len;
860   p_ctx->inflate_bytes_out += nrec->data_len;
861 
862   /*
863    * If the input is just an intermediate value free it now.
864    */
865   if (intermediate_value) { bareos_core_functions->FreeRecord(dcr->after_rec); }
866   dcr->after_rec = nrec;
867   retval = true;
868 
869 bail_out:
870   return retval;
871 }
872