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