2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2010 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)
125 szDrives[0] = 0; /* make empty */
128 findFILESET *fileset = ff->fileset;
133 for (i=0; i<fileset->include_list.size(); i++) {
134 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
136 /* look through all files and check */
137 foreach_dlist(node, &incexe->name_list) {
138 char *fname = node->c_str();
139 /* fname should match x:/ */
140 if (strlen(fname) >= 2 && B_ISALPHA(fname[0])
141 && fname[1] == ':') {
143 /* always add in uppercase */
144 char ch = toupper(fname[0]);
145 /* if not found in string, add drive letter */
146 if (!strchr(szDrives,ch)) {
147 szDrives[nCount] = ch;
148 szDrives[nCount+1] = 0;
159 * Call this subroutine with a callback subroutine as the first
160 * argument and a packet as the second argument, this packet
161 * will be passed back to the callback subroutine as the last
166 find_files(JCR *jcr, FF_PKT *ff, int file_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level),
167 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level))
169 ff->file_save = file_save;
170 ff->plugin_save = plugin_save;
172 /* This is the new way */
173 findFILESET *fileset = ff->fileset;
177 ff->VerifyOpts[0] = 'V';
178 ff->VerifyOpts[1] = 0;
179 strcpy(ff->AccurateOpts, "Cmcs"); /* mtime+ctime+size by default */
180 strcpy(ff->BaseJobOpts, "Jspug5"); /* size+perm+user+group+chk */
181 for (i=0; i<fileset->include_list.size(); i++) {
182 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
183 fileset->incexe = incexe;
185 * By setting all options, we in effect OR the global options
186 * which is what we want.
188 for (j=0; j<incexe->opts_list.size(); j++) {
189 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
190 ff->flags |= fo->flags;
191 ff->GZIP_level = fo->GZIP_level;
192 ff->strip_path = fo->strip_path;
193 ff->fstypes = fo->fstype;
194 ff->drivetypes = fo->drivetype;
195 bstrncat(ff->VerifyOpts, fo->VerifyOpts, sizeof(ff->VerifyOpts));
196 if (fo->AccurateOpts[0]) {
197 bstrncpy(ff->AccurateOpts, fo->AccurateOpts, sizeof(ff->AccurateOpts));
199 if (fo->BaseJobOpts[0]) {
200 bstrncpy(ff->BaseJobOpts, fo->BaseJobOpts, sizeof(ff->BaseJobOpts));
203 Dmsg3(50, "Verify=<%s> Accurate=<%s> BaseJob=<%s>\n", ff->VerifyOpts, ff->AccurateOpts, ff->BaseJobOpts);
205 foreach_dlist(node, &incexe->name_list) {
206 char *fname = node->c_str();
207 Dmsg1(dbglvl, "F %s\n", fname);
208 ff->top_fname = fname;
209 if (find_one_file(jcr, ff, our_callback, ff->top_fname, (dev_t)-1, true) == 0) {
210 return 0; /* error return */
212 if (job_canceled(jcr)) {
216 foreach_dlist(node, &incexe->plugin_list) {
217 char *fname = node->c_str();
219 Jmsg(jcr, M_FATAL, 0, _("Plugin: \"%s\" not found.\n"), fname);
222 Dmsg1(dbglvl, "PluginCommand: %s\n", fname);
223 ff->top_fname = fname;
224 ff->cmd_plugin = true;
225 plugin_save(jcr, ff, true);
226 ff->cmd_plugin = false;
227 if (job_canceled(jcr)) {
237 * Test if the currently selected directory (in ff->fname) is
238 * explicitly in the Include list or explicitly in the Exclude
241 bool is_in_fileset(FF_PKT *ff)
247 findFILESET *fileset = ff->fileset;
249 for (i=0; i<fileset->include_list.size(); i++) {
250 incexe = (findINCEXE *)fileset->include_list.get(i);
251 foreach_dlist(node, &incexe->name_list) {
252 fname = node->c_str();
253 Dmsg2(dbglvl, "Inc fname=%s ff->fname=%s\n", fname, ff->fname);
254 if (strcmp(fname, ff->fname) == 0) {
259 for (i=0; i<fileset->exclude_list.size(); i++) {
260 incexe = (findINCEXE *)fileset->exclude_list.get(i);
261 foreach_dlist(node, &incexe->name_list) {
262 fname = node->c_str();
263 Dmsg2(dbglvl, "Exc fname=%s ff->fname=%s\n", fname, ff->fname);
264 if (strcmp(fname, ff->fname) == 0) {
274 static bool accept_file(FF_PKT *ff)
278 findFILESET *fileset = ff->fileset;
279 findINCEXE *incexe = fileset->incexe;
280 const char *basename;
281 int (*match_func)(const char *pattern, const char *string, int flags);
283 Dmsg1(dbglvl, "enter accept_file: fname=%s\n", ff->fname);
284 if (ff->flags & FO_ENHANCEDWILD) {
285 // match_func = enh_fnmatch;
286 match_func = fnmatch;
287 if ((basename = last_path_separator(ff->fname)) != NULL)
290 basename = ff->fname;
292 match_func = fnmatch;
293 basename = ff->fname;
296 for (j = 0; j < incexe->opts_list.size(); j++) {
297 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
298 ff->flags = fo->flags;
299 ff->GZIP_level = fo->GZIP_level;
300 ff->fstypes = fo->fstype;
301 ff->drivetypes = fo->drivetype;
303 fnm_flags = (ff->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
304 fnm_flags |= (ff->flags & FO_ENHANCEDWILD) ? FNM_PATHNAME : 0;
306 if (S_ISDIR(ff->statp.st_mode)) {
307 for (k=0; k<fo->wilddir.size(); k++) {
308 if (match_func((char *)fo->wilddir.get(k), ff->fname, fnmode|fnm_flags) == 0) {
309 if (ff->flags & FO_EXCLUDE) {
310 Dmsg2(dbglvl, "Exclude wilddir: %s file=%s\n", (char *)fo->wilddir.get(k),
312 return false; /* reject dir */
314 return true; /* accept dir */
318 for (k=0; k<fo->wildfile.size(); k++) {
319 if (match_func((char *)fo->wildfile.get(k), ff->fname, fnmode|fnm_flags) == 0) {
320 if (ff->flags & FO_EXCLUDE) {
321 Dmsg2(dbglvl, "Exclude wildfile: %s file=%s\n", (char *)fo->wildfile.get(k),
323 return false; /* reject file */
325 return true; /* accept file */
329 for (k=0; k<fo->wildbase.size(); k++) {
330 if (match_func((char *)fo->wildbase.get(k), basename, fnmode|fnm_flags) == 0) {
331 if (ff->flags & FO_EXCLUDE) {
332 Dmsg2(dbglvl, "Exclude wildbase: %s file=%s\n", (char *)fo->wildbase.get(k),
334 return false; /* reject file */
336 return true; /* accept file */
340 for (k=0; k<fo->wild.size(); k++) {
341 if (match_func((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
342 if (ff->flags & FO_EXCLUDE) {
343 Dmsg2(dbglvl, "Exclude wild: %s file=%s\n", (char *)fo->wild.get(k),
345 return false; /* reject file */
347 return true; /* accept file */
350 if (S_ISDIR(ff->statp.st_mode)) {
351 for (k=0; k<fo->regexdir.size(); k++) {
352 const int nmatch = 30;
353 regmatch_t pmatch[nmatch];
354 if (regexec((regex_t *)fo->regexdir.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
355 if (ff->flags & FO_EXCLUDE) {
356 return false; /* reject file */
358 return true; /* accept file */
362 for (k=0; k<fo->regexfile.size(); k++) {
363 const int nmatch = 30;
364 regmatch_t pmatch[nmatch];
365 if (regexec((regex_t *)fo->regexfile.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
366 if (ff->flags & FO_EXCLUDE) {
367 return false; /* reject file */
369 return true; /* accept file */
373 for (k=0; k<fo->regex.size(); k++) {
374 const int nmatch = 30;
375 regmatch_t pmatch[nmatch];
376 if (regexec((regex_t *)fo->regex.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
377 if (ff->flags & FO_EXCLUDE) {
378 return false; /* reject file */
380 return true; /* accept file */
384 * If we have an empty Options clause with exclude, then
387 if (ff->flags & FO_EXCLUDE &&
388 fo->regex.size() == 0 && fo->wild.size() == 0 &&
389 fo->regexdir.size() == 0 && fo->wilddir.size() == 0 &&
390 fo->regexfile.size() == 0 && fo->wildfile.size() == 0 &&
391 fo->wildbase.size() == 0) {
392 return false; /* reject file */
396 /* Now apply the Exclude { } directive */
397 for (i=0; i<fileset->exclude_list.size(); i++) {
398 findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
399 for (j=0; j<incexe->opts_list.size(); j++) {
400 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
401 fnm_flags = (fo->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
402 for (k=0; k<fo->wild.size(); k++) {
403 if (fnmatch((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
404 Dmsg1(dbglvl, "Reject wild1: %s\n", ff->fname);
405 return false; /* reject file */
409 fnm_flags = (incexe->current_opts != NULL && incexe->current_opts->flags & FO_IGNORECASE)
412 foreach_dlist(node, &incexe->name_list) {
413 char *fname = node->c_str();
414 if (fnmatch(fname, ff->fname, fnmode|fnm_flags) == 0) {
415 Dmsg1(dbglvl, "Reject wild2: %s\n", ff->fname);
416 return false; /* reject file */
424 * The code comes here for each file examined.
425 * We filter the files, then call the user's callback if
426 * the file is included.
428 static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level)
431 return ff->file_save(jcr, ff, top_level); /* accept file */
445 // return ff->file_save(jcr, ff, top_level);
447 /* These items can be filtered */
458 if (accept_file(ff)) {
459 return ff->file_save(jcr, ff, top_level);
461 Dmsg1(dbglvl, "Skip file %s\n", ff->fname);
462 return -1; /* ignore this file */
466 Dmsg1(000, "Unknown FT code %d\n", ff->type);
473 * Terminate find_files() and release
474 * all allocated memory
477 term_find_files(FF_PKT *ff)
481 free_pool_memory(ff->sys_fname);
482 if (ff->fname_save) {
483 free_pool_memory(ff->fname_save);
486 free_pool_memory(ff->link_save);
488 hard_links = term_find_one(ff);