]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/inc_conf.c
c9e4333c30f30fdb759fcf53dd15045aab7f7c1b
[bacula/bacula] / bacula / src / dird / inc_conf.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU 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    General Public License for more details.
17
18    You should have received a copy of the GNU 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    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *   Configuration file parser for new and old Include and
30  *      Exclude records
31  *
32  *     Kern Sibbald, March MMIII
33  *
34  *     Version $Id$
35  */
36
37 #include "bacula.h"
38 #include "dird.h"
39 #ifndef HAVE_REGEX_H
40 #include "lib/bregex.h"
41 #else
42 #include <regex.h>
43 #endif
44
45 /* Forward referenced subroutines */
46
47 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass);
48
49 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass);
50 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass);
51 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass);
52 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass);
53 static void store_drivetype(LEX *lc, RES_ITEM *item, int index, int pass);
54 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass);
55 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass);
56 static void store_plugin(LEX *lc, RES_ITEM *item, int index, int pass);
57 static void setup_current_opts(void);
58
59 /* Include and Exclude items */
60 static void store_fname(LEX *lc, RES_ITEM2 *item, int index, int pass, bool exclude);
61 static void store_plugin_name(LEX *lc, RES_ITEM2 *item, int index, int pass, bool exclude);
62 static void options_res(LEX *lc, RES_ITEM2 *item, int index, int pass, bool exclude);
63 static void store_excludedir(LEX *lc, RES_ITEM2 *item, int index, int pass, bool exclude);
64
65
66 /* We build the current resource here as we are
67  * scanning the resource configuration definition,
68  * then move it to allocated memory when the resource
69  * scan is complete.
70  */
71 #if defined(_MSC_VER)
72 extern "C" { // work around visual compiler mangling variables
73    extern URES res_all;
74 }
75 #else
76 extern URES res_all;
77 #endif
78 extern int32_t  res_all_size;
79
80 /* We build the current new Include and Exclude items here */
81 static INCEXE res_incexe;
82
83 /*
84  * new Include/Exclude items
85  *   name             handler     value    code flags default_value
86  */
87 static RES_ITEM2 newinc_items[] = {
88    {"file",            store_fname,       {0},      0, 0, 0},
89    {"plugin",          store_plugin_name, {0},      0, 0, 0},
90    {"excludedircontaining", store_excludedir,  {0}, 0, 0, 0},
91    {"options",         options_res,       {0},      0, 0, 0},
92    {NULL, NULL, {0}, 0, 0, 0}
93 };
94
95 /*
96  * Items that are valid in an Options resource
97  */
98 static RES_ITEM options_items[] = {
99    {"compression",     store_opts,    {0},     0, 0, 0},
100    {"signature",       store_opts,    {0},     0, 0, 0},
101    {"basejob",         store_opts,    {0},     0, 0, 0},
102    {"accurate",        store_opts,    {0},     0, 0, 0},
103    {"verify",          store_opts,    {0},     0, 0, 0},
104    {"onefs",           store_opts,    {0},     0, 0, 0},
105    {"recurse",         store_opts,    {0},     0, 0, 0},
106    {"sparse",          store_opts,    {0},     0, 0, 0},
107    {"hardlinks",       store_opts,    {0},     0, 0, 0},
108    {"readfifo",        store_opts,    {0},     0, 0, 0},
109    {"replace",         store_opts,    {0},     0, 0, 0},
110    {"portable",        store_opts,    {0},     0, 0, 0},
111    {"mtimeonly",       store_opts,    {0},     0, 0, 0},
112    {"keepatime",       store_opts,    {0},     0, 0, 0},
113    {"regex",           store_regex,   {0},     0, 0, 0},
114    {"regexdir",        store_regex,   {0},     1, 0, 0},
115    {"regexfile",       store_regex,   {0},     2, 0, 0},
116    {"base",            store_base,    {0},     0, 0, 0},
117    {"wild",            store_wild,    {0},     0, 0, 0},
118    {"wilddir",         store_wild,    {0},     1, 0, 0},
119    {"wildfile",        store_wild,    {0},     2, 0, 0},
120    {"exclude",         store_opts,    {0},     0, 0, 0},
121    {"aclsupport",      store_opts,    {0},     0, 0, 0},
122    {"plugin",          store_plugin,  {0},     0, 0, 0},
123    {"ignorecase",      store_opts,    {0},     0, 0, 0},
124    {"fstype",          store_fstype,  {0},     0, 0, 0},
125    {"hfsplussupport",  store_opts,    {0},     0, 0, 0},
126    {"noatime",         store_opts,    {0},     0, 0, 0},
127    {"enhancedwild",    store_opts,    {0},     0, 0, 0},
128    {"drivetype",       store_drivetype, {0},   0, 0, 0},
129    {"checkfilechanges",store_opts,    {0},     0, 0, 1},
130    {"strippath",       store_opts,    {0},     0, 0, 0},
131    {"honornodumpflag", store_opts,    {0},     0, 0, 0},
132    {"xattrsupport",    store_opts,    {0},     0, 0, 0},
133    {NULL, NULL, {0}, 0, 0, 0}
134 };
135
136
137 /* Define FileSet KeyWord values */
138 enum {
139    INC_KW_NONE,
140    INC_KW_COMPRESSION,
141    INC_KW_DIGEST,
142    INC_KW_ENCRYPTION,
143    INC_KW_VERIFY,
144    INC_KW_BASEJOB,
145    INC_KW_ACCURATE,
146    INC_KW_ONEFS,
147    INC_KW_RECURSE,
148    INC_KW_SPARSE,
149    INC_KW_HARDLINK,
150    INC_KW_REPLACE,               /* restore options */
151    INC_KW_READFIFO,              /* Causes fifo data to be read */
152    INC_KW_PORTABLE,
153    INC_KW_MTIMEONLY,
154    INC_KW_KEEPATIME,
155    INC_KW_EXCLUDE,
156    INC_KW_ACL,
157    INC_KW_IGNORECASE,
158    INC_KW_HFSPLUS,
159    INC_KW_NOATIME,
160    INC_KW_ENHANCEDWILD,
161    INC_KW_CHKCHANGES,
162    INC_KW_STRIPPATH,
163    INC_KW_HONOR_NODUMP,
164    INC_KW_XATTR
165 };
166
167 /*
168  * This is the list of options that can be stored by store_opts
169  *   Note, now that the old style Include/Exclude code is gone,
170  *   the INC_KW code could be put into the "code" field of the
171  *   options given above.
172  */
173 static struct s_kw FS_option_kw[] = {
174    {"compression", INC_KW_COMPRESSION},
175    {"signature",   INC_KW_DIGEST},
176    {"encryption",  INC_KW_ENCRYPTION},
177    {"verify",      INC_KW_VERIFY},
178    {"basejob",     INC_KW_BASEJOB},
179    {"accurate",    INC_KW_ACCURATE},
180    {"onefs",       INC_KW_ONEFS},
181    {"recurse",     INC_KW_RECURSE},
182    {"sparse",      INC_KW_SPARSE},
183    {"hardlinks",   INC_KW_HARDLINK},
184    {"replace",     INC_KW_REPLACE},
185    {"readfifo",    INC_KW_READFIFO},
186    {"portable",    INC_KW_PORTABLE},
187    {"mtimeonly",   INC_KW_MTIMEONLY},
188    {"keepatime",   INC_KW_KEEPATIME},
189    {"exclude",     INC_KW_EXCLUDE},
190    {"aclsupport",  INC_KW_ACL},
191    {"ignorecase",  INC_KW_IGNORECASE},
192    {"hfsplussupport", INC_KW_HFSPLUS},
193    {"noatime",     INC_KW_NOATIME},
194    {"enhancedwild", INC_KW_ENHANCEDWILD},
195    {"checkfilechanges", INC_KW_CHKCHANGES},
196    {"strippath",   INC_KW_STRIPPATH},
197    {"honornodumpflag", INC_KW_HONOR_NODUMP},
198    {"xattrsupport", INC_KW_XATTR},
199    {NULL,          0}
200 };
201
202 /* Options for FileSet keywords */
203
204 struct s_fs_opt {
205    const char *name;
206    int keyword;
207    const char *option;
208 };
209
210 /*
211  * Options permitted for each keyword and resulting value.
212  * The output goes into opts, which are then transmitted to
213  * the FD for application as options to the following list of
214  * included files.
215  */
216 static struct s_fs_opt FS_options[] = {
217    {"md5",      INC_KW_DIGEST,        "M"},
218    {"sha1",     INC_KW_DIGEST,        "S"},
219    {"sha256",   INC_KW_DIGEST,       "S2"},
220    {"sha512",   INC_KW_DIGEST,       "S3"},
221    {"gzip",     INC_KW_COMPRESSION,  "Z6"},
222    {"gzip1",    INC_KW_COMPRESSION,  "Z1"},
223    {"gzip2",    INC_KW_COMPRESSION,  "Z2"},
224    {"gzip3",    INC_KW_COMPRESSION,  "Z3"},
225    {"gzip4",    INC_KW_COMPRESSION,  "Z4"},
226    {"gzip5",    INC_KW_COMPRESSION,  "Z5"},
227    {"gzip6",    INC_KW_COMPRESSION,  "Z6"},
228    {"gzip7",    INC_KW_COMPRESSION,  "Z7"},
229    {"gzip8",    INC_KW_COMPRESSION,  "Z8"},
230    {"gzip9",    INC_KW_COMPRESSION,  "Z9"},
231    {"blowfish", INC_KW_ENCRYPTION,    "B"},   /* ***FIXME*** not implemented */
232    {"3des",     INC_KW_ENCRYPTION,    "3"},   /* ***FIXME*** not implemented */
233    {"yes",      INC_KW_ONEFS,         "0"},
234    {"no",       INC_KW_ONEFS,         "f"},
235    {"yes",      INC_KW_RECURSE,       "0"},
236    {"no",       INC_KW_RECURSE,       "h"},
237    {"yes",      INC_KW_SPARSE,        "s"},
238    {"no",       INC_KW_SPARSE,        "0"},
239    {"yes",      INC_KW_HARDLINK,      "0"},
240    {"no",       INC_KW_HARDLINK,      "H"},
241    {"always",   INC_KW_REPLACE,       "a"},
242    {"ifnewer",  INC_KW_REPLACE,       "w"},
243    {"never",    INC_KW_REPLACE,       "n"},
244    {"yes",      INC_KW_READFIFO,      "r"},
245    {"no",       INC_KW_READFIFO,      "0"},
246    {"yes",      INC_KW_PORTABLE,      "p"},
247    {"no",       INC_KW_PORTABLE,      "0"},
248    {"yes",      INC_KW_MTIMEONLY,     "m"},
249    {"no",       INC_KW_MTIMEONLY,     "0"},
250    {"yes",      INC_KW_KEEPATIME,     "k"},
251    {"no",       INC_KW_KEEPATIME,     "0"},
252    {"yes",      INC_KW_EXCLUDE,       "e"},
253    {"no",       INC_KW_EXCLUDE,       "0"},
254    {"yes",      INC_KW_ACL,           "A"},
255    {"no",       INC_KW_ACL,           "0"},
256    {"yes",      INC_KW_IGNORECASE,    "i"},
257    {"no",       INC_KW_IGNORECASE,    "0"},
258    {"yes",      INC_KW_HFSPLUS,       "R"},   /* "R" for resource fork */
259    {"no",       INC_KW_HFSPLUS,       "0"},
260    {"yes",      INC_KW_NOATIME,       "K"},
261    {"no",       INC_KW_NOATIME,       "0"},
262    {"yes",      INC_KW_ENHANCEDWILD,  "K"},
263    {"no",       INC_KW_ENHANCEDWILD,  "0"},
264    {"yes",      INC_KW_CHKCHANGES,    "c"},
265    {"no",       INC_KW_CHKCHANGES,    "0"},
266    {"yes",      INC_KW_HONOR_NODUMP,  "N"},
267    {"no",       INC_KW_HONOR_NODUMP,  "0"},
268    {"yes",      INC_KW_XATTR,         "X"},
269    {"no",       INC_KW_XATTR,         "0"},
270    {NULL,       0,                      0}
271 };
272
273
274
275 /*
276  * Scan for right hand side of Include options (keyword=option) is
277  *    converted into one or two characters. Verifyopts=xxxx is Vxxxx:
278  *    Whatever is found is concatenated to the opts string.
279  * This code is also used inside an Options resource.
280  */
281 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
282 {
283    int token, i;
284    char option[3];
285    int lcopts = lc->options;
286
287    option[0] = 0;                     /* default option = none */
288    option[2] = 0;                     /* terminate options */
289    lc->options |= LOPT_STRING;        /* force string */
290    token = lex_get_token(lc, T_STRING);          /* expect at least one option */
291    if (keyword == INC_KW_VERIFY) { /* special case */
292       /* ***FIXME**** ensure these are in permitted set */
293       bstrncat(opts, "V", optlen);         /* indicate Verify */
294       bstrncat(opts, lc->str, optlen);
295       bstrncat(opts, ":", optlen);         /* terminate it */
296       Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
297    } else if (keyword == INC_KW_ACCURATE) { /* special case */
298       /* ***FIXME**** ensure these are in permitted set */
299       bstrncat(opts, "C", optlen);         /* indicate Accurate */
300       bstrncat(opts, lc->str, optlen);
301       bstrncat(opts, ":", optlen);         /* terminate it */
302       Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
303    } else if (keyword == INC_KW_BASEJOB) { /* special case */
304       /* ***FIXME**** ensure these are in permitted set */
305       bstrncat(opts, "J", optlen);         /* indicate BaseJob */
306       bstrncat(opts, lc->str, optlen);
307       bstrncat(opts, ":", optlen);         /* terminate it */
308       Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
309    } else if (keyword == INC_KW_STRIPPATH) { /* another special case */
310       if (!is_an_integer(lc->str)) {
311          scan_err1(lc, _("Expected a strip path positive integer, got:%s:"), lc->str);
312       }
313       bstrncat(opts, "P", optlen);         /* indicate strip path */
314       bstrncat(opts, lc->str, optlen);
315       bstrncat(opts, ":", optlen);         /* terminate it */
316       Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
317    /*
318     * Standard keyword options for Include/Exclude
319     */
320    } else {
321       for (i=0; FS_options[i].name; i++) {
322          if (FS_options[i].keyword == keyword && strcasecmp(lc->str, FS_options[i].name) == 0) {
323             /* NOTE! maximum 2 letters here or increase option[3] */
324             option[0] = FS_options[i].option[0];
325             option[1] = FS_options[i].option[1];
326             i = 0;
327             break;
328          }
329       }
330       if (i != 0) {
331          scan_err1(lc, _("Expected a FileSet option keyword, got:%s:"), lc->str);
332       } else { /* add option */
333          bstrncat(opts, option, optlen);
334          Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
335       }
336    }
337    lc->options = lcopts;
338
339    /* If option terminated by comma, eat it */
340    if (lc->ch == ',') {
341       token = lex_get_token(lc, T_ALL);      /* yes, eat comma */
342    }
343 }
344
345 /*
346  *
347  * Store FileSet Include/Exclude info
348  *  new style includes are handled in store_newinc()
349  */
350 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass)
351 {
352    int token;
353
354    /*
355     * Decide if we are doing a new Include or an old include. The
356     *  new Include is followed immediately by open brace, whereas the
357     *  old include has options following the Include.
358     */
359    token = lex_get_token(lc, T_SKIP_EOL);
360    if (token == T_BOB) {
361       store_newinc(lc, item, index, pass);
362       return;
363    }
364    scan_err0(lc, _("Old style Include/Exclude not supported\n"));
365 }
366
367
368 /*
369  * Store new style FileSet Include/Exclude info
370  *
371  *  Note, when this routine is called, we are inside a FileSet
372  *  resource.  We treat the Include/Execlude like a sort of
373  *  mini-resource within the FileSet resource.
374  */
375 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass)
376 {
377    int token, i;
378    INCEXE *incexe;
379    bool options;
380
381    if (!res_all.res_fs.have_MD5) {
382       MD5Init(&res_all.res_fs.md5c);
383       res_all.res_fs.have_MD5 = true;
384    }
385    memset(&res_incexe, 0, sizeof(INCEXE));
386    res_all.res_fs.new_include = true;
387    while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
388       if (token == T_EOB) {
389          break;
390       }
391       if (token != T_IDENTIFIER) {
392          scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
393       }
394       for (i=0; newinc_items[i].name; i++) {
395          options = strcasecmp(lc->str, "options") == 0;
396          if (strcasecmp(newinc_items[i].name, lc->str) == 0) {
397             if (!options) {
398                token = lex_get_token(lc, T_SKIP_EOL);
399                if (token != T_EQUALS) {
400                   scan_err1(lc, _("expected an equals, got: %s"), lc->str);
401                }
402             }
403             /* Call item handler */
404             newinc_items[i].handler(lc, &newinc_items[i], i, pass, item->code);
405             i = -1;
406             break;
407          }
408       }
409       if (i >=0) {
410          scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
411       }
412    }
413    if (pass == 1) {
414       incexe = (INCEXE *)malloc(sizeof(INCEXE));
415       memcpy(incexe, &res_incexe, sizeof(INCEXE));
416       memset(&res_incexe, 0, sizeof(INCEXE));
417       if (item->code == 0) { /* include */
418          if (res_all.res_fs.num_includes == 0) {
419             res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
420          } else {
421             res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
422                            sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
423          }
424          res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
425          Dmsg1(900, "num_includes=%d\n", res_all.res_fs.num_includes);
426       } else {    /* exclude */
427          if (res_all.res_fs.num_excludes == 0) {
428             res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
429          } else {
430             res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
431                            sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
432          }
433          res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
434          Dmsg1(900, "num_excludes=%d\n", res_all.res_fs.num_excludes);
435       }
436    }
437    scan_to_eol(lc);
438    set_bit(index, res_all.hdr.item_present);
439 }
440
441
442 /* Store regex info */
443 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass)
444 {
445    int token, rc;
446    regex_t preg;
447    char prbuf[500];
448    const char *type;
449    int newsize;
450
451    token = lex_get_token(lc, T_SKIP_EOL);
452    if (pass == 1) {
453       /* Pickup regex string
454        */
455       switch (token) {
456       case T_IDENTIFIER:
457       case T_UNQUOTED_STRING:
458       case T_QUOTED_STRING:
459          rc = regcomp(&preg, lc->str, REG_EXTENDED);
460          if (rc != 0) {
461             regerror(rc, &preg, prbuf, sizeof(prbuf));
462             regfree(&preg);
463             scan_err1(lc, _("Regex compile error. ERR=%s\n"), prbuf);
464             break;
465          }
466          regfree(&preg);
467          if (item->code == 1) {
468             type = "regexdir";
469             res_incexe.current_opts->regexdir.append(bstrdup(lc->str));
470             newsize = res_incexe.current_opts->regexdir.size();
471          } else if (item->code == 2) {
472             type = "regexfile";
473             res_incexe.current_opts->regexfile.append(bstrdup(lc->str));
474             newsize = res_incexe.current_opts->regexfile.size();
475          } else {
476             type = "regex";
477             res_incexe.current_opts->regex.append(bstrdup(lc->str));
478             newsize = res_incexe.current_opts->regex.size();
479          }
480          Dmsg4(900, "set %s %p size=%d %s\n",
481             type, res_incexe.current_opts, newsize, lc->str);
482          break;
483       default:
484          scan_err1(lc, _("Expected a regex string, got: %s\n"), lc->str);
485       }
486    }
487    scan_to_eol(lc);
488 }
489
490 /* Store Base info */
491 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass)
492 {
493    int token;
494
495    token = lex_get_token(lc, T_NAME);
496    if (pass == 1) {
497       /*
498        * Pickup Base Job Name
499        */
500       res_incexe.current_opts->base.append(bstrdup(lc->str));
501    }
502    scan_to_eol(lc);
503 }
504
505 /* Store reader info */
506 static void store_plugin(LEX *lc, RES_ITEM *item, int index, int pass)
507 {
508    int token;
509
510    token = lex_get_token(lc, T_NAME);
511    if (pass == 1) {
512       /*
513        * Pickup plugin command
514        */
515       res_incexe.current_opts->plugin = bstrdup(lc->str);
516    }
517    scan_to_eol(lc);
518 }
519
520
521 /* Store Wild-card info */
522 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass)
523 {
524    int token;
525    const char *type;
526    int newsize;
527
528    token = lex_get_token(lc, T_SKIP_EOL);
529    if (pass == 1) {
530       /*
531        * Pickup Wild-card string
532        */
533       switch (token) {
534       case T_IDENTIFIER:
535       case T_UNQUOTED_STRING:
536       case T_QUOTED_STRING:
537          if (item->code == 1) {
538             type = "wilddir";
539             res_incexe.current_opts->wilddir.append(bstrdup(lc->str));
540             newsize = res_incexe.current_opts->wilddir.size();
541          } else if (item->code == 2) {
542             if (strpbrk(lc->str, "/\\") != NULL) {
543                type = "wildfile";
544                res_incexe.current_opts->wildfile.append(bstrdup(lc->str));
545                newsize = res_incexe.current_opts->wildfile.size();
546             } else {
547                type = "wildbase";
548                res_incexe.current_opts->wildbase.append(bstrdup(lc->str));
549                newsize = res_incexe.current_opts->wildbase.size();
550             }
551          } else {
552             type = "wild";
553             res_incexe.current_opts->wild.append(bstrdup(lc->str));
554             newsize = res_incexe.current_opts->wild.size();
555          }
556          Dmsg4(9, "set %s %p size=%d %s\n",
557             type, res_incexe.current_opts, newsize, lc->str);
558          break;
559       default:
560          scan_err1(lc, _("Expected a wild-card string, got: %s\n"), lc->str);
561       }
562    }
563    scan_to_eol(lc);
564 }
565
566 /* Store fstype info */
567 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass)
568 {
569    int token;
570
571    token = lex_get_token(lc, T_SKIP_EOL);
572    if (pass == 1) {
573       /* Pickup fstype string */
574       switch (token) {
575       case T_IDENTIFIER:
576       case T_UNQUOTED_STRING:
577       case T_QUOTED_STRING:
578          res_incexe.current_opts->fstype.append(bstrdup(lc->str));
579          Dmsg3(900, "set fstype %p size=%d %s\n",
580             res_incexe.current_opts, res_incexe.current_opts->fstype.size(), lc->str);
581          break;
582       default:
583          scan_err1(lc, _("Expected an fstype string, got: %s\n"), lc->str);
584       }
585    }
586    scan_to_eol(lc);
587 }
588
589 /* Store exclude directory containing  info */
590 static void store_excludedir(LEX *lc, RES_ITEM2 *item, int index, int pass, bool exclude)
591 {
592    int token;
593
594    if (exclude) {
595       scan_err0(lc, _("ExcludeDirContaining directive not permitted in Exclude.\n"));
596       /* NOT REACHED */
597    }
598    token = lex_get_token(lc, T_NAME);
599    if (pass == 1) {
600       res_incexe.ignoredir = bstrdup(lc->str);
601    }
602    scan_to_eol(lc);
603 }
604
605 /* Store drivetype info */
606 static void store_drivetype(LEX *lc, RES_ITEM *item, int index, int pass)
607 {
608    int token;
609
610    token = lex_get_token(lc, T_SKIP_EOL);
611    if (pass == 1) {
612       /* Pickup drivetype string */
613       switch (token) {
614       case T_IDENTIFIER:
615       case T_UNQUOTED_STRING:
616       case T_QUOTED_STRING:
617          res_incexe.current_opts->drivetype.append(bstrdup(lc->str));
618          Dmsg3(900, "set drivetype %p size=%d %s\n",
619             res_incexe.current_opts, res_incexe.current_opts->drivetype.size(), lc->str);
620          break;
621       default:
622          scan_err1(lc, _("Expected an drivetype string, got: %s\n"), lc->str);
623       }
624    }
625    scan_to_eol(lc);
626 }
627
628 /*
629  * Store Filename info. Note, for minor efficiency reasons, we
630  * always increase the name buffer by 10 items because we expect
631  * to add more entries.
632  */
633 static void store_fname(LEX *lc, RES_ITEM2 *item, int index, int pass, bool exclude)
634 {
635    int token;
636    INCEXE *incexe;
637
638    token = lex_get_token(lc, T_SKIP_EOL);
639    if (pass == 1) {
640       /* Pickup Filename string
641        */
642       switch (token) {
643       case T_IDENTIFIER:
644       case T_UNQUOTED_STRING:
645          if (strchr(lc->str, '\\')) {
646             scan_err1(lc, _("Backslash found. Use forward slashes or quote the string.: %s\n"), lc->str);
647             /* NOT REACHED */
648          }
649       case T_QUOTED_STRING:
650          if (res_all.res_fs.have_MD5) {
651             MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
652          }
653          incexe = &res_incexe;
654          if (incexe->name_list.size() == 0) {
655             incexe->name_list.init(10, true);
656          }
657          incexe->name_list.append(bstrdup(lc->str));
658          Dmsg1(900, "Add to name_list %s\n", lc->str);
659          break;
660       default:
661          scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
662       }
663    }
664    scan_to_eol(lc);
665 }
666
667 /*
668  * Store Filename info. Note, for minor efficiency reasons, we
669  * always increase the name buffer by 10 items because we expect
670  * to add more entries.
671  */
672 static void store_plugin_name(LEX *lc, RES_ITEM2 *item, int index, int pass, bool exclude)
673 {
674    int token;
675    INCEXE *incexe;
676
677    if (exclude) {
678       scan_err0(lc, _("Plugin directive not permitted in Exclude\n"));
679       /* NOT REACHED */
680    }
681    token = lex_get_token(lc, T_SKIP_EOL);
682    if (pass == 1) {
683       /* Pickup Filename string
684        */
685       switch (token) {
686       case T_IDENTIFIER:
687       case T_UNQUOTED_STRING:
688          if (strchr(lc->str, '\\')) {
689             scan_err1(lc, _("Backslash found. Use forward slashes or quote the string.: %s\n"), lc->str);
690             /* NOT REACHED */
691          }
692       case T_QUOTED_STRING:
693          if (res_all.res_fs.have_MD5) {
694             MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
695          }
696          incexe = &res_incexe;
697          if (incexe->plugin_list.size() == 0) {
698             incexe->plugin_list.init(10, true);
699          }
700          incexe->plugin_list.append(bstrdup(lc->str));
701          Dmsg1(900, "Add to plugin_list %s\n", lc->str);
702          break;
703       default:
704          scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
705          /* NOT REACHED */
706       }
707    }
708    scan_to_eol(lc);
709 }
710
711
712
713 /*
714  * Come here when Options seen in Include/Exclude
715  */
716 static void options_res(LEX *lc, RES_ITEM2 *item, int index, int pass, bool exclude)
717 {
718    int token, i;
719
720    if (exclude) {
721       scan_err0(lc, _("Options section not permitted in Exclude\n"));
722       /* NOT REACHED */
723    }
724    token = lex_get_token(lc, T_SKIP_EOL);
725    if (token != T_BOB) {
726       scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
727    }
728
729    if (pass == 1) {
730       setup_current_opts();
731    }
732
733    while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
734       if (token == T_EOL) {
735          continue;
736       }
737       if (token == T_EOB) {
738          break;
739       }
740       if (token != T_IDENTIFIER) {
741          scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
742       }
743       for (i=0; options_items[i].name; i++) {
744          if (strcasecmp(options_items[i].name, lc->str) == 0) {
745             token = lex_get_token(lc, T_SKIP_EOL);
746             if (token != T_EQUALS) {
747                scan_err1(lc, _("expected an equals, got: %s"), lc->str);
748             }
749             /* Call item handler */
750             options_items[i].handler(lc, &options_items[i], i, pass);
751             i = -1;
752             break;
753          }
754       }
755       if (i >=0) {
756          scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
757       }
758    }
759 }
760
761
762 /*
763  * New style options come here
764  */
765 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass)
766 {
767    int i;
768    int keyword;
769    char inc_opts[100];
770
771    inc_opts[0] = 0;
772    keyword = INC_KW_NONE;
773    /* Look up the keyword */
774    for (i=0; FS_option_kw[i].name; i++) {
775       if (strcasecmp(item->name, FS_option_kw[i].name) == 0) {
776          keyword = FS_option_kw[i].token;
777          break;
778       }
779    }
780    if (keyword == INC_KW_NONE) {
781       scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
782    }
783    /* Now scan for the value */
784    scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
785    if (pass == 1) {
786       bstrncat(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
787       Dmsg2(900, "new pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
788    }
789    scan_to_eol(lc);
790 }
791
792
793
794 /* If current_opts not defined, create first entry */
795 static void setup_current_opts(void)
796 {
797    FOPTS *fo = (FOPTS *)malloc(sizeof(FOPTS));
798    memset(fo, 0, sizeof(FOPTS));
799    fo->regex.init(1, true);
800    fo->regexdir.init(1, true);
801    fo->regexfile.init(1, true);
802    fo->wild.init(1, true);
803    fo->wilddir.init(1, true);
804    fo->wildfile.init(1, true);
805    fo->wildbase.init(1, true);
806    fo->base.init(1, true);
807    fo->fstype.init(1, true);
808    fo->drivetype.init(1, true);
809    res_incexe.current_opts = fo;
810    if (res_incexe.num_opts == 0) {
811       res_incexe.opts_list = (FOPTS **)malloc(sizeof(FOPTS *));
812    } else {
813       res_incexe.opts_list = (FOPTS **)realloc(res_incexe.opts_list,
814                      sizeof(FOPTS *) * (res_incexe.num_opts + 1));
815    }
816    res_incexe.opts_list[res_incexe.num_opts++] = fo;
817 }