2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * Main routine for finding files on a file system.
21 * The heart of the work to find the files on the
22 * system is done in find_one.c. Here we have the
23 * higher level control as well as the matching
24 * routines for the new syntax Options resource.
34 static const int dbglvl = 450;
36 int32_t name_max; /* filename max length */
37 int32_t path_max; /* path name max length */
41 #define bmalloc(x) sm_malloc(__FILE__, __LINE__, x)
43 static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level);
45 static const int fnmode = 0;
48 * Initialize the find files "global" variables
50 FF_PKT *init_find_files()
54 ff = (FF_PKT *)bmalloc(sizeof(FF_PKT));
55 memset(ff, 0, sizeof(FF_PKT));
57 ff->sys_fname = get_pool_memory(PM_FNAME);
59 /* Get system path and filename maximum lengths */
60 path_max = pathconf(".", _PC_PATH_MAX);
61 if (path_max < 2048) {
65 name_max = pathconf(".", _PC_NAME_MAX);
66 if (name_max < 2048) {
69 path_max++; /* add for EOS */
70 name_max++; /* add for EOS */
72 Dmsg1(dbglvl, "init_find_files ff=%p\n", ff);
77 * Set find_files options. For the moment, we only
78 * provide for full/incremental saves, and setting
79 * of save_time. For additional options, see above
82 set_find_options(FF_PKT *ff, int incremental, time_t save_time)
84 Dmsg0(dbglvl, "Enter set_find_options()\n");
85 ff->incremental = incremental;
86 ff->save_time = save_time;
87 Dmsg0(dbglvl, "Leave set_find_options()\n");
91 set_find_changed_function(FF_PKT *ff, bool check_fct(JCR *jcr, FF_PKT *ff))
93 Dmsg0(dbglvl, "Enter set_find_changed_function()\n");
94 ff->check_fct = check_fct;
98 set_find_snapshot_function(FF_PKT *ff,
99 bool convert_path(JCR *jcr, FF_PKT *ff, dlist *filelist, dlistString *node))
101 ff->snapshot_convert_fct = convert_path;
105 * Call this subroutine with a callback subroutine as the first
106 * argument and a packet as the second argument, this packet
107 * will be passed back to the callback subroutine as the last
112 find_files(JCR *jcr, FF_PKT *ff, int file_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level),
113 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level))
115 ff->file_save = file_save;
116 ff->plugin_save = plugin_save;
118 /* This is the new way */
119 findFILESET *fileset = ff->fileset;
122 /* TODO: We probably need be move the initialization in the fileset loop,
123 * at this place flags options are "concatenated" accross Include {} blocks
124 * (not only Options{} blocks inside a Include{})
127 for (i=0; i<fileset->include_list.size(); i++) {
128 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
129 fileset->incexe = incexe;
131 /* Here, we reset some values between two different Include{} */
132 strcpy(ff->VerifyOpts, "V");
133 strcpy(ff->AccurateOpts, "Cmcs"); /* mtime+ctime+size by default */
134 strcpy(ff->BaseJobOpts, "Jspug5"); /* size+perm+user+group+chk */
136 ff->opt_plugin = false;
139 * By setting all options, we in effect OR the global options
140 * which is what we want.
142 for (j=0; j<incexe->opts_list.size(); j++) {
143 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
144 ff->flags |= fo->flags;
145 ff->Compress_algo = fo->Compress_algo;
146 ff->Compress_level = fo->Compress_level;
147 ff->strip_path = fo->strip_path;
148 ff->fstypes = fo->fstype;
149 ff->drivetypes = fo->drivetype;
150 if (fo->plugin != NULL) {
151 ff->plugin = fo->plugin; /* TODO: generate a plugin event ? */
152 ff->opt_plugin = true;
154 bstrncat(ff->VerifyOpts, fo->VerifyOpts, sizeof(ff->VerifyOpts)); /* TODO: Concat or replace? */
155 if (fo->AccurateOpts[0]) {
156 bstrncpy(ff->AccurateOpts, fo->AccurateOpts, sizeof(ff->AccurateOpts));
158 if (fo->BaseJobOpts[0]) {
159 bstrncpy(ff->BaseJobOpts, fo->BaseJobOpts, sizeof(ff->BaseJobOpts));
162 Dmsg4(50, "Verify=<%s> Accurate=<%s> BaseJob=<%s> flags=<%d>\n",
163 ff->VerifyOpts, ff->AccurateOpts, ff->BaseJobOpts, ff->flags);
165 foreach_dlist(node, &incexe->name_list) {
166 char *fname = node->c_str();
167 Dmsg1(dbglvl, "F %s\n", fname);
169 ff->top_fname = fname;
170 /* Convert the filename if needed */
171 if (ff->snapshot_convert_fct) {
172 ff->snapshot_convert_fct(jcr, ff, &incexe->name_list, node);
175 if (find_one_file(jcr, ff, our_callback, ff->top_fname, (dev_t)-1, true) == 0) {
176 return 0; /* error return */
179 if (job_canceled(jcr)) {
183 foreach_dlist(node, &incexe->plugin_list) {
184 char *fname = node->c_str();
186 Jmsg(jcr, M_FATAL, 0, _("Plugin: \"%s\" not found.\n"), fname);
189 Dmsg1(dbglvl, "PluginCommand: %s\n", fname);
190 ff->top_fname = fname;
191 ff->cmd_plugin = true;
193 /* Make sure that opt plugin is not set
194 * The current implementation doesn't allow option plugin
195 * and command plugin to run at the same time
197 ff->opt_plugin = false;
200 plugin_save(jcr, ff, true);
201 ff->cmd_plugin = false;
202 if (job_canceled(jcr)) {
212 * Test if the currently selected directory (in ff->fname) is
213 * explicitly in the Include list or explicitly in the Exclude
216 bool is_in_fileset(FF_PKT *ff)
222 findFILESET *fileset = ff->fileset;
224 for (i=0; i<fileset->include_list.size(); i++) {
225 incexe = (findINCEXE *)fileset->include_list.get(i);
226 foreach_dlist(node, &incexe->name_list) {
227 fname = node->c_str();
228 Dmsg2(dbglvl, "Inc fname=%s ff->fname=%s\n", fname, ff->fname);
229 if (strcmp(fname, ff->fname) == 0) {
234 for (i=0; i<fileset->exclude_list.size(); i++) {
235 incexe = (findINCEXE *)fileset->exclude_list.get(i);
236 foreach_dlist(node, &incexe->name_list) {
237 fname = node->c_str();
238 Dmsg2(dbglvl, "Exc fname=%s ff->fname=%s\n", fname, ff->fname);
239 if (strcmp(fname, ff->fname) == 0) {
249 bool accept_file(FF_PKT *ff)
253 findFILESET *fileset = ff->fileset;
254 findINCEXE *incexe = fileset->incexe;
255 const char *basename;
256 int (*match_func)(const char *pattern, const char *string, int flags);
258 Dmsg1(dbglvl, "enter accept_file: fname=%s\n", ff->fname);
259 if (ff->flags & FO_ENHANCEDWILD) {
260 // match_func = enh_fnmatch;
261 match_func = fnmatch;
262 if ((basename = last_path_separator(ff->fname)) != NULL)
265 basename = ff->fname;
267 match_func = fnmatch;
268 basename = ff->fname;
271 for (j = 0; j < incexe->opts_list.size(); j++) {
272 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
273 ff->flags = fo->flags;
274 ff->Compress_algo = fo->Compress_algo;
275 ff->Compress_level = fo->Compress_level;
276 ff->fstypes = fo->fstype;
277 ff->drivetypes = fo->drivetype;
279 fnm_flags = (ff->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
280 fnm_flags |= (ff->flags & FO_ENHANCEDWILD) ? FNM_PATHNAME : 0;
282 if (S_ISDIR(ff->statp.st_mode)) {
283 for (k=0; k<fo->wilddir.size(); k++) {
284 if (match_func((char *)fo->wilddir.get(k), ff->fname, fnmode|fnm_flags) == 0) {
285 if (ff->flags & FO_EXCLUDE) {
286 Dmsg2(dbglvl, "Exclude wilddir: %s file=%s\n", (char *)fo->wilddir.get(k),
288 return false; /* reject dir */
290 return true; /* accept dir */
294 for (k=0; k<fo->wildfile.size(); k++) {
295 if (match_func((char *)fo->wildfile.get(k), ff->fname, fnmode|fnm_flags) == 0) {
296 if (ff->flags & FO_EXCLUDE) {
297 Dmsg2(dbglvl, "Exclude wildfile: %s file=%s\n", (char *)fo->wildfile.get(k),
299 return false; /* reject file */
301 return true; /* accept file */
305 for (k=0; k<fo->wildbase.size(); k++) {
306 if (match_func((char *)fo->wildbase.get(k), basename, fnmode|fnm_flags) == 0) {
307 if (ff->flags & FO_EXCLUDE) {
308 Dmsg2(dbglvl, "Exclude wildbase: %s file=%s\n", (char *)fo->wildbase.get(k),
310 return false; /* reject file */
312 return true; /* accept file */
316 for (k=0; k<fo->wild.size(); k++) {
317 if (match_func((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
318 if (ff->flags & FO_EXCLUDE) {
319 Dmsg2(dbglvl, "Exclude wild: %s file=%s\n", (char *)fo->wild.get(k),
321 return false; /* reject file */
323 return true; /* accept file */
326 if (S_ISDIR(ff->statp.st_mode)) {
327 for (k=0; k<fo->regexdir.size(); k++) {
328 const int nmatch = 30;
329 regmatch_t pmatch[nmatch];
330 if (regexec((regex_t *)fo->regexdir.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
331 if (ff->flags & FO_EXCLUDE) {
332 return false; /* reject file */
334 return true; /* accept file */
338 for (k=0; k<fo->regexfile.size(); k++) {
339 const int nmatch = 30;
340 regmatch_t pmatch[nmatch];
341 if (regexec((regex_t *)fo->regexfile.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
342 if (ff->flags & FO_EXCLUDE) {
343 return false; /* reject file */
345 return true; /* accept file */
349 for (k=0; k<fo->regex.size(); k++) {
350 const int nmatch = 30;
351 regmatch_t pmatch[nmatch];
352 if (regexec((regex_t *)fo->regex.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
353 if (ff->flags & FO_EXCLUDE) {
354 return false; /* reject file */
356 return true; /* accept file */
360 * If we have an empty Options clause with exclude, then
363 if (ff->flags & FO_EXCLUDE &&
364 fo->regex.size() == 0 && fo->wild.size() == 0 &&
365 fo->regexdir.size() == 0 && fo->wilddir.size() == 0 &&
366 fo->regexfile.size() == 0 && fo->wildfile.size() == 0 &&
367 fo->wildbase.size() == 0) {
368 return false; /* reject file */
372 /* Now apply the Exclude { } directive */
373 for (i=0; i<fileset->exclude_list.size(); i++) {
374 findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
375 for (j=0; j<incexe->opts_list.size(); j++) {
376 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
377 fnm_flags = (fo->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
378 for (k=0; k<fo->wild.size(); k++) {
379 if (fnmatch((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
380 Dmsg1(dbglvl, "Reject wild1: %s\n", ff->fname);
381 return false; /* reject file */
385 fnm_flags = (incexe->current_opts != NULL && incexe->current_opts->flags & FO_IGNORECASE)
388 foreach_dlist(node, &incexe->name_list) {
389 char *fname = node->c_str();
390 if (fnmatch(fname, ff->fname, fnmode|fnm_flags) == 0) {
391 Dmsg1(dbglvl, "Reject wild2: %s\n", ff->fname);
392 return false; /* reject file */
400 * The code comes here for each file examined.
401 * We filter the files, then call the user's callback if
402 * the file is included.
404 static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level)
407 return ff->file_save(jcr, ff, top_level); /* accept file */
420 // return ff->file_save(jcr, ff, top_level);
422 /* These items can be filtered */
435 if (accept_file(ff)) {
436 return ff->file_save(jcr, ff, top_level);
438 Dmsg1(dbglvl, "Skip file %s\n", ff->fname);
439 return -1; /* ignore this file */
443 Dmsg1(000, "Unknown FT code %d\n", ff->type);
450 * Terminate find_files() and release
451 * all allocated memory
454 term_find_files(FF_PKT *ff)
458 free_pool_memory(ff->sys_fname);
459 if (ff->fname_save) {
460 free_pool_memory(ff->fname_save);
463 free_pool_memory(ff->link_save);
465 if (ff->ignoredir_fname) {
466 free_pool_memory(ff->ignoredir_fname);
468 if (ff->snap_fname) {
469 free_pool_memory(ff->snap_fname);
471 if (ff->snap_top_fname) {
472 free_pool_memory(ff->snap_top_fname);
475 delete ff->mtab_list;
477 hard_links = term_find_one(ff);