2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
21 * Main routine for finding files on a file system.
22 * The heart of the work to find the files on the
23 * system is done in find_one.c. Here we have the
24 * higher level control as well as the matching
25 * routines for the new syntax Options resource.
35 static const int dbglvl = 450;
37 int32_t name_max; /* filename max length */
38 int32_t path_max; /* path name max length */
42 #define bmalloc(x) sm_malloc(__FILE__, __LINE__, x)
44 static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level);
46 static const int fnmode = 0;
49 * Initialize the find files "global" variables
51 FF_PKT *init_find_files()
55 ff = (FF_PKT *)bmalloc(sizeof(FF_PKT));
56 memset(ff, 0, sizeof(FF_PKT));
58 ff->sys_fname = get_pool_memory(PM_FNAME);
60 /* Get system path and filename maximum lengths */
61 path_max = pathconf(".", _PC_PATH_MAX);
62 if (path_max < 2048) {
66 name_max = pathconf(".", _PC_NAME_MAX);
67 if (name_max < 2048) {
70 path_max++; /* add for EOS */
71 name_max++; /* add for EOS */
73 Dmsg1(dbglvl, "init_find_files ff=%p\n", ff);
78 * Set find_files options. For the moment, we only
79 * provide for full/incremental saves, and setting
80 * of save_time. For additional options, see above
83 set_find_options(FF_PKT *ff, int incremental, time_t save_time)
85 Dmsg0(dbglvl, "Enter set_find_options()\n");
86 ff->incremental = incremental;
87 ff->save_time = save_time;
88 Dmsg0(dbglvl, "Leave set_find_options()\n");
92 set_find_changed_function(FF_PKT *ff, bool check_fct(JCR *jcr, FF_PKT *ff))
94 Dmsg0(dbglvl, "Enter set_find_changed_function()\n");
95 ff->check_fct = check_fct;
99 set_find_snapshot_function(FF_PKT *ff,
100 bool convert_path(JCR *jcr, FF_PKT *ff, dlist *filelist, dlistString *node))
102 ff->snapshot_convert_fct = convert_path;
106 * Call this subroutine with a callback subroutine as the first
107 * argument and a packet as the second argument, this packet
108 * will be passed back to the callback subroutine as the last
113 find_files(JCR *jcr, FF_PKT *ff, int file_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level),
114 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level))
116 ff->file_save = file_save;
117 ff->plugin_save = plugin_save;
119 /* This is the new way */
120 findFILESET *fileset = ff->fileset;
123 /* TODO: We probably need be move the initialization in the fileset loop,
124 * at this place flags options are "concatenated" accross Include {} blocks
125 * (not only Options{} blocks inside a Include{})
128 for (i=0; i<fileset->include_list.size(); i++) {
129 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
130 fileset->incexe = incexe;
132 /* Here, we reset some values between two different Include{} */
133 strcpy(ff->VerifyOpts, "V");
134 strcpy(ff->AccurateOpts, "Cmcs"); /* mtime+ctime+size by default */
135 strcpy(ff->BaseJobOpts, "Jspug5"); /* size+perm+user+group+chk */
137 ff->opt_plugin = false;
140 * By setting all options, we in effect OR the global options
141 * which is what we want.
143 for (j=0; j<incexe->opts_list.size(); j++) {
144 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
145 ff->flags |= fo->flags;
146 ff->Compress_algo = fo->Compress_algo;
147 ff->Compress_level = fo->Compress_level;
148 ff->strip_path = fo->strip_path;
149 ff->fstypes = fo->fstype;
150 ff->drivetypes = fo->drivetype;
151 if (fo->plugin != NULL) {
152 ff->plugin = fo->plugin; /* TODO: generate a plugin event ? */
153 ff->opt_plugin = true;
155 bstrncat(ff->VerifyOpts, fo->VerifyOpts, sizeof(ff->VerifyOpts)); /* TODO: Concat or replace? */
156 if (fo->AccurateOpts[0]) {
157 bstrncpy(ff->AccurateOpts, fo->AccurateOpts, sizeof(ff->AccurateOpts));
159 if (fo->BaseJobOpts[0]) {
160 bstrncpy(ff->BaseJobOpts, fo->BaseJobOpts, sizeof(ff->BaseJobOpts));
163 Dmsg4(50, "Verify=<%s> Accurate=<%s> BaseJob=<%s> flags=<%d>\n",
164 ff->VerifyOpts, ff->AccurateOpts, ff->BaseJobOpts, ff->flags);
166 foreach_dlist(node, &incexe->name_list) {
167 char *fname = node->c_str();
168 Dmsg1(dbglvl, "F %s\n", fname);
170 ff->top_fname = fname;
171 /* Convert the filename if needed */
172 if (ff->snapshot_convert_fct) {
173 ff->snapshot_convert_fct(jcr, ff, &incexe->name_list, node);
176 if (find_one_file(jcr, ff, our_callback, ff->top_fname, (dev_t)-1, true) == 0) {
177 return 0; /* error return */
180 if (job_canceled(jcr)) {
184 foreach_dlist(node, &incexe->plugin_list) {
185 char *fname = node->c_str();
187 Jmsg(jcr, M_FATAL, 0, _("Plugin: \"%s\" not found.\n"), fname);
190 Dmsg1(dbglvl, "PluginCommand: %s\n", fname);
191 ff->top_fname = fname;
192 ff->cmd_plugin = true;
194 /* Make sure that opt plugin is not set
195 * The current implementation doesn't allow option plugin
196 * and command plugin to run at the same time
198 ff->opt_plugin = false;
201 plugin_save(jcr, ff, true);
202 ff->cmd_plugin = false;
203 if (job_canceled(jcr)) {
213 * Test if the currently selected directory (in ff->fname) is
214 * explicitly in the Include list or explicitly in the Exclude
217 bool is_in_fileset(FF_PKT *ff)
223 findFILESET *fileset = ff->fileset;
225 for (i=0; i<fileset->include_list.size(); i++) {
226 incexe = (findINCEXE *)fileset->include_list.get(i);
227 foreach_dlist(node, &incexe->name_list) {
228 fname = node->c_str();
229 Dmsg2(dbglvl, "Inc fname=%s ff->fname=%s\n", fname, ff->fname);
230 if (strcmp(fname, ff->fname) == 0) {
235 for (i=0; i<fileset->exclude_list.size(); i++) {
236 incexe = (findINCEXE *)fileset->exclude_list.get(i);
237 foreach_dlist(node, &incexe->name_list) {
238 fname = node->c_str();
239 Dmsg2(dbglvl, "Exc fname=%s ff->fname=%s\n", fname, ff->fname);
240 if (strcmp(fname, ff->fname) == 0) {
250 bool accept_file(FF_PKT *ff)
254 findFILESET *fileset = ff->fileset;
255 findINCEXE *incexe = fileset->incexe;
256 const char *basename;
257 int (*match_func)(const char *pattern, const char *string, int flags);
259 Dmsg1(dbglvl, "enter accept_file: fname=%s\n", ff->fname);
260 if (ff->flags & FO_ENHANCEDWILD) {
261 // match_func = enh_fnmatch;
262 match_func = fnmatch;
263 if ((basename = last_path_separator(ff->fname)) != NULL)
266 basename = ff->fname;
268 match_func = fnmatch;
269 basename = ff->fname;
272 for (j = 0; j < incexe->opts_list.size(); j++) {
273 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
274 ff->flags = fo->flags;
275 ff->Compress_algo = fo->Compress_algo;
276 ff->Compress_level = fo->Compress_level;
277 ff->fstypes = fo->fstype;
278 ff->drivetypes = fo->drivetype;
280 fnm_flags = (ff->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
281 fnm_flags |= (ff->flags & FO_ENHANCEDWILD) ? FNM_PATHNAME : 0;
283 if (S_ISDIR(ff->statp.st_mode)) {
284 for (k=0; k<fo->wilddir.size(); k++) {
285 if (match_func((char *)fo->wilddir.get(k), ff->fname, fnmode|fnm_flags) == 0) {
286 if (ff->flags & FO_EXCLUDE) {
287 Dmsg2(dbglvl, "Exclude wilddir: %s file=%s\n", (char *)fo->wilddir.get(k),
289 return false; /* reject dir */
291 return true; /* accept dir */
295 for (k=0; k<fo->wildfile.size(); k++) {
296 if (match_func((char *)fo->wildfile.get(k), ff->fname, fnmode|fnm_flags) == 0) {
297 if (ff->flags & FO_EXCLUDE) {
298 Dmsg2(dbglvl, "Exclude wildfile: %s file=%s\n", (char *)fo->wildfile.get(k),
300 return false; /* reject file */
302 return true; /* accept file */
306 for (k=0; k<fo->wildbase.size(); k++) {
307 if (match_func((char *)fo->wildbase.get(k), basename, fnmode|fnm_flags) == 0) {
308 if (ff->flags & FO_EXCLUDE) {
309 Dmsg2(dbglvl, "Exclude wildbase: %s file=%s\n", (char *)fo->wildbase.get(k),
311 return false; /* reject file */
313 return true; /* accept file */
317 for (k=0; k<fo->wild.size(); k++) {
318 if (match_func((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
319 if (ff->flags & FO_EXCLUDE) {
320 Dmsg2(dbglvl, "Exclude wild: %s file=%s\n", (char *)fo->wild.get(k),
322 return false; /* reject file */
324 return true; /* accept file */
327 if (S_ISDIR(ff->statp.st_mode)) {
328 for (k=0; k<fo->regexdir.size(); k++) {
329 const int nmatch = 30;
330 regmatch_t pmatch[nmatch];
331 if (regexec((regex_t *)fo->regexdir.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
332 if (ff->flags & FO_EXCLUDE) {
333 return false; /* reject file */
335 return true; /* accept file */
339 for (k=0; k<fo->regexfile.size(); k++) {
340 const int nmatch = 30;
341 regmatch_t pmatch[nmatch];
342 if (regexec((regex_t *)fo->regexfile.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
343 if (ff->flags & FO_EXCLUDE) {
344 return false; /* reject file */
346 return true; /* accept file */
350 for (k=0; k<fo->regex.size(); k++) {
351 const int nmatch = 30;
352 regmatch_t pmatch[nmatch];
353 if (regexec((regex_t *)fo->regex.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
354 if (ff->flags & FO_EXCLUDE) {
355 return false; /* reject file */
357 return true; /* accept file */
361 * If we have an empty Options clause with exclude, then
364 if (ff->flags & FO_EXCLUDE &&
365 fo->regex.size() == 0 && fo->wild.size() == 0 &&
366 fo->regexdir.size() == 0 && fo->wilddir.size() == 0 &&
367 fo->regexfile.size() == 0 && fo->wildfile.size() == 0 &&
368 fo->wildbase.size() == 0) {
369 return false; /* reject file */
373 /* Now apply the Exclude { } directive */
374 for (i=0; i<fileset->exclude_list.size(); i++) {
375 findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
376 for (j=0; j<incexe->opts_list.size(); j++) {
377 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
378 fnm_flags = (fo->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
379 for (k=0; k<fo->wild.size(); k++) {
380 if (fnmatch((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
381 Dmsg1(dbglvl, "Reject wild1: %s\n", ff->fname);
382 return false; /* reject file */
386 fnm_flags = (incexe->current_opts != NULL && incexe->current_opts->flags & FO_IGNORECASE)
389 foreach_dlist(node, &incexe->name_list) {
390 char *fname = node->c_str();
391 if (fnmatch(fname, ff->fname, fnmode|fnm_flags) == 0) {
392 Dmsg1(dbglvl, "Reject wild2: %s\n", ff->fname);
393 return false; /* reject file */
401 * The code comes here for each file examined.
402 * We filter the files, then call the user's callback if
403 * the file is included.
405 static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level)
408 return ff->file_save(jcr, ff, top_level); /* accept file */
421 // return ff->file_save(jcr, ff, top_level);
423 /* These items can be filtered */
436 if (accept_file(ff)) {
437 return ff->file_save(jcr, ff, top_level);
439 Dmsg1(dbglvl, "Skip file %s\n", ff->fname);
440 return -1; /* ignore this file */
444 Dmsg1(000, "Unknown FT code %d\n", ff->type);
451 * Terminate find_files() and release
452 * all allocated memory
455 term_find_files(FF_PKT *ff)
459 free_pool_memory(ff->sys_fname);
460 if (ff->fname_save) {
461 free_pool_memory(ff->fname_save);
464 free_pool_memory(ff->link_save);
466 if (ff->ignoredir_fname) {
467 free_pool_memory(ff->ignoredir_fname);
469 if (ff->snap_fname) {
470 free_pool_memory(ff->snap_fname);
472 if (ff->snap_top_fname) {
473 free_pool_memory(ff->snap_top_fname);
476 delete ff->mtab_list;
478 hard_links = term_find_one(ff);