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