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-2016 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     }
416     bstrncat(opts, "P", optlen); /* indicate strip path */
417     bstrncat(opts, lc->str, optlen);
418     bstrncat(opts, ":", optlen); /* Terminate it */
419     Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option, optlen);
420   } else if (keyword == INC_KW_SIZE) { /* special case */
421     if (!ParseSizeMatch(lc->str, &size_matching)) {
422       scan_err1(lc, _("Expected a parseable size, got: %s:"), lc->str);
423     }
424     bstrncat(opts, "z", optlen); /* indicate size */
425     bstrncat(opts, lc->str, optlen);
426     bstrncat(opts, ":", optlen); /* Terminate it */
427     Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option, optlen);
428   } else {
429     /*
430      * Standard keyword options for Include/Exclude
431      */
432     for (i = 0; FS_options[i].name; i++) {
433       if (FS_options[i].keyword == keyword &&
434           Bstrcasecmp(lc->str, FS_options[i].name)) {
435         bstrncpy(option, FS_options[i].option, sizeof(option));
436         i = 0;
437         break;
438       }
439     }
440     if (i != 0) {
441       scan_err1(lc, _("Expected a FileSet option keyword, got: %s:"), lc->str);
442     } else { /* add option */
443       bstrncat(opts, option, optlen);
444       Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option, optlen);
445     }
446   }
447   lc->options = lcopts;
448 
449   /*
450    * If option terminated by comma, eat it
451    */
452   if (lc->ch == ',') { LexGetToken(lc, BCT_ALL); /* yes, eat comma */ }
453 }
454 
455 /**
456  * Store regex info
457  */
StoreRegex(LEX * lc,ResourceItem * item,int index,int pass)458 static void StoreRegex(LEX* lc, ResourceItem* item, int index, int pass)
459 {
460   int token, rc;
461   regex_t preg{};
462   char prbuf[500];
463   const char* type;
464   int newsize;
465 
466   token = LexGetToken(lc, BCT_SKIP_EOL);
467   if (pass == 1) {
468     /* Pickup regex string
469      */
470     switch (token) {
471       case BCT_IDENTIFIER:
472       case BCT_UNQUOTED_STRING:
473       case BCT_QUOTED_STRING:
474         rc = regcomp(&preg, lc->str, REG_EXTENDED);
475         if (rc != 0) {
476           regerror(rc, &preg, prbuf, sizeof(prbuf));
477           regfree(&preg);
478           scan_err1(lc, _("Regex compile error. ERR=%s\n"), prbuf);
479           break;
480         }
481         regfree(&preg);
482         if (item->code == 1) {
483           type = "regexdir";
484           res_incexe->current_opts->regexdir.append(strdup(lc->str));
485           newsize = res_incexe->current_opts->regexdir.size();
486         } else if (item->code == 2) {
487           type = "regexfile";
488           res_incexe->current_opts->regexfile.append(strdup(lc->str));
489           newsize = res_incexe->current_opts->regexfile.size();
490         } else {
491           type = "regex";
492           res_incexe->current_opts->regex.append(strdup(lc->str));
493           newsize = res_incexe->current_opts->regex.size();
494         }
495         Dmsg4(900, "set %s %p size=%d %s\n", type, res_incexe->current_opts,
496               newsize, lc->str);
497         break;
498       default:
499         scan_err1(lc, _("Expected a regex string, got: %s\n"), lc->str);
500     }
501   }
502   ScanToEol(lc);
503 }
504 
505 /**
506  * Store Base info
507  */
StoreBase(LEX * lc,ResourceItem * item,int index,int pass)508 static void StoreBase(LEX* lc, ResourceItem* item, int index, int pass)
509 {
510   LexGetToken(lc, BCT_NAME);
511   if (pass == 1) {
512     /*
513      * Pickup Base Job Name
514      */
515     res_incexe->current_opts->base.append(strdup(lc->str));
516   }
517   ScanToEol(lc);
518 }
519 
520 /**
521  * Store reader info
522  */
StorePlugin(LEX * lc,ResourceItem * item,int index,int pass)523 static void StorePlugin(LEX* lc, ResourceItem* item, int index, int pass)
524 {
525   LexGetToken(lc, BCT_NAME);
526   if (pass == 1) {
527     /*
528      * Pickup plugin command
529      */
530     res_incexe->current_opts->plugin = strdup(lc->str);
531   }
532   ScanToEol(lc);
533 }
534 
535 /**
536  * Store Wild-card info
537  */
StoreWild(LEX * lc,ResourceItem * item,int index,int pass)538 static void StoreWild(LEX* lc, ResourceItem* item, int index, int pass)
539 {
540   int token;
541   const char* type;
542   int newsize;
543 
544   token = LexGetToken(lc, BCT_SKIP_EOL);
545   if (pass == 1) {
546     /*
547      * Pickup Wild-card string
548      */
549     switch (token) {
550       case BCT_IDENTIFIER:
551       case BCT_UNQUOTED_STRING:
552       case BCT_QUOTED_STRING:
553         if (item->code == 1) {
554           type = "wilddir";
555           res_incexe->current_opts->wilddir.append(strdup(lc->str));
556           newsize = res_incexe->current_opts->wilddir.size();
557         } else if (item->code == 2) {
558           if (strpbrk(lc->str, "/\\") != NULL) {
559             type = "wildfile";
560             res_incexe->current_opts->wildfile.append(strdup(lc->str));
561             newsize = res_incexe->current_opts->wildfile.size();
562           } else {
563             type = "wildbase";
564             res_incexe->current_opts->wildbase.append(strdup(lc->str));
565             newsize = res_incexe->current_opts->wildbase.size();
566           }
567         } else {
568           type = "wild";
569           res_incexe->current_opts->wild.append(strdup(lc->str));
570           newsize = res_incexe->current_opts->wild.size();
571         }
572         Dmsg4(9, "set %s %p size=%d %s\n", type, res_incexe->current_opts,
573               newsize, lc->str);
574         break;
575       default:
576         scan_err1(lc, _("Expected a wild-card string, got: %s\n"), lc->str);
577     }
578   }
579   ScanToEol(lc);
580 }
581 
582 /**
583  * Store fstype info
584  */
StoreFstype(LEX * lc,ResourceItem * item,int index,int pass)585 static void StoreFstype(LEX* lc, ResourceItem* item, int index, int pass)
586 {
587   int token;
588 
589   token = LexGetToken(lc, BCT_SKIP_EOL);
590   if (pass == 1) {
591     /* Pickup fstype string */
592     switch (token) {
593       case BCT_IDENTIFIER:
594       case BCT_UNQUOTED_STRING:
595       case BCT_QUOTED_STRING:
596         res_incexe->current_opts->fstype.append(strdup(lc->str));
597         Dmsg3(900, "set fstype %p size=%d %s\n", res_incexe->current_opts,
598               res_incexe->current_opts->fstype.size(), lc->str);
599         break;
600       default:
601         scan_err1(lc, _("Expected a fstype string, got: %s\n"), lc->str);
602     }
603   }
604   ScanToEol(lc);
605 }
606 
607 /**
608  * Store Drivetype info
609  */
StoreDrivetype(LEX * lc,ResourceItem * item,int index,int pass)610 static void StoreDrivetype(LEX* lc, ResourceItem* item, int index, int pass)
611 {
612   int token;
613 
614   token = LexGetToken(lc, BCT_SKIP_EOL);
615   if (pass == 1) {
616     /* Pickup Drivetype string */
617     switch (token) {
618       case BCT_IDENTIFIER:
619       case BCT_UNQUOTED_STRING:
620       case BCT_QUOTED_STRING:
621         res_incexe->current_opts->Drivetype.append(strdup(lc->str));
622         Dmsg3(900, "set Drivetype %p size=%d %s\n", res_incexe->current_opts,
623               res_incexe->current_opts->Drivetype.size(), lc->str);
624         break;
625       default:
626         scan_err1(lc, _("Expected a Drivetype string, got: %s\n"), lc->str);
627     }
628   }
629   ScanToEol(lc);
630 }
631 
StoreMeta(LEX * lc,ResourceItem * item,int index,int pass)632 static void StoreMeta(LEX* lc, ResourceItem* item, int index, int pass)
633 {
634   int token;
635 
636   token = LexGetToken(lc, BCT_SKIP_EOL);
637   if (pass == 1) {
638     /* Pickup fstype string */
639     switch (token) {
640       case BCT_IDENTIFIER:
641       case BCT_UNQUOTED_STRING:
642       case BCT_QUOTED_STRING:
643         res_incexe->current_opts->meta.append(strdup(lc->str));
644         Dmsg3(900, "set meta %p size=%d %s\n", res_incexe->current_opts,
645               res_incexe->current_opts->meta.size(), lc->str);
646         break;
647       default:
648         scan_err1(lc, _("Expected a meta string, got: %s\n"), lc->str);
649     }
650   }
651   ScanToEol(lc);
652 }
653 
654 /**
655  * New style options come here
656  */
StoreOption(LEX * lc,ResourceItem * item,int index,int pass,std::map<int,options_default_value_s> & option_default_values)657 static void StoreOption(
658     LEX* lc,
659     ResourceItem* item,
660     int index,
661     int pass,
662     std::map<int, options_default_value_s>& option_default_values)
663 {
664   int i;
665   int keyword;
666   char inc_opts[100];
667 
668   inc_opts[0] = 0;
669   keyword = INC_KW_NONE;
670 
671   /*
672    * Look up the keyword
673    */
674   for (i = 0; FS_option_kw[i].name; i++) {
675     if (Bstrcasecmp(item->name, FS_option_kw[i].name)) {
676       keyword = FS_option_kw[i].token;
677       if (option_default_values.find(keyword) != option_default_values.end()) {
678         option_default_values[keyword].configured = true;
679       }
680       break;
681     }
682   }
683 
684   if (keyword == INC_KW_NONE) {
685     scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
686   }
687 
688   /*
689    * Now scan for the value
690    */
691   ScanIncludeOptions(lc, keyword, inc_opts, sizeof(inc_opts));
692   if (pass == 1) {
693     bstrncat(res_incexe->current_opts->opts, inc_opts, MAX_FOPTS);
694     Dmsg2(900, "new pass=%d incexe opts=%s\n", pass,
695           res_incexe->current_opts->opts);
696   }
697 
698   ScanToEol(lc);
699 }
700 
701 /**
702  * If current_opts not defined, create first entry
703  */
SetupCurrentOpts(void)704 static void SetupCurrentOpts(void)
705 {
706   FileOptions* fo = new FileOptions;
707   fo->regex.init(1, true);
708   fo->regexdir.init(1, true);
709   fo->regexfile.init(1, true);
710   fo->wild.init(1, true);
711   fo->wilddir.init(1, true);
712   fo->wildfile.init(1, true);
713   fo->wildbase.init(1, true);
714   fo->base.init(1, true);
715   fo->fstype.init(1, true);
716   fo->Drivetype.init(1, true);
717   fo->meta.init(1, true);
718   res_incexe->current_opts = fo;
719   res_incexe->file_options_list.push_back(fo);
720 }
721 
722 /**
723  * Come here when Options seen in Include/Exclude
724  */
StoreOptionsRes(LEX * lc,ResourceItem * item,int index,int pass,bool exclude)725 static void StoreOptionsRes(LEX* lc,
726                             ResourceItem* item,
727                             int index,
728                             int pass,
729                             bool exclude)
730 {
731   int token, i;
732   std::map<int, options_default_value_s> option_default_values = {
733       {INC_KW_ACL, {false, "A"}}, {INC_KW_XATTR, {false, "X"}}};
734 
735   if (exclude) {
736     scan_err0(lc, _("Options section not permitted in Exclude\n"));
737     /* NOT REACHED */
738   }
739   token = LexGetToken(lc, BCT_SKIP_EOL);
740   if (token != BCT_BOB) {
741     scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
742   }
743 
744   if (pass == 1) { SetupCurrentOpts(); }
745 
746   while ((token = LexGetToken(lc, BCT_ALL)) != BCT_EOF) {
747     if (token == BCT_EOL) { continue; }
748     if (token == BCT_EOB) { break; }
749     if (token != BCT_IDENTIFIER) {
750       scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
751     }
752     bool found = false;
753     for (i = 0; options_items[i].name; i++) {
754       if (Bstrcasecmp(options_items[i].name, lc->str)) {
755         token = LexGetToken(lc, BCT_SKIP_EOL);
756         if (token != BCT_EQUALS) {
757           scan_err1(lc, _("expected an equals, got: %s"), lc->str);
758         }
759         /* Call item handler */
760         switch (options_items[i].type) {
761           case CFG_TYPE_OPTION:
762             StoreOption(lc, &options_items[i], i, pass, option_default_values);
763             break;
764           case CFG_TYPE_REGEX:
765             StoreRegex(lc, &options_items[i], i, pass);
766             break;
767           case CFG_TYPE_BASE:
768             StoreBase(lc, &options_items[i], i, pass);
769             break;
770           case CFG_TYPE_WILD:
771             StoreWild(lc, &options_items[i], i, pass);
772             break;
773           case CFG_TYPE_PLUGIN:
774             StorePlugin(lc, &options_items[i], i, pass);
775             break;
776           case CFG_TYPE_FSTYPE:
777             StoreFstype(lc, &options_items[i], i, pass);
778             break;
779           case CFG_TYPE_DRIVETYPE:
780             StoreDrivetype(lc, &options_items[i], i, pass);
781             break;
782           case CFG_TYPE_META:
783             StoreMeta(lc, &options_items[i], i, pass);
784             break;
785           default:
786             break;
787         }
788         found = true;
789         break;
790       }
791     }
792     if (!found) {
793       scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
794     }
795   }
796 
797   /* apply default values for unset options */
798   if (pass == 1) {
799     for (auto const& o : option_default_values) {
800       int keyword_id = o.first;
801       bool was_set_in_config = o.second.configured;
802       std::string default_value = o.second.default_value;
803       if (!was_set_in_config) {
804         bstrncat(res_incexe->current_opts->opts, default_value.c_str(),
805                  MAX_FOPTS);
806         Dmsg2(900, "setting default value for keyword-id=%d, %s\n", keyword_id,
807               default_value.c_str());
808       }
809     }
810   }
811 }
812 
GetStaticFilesetResource()813 static FilesetResource* GetStaticFilesetResource()
814 {
815   FilesetResource* res_fs = nullptr;
816   ResourceTable* t = my_config->GetResourceTable("FileSet");
817   assert(t);
818   if (t) { res_fs = dynamic_cast<FilesetResource*>(*t->allocated_resource_); }
819   assert(res_fs);
820   return res_fs;
821 }
822 
823 /**
824  * Store Filename info. Note, for minor efficiency reasons, we
825  * always increase the name buffer by 10 items because we expect
826  * to add more entries.
827  */
StoreFname(LEX * lc,ResourceItem * item,int index,int pass,bool exclude)828 static void StoreFname(LEX* lc,
829                        ResourceItem* item,
830                        int index,
831                        int pass,
832                        bool exclude)
833 {
834   int token;
835 
836   token = LexGetToken(lc, BCT_SKIP_EOL);
837   if (pass == 1) {
838     /* Pickup Filename string
839      */
840     switch (token) {
841       case BCT_IDENTIFIER:
842       case BCT_UNQUOTED_STRING:
843         if (strchr(lc->str, '\\')) {
844           scan_err1(lc,
845                     _("Backslash found. Use forward slashes or quote the "
846                       "string.: %s\n"),
847                     lc->str);
848           /* NOT REACHED */
849         }
850       case BCT_QUOTED_STRING: {
851         FilesetResource* res_fs = GetStaticFilesetResource();
852         if (res_fs->have_MD5) {
853           MD5_Update(&res_fs->md5c, (unsigned char*)lc->str, lc->str_len);
854         }
855 
856         if (res_incexe->name_list.size() == 0) {
857           res_incexe->name_list.init(10, true);
858         }
859         res_incexe->name_list.append(strdup(lc->str));
860         Dmsg1(900, "Add to name_list %s\n", lc->str);
861         break;
862       }
863       default:
864         scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
865     }
866   }
867   ScanToEol(lc);
868 }  // namespace directordaemon
869 
870 /**
871  * Store Filename info. Note, for minor efficiency reasons, we
872  * always increase the name buffer by 10 items because we expect
873  * to add more entries.
874  */
StorePluginName(LEX * lc,ResourceItem * item,int index,int pass,bool exclude)875 static void StorePluginName(LEX* lc,
876                             ResourceItem* item,
877                             int index,
878                             int pass,
879                             bool exclude)
880 {
881   int token;
882 
883   if (exclude) {
884     scan_err0(lc, _("Plugin directive not permitted in Exclude\n"));
885     /* NOT REACHED */
886   }
887   token = LexGetToken(lc, BCT_SKIP_EOL);
888   if (pass == 1) {
889     /* Pickup Filename string
890      */
891     switch (token) {
892       case BCT_IDENTIFIER:
893       case BCT_UNQUOTED_STRING:
894         if (strchr(lc->str, '\\')) {
895           scan_err1(lc,
896                     _("Backslash found. Use forward slashes or quote the "
897                       "string.: %s\n"),
898                     lc->str);
899           /* NOT REACHED */
900         }
901       case BCT_QUOTED_STRING: {
902         FilesetResource* res_fs = GetStaticFilesetResource();
903 
904         if (res_fs->have_MD5) {
905           MD5_Update(&res_fs->md5c, (unsigned char*)lc->str, lc->str_len);
906         }
907         if (res_incexe->plugin_list.size() == 0) {
908           res_incexe->plugin_list.init(10, true);
909         }
910         res_incexe->plugin_list.append(strdup(lc->str));
911         Dmsg1(900, "Add to plugin_list %s\n", lc->str);
912         break;
913       }
914       default:
915         scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
916         /* NOT REACHED */
917     }
918   }
919   ScanToEol(lc);
920 }
921 
922 /**
923  * Store exclude directory containing info
924  */
StoreExcludedir(LEX * lc,ResourceItem * item,int index,int pass,bool exclude)925 static void StoreExcludedir(LEX* lc,
926                             ResourceItem* item,
927                             int index,
928                             int pass,
929                             bool exclude)
930 {
931   if (exclude) {
932     scan_err0(lc,
933               _("ExcludeDirContaining directive not permitted in Exclude.\n"));
934     /* NOT REACHED */
935     return;
936   }
937 
938   LexGetToken(lc, BCT_NAME);
939   if (pass == 1) {
940     if (res_incexe->ignoredir.size() == 0) {
941       res_incexe->ignoredir.init(10, true);
942     }
943     res_incexe->ignoredir.append(strdup(lc->str));
944     Dmsg1(900, "Add to ignoredir_list %s\n", lc->str);
945   }
946   ScanToEol(lc);
947 }
948 
949 /**
950  * Store new style FileSet Include/Exclude info
951  *
952  *  Note, when this routine is called, we are inside a FileSet
953  *  resource.  We treat the Include/Exclude like a sort of
954  *  mini-resource within the FileSet resource.
955  */
StoreNewinc(LEX * lc,ResourceItem * item,int index,int pass)956 static void StoreNewinc(LEX* lc, ResourceItem* item, int index, int pass)
957 {
958   FilesetResource* res_fs = GetStaticFilesetResource();
959 
960   // Store.. functions below only store in pass = 1
961   if (pass == 1) { res_incexe = new IncludeExcludeItem; }
962 
963   if (!res_fs->have_MD5) {
964     MD5_Init(&res_fs->md5c);
965     res_fs->have_MD5 = true;
966   }
967   res_fs->new_include = true;
968   int token;
969   while ((token = LexGetToken(lc, BCT_SKIP_EOL)) != BCT_EOF) {
970     if (token == BCT_EOB) { break; }
971     if (token != BCT_IDENTIFIER) {
972       scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
973     }
974     bool found = false;
975     for (int i = 0; newinc_items[i].name; i++) {
976       bool options = Bstrcasecmp(lc->str, "options");
977       if (Bstrcasecmp(newinc_items[i].name, lc->str)) {
978         if (!options) {
979           token = LexGetToken(lc, BCT_SKIP_EOL);
980           if (token != BCT_EQUALS) {
981             scan_err1(lc, _("expected an equals, got: %s"), lc->str);
982           }
983         }
984         switch (newinc_items[i].type) {
985           case CFG_TYPE_FNAME:
986             StoreFname(lc, &newinc_items[i], i, pass, item->code);
987             break;
988           case CFG_TYPE_PLUGINNAME:
989             StorePluginName(lc, &newinc_items[i], i, pass, item->code);
990             break;
991           case CFG_TYPE_EXCLUDEDIR:
992             StoreExcludedir(lc, &newinc_items[i], i, pass, item->code);
993             break;
994           case CFG_TYPE_OPTIONS:
995             StoreOptionsRes(lc, &newinc_items[i], i, pass, item->code);
996             break;
997           default:
998             break;
999         }
1000         found = true;
1001         break;
1002       }
1003     }
1004     if (!found) {
1005       scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1006     }
1007   }
1008 
1009   if (pass == 1) {
1010     // store the pointer from res_incexe in each appropriate container
1011     if (item->code == 0) { /* include */
1012       res_fs->include_items.push_back(res_incexe);
1013       Dmsg1(900, "num_includes=%d\n", res_fs->include_items.size());
1014     } else { /* exclude */
1015       res_fs->exclude_items.push_back(res_incexe);
1016       Dmsg1(900, "num_excludes=%d\n", res_fs->exclude_items.size());
1017     }
1018     res_incexe = nullptr;
1019   }
1020 
1021   // all pointers must push_back above
1022   ASSERT(!res_incexe);
1023 
1024   ScanToEol(lc);
1025   SetBit(index, (*item->allocated_resource)->item_present_);
1026   ClearBit(index, (*item->allocated_resource)->inherit_content_);
1027 }
1028 
1029 /**
1030  * Store FileSet Include/Exclude info
1031  *  new style includes are handled in StoreNewinc()
1032  */
StoreInc(LEX * lc,ResourceItem * item,int index,int pass)1033 void StoreInc(LEX* lc, ResourceItem* item, int index, int pass)
1034 {
1035   int token;
1036 
1037   /*
1038    * Decide if we are doing a new Include or an old include. The
1039    *  new Include is followed immediately by open brace, whereas the
1040    *  old include has options following the Include.
1041    */
1042   token = LexGetToken(lc, BCT_SKIP_EOL);
1043   if (token == BCT_BOB) {
1044     StoreNewinc(lc, item, index, pass);
1045     return;
1046   }
1047   scan_err0(lc, _("Old style Include/Exclude not supported\n"));
1048 }
1049 
1050 #ifdef HAVE_JANSSON
json_incexc(const int type)1051 json_t* json_incexc(const int type)
1052 {
1053   return json_datatype(type, newinc_items);
1054 }
1055 
json_options(const int type)1056 json_t* json_options(const int type)
1057 {
1058   return json_datatype(type, options_items);
1059 }
1060 #endif
1061 } /* namespace directordaemon */
1062