1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2007-2011 Free Software Foundation Europe e.V.
5    Copyright (C) 2016-2016 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  * @file
24  * A simple delta plugin for the Bareos File Daemon
25  *
26  *
27  */
28 #include "include/bareos.h"
29 #include "fd_plugins.h"
30 #include "fd_common.h"
31 
32 namespace filedaemon {
33 
34 static const int debuglevel = 0;
35 
36 #define PLUGIN_LICENSE      "Bareos AGPLv3"
37 #define PLUGIN_AUTHOR       "Eric Bollengier"
38 #define PLUGIN_DATE         "November 2010"
39 #define PLUGIN_VERSION      "1"
40 #define PLUGIN_DESCRIPTION  "Bareos Delta Test Plugin"
41 
42 /* Forward referenced functions */
43 static bRC newPlugin(bpContext *ctx);
44 static bRC freePlugin(bpContext *ctx);
45 static bRC getPluginValue(bpContext *ctx, pVariable var, void *value);
46 static bRC setPluginValue(bpContext *ctx, pVariable var, void *value);
47 static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value);
48 static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp);
49 static bRC endBackupFile(bpContext *ctx);
50 static bRC pluginIO(bpContext *ctx, struct io_pkt *io);
51 static bRC startRestoreFile(bpContext *ctx, const char *cmd);
52 static bRC endRestoreFile(bpContext *ctx);
53 static bRC createFile(bpContext *ctx, struct restore_pkt *rp);
54 static bRC setFileAttributes(bpContext *ctx, struct restore_pkt *rp);
55 
56 /* Pointers to Bareos functions */
57 static bFuncs *bfuncs = NULL;
58 static bInfo  *binfo = NULL;
59 
60 /* Plugin Information block */
61 static genpInfo pluginInfo = {
62    sizeof(pluginInfo),
63    FD_PLUGIN_INTERFACE_VERSION,
64    FD_PLUGIN_MAGIC,
65    PLUGIN_LICENSE,
66    PLUGIN_AUTHOR,
67    PLUGIN_DATE,
68    PLUGIN_VERSION,
69    PLUGIN_DESCRIPTION
70 };
71 
72 /* Plugin entry points for Bareos */
73 static pFuncs pluginFuncs = {
74    sizeof(pluginFuncs),
75    FD_PLUGIN_INTERFACE_VERSION,
76 
77    /* Entry points into plugin */
78    newPlugin,         /* new plugin instance */
79    freePlugin,        /* free plugin instance */
80    getPluginValue,
81    setPluginValue,
82    handlePluginEvent,
83    startBackupFile,
84    endBackupFile,
85    startRestoreFile,
86    endRestoreFile,
87    pluginIO,
88    createFile,
89    setFileAttributes,
90    NULL,              /* no checkFile */
91    NULL,              /* no getAcl */
92    NULL,              /* no setAcl */
93    NULL,              /* no getXattr */
94    NULL               /* no setXattr */
95 };
96 
97 #define get_self(x) ((delta_test*)((x)->pContext))
98 
99 class delta_test
100 {
101 private:
102    bpContext *ctx;
103 
104 public:
105    POOLMEM *fname;              /* Filename to save */
106    int32_t delta;
107    FILE *fd;
108    bool done;
109    int level;
110 
delta_test(bpContext * bpc)111    delta_test(bpContext *bpc) {
112       fd = NULL;
113       ctx = bpc;
114       done = false;
115       level = 0;
116       delta = 0;
117       fname = GetPoolMemory(PM_FNAME);
118    }
~delta_test()119    ~delta_test() {
120       FreeAndNullPoolMemory(fname);
121    }
122 };
123 
124 #ifdef __cplusplus
125 extern "C" {
126 #endif
127 
128 /**
129  * loadPlugin() and unloadPlugin() are entry points that are
130  *  exported, so Bareos can directly call these two entry points
131  *  they are common to all Bareos plugins.
132  */
133 /**
134  * External entry point called by Bareos to "load" the plugin
135  */
loadPlugin(bInfo * lbinfo,bFuncs * lbfuncs,genpInfo ** pinfo,pFuncs ** pfuncs)136 bRC loadPlugin(bInfo *lbinfo,
137                            bFuncs *lbfuncs,
138                            genpInfo **pinfo,
139                            pFuncs **pfuncs)
140 {
141    bfuncs = lbfuncs;                  /* set Bareos funct pointers */
142    binfo  = lbinfo;
143    *pinfo  = &pluginInfo;             /* return pointer to our info */
144    *pfuncs = &pluginFuncs;            /* return pointer to our functions */
145 
146    /* Activate this plugin only in developer mode */
147 #ifdef DEVELOPER
148    return bRC_OK;
149 #else
150    return bRC_Error;
151 #endif
152 }
153 
154 /**
155  * External entry point to unload the plugin
156  */
unloadPlugin()157 bRC unloadPlugin()
158 {
159 // Dmsg(NULL, debuglevel, "delta-test-fd: Unloaded\n");
160    return bRC_OK;
161 }
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 /**
173  * Create a new instance of the plugin i.e. allocate our private storage
174  */
newPlugin(bpContext * ctx)175 static bRC newPlugin(bpContext *ctx)
176 {
177    delta_test *self = new delta_test(ctx);
178    if (!self) {
179       return bRC_Error;
180    }
181    ctx->pContext = (void *)self;        /* set our context pointer */
182 
183    bfuncs->registerBareosEvents(ctx,
184                                 3,
185                                 bEventLevel,
186                                 bEventRestoreCommand,
187                                 bEventBackupCommand);
188    return bRC_OK;
189 }
190 
191 /**
192  * Free a plugin instance, i.e. release our private storage
193  */
freePlugin(bpContext * ctx)194 static bRC freePlugin(bpContext *ctx)
195 {
196    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
197 
198    if (!p_ctx) {
199       return bRC_Error;
200    }
201 
202    free(p_ctx);                          /* free our private context */
203    p_ctx = NULL;
204    return bRC_OK;
205 }
206 
207 /**
208  * Return some plugin value (none defined)
209  */
getPluginValue(bpContext * ctx,pVariable var,void * value)210 static bRC getPluginValue(bpContext *ctx, pVariable var, void *value)
211 {
212    return bRC_OK;
213 }
214 
215 /**
216  * Set a plugin value (none defined)
217  */
setPluginValue(bpContext * ctx,pVariable var,void * value)218 static bRC setPluginValue(bpContext *ctx, pVariable var, void *value)
219 {
220    return bRC_OK;
221 }
222 
223 /**
224  * Handle an event that was generated in Bareos
225  */
handlePluginEvent(bpContext * ctx,bEvent * event,void * value)226 static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value)
227 {
228    delta_test *self = get_self(ctx);
229    int accurate=0;
230 
231    if (!self) {
232       return bRC_Error;
233    }
234 
235 // char *name;
236 
237    switch (event->eventType) {
238    case bEventLevel:
239 //    Dmsg(ctx, debuglevel, "delta-test-fd: JobLevel=%c %d\n", (int)value, (int)value);
240       self->level = (int)(intptr_t)value;
241       break;
242    /* Plugin command e.g. plugin = <plugin-name>:<name-space>:read command:write command */
243    case bEventRestoreCommand:
244 //    Dmsg(ctx, debuglevel, "delta-test-fd: EventRestoreCommand cmd=%s\n", (char *)value);
245       /* Fall-through wanted */
246    case bEventBackupCommand:
247       Dmsg(ctx, debuglevel, "delta-test-fd: pluginEvent cmd=%s\n", (char *)value);
248       if (self->level == 'I' || self->level == 'D') {
249          bfuncs->getBareosValue(ctx, bVarAccurate, (void *)&accurate);
250          if (!accurate) {       /* can be changed to FATAL */
251             Jmsg(ctx, M_FATAL,
252                  "Accurate mode should be turned on when using the "
253                  "delta-test plugin\n");
254             return bRC_Error;
255          }
256       }
257       break;
258    default:
259 //    Dmsg(ctx, debuglevel, "delta-test-fd: unknown event=%d\n", event->eventType);
260       break;
261    }
262    return bRC_OK;
263 }
264 
265 static const char *files[] = {
266    "/etc/passwd",
267    "/etc/group",
268    "/etc/hosts",
269    "/etc/services"
270 };
271 static int nb_files = 4;
272 
273 /**
274  * Start the backup of a specific file
275  */
startBackupFile(bpContext * ctx,struct save_pkt * sp)276 static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp)
277 {
278    delta_test *self = get_self(ctx);
279    if (!self) {
280       return bRC_Error;
281    }
282    time_t now = time(NULL);
283    sp->fname = (char *)"/delta.txt";
284    sp->type = FT_REG;
285    sp->statp.st_mode = 0700 | S_IFREG;
286    sp->statp.st_ctime = now;
287    sp->statp.st_mtime = now;
288    sp->statp.st_atime = now;
289    sp->statp.st_size = -1;
290    sp->statp.st_blksize = 4096;
291    sp->statp.st_blocks = 1;
292    if (self->level == 'I' || self->level == 'D') {
293       bRC state = bfuncs->checkChanges(ctx, sp);
294       /* Should always be bRC_OK */
295       sp->type = (state == bRC_Seen)? FT_NOCHG : FT_REG;
296       SetBit(FO_DELTA, sp->flags);
297       SetBit(FO_OFFSETS, sp->flags);
298       self->delta = sp->delta_seq + 1;
299    }
300    PmStrcpy(self->fname, files[self->delta % nb_files]);
301    Dmsg(ctx, debuglevel, "delta-test-fd: delta_seq=%i delta=%i fname=%s\n",
302         sp->delta_seq, self->delta, self->fname);
303 // Dmsg(ctx, debuglevel, "delta-test-fd: startBackupFile\n");
304    return bRC_OK;
305 }
306 
307 /**
308  * Done with backup of this file
309  */
endBackupFile(bpContext * ctx)310 static bRC endBackupFile(bpContext *ctx)
311 {
312    /*
313     * We would return bRC_More if we wanted startBackupFile to be
314     * called again to backup another file
315     */
316    return bRC_OK;
317 }
318 
319 /**
320  * Bareos is calling us to do the actual I/O
321  */
pluginIO(bpContext * ctx,struct io_pkt * io)322 static bRC pluginIO(bpContext *ctx, struct io_pkt *io)
323 {
324    delta_test *self = get_self(ctx);
325    struct stat statp;
326    if (!self) {
327       return bRC_Error;
328    }
329 
330    io->status = 0;
331    io->io_errno = 0;
332    switch(io->func) {
333    case IO_OPEN:
334       Dmsg(ctx, debuglevel, "delta-test-fd: IO_OPEN\n");
335       if (io->flags & (O_CREAT | O_WRONLY)) {
336          /* TODO: if the file already exists, the result is undefined */
337          if (stat(io->fname, &statp) == 0) { /* file exists */
338             self->fd = fopen(io->fname, "r+");
339          } else {
340             self->fd = fopen(io->fname, "w"); /* file doesn't exist,create it */
341          }
342          if (!self->fd) {
343             io->io_errno = errno;
344             Jmsg(ctx, M_FATAL,
345                  "Open failed: ERR=%s\n", strerror(errno));
346             return bRC_Error;
347          }
348 
349       } else {
350          self->fd = fopen(self->fname, "r");
351          if (!self->fd) {
352             io->io_errno = errno;
353             Jmsg(ctx, M_FATAL,
354                "Open failed: ERR=%s\n", strerror(errno));
355             return bRC_Error;
356          }
357       }
358       break;
359 
360    case IO_READ:
361       if (!self->fd) {
362          Jmsg(ctx, M_FATAL, "Logic error: NULL read FD\n");
363          return bRC_Error;
364       }
365       if (self->done) {
366          io->status = 0;
367       } else {
368          /* first time, read 300, then replace 50-250 by other data */
369          if (self->delta == 0) {
370             io->status = fread(io->buf, 1, 400, self->fd);
371          } else {
372             io->offset = self->delta * 100 / 2; /* chunks are melted */
373             io->status = fread(io->buf, 1, 100, self->fd);
374          }
375          Dmsg(ctx, debuglevel, "delta-test-fd: READ offset=%lld\n", (int64_t)io->offset);
376          self->done = true;
377       }
378       if (io->status == 0 && ferror(self->fd)) {
379          Jmsg(ctx, M_FATAL,
380             "Pipe read error: ERR=%s\n", strerror(errno));
381          Dmsg(ctx, debuglevel,
382             "Pipe read error: ERR=%s\n", strerror(errno));
383          return bRC_Error;
384       }
385       Dmsg(ctx, debuglevel, "offset=%d\n", io->offset);
386       break;
387 
388    case IO_WRITE:
389       if (!self->fd) {
390          Jmsg(ctx, M_FATAL, "Logic error: NULL write FD\n");
391          return bRC_Error;
392       }
393       Dmsg(ctx, debuglevel, "delta-test-fd: WRITE count=%lld\n", (int64_t)io->count);
394       io->status = fwrite(io->buf, 1, io->count, self->fd);
395       if (io->status == 0 && ferror(self->fd)) {
396          Jmsg(ctx, M_FATAL,
397             "Pipe write error\n");
398          Dmsg(ctx, debuglevel,
399             "Pipe read error: ERR=%s\n", strerror(errno));
400          return bRC_Error;
401       }
402       break;
403 
404    case IO_CLOSE:
405       if (!self->fd) {
406          Jmsg(ctx, M_FATAL, "Logic error: NULL FD on delta close\n");
407          return bRC_Error;
408       }
409       io->status = fclose(self->fd);
410       break;
411 
412    case IO_SEEK:
413       if (!self->fd) {
414          Jmsg(ctx, M_FATAL, "Logic error: NULL FD on delta close\n");
415          return bRC_Error;
416       }
417       Dmsg(ctx, debuglevel, "delta-test-fd: SEEK offset=%lld\n", (int64_t)io->offset);
418       io->status = fseek(self->fd, io->offset, io->whence);
419       Dmsg(ctx, debuglevel, "after SEEK=%lld\n", (int64_t)ftell(self->fd));
420       break;
421    }
422    return bRC_OK;
423 }
424 
425 /**
426  * Bareos is notifying us that a plugin name string was found, and
427  *   passing us the plugin command, so we can prepare for a restore.
428  */
startRestoreFile(bpContext * ctx,const char * cmd)429 static bRC startRestoreFile(bpContext *ctx, const char *cmd)
430 {
431 // Dmsg(ctx, debuglevel, "delta-test-fd: startRestoreFile cmd=%s\n", cmd);
432    return bRC_OK;
433 }
434 
435 /**
436  * Bareos is notifying us that the plugin data has terminated, so
437  *  the restore for this particular file is done.
438  */
endRestoreFile(bpContext * ctx)439 static bRC endRestoreFile(bpContext *ctx)
440 {
441 // Dmsg(ctx, debuglevel, "delta-test-fd: endRestoreFile\n");
442    return bRC_OK;
443 }
444 
445 /**
446  * This is called during restore to create the file (if necessary)
447  * We must return in rp->create_status:
448  *
449  *  CF_ERROR    -- error
450  *  CF_SKIP     -- skip processing this file
451  *  CF_EXTRACT  -- extract the file (i.e.call i/o routines)
452  *  CF_CREATED  -- created, but no content to extract (typically directories)
453  *
454  */
createFile(bpContext * ctx,struct restore_pkt * rp)455 static bRC createFile(bpContext *ctx, struct restore_pkt *rp)
456 {
457    delta_test *self = get_self(ctx);
458    PmStrcpy(self->fname, rp->ofname);
459    rp->create_status = CF_EXTRACT;
460    return bRC_OK;
461 }
462 
setFileAttributes(bpContext * ctx,struct restore_pkt * rp)463 static bRC setFileAttributes(bpContext *ctx, struct restore_pkt *rp)
464 {
465 // Dmsg(ctx, debuglevel, "delta-test-fd: setFileAttributes\n");
466    return bRC_OK;
467 }
468 } /* namespace filedaemon */
469