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