1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2012 Planets Communications B.V.
6    Copyright (C) 2013-2020 Bareos GmbH & Co. KG
7 
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    Affero General Public License for more details.
17 
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22 */
23 /*
24  * Kern Sibbald, March MMIII
25  */
26 /*
27  * @file
28  * Configuration file parser for new and old Include and
29  * Exclude records
30  */
31 
32 #include "include/bareos.h"
33 #include "dird.h"
34 #include "dird/dird_globals.h"
35 #include "dird/jcr_private.h"
36 #include "findlib/match.h"
37 #include "lib/parse_conf.h"
38 
39 #ifndef HAVE_REGEX_H
40 #  include "lib/bregex.h"
41 #else
42 #  include <regex.h>
43 #endif
44 #include "findlib/find.h"
45 
46 #include "inc_conf.h"
47 #include "lib/edit.h"
48 
49 #include <cassert>
50 
51 namespace directordaemon {
52 
53 #define PERMITTED_VERIFY_OPTIONS (const char*)"ipnugsamcd51"
54 #define PERMITTED_ACCURATE_OPTIONS (const char*)"ipnugsamcd51A"
55 #define PERMITTED_BASEJOB_OPTIONS (const char*)"ipnugsamcd51"
56 
57 typedef struct {
58   bool configured;
59   std::string default_value;
60 } options_default_value_s;
61 
62 
63 /*
64  * Define FileSet KeyWord values
65  */
66 enum
67 {
68   INC_KW_NONE,
69   INC_KW_COMPRESSION,
70   INC_KW_DIGEST,
71   INC_KW_ENCRYPTION,
72   INC_KW_VERIFY,
73   INC_KW_BASEJOB,
74   INC_KW_ACCURATE,
75   INC_KW_ONEFS,
76   INC_KW_RECURSE,
77   INC_KW_SPARSE,
78   INC_KW_HARDLINK,
79   INC_KW_REPLACE,  /* restore options */
80   INC_KW_READFIFO, /* Causes fifo data to be read */
81   INC_KW_PORTABLE,
82   INC_KW_MTIMEONLY,
83   INC_KW_KEEPATIME,
84   INC_KW_EXCLUDE,
85   INC_KW_ACL,
86   INC_KW_IGNORECASE,
87   INC_KW_HFSPLUS,
88   INC_KW_NOATIME,
89   INC_KW_ENHANCEDWILD,
90   INC_KW_CHKCHANGES,
91   INC_KW_STRIPPATH,
92   INC_KW_HONOR_NODUMP,
93   INC_KW_XATTR,
94   INC_KW_SIZE,
95   INC_KW_SHADOWING,
96   INC_KW_AUTO_EXCLUDE,
97   INC_KW_FORCE_ENCRYPTION
98 };
99 
100 /*
101  * This is the list of options that can be stored by store_opts
102  * Note, now that the old style Include/Exclude code is gone,
103  * the INC_KW code could be put into the "code" field of the
104  * options given above.
105  */
106 static struct s_kw FS_option_kw[]
107     = {{"compression", INC_KW_COMPRESSION},
108        {"signature", INC_KW_DIGEST},
109        {"encryption", INC_KW_ENCRYPTION},
110        {"verify", INC_KW_VERIFY},
111        {"basejob", INC_KW_BASEJOB},
112        {"accurate", INC_KW_ACCURATE},
113        {"onefs", INC_KW_ONEFS},
114        {"recurse", INC_KW_RECURSE},
115        {"sparse", INC_KW_SPARSE},
116        {"hardlinks", INC_KW_HARDLINK},
117        {"replace", INC_KW_REPLACE},
118        {"readfifo", INC_KW_READFIFO},
119        {"portable", INC_KW_PORTABLE},
120        {"mtimeonly", INC_KW_MTIMEONLY},
121        {"keepatime", INC_KW_KEEPATIME},
122        {"exclude", INC_KW_EXCLUDE},
123        {"aclsupport", INC_KW_ACL},
124        {"ignorecase", INC_KW_IGNORECASE},
125        {"hfsplussupport", INC_KW_HFSPLUS},
126        {"noatime", INC_KW_NOATIME},
127        {"enhancedwild", INC_KW_ENHANCEDWILD},
128        {"checkfilechanges", INC_KW_CHKCHANGES},
129        {"strippath", INC_KW_STRIPPATH},
130        {"honornodumpflag", INC_KW_HONOR_NODUMP},
131        {"xattrsupport", INC_KW_XATTR},
132        {"size", INC_KW_SIZE},
133        {"shadowing", INC_KW_SHADOWING},
134        {"autoexclude", INC_KW_AUTO_EXCLUDE},
135        {"forceencryption", INC_KW_FORCE_ENCRYPTION},
136        {NULL, 0}};
137 
138 /*
139  * Options for FileSet keywords
140  */
141 struct s_fs_opt {
142   const char* name;
143   int keyword;
144   const char* option;
145 };
146 
147 /*
148  * Options permitted for each keyword and resulting value.
149  * The output goes into opts, which are then transmitted to
150  * the FD for application as options to the following list of
151  * included files.
152  */
153 static struct s_fs_opt FS_options[]
154     = {{"md5", INC_KW_DIGEST, "M"},
155        {"sha1", INC_KW_DIGEST, "S"},
156        {"sha256", INC_KW_DIGEST, "S2"},
157        {"sha512", INC_KW_DIGEST, "S3"},
158        {"gzip", INC_KW_COMPRESSION, "Z6"},
159        {"gzip1", INC_KW_COMPRESSION, "Z1"},
160        {"gzip2", INC_KW_COMPRESSION, "Z2"},
161        {"gzip3", INC_KW_COMPRESSION, "Z3"},
162        {"gzip4", INC_KW_COMPRESSION, "Z4"},
163        {"gzip5", INC_KW_COMPRESSION, "Z5"},
164        {"gzip6", INC_KW_COMPRESSION, "Z6"},
165        {"gzip7", INC_KW_COMPRESSION, "Z7"},
166        {"gzip8", INC_KW_COMPRESSION, "Z8"},
167        {"gzip9", INC_KW_COMPRESSION, "Z9"},
168        {"lzo", INC_KW_COMPRESSION, "Zo"},
169        {"lzfast", INC_KW_COMPRESSION, "Zff"},
170        {"lz4", INC_KW_COMPRESSION, "Zf4"},
171        {"lz4hc", INC_KW_COMPRESSION, "Zfh"},
172        {"blowfish", INC_KW_ENCRYPTION, "Eb"},
173        {"3des", INC_KW_ENCRYPTION, "E3"},
174        {"aes128", INC_KW_ENCRYPTION, "Ea1"},
175        {"aes192", INC_KW_ENCRYPTION, "Ea2"},
176        {"aes256", INC_KW_ENCRYPTION, "Ea3"},
177        {"camellia128", INC_KW_ENCRYPTION, "Ec1"},
178        {"camellia192", INC_KW_ENCRYPTION, "Ec2"},
179        {"camellia256", INC_KW_ENCRYPTION, "Ec3"},
180        {"aes128hmacsha1", INC_KW_ENCRYPTION, "Eh1"},
181        {"aes256hmacsha1", INC_KW_ENCRYPTION, "Eh2"},
182        {"yes", INC_KW_ONEFS, "0"},
183        {"no", INC_KW_ONEFS, "f"},
184        {"yes", INC_KW_RECURSE, "0"},
185        {"no", INC_KW_RECURSE, "h"},
186        {"yes", INC_KW_SPARSE, "s"},
187        {"no", INC_KW_SPARSE, "0"},
188        {"yes", INC_KW_HARDLINK, "0"},
189        {"no", INC_KW_HARDLINK, "H"},
190        {"always", INC_KW_REPLACE, "a"},
191        {"ifnewer", INC_KW_REPLACE, "w"},
192        {"never", INC_KW_REPLACE, "n"},
193        {"yes", INC_KW_READFIFO, "r"},
194        {"no", INC_KW_READFIFO, "0"},
195        {"yes", INC_KW_PORTABLE, "p"},
196        {"no", INC_KW_PORTABLE, "0"},
197        {"yes", INC_KW_MTIMEONLY, "m"},
198        {"no", INC_KW_MTIMEONLY, "0"},
199        {"yes", INC_KW_KEEPATIME, "k"},
200        {"no", INC_KW_KEEPATIME, "0"},
201        {"yes", INC_KW_EXCLUDE, "e"},
202        {"no", INC_KW_EXCLUDE, "0"},
203        {"yes", INC_KW_ACL, "A"},
204        {"no", INC_KW_ACL, "0"},
205        {"yes", INC_KW_IGNORECASE, "i"},
206        {"no", INC_KW_IGNORECASE, "0"},
207        {"yes", INC_KW_HFSPLUS, "R"}, /* "R" for resource fork */
208        {"no", INC_KW_HFSPLUS, "0"},
209        {"yes", INC_KW_NOATIME, "K"},
210        {"no", INC_KW_NOATIME, "0"},
211        {"yes", INC_KW_ENHANCEDWILD, "K"},
212        {"no", INC_KW_ENHANCEDWILD, "0"},
213        {"yes", INC_KW_CHKCHANGES, "c"},
214        {"no", INC_KW_CHKCHANGES, "0"},
215        {"yes", INC_KW_HONOR_NODUMP, "N"},
216        {"no", INC_KW_HONOR_NODUMP, "0"},
217        {"yes", INC_KW_XATTR, "X"},
218        {"no", INC_KW_XATTR, "0"},
219        {"localwarn", INC_KW_SHADOWING, "d1"},
220        {"localremove", INC_KW_SHADOWING, "d2"},
221        {"globalwarn", INC_KW_SHADOWING, "d3"},
222        {"globalremove", INC_KW_SHADOWING, "d4"},
223        {"none", INC_KW_SHADOWING, "0"},
224        {"yes", INC_KW_AUTO_EXCLUDE, "0"},
225        {"no", INC_KW_AUTO_EXCLUDE, "x"},
226        {"yes", INC_KW_FORCE_ENCRYPTION, "Ef"},
227        {"no", INC_KW_FORCE_ENCRYPTION, "0"},
228        {NULL, 0, 0}};
229 
230 /*
231  * Imported subroutines
232  */
233 extern void StoreInc(LEX* lc, ResourceItem* item, int index, int pass);
234 
235 /* We build the current new Include and Exclude items here */
236 static IncludeExcludeItem* res_incexe;
237 
238 /* clang-format off */
239 
240 /*
241  * new Include/Exclude items
242  * name handler value code flags default_value
243  */
244 ResourceItem newinc_items[] = {
245   { "File", CFG_TYPE_FNAME, 0, nullptr, 0, 0, NULL, NULL, NULL },
246   { "Plugin", CFG_TYPE_PLUGINNAME, 0, nullptr, 0, 0, NULL, NULL, NULL },
247   { "ExcludeDirContaining", CFG_TYPE_EXCLUDEDIR,  0, nullptr, 0, 0, NULL, NULL, NULL },
248   { "Options", CFG_TYPE_OPTIONS, 0, nullptr, 0, 0, NULL, NULL, NULL },
249   { NULL, 0, 0, nullptr, 0, 0, NULL, NULL, NULL }
250 };
251 
252 /*
253  * Items that are valid in an Options resource
254  * name handler value code flags default_value
255  */
256 ResourceItem options_items[] = {
257   { "Compression", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
258   { "Signature", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
259   { "BaseJob", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
260   { "Accurate", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
261   { "Verify", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
262   { "OneFs", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
263   { "Recurse", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
264   { "Sparse", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
265   { "HardLinks", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
266   { "ReadFifo", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
267   { "Replace", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
268   { "Portable", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
269   { "MtimeOnly", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
270   { "KeepAtime", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
271   { "Regex", CFG_TYPE_REGEX, 0, nullptr, 0, 0, NULL, NULL, NULL },
272   { "RegexDir", CFG_TYPE_REGEX, 0, nullptr, 1, 0, NULL, NULL, NULL },
273   { "RegexFile", CFG_TYPE_REGEX, 0, nullptr, 2, 0, NULL, NULL, NULL },
274   { "Base", CFG_TYPE_BASE, 0, nullptr, 0, 0, NULL, NULL, NULL },
275   { "Wild", CFG_TYPE_WILD, 0, nullptr, 0, 0, NULL, NULL, NULL },
276   { "WildDir", CFG_TYPE_WILD, 0, nullptr, 1, 0, NULL, NULL, NULL },
277   { "WildFile", CFG_TYPE_WILD, 0, nullptr, 2, 0, NULL, NULL, NULL },
278   { "Exclude", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
279   { "AclSupport", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
280   { "Plugin", CFG_TYPE_PLUGIN, 0, nullptr, 0, 0, NULL, NULL, NULL },
281   { "IgnoreCase", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
282   { "FsType", CFG_TYPE_FSTYPE, 0, nullptr, 0, 0, NULL, NULL, NULL },
283   { "HfsPlusSupport", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
284   { "NoAtime", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
285   { "EnhancedWild", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
286   { "DriveType", CFG_TYPE_DRIVETYPE, 0, nullptr, 0, 0, NULL, NULL, NULL },
287   { "CheckFileChanges", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
288   { "StripPath", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
289   { "HonornoDumpFlag", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
290   { "XAttrSupport", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
291   { "Size", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
292   { "Shadowing", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
293   { "AutoExclude", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
294   { "ForceEncryption", CFG_TYPE_OPTION, 0, nullptr, 0, 0, NULL, NULL, NULL },
295   { "Meta", CFG_TYPE_META, 0, nullptr, 0, 0, 0, NULL, NULL },
296   { NULL, 0, 0, nullptr, 0, 0, NULL, NULL, NULL }
297 };
298 
299 /* clang-format on */
300 
301 /**
302  * determine used compression algorithms
303  */
FindUsedCompressalgos(PoolMem * compressalgos,JobControlRecord * jcr)304 void FindUsedCompressalgos(PoolMem* compressalgos, JobControlRecord* jcr)
305 {
306   int cnt = 0;
307   IncludeExcludeItem* inc;
308   FileOptions* fopts;
309   FilesetResource* fs;
310   struct s_fs_opt* fs_opt;
311 
312   if (!jcr->impl->res.job || !jcr->impl->res.job->fileset) { return; }
313 
314   fs = jcr->impl->res.job->fileset;
315   for (std::size_t i = 0; i < fs->include_items.size(); i++) {
316     inc = fs->include_items[i];
317 
318     for (std::size_t j = 0; j < inc->file_options_list.size(); j++) {
319       fopts = inc->file_options_list[j];
320 
321       for (char* k = fopts->opts; *k; k++) { /* Try to find one request */
322         switch (*k) {
323           case 'Z': /* Compression */
324             for (fs_opt = FS_options; fs_opt->name; fs_opt++) {
325               if (fs_opt->keyword != INC_KW_COMPRESSION) { continue; }
326 
327               if (bstrncmp(k, fs_opt->option, strlen(fs_opt->option))) {
328                 if (cnt > 0) {
329                   compressalgos->strcat(",");
330                 } else {
331                   compressalgos->strcat(" (");
332                 }
333                 compressalgos->strcat(fs_opt->name);
334                 k += strlen(fs_opt->option) - 1;
335                 cnt++;
336                 continue;
337               }
338             }
339             break;
340           default:
341             break;
342         }
343       }
344     }
345   }
346 
347   if (cnt > 0) { compressalgos->strcat(")"); }
348 }
349 
350 /**
351  * Check if the configured options are valid.
352  */
IsInPermittedSet(LEX * lc,const char * SetType,const char * permitted_set)353 static inline void IsInPermittedSet(LEX* lc,
354                                     const char* SetType,
355                                     const char* permitted_set)
356 {
357   const char *p, *q;
358   bool found;
359 
360   for (p = lc->str; *p; p++) {
361     found = false;
362     for (q = permitted_set; *q; q++) {
363       if (*p == *q) {
364         found = true;
365         break;
366       }
367     }
368 
369     if (!found) {
370       scan_err3(lc, _("Illegal %s option %c, got option string: %s:"), SetType,
371                 *p, lc->str);
372     }
373   }
374 }
375 
376 /**
377  * Scan for right hand side of Include options (keyword=option) is
378  * converted into one or two characters. Verifyopts=xxxx is Vxxxx:
379  * Whatever is found is concatenated to the opts string.
380  *
381  * This code is also used inside an Options resource.
382  */
ScanIncludeOptions(LEX * lc,int keyword,char * opts,int optlen)383 static void ScanIncludeOptions(LEX* lc, int keyword, char* opts, int optlen)
384 {
385   int i;
386   char option[64];
387   int lcopts = lc->options;
388   struct s_sz_matching size_matching;
389 
390   memset(option, 0, sizeof(option));
391   lc->options |= LOPT_STRING;     /* force string */
392   LexGetToken(lc, BCT_STRING);    /* expect at least one option */
393   if (keyword == INC_KW_VERIFY) { /* special case */
394     IsInPermittedSet(lc, _("verify"), PERMITTED_VERIFY_OPTIONS);
395     bstrncat(opts, "V", optlen); /* indicate Verify */
396     bstrncat(opts, lc->str, optlen);
397     bstrncat(opts, ":", optlen); /* Terminate it */
398     Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option, optlen);
399   } else if (keyword == INC_KW_ACCURATE) { /* special case */
400     IsInPermittedSet(lc, _("accurate"), PERMITTED_ACCURATE_OPTIONS);
401     bstrncat(opts, "C", optlen); /* indicate Accurate */
402     bstrncat(opts, lc->str, optlen);
403     bstrncat(opts, ":", optlen); /* Terminate it */
404     Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option, optlen);
405   } else if (keyword == INC_KW_BASEJOB) { /* special case */
406     IsInPermittedSet(lc, _("base job"), PERMITTED_BASEJOB_OPTIONS);
407     bstrncat(opts, "J", optlen); /* indicate BaseJob */
408     bstrncat(opts, lc->str, optlen);
409     bstrncat(opts, ":", optlen); /* Terminate it */
410     Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option, optlen);
411   } else if (keyword == INC_KW_STRIPPATH) { /* special case */
412     if (!IsAnInteger(lc->str)) {
413       scan_err1(lc, _("Expected a strip path positive integer, got: %s:"),
414                 lc->str);
415       return;
416     }
417     bstrncat(opts, "P", optlen); /* indicate strip path */
418     bstrncat(opts, lc->str, optlen);
419     bstrncat(opts, ":", optlen); /* Terminate it */
420     Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option, optlen);
421   } else if (keyword == INC_KW_SIZE) { /* special case */
422     if (!ParseSizeMatch(lc->str, &size_matching)) {
423       scan_err1(lc, _("Expected a parseable size, got: %s:"), lc->str);
424       return;
425     }
426     bstrncat(opts, "z", optlen); /* indicate size */
427     bstrncat(opts, lc->str, optlen);
428     bstrncat(opts, ":", optlen); /* Terminate it */
429     Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option, optlen);
430   } else {
431     /*
432      * Standard keyword options for Include/Exclude
433      */
434     for (i = 0; FS_options[i].name; i++) {
435       if (FS_options[i].keyword == keyword
436           && Bstrcasecmp(lc->str, FS_options[i].name)) {
437         bstrncpy(option, FS_options[i].option, sizeof(option));
438         i = 0;
439         break;
440       }
441     }
442     if (i != 0) {
443       scan_err1(lc, _("Expected a FileSet option keyword, got: %s:"), lc->str);
444       return;
445     } else { /* add option */
446       bstrncat(opts, option, optlen);
447       Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option, optlen);
448     }
449   }
450   lc->options = lcopts;
451 
452   /*
453    * If option terminated by comma, eat it
454    */
455   if (lc->ch == ',') { LexGetToken(lc, BCT_ALL); /* yes, eat comma */ }
456 }
457 
458 /**
459  * Store regex info
460  */
StoreRegex(LEX * lc,ResourceItem * item,int index,int pass)461 static void StoreRegex(LEX* lc, ResourceItem* item, int index, int pass)
462 {
463   int token, rc;
464   regex_t preg{};
465   char prbuf[500];
466   const char* type;
467   int newsize;
468 
469   token = LexGetToken(lc, BCT_SKIP_EOL);
470   if (pass == 1) {
471     /* Pickup regex string
472      */
473     switch (token) {
474       case BCT_IDENTIFIER:
475       case BCT_UNQUOTED_STRING:
476       case BCT_QUOTED_STRING:
477         rc = regcomp(&preg, lc->str, REG_EXTENDED);
478         if (rc != 0) {
479           regerror(rc, &preg, prbuf, sizeof(prbuf));
480           regfree(&preg);
481           scan_err1(lc, _("Regex compile error. ERR=%s\n"), prbuf);
482           return;
483         }
484         regfree(&preg);
485         if (item->code == 1) {
486           type = "regexdir";
487           res_incexe->current_opts->regexdir.append(strdup(lc->str));
488           newsize = res_incexe->current_opts->regexdir.size();
489         } else if (item->code == 2) {
490           type = "regexfile";
491           res_incexe->current_opts->regexfile.append(strdup(lc->str));
492           newsize = res_incexe->current_opts->regexfile.size();
493         } else {
494           type = "regex";
495           res_incexe->current_opts->regex.append(strdup(lc->str));
496           newsize = res_incexe->current_opts->regex.size();
497         }
498         Dmsg4(900, "set %s %p size=%d %s\n", type, res_incexe->current_opts,
499               newsize, lc->str);
500         break;
501       default:
502         scan_err1(lc, _("Expected a regex string, got: %s\n"), lc->str);
503         return;
504     }
505   }
506   ScanToEol(lc);
507 }
508 
509 /**
510  * Store Base info
511  */
StoreBase(LEX * lc,ResourceItem * item,int index,int pass)512 static void StoreBase(LEX* lc, ResourceItem* item, int index, int pass)
513 {
514   LexGetToken(lc, BCT_NAME);
515   if (pass == 1) {
516     /*
517      * Pickup Base Job Name
518      */
519     res_incexe->current_opts->base.append(strdup(lc->str));
520   }
521   ScanToEol(lc);
522 }
523 
524 /**
525  * Store reader info
526  */
StorePlugin(LEX * lc,ResourceItem * item,int index,int pass)527 static void StorePlugin(LEX* lc, ResourceItem* item, int index, int pass)
528 {
529   LexGetToken(lc, BCT_NAME);
530   if (pass == 1) {
531     /*
532      * Pickup plugin command
533      */
534     res_incexe->current_opts->plugin = strdup(lc->str);
535   }
536   ScanToEol(lc);
537 }
538 
539 /**
540  * Store Wild-card info
541  */
StoreWild(LEX * lc,ResourceItem * item,int index,int pass)542 static void StoreWild(LEX* lc, ResourceItem* item, int index, int pass)
543 {
544   int token;
545   const char* type;
546   int newsize;
547 
548   token = LexGetToken(lc, BCT_SKIP_EOL);
549   if (pass == 1) {
550     /*
551      * Pickup Wild-card string
552      */
553     switch (token) {
554       case BCT_IDENTIFIER:
555       case BCT_UNQUOTED_STRING:
556       case BCT_QUOTED_STRING:
557         if (item->code == 1) {
558           type = "wilddir";
559           res_incexe->current_opts->wilddir.append(strdup(lc->str));
560           newsize = res_incexe->current_opts->wilddir.size();
561         } else if (item->code == 2) {
562           if (strpbrk(lc->str, "/\\") != NULL) {
563             type = "wildfile";
564             res_incexe->current_opts->wildfile.append(strdup(lc->str));
565             newsize = res_incexe->current_opts->wildfile.size();
566           } else {
567             type = "wildbase";
568             res_incexe->current_opts->wildbase.append(strdup(lc->str));
569             newsize = res_incexe->current_opts->wildbase.size();
570           }
571         } else {
572           type = "wild";
573           res_incexe->current_opts->wild.append(strdup(lc->str));
574           newsize = res_incexe->current_opts->wild.size();
575         }
576         Dmsg4(9, "set %s %p size=%d %s\n", type, res_incexe->current_opts,
577               newsize, lc->str);
578         break;
579       default:
580         scan_err1(lc, _("Expected a wild-card string, got: %s\n"), lc->str);
581         return;
582     }
583   }
584   ScanToEol(lc);
585 }
586 
587 /**
588  * Store fstype info
589  */
StoreFstype(LEX * lc,ResourceItem * item,int index,int pass)590 static void StoreFstype(LEX* lc, ResourceItem* item, int index, int pass)
591 {
592   int token;
593 
594   token = LexGetToken(lc, BCT_SKIP_EOL);
595   if (pass == 1) {
596     /* Pickup fstype string */
597     switch (token) {
598       case BCT_IDENTIFIER:
599       case BCT_UNQUOTED_STRING:
600       case BCT_QUOTED_STRING:
601         res_incexe->current_opts->fstype.append(strdup(lc->str));
602         Dmsg3(900, "set fstype %p size=%d %s\n", res_incexe->current_opts,
603               res_incexe->current_opts->fstype.size(), lc->str);
604         break;
605       default:
606         scan_err1(lc, _("Expected a fstype string, got: %s\n"), lc->str);
607         return;
608     }
609   }
610   ScanToEol(lc);
611 }
612 
613 /**
614  * Store Drivetype info
615  */
StoreDrivetype(LEX * lc,ResourceItem * item,int index,int pass)616 static void StoreDrivetype(LEX* lc, ResourceItem* item, int index, int pass)
617 {
618   int token;
619 
620   token = LexGetToken(lc, BCT_SKIP_EOL);
621   if (pass == 1) {
622     /* Pickup Drivetype string */
623     switch (token) {
624       case BCT_IDENTIFIER:
625       case BCT_UNQUOTED_STRING:
626       case BCT_QUOTED_STRING:
627         res_incexe->current_opts->Drivetype.append(strdup(lc->str));
628         Dmsg3(900, "set Drivetype %p size=%d %s\n", res_incexe->current_opts,
629               res_incexe->current_opts->Drivetype.size(), lc->str);
630         break;
631       default:
632         scan_err1(lc, _("Expected a Drivetype string, got: %s\n"), lc->str);
633         return;
634     }
635   }
636   ScanToEol(lc);
637 }
638 
StoreMeta(LEX * lc,ResourceItem * item,int index,int pass)639 static void StoreMeta(LEX* lc, ResourceItem* item, int index, int pass)
640 {
641   int token;
642 
643   token = LexGetToken(lc, BCT_SKIP_EOL);
644   if (pass == 1) {
645     /* Pickup fstype string */
646     switch (token) {
647       case BCT_IDENTIFIER:
648       case BCT_UNQUOTED_STRING:
649       case BCT_QUOTED_STRING:
650         res_incexe->current_opts->meta.append(strdup(lc->str));
651         Dmsg3(900, "set meta %p size=%d %s\n", res_incexe->current_opts,
652               res_incexe->current_opts->meta.size(), lc->str);
653         break;
654       default:
655         scan_err1(lc, _("Expected a meta string, got: %s\n"), lc->str);
656         return;
657     }
658   }
659   ScanToEol(lc);
660 }
661 
662 /**
663  * New style options come here
664  */
StoreOption(LEX * lc,ResourceItem * item,int index,int pass,std::map<int,options_default_value_s> & option_default_values)665 static void StoreOption(
666     LEX* lc,
667     ResourceItem* item,
668     int index,
669     int pass,
670     std::map<int, options_default_value_s>& option_default_values)
671 {
672   int i;
673   int keyword;
674   char inc_opts[100];
675 
676   inc_opts[0] = 0;
677   keyword = INC_KW_NONE;
678 
679   /*
680    * Look up the keyword
681    */
682   for (i = 0; FS_option_kw[i].name; i++) {
683     if (Bstrcasecmp(item->name, FS_option_kw[i].name)) {
684       keyword = FS_option_kw[i].token;
685       if (option_default_values.find(keyword) != option_default_values.end()) {
686         option_default_values[keyword].configured = true;
687       }
688       break;
689     }
690   }
691 
692   if (keyword == INC_KW_NONE) {
693     scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
694     return;
695   }
696 
697   /*
698    * Now scan for the value
699    */
700   ScanIncludeOptions(lc, keyword, inc_opts, sizeof(inc_opts));
701   if (pass == 1) {
702     bstrncat(res_incexe->current_opts->opts, inc_opts, MAX_FOPTS);
703     Dmsg2(900, "new pass=%d incexe opts=%s\n", pass,
704           res_incexe->current_opts->opts);
705   }
706 
707   ScanToEol(lc);
708 }
709 
710 /**
711  * If current_opts not defined, create first entry
712  */
SetupCurrentOpts(void)713 static void SetupCurrentOpts(void)
714 {
715   FileOptions* fo = new FileOptions;
716   fo->regex.init(1, true);
717   fo->regexdir.init(1, true);
718   fo->regexfile.init(1, true);
719   fo->wild.init(1, true);
720   fo->wilddir.init(1, true);
721   fo->wildfile.init(1, true);
722   fo->wildbase.init(1, true);
723   fo->base.init(1, true);
724   fo->fstype.init(1, true);
725   fo->Drivetype.init(1, true);
726   fo->meta.init(1, true);
727   res_incexe->current_opts = fo;
728   res_incexe->file_options_list.push_back(fo);
729 }
730 
731 /**
732  * Come here when Options seen in Include/Exclude
733  */
StoreOptionsRes(LEX * lc,ResourceItem * item,int index,int pass,bool exclude)734 static void StoreOptionsRes(LEX* lc,
735                             ResourceItem* item,
736                             int index,
737                             int pass,
738                             bool exclude)
739 {
740   int token, i;
741   std::map<int, options_default_value_s> option_default_values
742       = {{INC_KW_ACL, {false, "A"}}, {INC_KW_XATTR, {false, "X"}}};
743 
744   if (exclude) {
745     scan_err0(lc, _("Options section not permitted in Exclude\n"));
746     return;
747   }
748   token = LexGetToken(lc, BCT_SKIP_EOL);
749   if (token != BCT_BOB) {
750     scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
751     return;
752   }
753 
754   if (pass == 1) { SetupCurrentOpts(); }
755 
756   while ((token = LexGetToken(lc, BCT_ALL)) != BCT_EOF) {
757     if (token == BCT_EOL) { continue; }
758     if (token == BCT_EOB) { break; }
759     if (token != BCT_IDENTIFIER) {
760       scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
761       return;
762     }
763     bool found = false;
764     for (i = 0; options_items[i].name; i++) {
765       if (Bstrcasecmp(options_items[i].name, lc->str)) {
766         token = LexGetToken(lc, BCT_SKIP_EOL);
767         if (token != BCT_EQUALS) {
768           scan_err1(lc, _("expected an equals, got: %s"), lc->str);
769           return;
770         }
771         /* Call item handler */
772         switch (options_items[i].type) {
773           case CFG_TYPE_OPTION:
774             StoreOption(lc, &options_items[i], i, pass, option_default_values);
775             break;
776           case CFG_TYPE_REGEX:
777             StoreRegex(lc, &options_items[i], i, pass);
778             break;
779           case CFG_TYPE_BASE:
780             StoreBase(lc, &options_items[i], i, pass);
781             break;
782           case CFG_TYPE_WILD:
783             StoreWild(lc, &options_items[i], i, pass);
784             break;
785           case CFG_TYPE_PLUGIN:
786             StorePlugin(lc, &options_items[i], i, pass);
787             break;
788           case CFG_TYPE_FSTYPE:
789             StoreFstype(lc, &options_items[i], i, pass);
790             break;
791           case CFG_TYPE_DRIVETYPE:
792             StoreDrivetype(lc, &options_items[i], i, pass);
793             break;
794           case CFG_TYPE_META:
795             StoreMeta(lc, &options_items[i], i, pass);
796             break;
797           default:
798             break;
799         }
800         found = true;
801         break;
802       }
803     }
804     if (!found) {
805       scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
806       return;
807     }
808   }
809 
810   /* apply default values for unset options */
811   if (pass == 1) {
812     for (auto const& o : option_default_values) {
813       int keyword_id = o.first;
814       bool was_set_in_config = o.second.configured;
815       std::string default_value = o.second.default_value;
816       if (!was_set_in_config) {
817         bstrncat(res_incexe->current_opts->opts, default_value.c_str(),
818                  MAX_FOPTS);
819         Dmsg2(900, "setting default value for keyword-id=%d, %s\n", keyword_id,
820               default_value.c_str());
821       }
822     }
823   }
824 }
825 
GetStaticFilesetResource()826 static FilesetResource* GetStaticFilesetResource()
827 {
828   FilesetResource* res_fs = nullptr;
829   ResourceTable* t = my_config->GetResourceTable("FileSet");
830   assert(t);
831   if (t) { res_fs = dynamic_cast<FilesetResource*>(*t->allocated_resource_); }
832   assert(res_fs);
833   return res_fs;
834 }
835 
836 /**
837  * Store Filename info. Note, for minor efficiency reasons, we
838  * always increase the name buffer by 10 items because we expect
839  * to add more entries.
840  */
StoreFname(LEX * lc,ResourceItem * item,int index,int pass,bool exclude)841 static void StoreFname(LEX* lc,
842                        ResourceItem* item,
843                        int index,
844                        int pass,
845                        bool exclude)
846 {
847   int token;
848 
849   token = LexGetToken(lc, BCT_SKIP_EOL);
850   if (pass == 1) {
851     /* Pickup Filename string
852      */
853     switch (token) {
854       case BCT_IDENTIFIER:
855       case BCT_UNQUOTED_STRING:
856         if (strchr(lc->str, '\\')) {
857           scan_err1(lc,
858                     _("Backslash found. Use forward slashes or quote the "
859                       "string.: %s\n"),
860                     lc->str);
861           return;
862         }
863         FALLTHROUGH_INTENDED;
864       case BCT_QUOTED_STRING: {
865         FilesetResource* res_fs = GetStaticFilesetResource();
866         if (res_fs->have_MD5) {
867           MD5_Update(&res_fs->md5c, (unsigned char*)lc->str, lc->str_len);
868         }
869 
870         if (res_incexe->name_list.size() == 0) {
871           res_incexe->name_list.init(10, true);
872         }
873         res_incexe->name_list.append(strdup(lc->str));
874         Dmsg1(900, "Add to name_list %s\n", lc->str);
875         break;
876       }
877       default:
878         scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
879         return;
880     }
881   }
882   ScanToEol(lc);
883 }
884 
885 /**
886  * Store Filename info. Note, for minor efficiency reasons, we
887  * always increase the name buffer by 10 items because we expect
888  * to add more entries.
889  */
StorePluginName(LEX * lc,ResourceItem * item,int index,int pass,bool exclude)890 static void StorePluginName(LEX* lc,
891                             ResourceItem* item,
892                             int index,
893                             int pass,
894                             bool exclude)
895 {
896   int token;
897 
898   if (exclude) {
899     scan_err0(lc, _("Plugin directive not permitted in Exclude\n"));
900     return;
901   }
902   token = LexGetToken(lc, BCT_SKIP_EOL);
903   if (pass == 1) {
904     /*
905      * Pickup Filename string
906      */
907     switch (token) {
908       case BCT_IDENTIFIER:
909       case BCT_UNQUOTED_STRING:
910         if (strchr(lc->str, '\\')) {
911           scan_err1(lc,
912                     _("Backslash found. Use forward slashes or quote the "
913                       "string.: %s\n"),
914                     lc->str);
915           return;
916         }
917         FALLTHROUGH_INTENDED;
918       case BCT_QUOTED_STRING: {
919         FilesetResource* res_fs = GetStaticFilesetResource();
920 
921         if (res_fs->have_MD5) {
922           MD5_Update(&res_fs->md5c, (unsigned char*)lc->str, lc->str_len);
923         }
924         if (res_incexe->plugin_list.size() == 0) {
925           res_incexe->plugin_list.init(10, true);
926         }
927         res_incexe->plugin_list.append(strdup(lc->str));
928         Dmsg1(900, "Add to plugin_list %s\n", lc->str);
929         break;
930       }
931       default:
932         scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
933         return;
934     }
935   }
936   ScanToEol(lc);
937 }
938 
939 /**
940  * Store exclude directory containing info
941  */
StoreExcludedir(LEX * lc,ResourceItem * item,int index,int pass,bool exclude)942 static void StoreExcludedir(LEX* lc,
943                             ResourceItem* item,
944                             int index,
945                             int pass,
946                             bool exclude)
947 {
948   if (exclude) {
949     scan_err0(lc,
950               _("ExcludeDirContaining directive not permitted in Exclude.\n"));
951     return;
952   }
953 
954   LexGetToken(lc, BCT_NAME);
955   if (pass == 1) {
956     if (res_incexe->ignoredir.size() == 0) {
957       res_incexe->ignoredir.init(10, true);
958     }
959     res_incexe->ignoredir.append(strdup(lc->str));
960     Dmsg1(900, "Add to ignoredir_list %s\n", lc->str);
961   }
962   ScanToEol(lc);
963 }
964 
965 /**
966  * Store new style FileSet Include/Exclude info
967  *
968  *  Note, when this routine is called, we are inside a FileSet
969  *  resource.  We treat the Include/Exclude like a sort of
970  *  mini-resource within the FileSet resource.
971  */
StoreNewinc(LEX * lc,ResourceItem * item,int index,int pass)972 static void StoreNewinc(LEX* lc, ResourceItem* item, int index, int pass)
973 {
974   FilesetResource* res_fs = GetStaticFilesetResource();
975 
976   // Store.. functions below only store in pass = 1
977   if (pass == 1) { res_incexe = new IncludeExcludeItem; }
978 
979   if (!res_fs->have_MD5) {
980     MD5_Init(&res_fs->md5c);
981     res_fs->have_MD5 = true;
982   }
983   res_fs->new_include = true;
984   int token;
985   while ((token = LexGetToken(lc, BCT_SKIP_EOL)) != BCT_EOF) {
986     if (token == BCT_EOB) { break; }
987     if (token != BCT_IDENTIFIER) {
988       scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
989       return;
990     }
991     bool found = false;
992     for (int i = 0; newinc_items[i].name; i++) {
993       bool options = Bstrcasecmp(lc->str, "options");
994       if (Bstrcasecmp(newinc_items[i].name, lc->str)) {
995         if (!options) {
996           token = LexGetToken(lc, BCT_SKIP_EOL);
997           if (token != BCT_EQUALS) {
998             scan_err1(lc, _("expected an equals, got: %s"), lc->str);
999             return;
1000           }
1001         }
1002         switch (newinc_items[i].type) {
1003           case CFG_TYPE_FNAME:
1004             StoreFname(lc, &newinc_items[i], i, pass, item->code);
1005             break;
1006           case CFG_TYPE_PLUGINNAME:
1007             StorePluginName(lc, &newinc_items[i], i, pass, item->code);
1008             break;
1009           case CFG_TYPE_EXCLUDEDIR:
1010             StoreExcludedir(lc, &newinc_items[i], i, pass, item->code);
1011             break;
1012           case CFG_TYPE_OPTIONS:
1013             StoreOptionsRes(lc, &newinc_items[i], i, pass, item->code);
1014             break;
1015           default:
1016             break;
1017         }
1018         found = true;
1019         break;
1020       }
1021     }
1022     if (!found) {
1023       scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1024       return;
1025     }
1026   }
1027 
1028   if (pass == 1) {
1029     // store the pointer from res_incexe in each appropriate container
1030     if (item->code == 0) { /* include */
1031       res_fs->include_items.push_back(res_incexe);
1032       Dmsg1(900, "num_includes=%d\n", res_fs->include_items.size());
1033     } else { /* exclude */
1034       res_fs->exclude_items.push_back(res_incexe);
1035       Dmsg1(900, "num_excludes=%d\n", res_fs->exclude_items.size());
1036     }
1037     res_incexe = nullptr;
1038   }
1039 
1040   // all pointers must push_back above
1041   ASSERT(!res_incexe);
1042 
1043   ScanToEol(lc);
1044   SetBit(index, (*item->allocated_resource)->item_present_);
1045   ClearBit(index, (*item->allocated_resource)->inherit_content_);
1046 }
1047 
1048 /**
1049  * Store FileSet Include/Exclude info
1050  *  new style includes are handled in StoreNewinc()
1051  */
StoreInc(LEX * lc,ResourceItem * item,int index,int pass)1052 void StoreInc(LEX* lc, ResourceItem* item, int index, int pass)
1053 {
1054   int token;
1055 
1056   /*
1057    * Decide if we are doing a new Include or an old include. The
1058    *  new Include is followed immediately by open brace, whereas the
1059    *  old include has options following the Include.
1060    */
1061   token = LexGetToken(lc, BCT_SKIP_EOL);
1062   if (token == BCT_BOB) {
1063     StoreNewinc(lc, item, index, pass);
1064     return;
1065   }
1066   scan_err0(lc, _("Old style Include/Exclude not supported\n"));
1067 }
1068 
1069 #ifdef HAVE_JANSSON
json_incexc(const int type)1070 json_t* json_incexc(const int type)
1071 {
1072   return json_datatype(type, newinc_items);
1073 }
1074 
json_options(const int type)1075 json_t* json_options(const int type)
1076 {
1077   return json_datatype(type, options_items);
1078 }
1079 #endif
1080 } /* namespace directordaemon */
1081