2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 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 Bacula® is a registered trademark of Kern Sibbald.
17 * Main routine for finding files on a file system.
18 * The heart of the work to find the files on the
19 * system is done in find_one.c. Here we have the
20 * higher level control as well as the matching
21 * routines for the new syntax Options resource.
31 static const int dbglvl = 450;
33 int32_t name_max; /* filename max length */
34 int32_t path_max; /* path name max length */
38 #define bmalloc(x) sm_malloc(__FILE__, __LINE__, x)
40 static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level);
42 static const int fnmode = 0;
45 * Initialize the find files "global" variables
47 FF_PKT *init_find_files()
51 ff = (FF_PKT *)bmalloc(sizeof(FF_PKT));
52 memset(ff, 0, sizeof(FF_PKT));
54 ff->sys_fname = get_pool_memory(PM_FNAME);
56 /* Get system path and filename maximum lengths */
57 path_max = pathconf(".", _PC_PATH_MAX);
58 if (path_max < 2048) {
62 name_max = pathconf(".", _PC_NAME_MAX);
63 if (name_max < 2048) {
66 path_max++; /* add for EOS */
67 name_max++; /* add for EOS */
69 Dmsg1(dbglvl, "init_find_files ff=%p\n", ff);
74 * Set find_files options. For the moment, we only
75 * provide for full/incremental saves, and setting
76 * of save_time. For additional options, see above
79 set_find_options(FF_PKT *ff, int incremental, time_t save_time)
81 Dmsg0(dbglvl, "Enter set_find_options()\n");
82 ff->incremental = incremental;
83 ff->save_time = save_time;
84 Dmsg0(dbglvl, "Leave set_find_options()\n");
88 set_find_changed_function(FF_PKT *ff, bool check_fct(JCR *jcr, FF_PKT *ff))
90 Dmsg0(dbglvl, "Enter set_find_changed_function()\n");
91 ff->check_fct = check_fct;
95 * For VSS we need to know which windows drives
96 * are used, because we create a snapshot of all used
97 * drives before operation
99 * the function returns the number of used drives and
100 * fills "drives" with up to 26 (A..Z) drive names
102 * szDrives must be at least 27 bytes long
105 get_win32_driveletters(FF_PKT *ff, char* szDrives)
112 * Call this subroutine with a callback subroutine as the first
113 * argument and a packet as the second argument, this packet
114 * will be passed back to the callback subroutine as the last
119 find_files(JCR *jcr, FF_PKT *ff, int file_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level),
120 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level))
122 ff->file_save = file_save;
123 ff->plugin_save = plugin_save;
125 /* This is the new way */
126 findFILESET *fileset = ff->fileset;
129 /* TODO: We probably need be move the initialization in the fileset loop,
130 * at this place flags options are "concatenated" accross Include {} blocks
131 * (not only Options{} blocks inside a Include{})
134 for (i=0; i<fileset->include_list.size(); i++) {
135 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
136 fileset->incexe = incexe;
138 /* Here, we reset some values between two different Include{} */
139 strcpy(ff->VerifyOpts, "V");
140 strcpy(ff->AccurateOpts, "Cmcs"); /* mtime+ctime+size by default */
141 strcpy(ff->BaseJobOpts, "Jspug5"); /* size+perm+user+group+chk */
143 ff->opt_plugin = false;
146 * By setting all options, we in effect OR the global options
147 * which is what we want.
149 for (j=0; j<incexe->opts_list.size(); j++) {
150 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
151 ff->flags |= fo->flags;
152 ff->Compress_algo = fo->Compress_algo;
153 ff->Compress_level = fo->Compress_level;
154 ff->strip_path = fo->strip_path;
155 ff->fstypes = fo->fstype;
156 ff->drivetypes = fo->drivetype;
157 if (fo->plugin != NULL) {
158 ff->plugin = fo->plugin; /* TODO: generate a plugin event ? */
159 ff->opt_plugin = true;
161 bstrncat(ff->VerifyOpts, fo->VerifyOpts, sizeof(ff->VerifyOpts)); /* TODO: Concat or replace? */
162 if (fo->AccurateOpts[0]) {
163 bstrncpy(ff->AccurateOpts, fo->AccurateOpts, sizeof(ff->AccurateOpts));
165 if (fo->BaseJobOpts[0]) {
166 bstrncpy(ff->BaseJobOpts, fo->BaseJobOpts, sizeof(ff->BaseJobOpts));
169 Dmsg4(50, "Verify=<%s> Accurate=<%s> BaseJob=<%s> flags=<%d>\n",
170 ff->VerifyOpts, ff->AccurateOpts, ff->BaseJobOpts, ff->flags);
172 foreach_dlist(node, &incexe->name_list) {
173 char *fname = node->c_str();
174 Dmsg1(dbglvl, "F %s\n", fname);
175 ff->top_fname = fname;
176 if (find_one_file(jcr, ff, our_callback, ff->top_fname, (dev_t)-1, true) == 0) {
177 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 hard_links = term_find_one(ff);