1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  * A simple delta plugin for the Bacula File Daemon
21  *
22  *
23  */
24 #include "bacula.h"
25 #include "fd_plugins.h"
26 #include "fd_common.h"
27 
28 #undef malloc
29 #undef free
30 #undef strdup
31 
32 #ifdef __cplusplus
33 extern "C" {
34 #endif
35 
36 static const int dbglvl = 0;
37 
38 #define PLUGIN_LICENSE      "AGPLv3"
39 #define PLUGIN_AUTHOR       "Eric Bollengier"
40 #define PLUGIN_DATE         "November 2010"
41 #define PLUGIN_VERSION      "1"
42 #define PLUGIN_DESCRIPTION  "Bacula Delta Test Plugin"
43 
44 /* Forward referenced functions */
45 static bRC newPlugin(bpContext *ctx);
46 static bRC freePlugin(bpContext *ctx);
47 static bRC getPluginValue(bpContext *ctx, pVariable var, void *value);
48 static bRC setPluginValue(bpContext *ctx, pVariable var, void *value);
49 static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value);
50 static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp);
51 static bRC endBackupFile(bpContext *ctx);
52 static bRC pluginIO(bpContext *ctx, struct io_pkt *io);
53 static bRC startRestoreFile(bpContext *ctx, const char *cmd);
54 static bRC endRestoreFile(bpContext *ctx);
55 static bRC createFile(bpContext *ctx, struct restore_pkt *rp);
56 static bRC setFileAttributes(bpContext *ctx, struct restore_pkt *rp);
57 static bRC checkFile(bpContext *ctx, char *fname);
58 static bRC handleXACLdata(bpContext *ctx, struct xacl_pkt *xacl);
59 
60 /* Pointers to Bacula functions */
61 static bFuncs *bfuncs = NULL;
62 static bInfo  *binfo = NULL;
63 
64 /* Plugin Information block */
65 static pInfo pluginInfo = {
66    sizeof(pluginInfo),
67    FD_PLUGIN_INTERFACE_VERSION,
68    FD_PLUGIN_MAGIC,
69    PLUGIN_LICENSE,
70    PLUGIN_AUTHOR,
71    PLUGIN_DATE,
72    PLUGIN_VERSION,
73    PLUGIN_DESCRIPTION
74 };
75 
76 /* Plugin entry points for Bacula */
77 static pFuncs pluginFuncs = {
78    sizeof(pluginFuncs),
79    FD_PLUGIN_INTERFACE_VERSION,
80 
81    /* Entry points into plugin */
82    newPlugin,                         /* new plugin instance */
83    freePlugin,                        /* free plugin instance */
84    getPluginValue,
85    setPluginValue,
86    handlePluginEvent,
87    startBackupFile,
88    endBackupFile,
89    startRestoreFile,
90    endRestoreFile,
91    pluginIO,
92    createFile,
93    setFileAttributes,
94    checkFile,
95    handleXACLdata
96 };
97 
98 #define get_self(x) ((delta_test*)((x)->pContext))
99 #define FO_DELTA        (1<<28)       /* Do delta on file */
100 #define FO_OFFSETS      (1<<30)       /* Keep block offsets */
101 
102 class delta_test
103 {
104 private:
105    bpContext *ctx;
106 
107 public:
108    POOLMEM *fname;              /* Filename to save */
109    int32_t delta;
110    FILE *fd;
111    bool done;
112    int level;
113 
delta_test(bpContext * bpc)114    delta_test(bpContext *bpc) {
115       fd = NULL;
116       ctx = bpc;
117       done = false;
118       level = 0;
119       delta = 0;
120       fname = get_pool_memory(PM_FNAME);
121    }
~delta_test()122    ~delta_test() {
123       free_and_null_pool_memory(fname);
124    }
125 };
126 
127 /*
128  * loadPlugin() and unloadPlugin() are entry points that are
129  *  exported, so Bacula can directly call these two entry points
130  *  they are common to all Bacula plugins.
131  */
132 /*
133  * External entry point called by Bacula to "load the plugin
134  */
loadPlugin(bInfo * lbinfo,bFuncs * lbfuncs,pInfo ** pinfo,pFuncs ** pfuncs)135 bRC loadPlugin(bInfo *lbinfo, bFuncs *lbfuncs, pInfo **pinfo, pFuncs **pfuncs)
136 {
137    bfuncs = lbfuncs;                  /* set Bacula funct pointers */
138    binfo  = lbinfo;
139    *pinfo  = &pluginInfo;             /* return pointer to our info */
140    *pfuncs = &pluginFuncs;            /* return pointer to our functions */
141 
142    /* Activate this plugin only in developer mode */
143 #ifdef DEVELOPER
144    return bRC_OK;
145 #else
146    return bRC_Error;
147 #endif
148 }
149 
150 /*
151  * External entry point to unload the plugin
152  */
unloadPlugin()153 bRC unloadPlugin()
154 {
155 // Dmsg(NULL, dbglvl, "delta-test-fd: Unloaded\n");
156    return bRC_OK;
157 }
158 
159 /*
160  * The following entry points are accessed through the function
161  *   pointers we supplied to Bacula. Each plugin type (dir, fd, sd)
162  *   has its own set of entry points that the plugin must define.
163  */
164 /*
165  * Create a new instance of the plugin i.e. allocate our private storage
166  */
newPlugin(bpContext * ctx)167 static bRC newPlugin(bpContext *ctx)
168 {
169    delta_test *self = new delta_test(ctx);
170    if (!self) {
171       return bRC_Error;
172    }
173    ctx->pContext = (void *)self;        /* set our context pointer */
174    return bRC_OK;
175 }
176 
177 /*
178  * Free a plugin instance, i.e. release our private storage
179  */
freePlugin(bpContext * ctx)180 static bRC freePlugin(bpContext *ctx)
181 {
182    delta_test *self = get_self(ctx);
183    if (!self) {
184       return bRC_Error;
185    }
186    delete self;
187    return bRC_OK;
188 }
189 
190 /*
191  * Return some plugin value (none defined)
192  */
getPluginValue(bpContext * ctx,pVariable var,void * value)193 static bRC getPluginValue(bpContext *ctx, pVariable var, void *value)
194 {
195    return bRC_OK;
196 }
197 
198 /*
199  * Set a plugin value (none defined)
200  */
setPluginValue(bpContext * ctx,pVariable var,void * value)201 static bRC setPluginValue(bpContext *ctx, pVariable var, void *value)
202 {
203    return bRC_OK;
204 }
205 
206 /*
207  * Handle an event that was generated in Bacula
208  */
handlePluginEvent(bpContext * ctx,bEvent * event,void * value)209 static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value)
210 {
211    delta_test *self = get_self(ctx);
212    int accurate=0;
213 
214    if (!self) {
215       return bRC_Error;
216    }
217 
218 // char *name;
219 
220    /*
221     * Most events don't interest us so we ignore them.
222     *   the printfs are so that plugin writers can enable them to see
223     *   what is really going on.
224     */
225    switch (event->eventType) {
226    case bEventPluginCommand:
227 //    Dmsg(ctx, dbglvl,
228 //         "delta-test-fd: PluginCommand=%s\n", (char *)value);
229       break;
230    case bEventJobStart:
231 //    Dmsg(ctx, dbglvl, "delta-test-fd: JobStart=%s\n", (char *)value);
232       break;
233    case bEventJobEnd:
234 //    Dmsg(ctx, dbglvl, "delta-test-fd: JobEnd\n");
235       break;
236    case bEventStartBackupJob:
237 //    Dmsg(ctx, dbglvl, "delta-test-fd: StartBackupJob\n");
238       break;
239    case bEventEndBackupJob:
240 //    Dmsg(ctx, dbglvl, "delta-test-fd: EndBackupJob\n");
241       break;
242    case bEventLevel:
243 //    Dmsg(ctx, dbglvl, "delta-test-fd: JobLevel=%c %d\n", (int)value, (int)value);
244       self->level = (int)(intptr_t)value;
245       break;
246    case bEventSince:
247 //    Dmsg(ctx, dbglvl, "delta-test-fd: since=%d\n", (int)value);
248       break;
249 
250    case bEventStartRestoreJob:
251 //    Dmsg(ctx, dbglvl, "delta-test-fd: StartRestoreJob\n");
252       break;
253 
254    case bEventEndRestoreJob:
255 //    Dmsg(ctx, dbglvl, "delta-test-fd: EndRestoreJob\n");
256       break;
257 
258    /* Plugin command e.g. plugin = <plugin-name>:<name-space>:read command:write command */
259    case bEventRestoreCommand:
260 //    Dmsg(ctx, dbglvl, "delta-test-fd: EventRestoreCommand cmd=%s\n", (char *)value);
261       /* Fall-through wanted */
262       break;
263    case bEventBackupCommand:
264       Dmsg(ctx, dbglvl, "delta-test-fd: pluginEvent cmd=%s\n", (char *)value);
265       if (self->level == 'I' || self->level == 'D') {
266          bfuncs->getBaculaValue(ctx, bVarAccurate, (void *)&accurate);
267          if (!accurate) {       /* can be changed to FATAL */
268             Jmsg(ctx, M_FATAL,
269                  "Accurate mode should be turned on when using the "
270                  "delta-test plugin\n");
271             return bRC_Error;
272          }
273       }
274       break;
275 
276    default:
277 //    Dmsg(ctx, dbglvl, "delta-test-fd: unknown event=%d\n", event->eventType);
278       break;
279    }
280    return bRC_OK;
281 }
282 
283 static const char *files[] = {
284    "/etc/passwd",
285    "/etc/group",
286    "/etc/hosts",
287    "/etc/services"
288 };
289 static int nb_files = 4;
290 
291 /*
292  * Start the backup of a specific file
293  */
startBackupFile(bpContext * ctx,struct save_pkt * sp)294 static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp)
295 {
296    delta_test *self = get_self(ctx);
297    if (!self) {
298       return bRC_Error;
299    }
300    time_t now = time(NULL);
301    sp->fname = (char *)"/delta.txt";
302    sp->type = FT_REG;
303    sp->statp.st_mode = 0700 | S_IFREG;
304    sp->statp.st_ctime = now;
305    sp->statp.st_mtime = now;
306    sp->statp.st_atime = now;
307    sp->statp.st_size = -1;
308    sp->statp.st_blksize = 4096;
309    sp->statp.st_blocks = 1;
310    if (self->level == 'I' || self->level == 'D') {
311       bRC state = bfuncs->checkChanges(ctx, sp);
312       /* Should always be bRC_OK */
313       sp->type = (state == bRC_Seen)? FT_NOCHG : FT_REG;
314       sp->flags |= (FO_DELTA|FO_OFFSETS);
315       self->delta = sp->delta_seq + 1;
316    }
317    pm_strcpy(self->fname, files[self->delta % nb_files]);
318    Dmsg(ctx, dbglvl, "delta-test-fd: delta_seq=%i delta=%i fname=%s\n",
319         sp->delta_seq, self->delta, self->fname);
320 // Dmsg(ctx, dbglvl, "delta-test-fd: startBackupFile\n");
321    return bRC_OK;
322 }
323 
324 /*
325  * Done with backup of this file
326  */
endBackupFile(bpContext * ctx)327 static bRC endBackupFile(bpContext *ctx)
328 {
329    /*
330     * We would return bRC_More if we wanted startBackupFile to be
331     * called again to backup another file
332     */
333    return bRC_OK;
334 }
335 
336 
337 /*
338  * Bacula is calling us to do the actual I/O
339  */
pluginIO(bpContext * ctx,struct io_pkt * io)340 static bRC pluginIO(bpContext *ctx, struct io_pkt *io)
341 {
342    delta_test *self = get_self(ctx);
343    struct stat statp;
344    if (!self) {
345       return bRC_Error;
346    }
347 
348    io->status = 0;
349    io->io_errno = 0;
350    switch(io->func) {
351    case IO_OPEN:
352       Dmsg(ctx, dbglvl, "delta-test-fd: IO_OPEN\n");
353       if (io->flags & (O_CREAT | O_WRONLY)) {
354          /* TODO: if the file already exists, the result is undefined */
355          if (stat(io->fname, &statp) == 0) { /* file exists */
356             self->fd = fopen(io->fname, "r+");
357          } else {
358             self->fd = fopen(io->fname, "w"); /* file doesn't exist,create it */
359          }
360          if (!self->fd) {
361             io->io_errno = errno;
362             Jmsg(ctx, M_FATAL,
363                  "Open failed: ERR=%s\n", strerror(errno));
364             return bRC_Error;
365          }
366 
367       } else {
368          self->fd = fopen(self->fname, "r");
369          if (!self->fd) {
370             io->io_errno = errno;
371             Jmsg(ctx, M_FATAL,
372                "Open failed: ERR=%s\n", strerror(errno));
373             return bRC_Error;
374          }
375       }
376       break;
377 
378    case IO_READ:
379       if (!self->fd) {
380          Jmsg(ctx, M_FATAL, "Logic error: NULL read FD\n");
381          return bRC_Error;
382       }
383       if (self->done) {
384          io->status = 0;
385       } else {
386          /* first time, read 300, then replace 50-250 by other data */
387          if (self->delta == 0) {
388             io->status = fread(io->buf, 1, 400, self->fd);
389          } else {
390             io->offset = self->delta * 100 / 2; /* chunks are melted */
391             io->status = fread(io->buf, 1, 100, self->fd);
392          }
393          Dmsg(ctx, dbglvl, "delta-test-fd: READ offset=%lld\n", (int64_t)io->offset);
394          self->done = true;
395       }
396       if (io->status == 0 && ferror(self->fd)) {
397          Jmsg(ctx, M_FATAL,
398             "Pipe read error: ERR=%s\n", strerror(errno));
399          Dmsg(ctx, dbglvl,
400             "Pipe read error: ERR=%s\n", strerror(errno));
401          return bRC_Error;
402       }
403       Dmsg(ctx, dbglvl, "offset=%d\n", io->offset);
404       break;
405 
406    case IO_WRITE:
407       if (!self->fd) {
408          Jmsg(ctx, M_FATAL, "Logic error: NULL write FD\n");
409          return bRC_Error;
410       }
411       Dmsg(ctx, dbglvl, "delta-test-fd: WRITE count=%lld\n", (int64_t)io->count);
412       io->status = fwrite(io->buf, 1, io->count, self->fd);
413       if (io->status == 0 && ferror(self->fd)) {
414          Jmsg(ctx, M_FATAL,
415             "Pipe write error\n");
416          Dmsg(ctx, dbglvl,
417             "Pipe read error: ERR=%s\n", strerror(errno));
418          return bRC_Error;
419       }
420       break;
421 
422    case IO_CLOSE:
423       if (!self->fd) {
424          Jmsg(ctx, M_FATAL, "Logic error: NULL FD on delta close\n");
425          return bRC_Error;
426       }
427       io->status = fclose(self->fd);
428       break;
429 
430    case IO_SEEK:
431       if (!self->fd) {
432          Jmsg(ctx, M_FATAL, "Logic error: NULL FD on delta close\n");
433          return bRC_Error;
434       }
435       Dmsg(ctx, dbglvl, "delta-test-fd: SEEK offset=%lld\n", (int64_t)io->offset);
436       io->status = fseek(self->fd, io->offset, io->whence);
437       Dmsg(ctx, dbglvl, "after SEEK=%lld\n", (int64_t)ftell(self->fd));
438       break;
439    }
440    return bRC_OK;
441 }
442 
443 /*
444  * Bacula is notifying us that a plugin name string was found, and
445  *   passing us the plugin command, so we can prepare for a restore.
446  */
startRestoreFile(bpContext * ctx,const char * cmd)447 static bRC startRestoreFile(bpContext *ctx, const char *cmd)
448 {
449 // Dmsg(ctx, dbglvl, "delta-test-fd: startRestoreFile cmd=%s\n", cmd);
450    return bRC_OK;
451 }
452 
453 /*
454  * Bacula is notifying us that the plugin data has terminated, so
455  *  the restore for this particular file is done.
456  */
endRestoreFile(bpContext * ctx)457 static bRC endRestoreFile(bpContext *ctx)
458 {
459 // Dmsg(ctx, dbglvl, "delta-test-fd: endRestoreFile\n");
460    return bRC_OK;
461 }
462 
463 /*
464  * This is called during restore to create the file (if necessary)
465  * We must return in rp->create_status:
466  *
467  *  CF_ERROR    -- error
468  *  CF_SKIP     -- skip processing this file
469  *  CF_EXTRACT  -- extract the file (i.e.call i/o routines)
470  *  CF_CREATED  -- created, but no content to extract (typically directories)
471  *
472  */
createFile(bpContext * ctx,struct restore_pkt * rp)473 static bRC createFile(bpContext *ctx, struct restore_pkt *rp)
474 {
475    delta_test *self = get_self(ctx);
476    pm_strcpy(self->fname, rp->ofname);
477    rp->create_status = CF_EXTRACT;
478    return bRC_OK;
479 }
480 
481 /*
482  * We will get here if the File is a directory after everything
483  * is written in the directory.
484  */
setFileAttributes(bpContext * ctx,struct restore_pkt * rp)485 static bRC setFileAttributes(bpContext *ctx, struct restore_pkt *rp)
486 {
487 // Dmsg(ctx, dbglvl, "delta-test-fd: setFileAttributes\n");
488    return bRC_OK;
489 }
490 
491 /* When using Incremental dump, all previous dumps are necessary */
checkFile(bpContext * ctx,char * fname)492 static bRC checkFile(bpContext *ctx, char *fname)
493 {
494    return bRC_OK;
495 }
496 
497 /*
498  * New Bacula Plugin API require this
499  */
handleXACLdata(bpContext * ctx,struct xacl_pkt * xacl)500 static bRC handleXACLdata(bpContext *ctx, struct xacl_pkt *xacl)
501 {
502    return bRC_OK;
503 }
504 
505 #ifdef __cplusplus
506 }
507 #endif
508