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 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.
43 static const int dbglvl = 150;
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, "C:mcs"); /* mtime+ctime+size by default */
180 strcpy(ff->BaseJobOpts, "J:mspug5"); /* mtime+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 bstrncat(ff->AccurateOpts, fo->AccurateOpts, sizeof(ff->AccurateOpts));
197 bstrncat(ff->BaseJobOpts, fo->BaseJobOpts, sizeof(ff->BaseJobOpts));
200 foreach_dlist(node, &incexe->name_list) {
201 char *fname = node->c_str();
202 Dmsg1(dbglvl, "F %s\n", fname);
203 ff->top_fname = fname;
204 if (find_one_file(jcr, ff, our_callback, ff->top_fname, (dev_t)-1, true) == 0) {
205 return 0; /* error return */
207 if (job_canceled(jcr)) {
211 foreach_dlist(node, &incexe->plugin_list) {
212 char *fname = node->c_str();
214 Jmsg(jcr, M_FATAL, 0, _("Plugin: \"%s\" not found.\n"), fname);
217 Dmsg1(dbglvl, "PluginCommand: %s\n", fname);
218 ff->top_fname = fname;
219 ff->cmd_plugin = true;
220 plugin_save(jcr, ff, true);
221 ff->cmd_plugin = false;
222 if (job_canceled(jcr)) {
232 * Test if the currently selected directory (in ff->fname) is
233 * explicitly in the Include list or explicitly in the Exclude
236 bool is_in_fileset(FF_PKT *ff)
242 findFILESET *fileset = ff->fileset;
244 for (i=0; i<fileset->include_list.size(); i++) {
245 incexe = (findINCEXE *)fileset->include_list.get(i);
246 foreach_dlist(node, &incexe->name_list) {
247 fname = node->c_str();
248 Dmsg2(dbglvl, "Inc fname=%s ff->fname=%s\n", fname, ff->fname);
249 if (strcmp(fname, ff->fname) == 0) {
254 for (i=0; i<fileset->exclude_list.size(); i++) {
255 incexe = (findINCEXE *)fileset->exclude_list.get(i);
256 foreach_dlist(node, &incexe->name_list) {
257 fname = node->c_str();
258 Dmsg2(dbglvl, "Exc fname=%s ff->fname=%s\n", fname, ff->fname);
259 if (strcmp(fname, ff->fname) == 0) {
269 static bool accept_file(FF_PKT *ff)
273 findFILESET *fileset = ff->fileset;
274 findINCEXE *incexe = fileset->incexe;
275 const char *basename;
276 int (*match_func)(const char *pattern, const char *string, int flags);
278 Dmsg1(dbglvl, "enter accept_file: fname=%s\n", ff->fname);
279 if (ff->flags & FO_ENHANCEDWILD) {
280 // match_func = enh_fnmatch;
281 match_func = fnmatch;
282 if ((basename = last_path_separator(ff->fname)) != NULL)
285 basename = ff->fname;
287 match_func = fnmatch;
288 basename = ff->fname;
291 for (j = 0; j < incexe->opts_list.size(); j++) {
292 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
293 ff->flags = fo->flags;
294 ff->GZIP_level = fo->GZIP_level;
295 ff->fstypes = fo->fstype;
296 ff->drivetypes = fo->drivetype;
298 fnm_flags = (ff->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
299 fnm_flags |= (ff->flags & FO_ENHANCEDWILD) ? FNM_PATHNAME : 0;
301 if (S_ISDIR(ff->statp.st_mode)) {
302 for (k=0; k<fo->wilddir.size(); k++) {
303 if (match_func((char *)fo->wilddir.get(k), ff->fname, fnmode|fnm_flags) == 0) {
304 if (ff->flags & FO_EXCLUDE) {
305 Dmsg2(dbglvl, "Exclude wilddir: %s file=%s\n", (char *)fo->wilddir.get(k),
307 return false; /* reject dir */
309 return true; /* accept dir */
313 for (k=0; k<fo->wildfile.size(); k++) {
314 if (match_func((char *)fo->wildfile.get(k), ff->fname, fnmode|fnm_flags) == 0) {
315 if (ff->flags & FO_EXCLUDE) {
316 Dmsg2(dbglvl, "Exclude wildfile: %s file=%s\n", (char *)fo->wildfile.get(k),
318 return false; /* reject file */
320 return true; /* accept file */
324 for (k=0; k<fo->wildbase.size(); k++) {
325 if (match_func((char *)fo->wildbase.get(k), basename, fnmode|fnm_flags) == 0) {
326 if (ff->flags & FO_EXCLUDE) {
327 Dmsg2(dbglvl, "Exclude wildbase: %s file=%s\n", (char *)fo->wildbase.get(k),
329 return false; /* reject file */
331 return true; /* accept file */
335 for (k=0; k<fo->wild.size(); k++) {
336 if (match_func((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
337 if (ff->flags & FO_EXCLUDE) {
338 Dmsg2(dbglvl, "Exclude wild: %s file=%s\n", (char *)fo->wild.get(k),
340 return false; /* reject file */
342 return true; /* accept file */
345 if (S_ISDIR(ff->statp.st_mode)) {
346 for (k=0; k<fo->regexdir.size(); k++) {
347 const int nmatch = 30;
348 regmatch_t pmatch[nmatch];
349 if (regexec((regex_t *)fo->regexdir.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
350 if (ff->flags & FO_EXCLUDE) {
351 return false; /* reject file */
353 return true; /* accept file */
357 for (k=0; k<fo->regexfile.size(); k++) {
358 const int nmatch = 30;
359 regmatch_t pmatch[nmatch];
360 if (regexec((regex_t *)fo->regexfile.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
361 if (ff->flags & FO_EXCLUDE) {
362 return false; /* reject file */
364 return true; /* accept file */
368 for (k=0; k<fo->regex.size(); k++) {
369 const int nmatch = 30;
370 regmatch_t pmatch[nmatch];
371 if (regexec((regex_t *)fo->regex.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
372 if (ff->flags & FO_EXCLUDE) {
373 return false; /* reject file */
375 return true; /* accept file */
379 * If we have an empty Options clause with exclude, then
382 if (ff->flags & FO_EXCLUDE &&
383 fo->regex.size() == 0 && fo->wild.size() == 0 &&
384 fo->regexdir.size() == 0 && fo->wilddir.size() == 0 &&
385 fo->regexfile.size() == 0 && fo->wildfile.size() == 0 &&
386 fo->wildbase.size() == 0) {
387 return false; /* reject file */
391 /* Now apply the Exclude { } directive */
392 for (i=0; i<fileset->exclude_list.size(); i++) {
393 findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
394 for (j=0; j<incexe->opts_list.size(); j++) {
395 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
396 fnm_flags = (fo->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
397 for (k=0; k<fo->wild.size(); k++) {
398 if (fnmatch((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
399 Dmsg1(dbglvl, "Reject wild1: %s\n", ff->fname);
400 return false; /* reject file */
404 fnm_flags = (incexe->current_opts != NULL && incexe->current_opts->flags & FO_IGNORECASE)
407 foreach_dlist(node, &incexe->name_list) {
408 char *fname = node->c_str();
409 if (fnmatch(fname, ff->fname, fnmode|fnm_flags) == 0) {
410 Dmsg1(dbglvl, "Reject wild2: %s\n", ff->fname);
411 return false; /* reject file */
419 * The code comes here for each file examined.
420 * We filter the files, then call the user's callback if
421 * the file is included.
423 static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level)
426 return ff->file_save(jcr, ff, top_level); /* accept file */
440 // return ff->file_save(jcr, ff, top_level);
442 /* These items can be filtered */
453 if (accept_file(ff)) {
454 return ff->file_save(jcr, ff, top_level);
456 Dmsg1(dbglvl, "Skip file %s\n", ff->fname);
457 return -1; /* ignore this file */
461 Dmsg1(000, "Unknown FT code %d\n", ff->type);
468 * Terminate find_files() and release
469 * all allocated memory
472 term_find_files(FF_PKT *ff)
476 free_pool_memory(ff->sys_fname);
477 if (ff->fname_save) {
478 free_pool_memory(ff->fname_save);
481 free_pool_memory(ff->link_save);
483 hard_links = term_find_one(ff);