2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2012 Free Software Foundation Europe e.V.
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 three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
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.
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
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.
29 * Main routine for finding files on a file system.
30 * The heart of the work to find the files on the
31 * system is done in find_one.c. Here we have the
32 * higher level control as well as the matching
33 * routines for the new syntax Options resource.
43 static const int dbglvl = 450;
45 int32_t name_max; /* filename max length */
46 int32_t path_max; /* path name max length */
50 #define bmalloc(x) sm_malloc(__FILE__, __LINE__, x)
52 static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level);
53 static bool accept_file(FF_PKT *ff);
55 static const int fnmode = 0;
58 * Initialize the find files "global" variables
60 FF_PKT *init_find_files()
64 ff = (FF_PKT *)bmalloc(sizeof(FF_PKT));
65 memset(ff, 0, sizeof(FF_PKT));
67 ff->sys_fname = get_pool_memory(PM_FNAME);
69 /* Get system path and filename maximum lengths */
70 path_max = pathconf(".", _PC_PATH_MAX);
71 if (path_max < 2048) {
75 name_max = pathconf(".", _PC_NAME_MAX);
76 if (name_max < 2048) {
79 path_max++; /* add for EOS */
80 name_max++; /* add for EOS */
82 Dmsg1(dbglvl, "init_find_files ff=%p\n", ff);
87 * Set find_files options. For the moment, we only
88 * provide for full/incremental saves, and setting
89 * of save_time. For additional options, see above
92 set_find_options(FF_PKT *ff, int incremental, time_t save_time)
94 Dmsg0(dbglvl, "Enter set_find_options()\n");
95 ff->incremental = incremental;
96 ff->save_time = save_time;
97 Dmsg0(dbglvl, "Leave set_find_options()\n");
101 set_find_changed_function(FF_PKT *ff, bool check_fct(JCR *jcr, FF_PKT *ff))
103 Dmsg0(dbglvl, "Enter set_find_changed_function()\n");
104 ff->check_fct = check_fct;
108 * For VSS we need to know which windows drives
109 * are used, because we create a snapshot of all used
110 * drives before operation
112 * the function returns the number of used drives and
113 * fills "drives" with up to 26 (A..Z) drive names
117 get_win32_driveletters(FF_PKT *ff, char* szDrives)
119 /* szDrives must be at least 27 bytes long */
121 #if !defined(HAVE_WIN32)
126 * Can be already filled by plugin, so check that all
127 * letters are in upper case. There should be no duplicates.
129 for (nCount = 0; nCount < 27 && szDrives[nCount] ; nCount++) {
130 szDrives[nCount] = toupper(szDrives[nCount]);
133 findFILESET *fileset = ff->fileset;
138 for (i=0; i<fileset->include_list.size(); i++) {
139 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
141 /* look through all files and check */
142 foreach_dlist(node, &incexe->name_list) {
143 char *fname = node->c_str();
144 /* fname should match x:/ */
145 if (strlen(fname) >= 2 && B_ISALPHA(fname[0])
146 && fname[1] == ':') {
148 /* always add in uppercase */
149 char ch = toupper(fname[0]);
150 /* if not found in string, add drive letter */
151 if (!strchr(szDrives,ch)) {
152 szDrives[nCount] = ch;
153 szDrives[nCount+1] = 0;
164 * Call this subroutine with a callback subroutine as the first
165 * argument and a packet as the second argument, this packet
166 * will be passed back to the callback subroutine as the last
171 find_files(JCR *jcr, FF_PKT *ff, int file_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level),
172 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level))
174 ff->file_save = file_save;
175 ff->plugin_save = plugin_save;
177 /* This is the new way */
178 findFILESET *fileset = ff->fileset;
181 /* TODO: We probably need be move the initialization in the fileset loop,
182 * at this place flags options are "concatenated" accross Include {} blocks
183 * (not only Options{} blocks inside a Include{})
186 for (i=0; i<fileset->include_list.size(); i++) {
187 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
188 fileset->incexe = incexe;
190 strcpy(ff->VerifyOpts, "V");
191 strcpy(ff->AccurateOpts, "Cmcs"); /* mtime+ctime+size by default */
192 strcpy(ff->BaseJobOpts, "Jspug5"); /* size+perm+user+group+chk */
195 * By setting all options, we in effect OR the global options
196 * which is what we want.
198 for (j=0; j<incexe->opts_list.size(); j++) {
199 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
200 ff->flags |= fo->flags;
201 ff->Compress_algo = fo->Compress_algo;
202 ff->Compress_level = fo->Compress_level;
203 ff->strip_path = fo->strip_path;
204 ff->fstypes = fo->fstype;
205 ff->drivetypes = fo->drivetype;
206 ff->plugin = fo->plugin; /* TODO: generate a plugin event ? */
207 ff->opt_plugin = (ff->plugin != NULL)? true : false;
208 bstrncat(ff->VerifyOpts, fo->VerifyOpts, sizeof(ff->VerifyOpts)); /* TODO: Concat or replace? */
209 if (fo->AccurateOpts[0]) {
210 bstrncpy(ff->AccurateOpts, fo->AccurateOpts, sizeof(ff->AccurateOpts));
212 if (fo->BaseJobOpts[0]) {
213 bstrncpy(ff->BaseJobOpts, fo->BaseJobOpts, sizeof(ff->BaseJobOpts));
216 Dmsg4(50, "Verify=<%s> Accurate=<%s> BaseJob=<%s> flags=<%d>\n",
217 ff->VerifyOpts, ff->AccurateOpts, ff->BaseJobOpts, ff->flags);
219 foreach_dlist(node, &incexe->name_list) {
220 char *fname = node->c_str();
221 Dmsg1(dbglvl, "F %s\n", fname);
222 ff->top_fname = fname;
223 if (find_one_file(jcr, ff, our_callback, ff->top_fname, (dev_t)-1, true) == 0) {
224 return 0; /* error return */
226 if (job_canceled(jcr)) {
230 foreach_dlist(node, &incexe->plugin_list) {
231 char *fname = node->c_str();
233 Jmsg(jcr, M_FATAL, 0, _("Plugin: \"%s\" not found.\n"), fname);
236 Dmsg1(dbglvl, "PluginCommand: %s\n", fname);
237 ff->top_fname = fname;
238 ff->cmd_plugin = true;
239 plugin_save(jcr, ff, true);
240 ff->cmd_plugin = false;
241 if (job_canceled(jcr)) {
251 * Test if the currently selected directory (in ff->fname) is
252 * explicitly in the Include list or explicitly in the Exclude
255 bool is_in_fileset(FF_PKT *ff)
261 findFILESET *fileset = ff->fileset;
263 for (i=0; i<fileset->include_list.size(); i++) {
264 incexe = (findINCEXE *)fileset->include_list.get(i);
265 foreach_dlist(node, &incexe->name_list) {
266 fname = node->c_str();
267 Dmsg2(dbglvl, "Inc fname=%s ff->fname=%s\n", fname, ff->fname);
268 if (strcmp(fname, ff->fname) == 0) {
273 for (i=0; i<fileset->exclude_list.size(); i++) {
274 incexe = (findINCEXE *)fileset->exclude_list.get(i);
275 foreach_dlist(node, &incexe->name_list) {
276 fname = node->c_str();
277 Dmsg2(dbglvl, "Exc fname=%s ff->fname=%s\n", fname, ff->fname);
278 if (strcmp(fname, ff->fname) == 0) {
288 static bool accept_file(FF_PKT *ff)
292 findFILESET *fileset = ff->fileset;
293 findINCEXE *incexe = fileset->incexe;
294 const char *basename;
295 int (*match_func)(const char *pattern, const char *string, int flags);
297 Dmsg1(dbglvl, "enter accept_file: fname=%s\n", ff->fname);
298 if (ff->flags & FO_ENHANCEDWILD) {
299 // match_func = enh_fnmatch;
300 match_func = fnmatch;
301 if ((basename = last_path_separator(ff->fname)) != NULL)
304 basename = ff->fname;
306 match_func = fnmatch;
307 basename = ff->fname;
310 for (j = 0; j < incexe->opts_list.size(); j++) {
311 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
312 ff->flags = fo->flags;
313 ff->Compress_algo = fo->Compress_algo;
314 ff->Compress_level = fo->Compress_level;
315 ff->fstypes = fo->fstype;
316 ff->drivetypes = fo->drivetype;
318 fnm_flags = (ff->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
319 fnm_flags |= (ff->flags & FO_ENHANCEDWILD) ? FNM_PATHNAME : 0;
321 if (S_ISDIR(ff->statp.st_mode)) {
322 for (k=0; k<fo->wilddir.size(); k++) {
323 if (match_func((char *)fo->wilddir.get(k), ff->fname, fnmode|fnm_flags) == 0) {
324 if (ff->flags & FO_EXCLUDE) {
325 Dmsg2(dbglvl, "Exclude wilddir: %s file=%s\n", (char *)fo->wilddir.get(k),
327 return false; /* reject dir */
329 return true; /* accept dir */
333 for (k=0; k<fo->wildfile.size(); k++) {
334 if (match_func((char *)fo->wildfile.get(k), ff->fname, fnmode|fnm_flags) == 0) {
335 if (ff->flags & FO_EXCLUDE) {
336 Dmsg2(dbglvl, "Exclude wildfile: %s file=%s\n", (char *)fo->wildfile.get(k),
338 return false; /* reject file */
340 return true; /* accept file */
344 for (k=0; k<fo->wildbase.size(); k++) {
345 if (match_func((char *)fo->wildbase.get(k), basename, fnmode|fnm_flags) == 0) {
346 if (ff->flags & FO_EXCLUDE) {
347 Dmsg2(dbglvl, "Exclude wildbase: %s file=%s\n", (char *)fo->wildbase.get(k),
349 return false; /* reject file */
351 return true; /* accept file */
355 for (k=0; k<fo->wild.size(); k++) {
356 if (match_func((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
357 if (ff->flags & FO_EXCLUDE) {
358 Dmsg2(dbglvl, "Exclude wild: %s file=%s\n", (char *)fo->wild.get(k),
360 return false; /* reject file */
362 return true; /* accept file */
365 if (S_ISDIR(ff->statp.st_mode)) {
366 for (k=0; k<fo->regexdir.size(); k++) {
367 const int nmatch = 30;
368 regmatch_t pmatch[nmatch];
369 if (regexec((regex_t *)fo->regexdir.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
370 if (ff->flags & FO_EXCLUDE) {
371 return false; /* reject file */
373 return true; /* accept file */
377 for (k=0; k<fo->regexfile.size(); k++) {
378 const int nmatch = 30;
379 regmatch_t pmatch[nmatch];
380 if (regexec((regex_t *)fo->regexfile.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
381 if (ff->flags & FO_EXCLUDE) {
382 return false; /* reject file */
384 return true; /* accept file */
388 for (k=0; k<fo->regex.size(); k++) {
389 const int nmatch = 30;
390 regmatch_t pmatch[nmatch];
391 if (regexec((regex_t *)fo->regex.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
392 if (ff->flags & FO_EXCLUDE) {
393 return false; /* reject file */
395 return true; /* accept file */
399 * If we have an empty Options clause with exclude, then
402 if (ff->flags & FO_EXCLUDE &&
403 fo->regex.size() == 0 && fo->wild.size() == 0 &&
404 fo->regexdir.size() == 0 && fo->wilddir.size() == 0 &&
405 fo->regexfile.size() == 0 && fo->wildfile.size() == 0 &&
406 fo->wildbase.size() == 0) {
407 return false; /* reject file */
411 /* Now apply the Exclude { } directive */
412 for (i=0; i<fileset->exclude_list.size(); i++) {
413 findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
414 for (j=0; j<incexe->opts_list.size(); j++) {
415 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
416 fnm_flags = (fo->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
417 for (k=0; k<fo->wild.size(); k++) {
418 if (fnmatch((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
419 Dmsg1(dbglvl, "Reject wild1: %s\n", ff->fname);
420 return false; /* reject file */
424 fnm_flags = (incexe->current_opts != NULL && incexe->current_opts->flags & FO_IGNORECASE)
427 foreach_dlist(node, &incexe->name_list) {
428 char *fname = node->c_str();
429 if (fnmatch(fname, ff->fname, fnmode|fnm_flags) == 0) {
430 Dmsg1(dbglvl, "Reject wild2: %s\n", ff->fname);
431 return false; /* reject file */
439 * The code comes here for each file examined.
440 * We filter the files, then call the user's callback if
441 * the file is included.
443 static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level)
446 return ff->file_save(jcr, ff, top_level); /* accept file */
459 // return ff->file_save(jcr, ff, top_level);
461 /* These items can be filtered */
474 if (accept_file(ff)) {
475 return ff->file_save(jcr, ff, top_level);
477 Dmsg1(dbglvl, "Skip file %s\n", ff->fname);
478 return -1; /* ignore this file */
482 Dmsg1(000, "Unknown FT code %d\n", ff->type);
489 * Terminate find_files() and release
490 * all allocated memory
493 term_find_files(FF_PKT *ff)
497 free_pool_memory(ff->sys_fname);
498 if (ff->fname_save) {
499 free_pool_memory(ff->fname_save);
502 free_pool_memory(ff->link_save);
504 hard_links = term_find_one(ff);