2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2009 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 two of the GNU 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 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.
44 int32_t name_max; /* filename max length */
45 int32_t path_max; /* path name max length */
49 #define bmalloc(x) sm_malloc(__FILE__, __LINE__, x)
51 static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level);
52 static bool accept_file(FF_PKT *ff);
54 static const int fnmode = 0;
57 * Initialize the find files "global" variables
59 FF_PKT *init_find_files()
63 ff = (FF_PKT *)bmalloc(sizeof(FF_PKT));
64 memset(ff, 0, sizeof(FF_PKT));
66 ff->sys_fname = get_pool_memory(PM_FNAME);
68 /* Get system path and filename maximum lengths */
69 path_max = pathconf(".", _PC_PATH_MAX);
70 if (path_max < 2048) {
74 name_max = pathconf(".", _PC_NAME_MAX);
75 if (name_max < 2048) {
78 path_max++; /* add for EOS */
79 name_max++; /* add for EOS */
81 Dmsg1(100, "init_find_files ff=%p\n", ff);
86 * Set find_files options. For the moment, we only
87 * provide for full/incremental saves, and setting
88 * of save_time. For additional options, see above
91 set_find_options(FF_PKT *ff, int incremental, time_t save_time)
93 Dmsg0(100, "Enter set_find_options()\n");
94 ff->incremental = incremental;
95 ff->save_time = save_time;
96 Dmsg0(100, "Leave set_find_options()\n");
100 set_find_changed_function(FF_PKT *ff, bool check_fct(JCR *jcr, FF_PKT *ff))
102 Dmsg0(100, "Enter set_find_changed_function()\n");
103 ff->check_fct = check_fct;
107 * For VSS we need to know which windows drives
108 * are used, because we create a snapshot of all used
109 * drives before operation
111 * the function returns the number of used drives and
112 * fills "drives" with up to 26 (A..Z) drive names
116 get_win32_driveletters(FF_PKT *ff, char* szDrives)
118 /* szDrives must be at least 27 bytes long */
120 #if !defined(HAVE_WIN32)
124 szDrives[0] = 0; /* make empty */
127 findFILESET *fileset = ff->fileset;
132 for (i=0; i<fileset->include_list.size(); i++) {
133 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
135 /* look through all files and check */
136 foreach_dlist(node, &incexe->name_list) {
137 char *fname = node->c_str();
138 /* fname should match x:/ */
139 if (strlen(fname) >= 2 && B_ISALPHA(fname[0])
140 && fname[1] == ':') {
142 /* always add in uppercase */
143 char ch = toupper(fname[0]);
144 /* if not found in string, add drive letter */
145 if (!strchr(szDrives,ch)) {
146 szDrives[nCount] = ch;
147 szDrives[nCount+1] = 0;
158 * Call this subroutine with a callback subroutine as the first
159 * argument and a packet as the second argument, this packet
160 * will be passed back to the callback subroutine as the last
165 find_files(JCR *jcr, FF_PKT *ff, int file_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level),
166 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level))
168 ff->file_save = file_save;
169 ff->plugin_save = plugin_save;
171 /* This is the new way */
172 findFILESET *fileset = ff->fileset;
176 ff->VerifyOpts[0] = 'V';
177 ff->VerifyOpts[1] = 0;
178 strcpy(ff->AccurateOpts, "C:mcs"); /* mtime+ctime+size by default */
179 strcpy(ff->BaseJobOpts, "J:mspug5"); /* mtime+size+perm+user+group+chk */
180 for (i=0; i<fileset->include_list.size(); i++) {
181 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
182 fileset->incexe = incexe;
184 * By setting all options, we in effect or the global options
185 * which is what we want.
187 for (j=0; j<incexe->opts_list.size(); j++) {
188 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
189 ff->flags |= fo->flags;
190 ff->GZIP_level = fo->GZIP_level;
191 ff->strip_path = fo->strip_path;
192 ff->fstypes = fo->fstype;
193 ff->drivetypes = fo->drivetype;
194 bstrncat(ff->VerifyOpts, fo->VerifyOpts, sizeof(ff->VerifyOpts));
195 bstrncat(ff->AccurateOpts, fo->AccurateOpts, sizeof(ff->AccurateOpts));
196 bstrncat(ff->BaseJobOpts, fo->BaseJobOpts, sizeof(ff->BaseJobOpts));
199 foreach_dlist(node, &incexe->name_list) {
200 char *fname = node->c_str();
201 Dmsg1(100, "F %s\n", fname);
202 ff->top_fname = fname;
203 if (find_one_file(jcr, ff, our_callback, ff->top_fname, (dev_t)-1, true) == 0) {
204 return 0; /* error return */
207 foreach_dlist(node, &incexe->plugin_list) {
208 char *fname = node->c_str();
210 Jmsg(jcr, M_FATAL, 0, _("Plugin: \"%s\" not found.\n"), fname);
213 Dmsg1(100, "PluginCommand: %s\n", fname);
214 ff->top_fname = fname;
215 ff->cmd_plugin = true;
216 plugin_save(jcr, ff, true);
217 ff->cmd_plugin = false;
225 * Test if the currently selected directory (in ff->fname) is
226 * explicitly in the Include list or explicitly in the Exclude
229 bool is_in_fileset(FF_PKT *ff)
235 findFILESET *fileset = ff->fileset;
237 for (i=0; i<fileset->include_list.size(); i++) {
238 incexe = (findINCEXE *)fileset->include_list.get(i);
239 foreach_dlist(node, &incexe->name_list) {
240 fname = node->c_str();
241 Dmsg2(100, "Inc fname=%s ff->fname=%s\n", fname, ff->fname);
242 if (strcmp(fname, ff->fname) == 0) {
247 for (i=0; i<fileset->exclude_list.size(); i++) {
248 incexe = (findINCEXE *)fileset->exclude_list.get(i);
249 foreach_dlist(node, &incexe->name_list) {
250 fname = node->c_str();
251 Dmsg2(100, "Exc fname=%s ff->fname=%s\n", fname, ff->fname);
252 if (strcmp(fname, ff->fname) == 0) {
262 static bool accept_file(FF_PKT *ff)
266 findFILESET *fileset = ff->fileset;
267 findINCEXE *incexe = fileset->incexe;
268 const char *basename;
269 int (*match_func)(const char *pattern, const char *string, int flags);
271 if (ff->flags & FO_ENHANCEDWILD) {
272 // match_func = enh_fnmatch;
273 match_func = fnmatch;
274 if ((basename = last_path_separator(ff->fname)) != NULL)
277 basename = ff->fname;
279 match_func = fnmatch;
280 basename = ff->fname;
283 for (j = 0; j < incexe->opts_list.size(); j++) {
284 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
285 ff->flags = fo->flags;
286 ff->GZIP_level = fo->GZIP_level;
287 ff->fstypes = fo->fstype;
288 ff->drivetypes = fo->drivetype;
290 fnm_flags = (ff->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
291 fnm_flags |= (ff->flags & FO_ENHANCEDWILD) ? FNM_PATHNAME : 0;
293 if (S_ISDIR(ff->statp.st_mode)) {
294 for (k=0; k<fo->wilddir.size(); k++) {
295 if (match_func((char *)fo->wilddir.get(k), ff->fname, fnmode|fnm_flags) == 0) {
296 if (ff->flags & FO_EXCLUDE) {
297 Dmsg2(100, "Exclude wilddir: %s file=%s\n", (char *)fo->wilddir.get(k),
299 return false; /* reject dir */
301 return true; /* accept dir */
305 for (k=0; k<fo->wildfile.size(); k++) {
306 if (match_func((char *)fo->wildfile.get(k), ff->fname, fnmode|fnm_flags) == 0) {
307 if (ff->flags & FO_EXCLUDE) {
308 Dmsg2(100, "Exclude wildfile: %s file=%s\n", (char *)fo->wildfile.get(k),
310 return false; /* reject file */
312 return true; /* accept file */
316 for (k=0; k<fo->wildbase.size(); k++) {
317 if (match_func((char *)fo->wildbase.get(k), basename, fnmode|fnm_flags) == 0) {
318 if (ff->flags & FO_EXCLUDE) {
319 Dmsg2(100, "Exclude wildbase: %s file=%s\n", (char *)fo->wildbase.get(k),
321 return false; /* reject file */
323 return true; /* accept file */
327 for (k=0; k<fo->wild.size(); k++) {
328 if (match_func((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
329 if (ff->flags & FO_EXCLUDE) {
330 Dmsg2(100, "Exclude wild: %s file=%s\n", (char *)fo->wild.get(k),
332 return false; /* reject file */
334 return true; /* accept file */
337 if (S_ISDIR(ff->statp.st_mode)) {
338 for (k=0; k<fo->regexdir.size(); k++) {
339 const int nmatch = 30;
340 regmatch_t pmatch[nmatch];
341 if (regexec((regex_t *)fo->regexdir.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->regexfile.size(); k++) {
350 const int nmatch = 30;
351 regmatch_t pmatch[nmatch];
352 if (regexec((regex_t *)fo->regexfile.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 for (k=0; k<fo->regex.size(); k++) {
361 const int nmatch = 30;
362 regmatch_t pmatch[nmatch];
363 if (regexec((regex_t *)fo->regex.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
364 if (ff->flags & FO_EXCLUDE) {
365 return false; /* reject file */
367 return true; /* accept file */
371 * If we have an empty Options clause with exclude, then
374 if (ff->flags & FO_EXCLUDE &&
375 fo->regex.size() == 0 && fo->wild.size() == 0 &&
376 fo->regexdir.size() == 0 && fo->wilddir.size() == 0 &&
377 fo->regexfile.size() == 0 && fo->wildfile.size() == 0 &&
378 fo->wildbase.size() == 0) {
379 return false; /* reject file */
383 /* Now apply the Exclude { } directive */
384 for (i=0; i<fileset->exclude_list.size(); i++) {
385 findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
386 for (j=0; j<incexe->opts_list.size(); j++) {
387 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
388 fnm_flags = (fo->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
389 for (k=0; k<fo->wild.size(); k++) {
390 if (fnmatch((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
391 Dmsg1(100, "Reject wild1: %s\n", ff->fname);
392 return false; /* reject file */
396 fnm_flags = (incexe->current_opts != NULL && incexe->current_opts->flags & FO_IGNORECASE)
399 foreach_dlist(node, &incexe->name_list) {
400 char *fname = node->c_str();
401 if (fnmatch(fname, ff->fname, fnmode|fnm_flags) == 0) {
402 Dmsg1(100, "Reject wild2: %s\n", ff->fname);
403 return false; /* reject file */
411 * The code comes here for each file examined.
412 * We filter the files, then call the user's callback if
413 * the file is included.
415 static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level)
418 return ff->file_save(jcr, ff, top_level); /* accept file */
432 // return ff->file_save(jcr, ff, top_level);
434 /* These items can be filtered */
445 if (accept_file(ff)) {
446 return ff->file_save(jcr, ff, top_level);
448 Dmsg1(100, "Skip file %s\n", ff->fname);
449 return -1; /* ignore this file */
453 Dmsg1(000, "Unknown FT code %d\n", ff->type);
460 * Terminate find_files() and release
461 * all allocated memory
464 term_find_files(FF_PKT *ff)
468 free_pool_memory(ff->sys_fname);
469 if (ff->fname_save) {
470 free_pool_memory(ff->fname_save);
473 free_pool_memory(ff->link_save);
475 hard_links = term_find_one(ff);