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 pipe plugin for the Bacula File Daemon
21  *
22  *  Kern Sibbald, October 2007
23  *
24  */
25 #include "bacula.h"
26 #define USE_FULL_WRITE
27 #include "fd_common.h"
28 #include "fd_plugins.h"
29 #include "lib/ini.h"
30 
31 #undef malloc
32 #undef free
33 #undef strdup
34 
35 #define fi __FILE__
36 #define li __LINE__
37 
38 #ifdef __cplusplus
39 extern "C" {
40 #endif
41 
42 static const int dbglvl = 150;
43 
44 #define PLUGIN_LICENSE      "AGPLv3"
45 #define PLUGIN_AUTHOR       "Kern Sibbald"
46 #define PLUGIN_DATE         "January 2008"
47 #define PLUGIN_VERSION      "1"
48 #define PLUGIN_DESCRIPTION  "Bacula Pipe File Daemon Plugin"
49 
50 /* Forward referenced functions */
51 static bRC newPlugin(bpContext *ctx);
52 static bRC freePlugin(bpContext *ctx);
53 static bRC getPluginValue(bpContext *ctx, pVariable var, void *value);
54 static bRC setPluginValue(bpContext *ctx, pVariable var, void *value);
55 static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value);
56 static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp);
57 static bRC endBackupFile(bpContext *ctx);
58 static bRC pluginIO(bpContext *ctx, struct io_pkt *io);
59 static bRC startRestoreFile(bpContext *ctx, const char *cmd);
60 static bRC endRestoreFile(bpContext *ctx);
61 static bRC createFile(bpContext *ctx, struct restore_pkt *rp);
62 static bRC setFileAttributes(bpContext *ctx, struct restore_pkt *rp);
63 static bRC checkFile(bpContext *ctx, char *fname);
64 static bRC handleXACLdata(bpContext *ctx, struct xacl_pkt *xacl);
65 
66 static char *apply_rp_codes(struct plugin_ctx * p_ctx);
67 
68 /* Pointers to Bacula functions */
69 static bFuncs *bfuncs = NULL;
70 static bInfo  *binfo = NULL;
71 
72 /* Plugin Information block */
73 static pInfo pluginInfo = {
74    sizeof(pluginInfo),
75    FD_PLUGIN_INTERFACE_VERSION,
76    FD_PLUGIN_MAGIC,
77    PLUGIN_LICENSE,
78    PLUGIN_AUTHOR,
79    PLUGIN_DATE,
80    PLUGIN_VERSION,
81    PLUGIN_DESCRIPTION
82 };
83 
84 /* Plugin entry points for Bacula */
85 static pFuncs pluginFuncs = {
86    sizeof(pluginFuncs),
87    FD_PLUGIN_INTERFACE_VERSION,
88 
89    /* Entry points into plugin */
90    newPlugin,                         /* new plugin instance */
91    freePlugin,                        /* free plugin instance */
92    getPluginValue,
93    setPluginValue,
94    handlePluginEvent,
95    startBackupFile,
96    endBackupFile,
97    startRestoreFile,
98    endRestoreFile,
99    pluginIO,
100    createFile,
101    setFileAttributes,
102    checkFile,
103    handleXACLdata,
104    NULL                          /* No checkStream */
105 };
106 
107 /*
108  * Plugin private context
109  */
110 struct plugin_ctx {
111    boffset_t offset;
112    BPIPE *pfd;                        /* bpipe file descriptor */
113    int  efd;                          /* stderr */
114    int  rfd;                          /* stdout */
115    int  wfd;                          /* stdin */
116    int  maxfd;                        /* max(stderr, stdout) */
117    bool backup;                       /* set when the backup is done */
118    bool canceled;
119    char *cmd;                         /* plugin command line */
120    char *fname;                       /* filename to "backup/restore" */
121    char *reader;                      /* reader program for backup */
122    char *writer;                      /* writer program for backup */
123    char where[512];
124    int replace;
125    int job_level;
126    int estimate_mode;
127    int64_t total_bytes;         /* number of bytes read/write */
128 };
129 
130 /*
131  * loadPlugin() and unloadPlugin() are entry points that are
132  *  exported, so Bacula can directly call these two entry points
133  *  they are common to all Bacula plugins.
134  */
135 /*
136  * External entry point called by Bacula to "load the plugin
137  */
loadPlugin(bInfo * lbinfo,bFuncs * lbfuncs,pInfo ** pinfo,pFuncs ** pfuncs)138 bRC loadPlugin(bInfo *lbinfo, bFuncs *lbfuncs, pInfo **pinfo, pFuncs **pfuncs)
139 {
140    bfuncs = lbfuncs;                  /* set Bacula funct pointers */
141    binfo  = lbinfo;
142    *pinfo  = &pluginInfo;             /* return pointer to our info */
143    *pfuncs = &pluginFuncs;            /* return pointer to our functions */
144 
145    return bRC_OK;
146 }
147 
148 /*
149  * External entry point to unload the plugin
150  */
unloadPlugin()151 bRC unloadPlugin()
152 {
153 // printf("bpipe-fd: Unloaded\n");
154    return bRC_OK;
155 }
156 
157 /*
158  * The following entry points are accessed through the function
159  *   pointers we supplied to Bacula. Each plugin type (dir, fd, sd)
160  *   has its own set of entry points that the plugin must define.
161  */
162 /*
163  * Create a new instance of the plugin i.e. allocate our private storage
164  */
newPlugin(bpContext * ctx)165 static bRC newPlugin(bpContext *ctx)
166 {
167    struct plugin_ctx *p_ctx = (struct plugin_ctx *)malloc(sizeof(struct plugin_ctx));
168    if (!p_ctx) {
169       return bRC_Error;
170    }
171    memset(p_ctx, 0, sizeof(struct plugin_ctx));
172    ctx->pContext = (void *)p_ctx;        /* set our context pointer */
173    return bRC_OK;
174 }
175 
176 /*
177  * Free a plugin instance, i.e. release our private storage
178  */
freePlugin(bpContext * ctx)179 static bRC freePlugin(bpContext *ctx)
180 {
181    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
182    if (!p_ctx) {
183       return bRC_Error;
184    }
185    if (p_ctx->cmd) {
186       free(p_ctx->cmd);                  /* free any allocated command string */
187    }
188    free(p_ctx);                          /* free our private context */
189    p_ctx = NULL;
190    return bRC_OK;
191 }
192 
193 /*
194  * Return some plugin value (none defined)
195  */
getPluginValue(bpContext * ctx,pVariable var,void * value)196 static bRC getPluginValue(bpContext *ctx, pVariable var, void *value)
197 {
198    return bRC_OK;
199 }
200 
201 /*
202  * Set a plugin value (none defined)
203  */
setPluginValue(bpContext * ctx,pVariable var,void * value)204 static bRC setPluginValue(bpContext *ctx, pVariable var, void *value)
205 {
206    return bRC_OK;
207 }
208 
209 /*
210  * Handle an event that was generated in Bacula
211  */
handlePluginEvent(bpContext * ctx,bEvent * event,void * value)212 static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value)
213 {
214    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
215 
216    if (!p_ctx) {
217       return bRC_Error;
218    }
219 
220 // char *name;
221 
222    /*
223     * Most events don't interest us so we ignore them.
224     *   the printfs are so that plugin writers can enable them to see
225     *   what is really going on.
226     */
227    switch (event->eventType) {
228    case bEventLevel:
229       p_ctx->job_level = ((intptr_t)value);
230       break;
231 
232    case bEventCancelCommand:
233       p_ctx->canceled = true;
234       break;
235 
236    case bEventPluginCommand:
237       bfuncs->DebugMessage(ctx, fi, li, dbglvl,
238                            "bpipe-fd: PluginCommand=%s\n", (char *)value);
239       break;
240    case bEventJobStart:
241       bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: JobStart=%s\n", (char *)value);
242       break;
243    case bEventJobEnd:
244 //    printf("bpipe-fd: JobEnd\n");
245       break;
246    case bEventStartBackupJob:
247 //    printf("bpipe-fd: StartBackupJob\n");
248       break;
249    case bEventEndBackupJob:
250 //    printf("bpipe-fd: EndBackupJob\n");
251       break;
252    case bEventSince:
253 //    printf("bpipe-fd: since=%d\n", (int)value);
254       break;
255    case bEventStartRestoreJob:
256 //    printf("bpipe-fd: StartRestoreJob\n");
257       break;
258 
259    case bEventEndRestoreJob:
260 //    printf("bpipe-fd: EndRestoreJob\n");
261       break;
262 
263    /* Plugin command e.g. plugin = <plugin-name>:<name-space>:read command:write command */
264    case bEventEstimateCommand:
265       p_ctx->estimate_mode = true;
266       /* Fall-through wanted */
267    case bEventRestoreCommand:
268 //    printf("bpipe-fd: EventRestoreCommand cmd=%s\n", (char *)value);
269       /* Fall-through wanted */
270    case bEventBackupCommand:
271       char *p;
272       bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: pluginEvent cmd=%s\n", (char *)value);
273       p_ctx->backup = false;
274       p_ctx->cmd = strdup((char *)value);
275       p = strchr(p_ctx->cmd, ':');
276       if (!p) {
277          bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Plugin terminator not found: %s\n", (char *)value);
278          return bRC_Error;
279       }
280       *p++ = 0;           /* terminate plugin */
281       p_ctx->fname = p;
282       p = strchr(p, ':');
283       if (!p) {
284          bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "File terminator not found: %s\n", (char *)value);
285          return bRC_Error;
286       }
287       *p++ = 0;           /* terminate file */
288       p_ctx->reader = p;
289       p = strchr(p, ':');
290       if (!p) {
291          bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Reader terminator not found: %s\n", (char *)value);
292          return bRC_Error;
293       }
294       *p++ = 0;           /* terminate reader string */
295       p_ctx->writer = p;
296 
297 //    printf("bpipe-fd: plugin=%s fname=%s reader=%s writer=%s\n",
298 //         p_ctx->cmd, p_ctx->fname, p_ctx->reader, p_ctx->writer);
299       break;
300 
301    default:
302 //    printf("bpipe-fd: unknown event=%d\n", event->eventType);
303       break;
304    }
305    return bRC_OK;
306 }
307 
308 
309 /*
310  * Start the backup of a specific file
311  */
startBackupFile(bpContext * ctx,struct save_pkt * sp)312 static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp)
313 {
314    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
315    if (!p_ctx) {
316       return bRC_Error;
317    }
318 
319    time_t now = time(NULL);
320    sp->fname = p_ctx->fname;
321    sp->type = FT_REG;
322    sp->statp.st_mode = 0700 | S_IFREG;
323    sp->statp.st_ctime = now;
324    sp->statp.st_mtime = now;
325    sp->statp.st_atime = now;
326    sp->statp.st_size = -1;
327    sp->statp.st_blksize = 4096;
328    sp->statp.st_blocks = 1;
329    p_ctx->backup = true;
330 // printf("bpipe-fd: startBackupFile\n");
331    return bRC_OK;
332 }
333 
334 /*
335  * Done with backup of this file
336  */
endBackupFile(bpContext * ctx)337 static bRC endBackupFile(bpContext *ctx)
338 {
339    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
340    if (!p_ctx) {
341       return bRC_Error;
342    }
343 
344    /*
345     * We would return bRC_More if we wanted startBackupFile to be
346     * called again to backup another file
347     */
348    if (!p_ctx->backup) {
349       return bRC_More;
350    }
351    return bRC_OK;
352 }
353 
send_log(bpContext * ctx,char * buf)354 static void send_log(bpContext *ctx, char *buf)
355 {
356    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
357    strip_trailing_newline(buf);
358    bfuncs->JobMessage(ctx, fi, li, M_INFO, 0, "%s: %s\n", p_ctx->fname, buf);
359 }
360 
361 /*
362  * Bacula is calling us to do the actual I/O
363  */
pluginIO(bpContext * ctx,struct io_pkt * io)364 static bRC pluginIO(bpContext *ctx, struct io_pkt *io)
365 {
366    fd_set rfds;
367    fd_set wfds;
368    bool ok=false;
369    char buf[1024];
370    struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext;
371    if (!p_ctx) {
372       return bRC_Error;
373    }
374 
375    io->status = -1;
376    io->io_errno = 0;
377    switch(io->func) {
378    case IO_OPEN:
379       p_ctx->total_bytes = 0;
380       p_ctx->wfd = p_ctx->efd = p_ctx->rfd = -1;
381       bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: IO_OPEN\n");
382       if (io->flags & (O_CREAT | O_WRONLY)) {
383          char *writer_codes = apply_rp_codes(p_ctx);
384 
385          p_ctx->pfd = open_bpipe(writer_codes, 0, "rws");
386          bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: IO_OPEN fd=%p writer=%s\n",
387              p_ctx->pfd, writer_codes);
388          if (!p_ctx->pfd) {
389             io->io_errno = errno;
390             bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0,
391                "Open pipe writer=%s failed: ERR=%s\n", writer_codes, strerror(errno));
392             if (writer_codes) {
393                free(writer_codes);
394             }
395             return bRC_Error;
396          }
397          if (writer_codes) {
398             free(writer_codes);
399          }
400          /* We need to read from stdout/stderr for messages to display to the user */
401          p_ctx->rfd = fileno(p_ctx->pfd->rfd);
402          p_ctx->wfd = fileno(p_ctx->pfd->wfd);
403          p_ctx->maxfd = MAX(p_ctx->wfd, p_ctx->rfd);
404          io->status = p_ctx->wfd;
405 
406       } else {
407          /* Use shell mode and split stderr/stdout */
408          p_ctx->pfd = open_bpipe(p_ctx->reader, 0, "rse");
409          bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: IO_OPEN fd=%p reader=%s\n",
410             p_ctx->pfd, p_ctx->reader);
411          if (!p_ctx->pfd) {
412             io->io_errno = errno;
413             bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0,
414                "Open pipe reader=%s failed: ERR=%s\n", p_ctx->reader, strerror(errno));
415             return bRC_Error;
416          }
417          /* We need to read from stderr for job log and stdout for the data */
418          p_ctx->efd = fileno(p_ctx->pfd->efd);
419          p_ctx->rfd = fileno(p_ctx->pfd->rfd);
420          p_ctx->maxfd = MAX(p_ctx->efd, p_ctx->rfd);
421          io->status = p_ctx->rfd;
422       }
423       sleep(1);                 /* let pipe connect */
424       break;
425 
426    case IO_READ:
427       if (!p_ctx->pfd) {
428          bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Logic error: NULL read FD\n");
429          return bRC_Error;
430       }
431 
432       /* We first try to read stderr, but keep monitoring for data on stdout (when stderr is empty) */
433       while (!p_ctx->canceled) {
434          FD_ZERO(&rfds);
435          FD_SET(p_ctx->rfd, &rfds);
436          FD_SET(p_ctx->efd, &rfds);
437          select(p_ctx->maxfd+1, &rfds, NULL, NULL, NULL);
438 
439          if (!FD_ISSET(p_ctx->efd, &rfds)) {
440             /* nothing in stderr, then we should have something in stdout */
441             break;
442          }
443          int ret = read(p_ctx->efd, buf, sizeof(buf));
444          if (ret <= 0) {
445             /* stderr is closed or in error, stdout should be in the same state */
446             /* let handle it at the stdout level */
447             break;
448          }
449          /* TODO: buffer and split lines */
450          buf[ret]=0;
451          send_log(ctx, buf);
452       }
453 
454       io->status = read(p_ctx->rfd, io->buf, io->count);
455 //    bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: IO_READ buf=%p len=%d\n", io->buf, io->status);
456       if (io->status < 0) {
457          berrno be;
458          bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0,
459                             "Pipe read error: ERR=%s\n", be.bstrerror());
460          bfuncs->DebugMessage(ctx, fi, li, dbglvl,
461                               "Pipe read error: count=%lld errno=%d ERR=%s\n",
462                               p_ctx->total_bytes, (int)errno, be.bstrerror());
463          return bRC_Error;
464       }
465       p_ctx->total_bytes += io->status;
466       break;
467 
468    case IO_WRITE:
469       if (!p_ctx->pfd) {
470          bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Logic error: NULL write FD\n");
471          return bRC_Error;
472       }
473 
474       /* When we write, we must check for the error channel (stdout+stderr) as well */
475       while (!ok && !p_ctx->canceled) {
476          FD_ZERO(&wfds);
477          FD_SET(p_ctx->wfd, &wfds);
478          FD_ZERO(&rfds);
479          FD_SET(p_ctx->rfd, &rfds);
480 
481          select(p_ctx->maxfd+1, &rfds, &wfds, NULL, NULL);
482 
483          if (FD_ISSET(p_ctx->rfd, &rfds)) {
484             int ret = read(p_ctx->rfd, buf, sizeof(buf)); /* TODO: simulate fgets() */
485             if (ret > 0) {
486                buf[ret]=0;
487                send_log(ctx, buf);
488             } else {
489                ok = true;       /* nothing to read */
490             }
491          }
492 
493          if (FD_ISSET(p_ctx->wfd, &wfds)) {
494             ok = true;
495          }
496       }
497 
498 //    printf("bpipe-fd: IO_WRITE fd=%p buf=%p len=%d\n", p_ctx->fd, io->buf, io->count);
499       io->status = full_write(p_ctx->wfd, io->buf, io->count, &p_ctx->canceled);
500 //    printf("bpipe-fd: IO_WRITE buf=%p len=%d\n", io->buf, io->status);
501       if (io->status <= 0) {
502          berrno be;
503          bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0,
504                             "Pipe write error: ERR=%s\n", be.bstrerror());
505          bfuncs->DebugMessage(ctx, fi, li, dbglvl,
506                               "Pipe write error: count=%lld errno=%d ERR=%s\n",
507                               p_ctx->total_bytes, (int)errno, be.bstrerror());
508          return bRC_Error;
509       }
510       p_ctx->total_bytes += io->status;
511       break;
512 
513    case IO_CLOSE:
514       if (!p_ctx->pfd) {
515          bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Logic error: NULL FD on bpipe close\n");
516          return bRC_Error;
517       }
518 
519       /* We inform the other side that we have nothing more to send */
520       if (p_ctx->wfd >= 0) {
521          int ret = close_wpipe(p_ctx->pfd);
522          if (ret == 0) {
523             bfuncs->JobMessage(ctx, fi, li, M_ERROR, 0, "bpipe-fd: Error closing for file %s: %d\n",
524                                p_ctx->fname, ret);
525          }
526       }
527 
528       /* We flush what the other program has to say */
529       while (!ok && !p_ctx->canceled) {
530          struct timeval tv = {10, 0};   // sleep for 10secs
531          FD_ZERO(&rfds);
532          p_ctx->maxfd = -1;
533 
534          if (p_ctx->rfd >= 0) {
535             FD_SET(p_ctx->rfd, &rfds);
536             p_ctx->maxfd = MAX(p_ctx->maxfd, p_ctx->rfd);
537          }
538 
539          if (p_ctx->efd >= 0) {
540             FD_SET(p_ctx->efd, &rfds);
541             p_ctx->maxfd = MAX(p_ctx->maxfd, p_ctx->efd);
542          }
543 
544          if (p_ctx->maxfd == -1) {
545             ok = true;          /* exit the loop */
546          } else {
547             select(p_ctx->maxfd+1, &rfds, NULL, NULL, &tv);
548          }
549 
550          if (p_ctx->rfd >= 0 && FD_ISSET(p_ctx->rfd, &rfds)) {
551             int ret = read(p_ctx->rfd, buf, sizeof(buf));
552             if (ret > 0) {
553                buf[ret]=0;
554                send_log(ctx, buf);
555             } else {
556                p_ctx->rfd = -1; /* closed, keep the reference in bpipe */
557             }
558          }
559 
560          /* The stderr can be melted with stdout or not */
561          if (p_ctx->efd >= 0 && FD_ISSET(p_ctx->efd, &rfds)) {
562             int ret = read(p_ctx->efd, buf, sizeof(buf));
563             if (ret > 0) {
564                buf[ret]=0;
565                send_log(ctx, buf);
566             } else {
567                p_ctx->efd = -1; /* closed, keep the reference in bpipe */
568             }
569          }
570       }
571 
572       io->status = close_bpipe(p_ctx->pfd);
573       if (io->status != 0) {
574          bfuncs->JobMessage(ctx, fi, li, M_ERROR, 0, "bpipe-fd: Error closing for file %s: %d\n",
575                             p_ctx->fname, io->status);
576       }
577       break;
578 
579    case IO_SEEK:
580       io->offset = p_ctx->offset;
581       io->status = 0;
582       break;
583    }
584    return bRC_OK;
585 }
586 
587 /*
588  * Bacula is notifying us that a plugin name string was found, and
589  *   passing us the plugin command, so we can prepare for a restore.
590  */
startRestoreFile(bpContext * ctx,const char * cmd)591 static bRC startRestoreFile(bpContext *ctx, const char *cmd)
592 {
593 // printf("bpipe-fd: startRestoreFile cmd=%s\n", cmd);
594    return bRC_OK;
595 }
596 
597 /*
598  * Bacula is notifying us that the plugin data has terminated, so
599  *  the restore for this particular file is done.
600  */
endRestoreFile(bpContext * ctx)601 static bRC endRestoreFile(bpContext *ctx)
602 {
603 // printf("bpipe-fd: endRestoreFile\n");
604    return bRC_OK;
605 }
606 
607 /*
608  * This is called during restore to create the file (if necessary)
609  * We must return in rp->create_status:
610  *
611  *  CF_ERROR    -- error
612  *  CF_SKIP     -- skip processing this file
613  *  CF_EXTRACT  -- extract the file (i.e.call i/o routines)
614  *  CF_CREATED  -- created, but no content to extract (typically directories)
615  *
616  */
createFile(bpContext * ctx,struct restore_pkt * rp)617 static bRC createFile(bpContext *ctx, struct restore_pkt *rp)
618 {
619 // printf("bpipe-fd: createFile\n");
620    if (strlen(rp->where) > 512) {
621       printf("Restore target dir too long. Restricting to first 512 bytes.\n");
622    }
623    bstrncpy(((struct plugin_ctx *)ctx->pContext)->where, rp->where, 512);
624    ((struct plugin_ctx *)ctx->pContext)->replace = rp->replace;
625    rp->create_status = CF_EXTRACT;
626    return bRC_OK;
627 }
628 
629 /*
630  * We will get here if the File is a directory after everything
631  * is written in the directory.
632  */
setFileAttributes(bpContext * ctx,struct restore_pkt * rp)633 static bRC setFileAttributes(bpContext *ctx, struct restore_pkt *rp)
634 {
635 // printf("bpipe-fd: setFileAttributes\n");
636    return bRC_OK;
637 }
638 
639 /* When using Incremental dump, all previous dumps are necessary */
checkFile(bpContext * ctx,char * fname)640 static bRC checkFile(bpContext *ctx, char *fname)
641 {
642    return bRC_OK;
643 }
644 
645 /*
646  * New Bacula Plugin API require this
647  */
handleXACLdata(bpContext * ctx,struct xacl_pkt * xacl)648 static bRC handleXACLdata(bpContext *ctx, struct xacl_pkt *xacl)
649 {
650    return bRC_OK;
651 }
652 
653 /*************************************************************************
654  * Apply codes in writer command:
655  * %w -> "where"
656  * %r -> "replace"
657  *
658  * Replace:
659  * 'always' => 'a', chr(97)
660  * 'ifnewer' => 'w', chr(119)
661  * 'ifolder' => 'o', chr(111)
662  * 'never' => 'n', chr(110)
663  *
664  * This function will allocate the required amount of memory with malloc.
665  * Need to be free()d manually.
666  * Inspired by edit_job_codes in lib/util.c
667  */
668 
apply_rp_codes(struct plugin_ctx * p_ctx)669 static char *apply_rp_codes(struct plugin_ctx * p_ctx)
670 {
671    char *p, *q;
672    const char *str;
673    char add[10];
674    int w_count = 0, r_count = 0;
675    char *omsg;
676 
677    char *imsg = p_ctx->writer;
678 
679    if (!imsg) {
680       return NULL;
681    }
682 
683    if ((p = imsg)) {
684       while ((q = strstr(p, "%w"))) {
685          w_count++;
686          p=q+1;
687       }
688 
689       p = imsg;
690       while ((q = strstr(p, "%r"))) {
691          r_count++;
692          p=q+1;
693       }
694    }
695 
696    /* Required mem:
697     * len(imsg)
698     * + number of "where" codes * (len(where)-2)
699     * - number of "replace" codes
700     */
701    omsg = (char*)malloc(strlen(imsg) + (w_count * (strlen(p_ctx->where)-2)) - r_count + 1);
702    if (!omsg) {
703       fprintf(stderr, "Out of memory.");
704       return NULL;
705    }
706 
707    *omsg = 0;
708    //printf("apply_rp_codes: %s\n", imsg);
709    for (p=imsg; *p; p++) {
710       if (*p == '%') {
711          switch (*++p) {
712          case '%':
713             str = "%";
714             break;
715          case 'w':
716              str = p_ctx->where;
717              break;
718          case 'r':
719             snprintf(add, 2, "%c", p_ctx->replace);
720             str = add;
721             break;
722          default:
723             add[0] = '%';
724             add[1] = *p;
725             add[2] = 0;
726             str = add;
727             break;
728          }
729       } else {
730          add[0] = *p;
731          add[1] = 0;
732          str = add;
733       }
734       //printf("add_str %s\n", str);
735       strcat(omsg, str);
736       //printf("omsg=%s\n", omsg);
737    }
738    return omsg;
739 }
740 
741 #ifdef __cplusplus
742 }
743 #endif
744