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 /* Can be already filled by plugin, so check that everything
126 * is on upper case. TODO: can check for dupplicate?
128 for (nCount = 0; nCount < 27 && szDrives[nCount] ; nCount++) {
129 szDrives[nCount] = toupper(szDrives[nCount]);
132 findFILESET *fileset = ff->fileset;
137 for (i=0; i<fileset->include_list.size(); i++) {
138 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
140 /* look through all files and check */
141 foreach_dlist(node, &incexe->name_list) {
142 char *fname = node->c_str();
143 /* fname should match x:/ */
144 if (strlen(fname) >= 2 && B_ISALPHA(fname[0])
145 && fname[1] == ':') {
147 /* always add in uppercase */
148 char ch = toupper(fname[0]);
149 /* if not found in string, add drive letter */
150 if (!strchr(szDrives,ch)) {
151 szDrives[nCount] = ch;
152 szDrives[nCount+1] = 0;
163 * Call this subroutine with a callback subroutine as the first
164 * argument and a packet as the second argument, this packet
165 * will be passed back to the callback subroutine as the last
170 find_files(JCR *jcr, FF_PKT *ff, int file_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level),
171 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level))
173 ff->file_save = file_save;
174 ff->plugin_save = plugin_save;
176 /* This is the new way */
177 findFILESET *fileset = ff->fileset;
181 ff->VerifyOpts[0] = 'V';
182 ff->VerifyOpts[1] = 0;
183 strcpy(ff->AccurateOpts, "Cmcs"); /* mtime+ctime+size by default */
184 strcpy(ff->BaseJobOpts, "Jspug5"); /* size+perm+user+group+chk */
185 for (i=0; i<fileset->include_list.size(); i++) {
186 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
187 fileset->incexe = incexe;
189 * By setting all options, we in effect OR the global options
190 * which is what we want.
192 for (j=0; j<incexe->opts_list.size(); j++) {
193 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
194 ff->flags |= fo->flags;
195 ff->Compress_algo = fo->Compress_algo;
196 ff->Compress_level = fo->Compress_level;
197 ff->strip_path = fo->strip_path;
198 ff->fstypes = fo->fstype;
199 ff->drivetypes = fo->drivetype;
200 ff->plugin = fo->plugin; /* TODO: generate a plugin event ? */
201 ff->opt_plugin = (ff->plugin != NULL)? true : false;
202 bstrncat(ff->VerifyOpts, fo->VerifyOpts, sizeof(ff->VerifyOpts));
203 if (fo->AccurateOpts[0]) {
204 bstrncpy(ff->AccurateOpts, fo->AccurateOpts, sizeof(ff->AccurateOpts));
206 if (fo->BaseJobOpts[0]) {
207 bstrncpy(ff->BaseJobOpts, fo->BaseJobOpts, sizeof(ff->BaseJobOpts));
210 Dmsg3(50, "Verify=<%s> Accurate=<%s> BaseJob=<%s>\n", ff->VerifyOpts, ff->AccurateOpts, ff->BaseJobOpts);
212 foreach_dlist(node, &incexe->name_list) {
213 char *fname = node->c_str();
214 Dmsg1(dbglvl, "F %s\n", fname);
215 ff->top_fname = fname;
216 if (find_one_file(jcr, ff, our_callback, ff->top_fname, (dev_t)-1, true) == 0) {
217 return 0; /* error return */
219 if (job_canceled(jcr)) {
223 foreach_dlist(node, &incexe->plugin_list) {
224 char *fname = node->c_str();
226 Jmsg(jcr, M_FATAL, 0, _("Plugin: \"%s\" not found.\n"), fname);
229 Dmsg1(dbglvl, "PluginCommand: %s\n", fname);
230 ff->top_fname = fname;
231 ff->cmd_plugin = true;
232 plugin_save(jcr, ff, true);
233 ff->cmd_plugin = false;
234 if (job_canceled(jcr)) {
244 * Test if the currently selected directory (in ff->fname) is
245 * explicitly in the Include list or explicitly in the Exclude
248 bool is_in_fileset(FF_PKT *ff)
254 findFILESET *fileset = ff->fileset;
256 for (i=0; i<fileset->include_list.size(); i++) {
257 incexe = (findINCEXE *)fileset->include_list.get(i);
258 foreach_dlist(node, &incexe->name_list) {
259 fname = node->c_str();
260 Dmsg2(dbglvl, "Inc fname=%s ff->fname=%s\n", fname, ff->fname);
261 if (strcmp(fname, ff->fname) == 0) {
266 for (i=0; i<fileset->exclude_list.size(); i++) {
267 incexe = (findINCEXE *)fileset->exclude_list.get(i);
268 foreach_dlist(node, &incexe->name_list) {
269 fname = node->c_str();
270 Dmsg2(dbglvl, "Exc fname=%s ff->fname=%s\n", fname, ff->fname);
271 if (strcmp(fname, ff->fname) == 0) {
281 static bool accept_file(FF_PKT *ff)
285 findFILESET *fileset = ff->fileset;
286 findINCEXE *incexe = fileset->incexe;
287 const char *basename;
288 int (*match_func)(const char *pattern, const char *string, int flags);
290 Dmsg1(dbglvl, "enter accept_file: fname=%s\n", ff->fname);
291 if (ff->flags & FO_ENHANCEDWILD) {
292 // match_func = enh_fnmatch;
293 match_func = fnmatch;
294 if ((basename = last_path_separator(ff->fname)) != NULL)
297 basename = ff->fname;
299 match_func = fnmatch;
300 basename = ff->fname;
303 for (j = 0; j < incexe->opts_list.size(); j++) {
304 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
305 ff->flags = fo->flags;
306 ff->Compress_algo = fo->Compress_algo;
307 ff->Compress_level = fo->Compress_level;
308 ff->fstypes = fo->fstype;
309 ff->drivetypes = fo->drivetype;
311 fnm_flags = (ff->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
312 fnm_flags |= (ff->flags & FO_ENHANCEDWILD) ? FNM_PATHNAME : 0;
314 if (S_ISDIR(ff->statp.st_mode)) {
315 for (k=0; k<fo->wilddir.size(); k++) {
316 if (match_func((char *)fo->wilddir.get(k), ff->fname, fnmode|fnm_flags) == 0) {
317 if (ff->flags & FO_EXCLUDE) {
318 Dmsg2(dbglvl, "Exclude wilddir: %s file=%s\n", (char *)fo->wilddir.get(k),
320 return false; /* reject dir */
322 return true; /* accept dir */
326 for (k=0; k<fo->wildfile.size(); k++) {
327 if (match_func((char *)fo->wildfile.get(k), ff->fname, fnmode|fnm_flags) == 0) {
328 if (ff->flags & FO_EXCLUDE) {
329 Dmsg2(dbglvl, "Exclude wildfile: %s file=%s\n", (char *)fo->wildfile.get(k),
331 return false; /* reject file */
333 return true; /* accept file */
337 for (k=0; k<fo->wildbase.size(); k++) {
338 if (match_func((char *)fo->wildbase.get(k), basename, fnmode|fnm_flags) == 0) {
339 if (ff->flags & FO_EXCLUDE) {
340 Dmsg2(dbglvl, "Exclude wildbase: %s file=%s\n", (char *)fo->wildbase.get(k),
342 return false; /* reject file */
344 return true; /* accept file */
348 for (k=0; k<fo->wild.size(); k++) {
349 if (match_func((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
350 if (ff->flags & FO_EXCLUDE) {
351 Dmsg2(dbglvl, "Exclude wild: %s file=%s\n", (char *)fo->wild.get(k),
353 return false; /* reject file */
355 return true; /* accept file */
358 if (S_ISDIR(ff->statp.st_mode)) {
359 for (k=0; k<fo->regexdir.size(); k++) {
360 const int nmatch = 30;
361 regmatch_t pmatch[nmatch];
362 if (regexec((regex_t *)fo->regexdir.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
363 if (ff->flags & FO_EXCLUDE) {
364 return false; /* reject file */
366 return true; /* accept file */
370 for (k=0; k<fo->regexfile.size(); k++) {
371 const int nmatch = 30;
372 regmatch_t pmatch[nmatch];
373 if (regexec((regex_t *)fo->regexfile.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
374 if (ff->flags & FO_EXCLUDE) {
375 return false; /* reject file */
377 return true; /* accept file */
381 for (k=0; k<fo->regex.size(); k++) {
382 const int nmatch = 30;
383 regmatch_t pmatch[nmatch];
384 if (regexec((regex_t *)fo->regex.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
385 if (ff->flags & FO_EXCLUDE) {
386 return false; /* reject file */
388 return true; /* accept file */
392 * If we have an empty Options clause with exclude, then
395 if (ff->flags & FO_EXCLUDE &&
396 fo->regex.size() == 0 && fo->wild.size() == 0 &&
397 fo->regexdir.size() == 0 && fo->wilddir.size() == 0 &&
398 fo->regexfile.size() == 0 && fo->wildfile.size() == 0 &&
399 fo->wildbase.size() == 0) {
400 return false; /* reject file */
404 /* Now apply the Exclude { } directive */
405 for (i=0; i<fileset->exclude_list.size(); i++) {
406 findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
407 for (j=0; j<incexe->opts_list.size(); j++) {
408 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
409 fnm_flags = (fo->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
410 for (k=0; k<fo->wild.size(); k++) {
411 if (fnmatch((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
412 Dmsg1(dbglvl, "Reject wild1: %s\n", ff->fname);
413 return false; /* reject file */
417 fnm_flags = (incexe->current_opts != NULL && incexe->current_opts->flags & FO_IGNORECASE)
420 foreach_dlist(node, &incexe->name_list) {
421 char *fname = node->c_str();
422 if (fnmatch(fname, ff->fname, fnmode|fnm_flags) == 0) {
423 Dmsg1(dbglvl, "Reject wild2: %s\n", ff->fname);
424 return false; /* reject file */
432 * The code comes here for each file examined.
433 * We filter the files, then call the user's callback if
434 * the file is included.
436 static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level)
439 return ff->file_save(jcr, ff, top_level); /* accept file */
452 // return ff->file_save(jcr, ff, top_level);
454 /* These items can be filtered */
467 if (accept_file(ff)) {
468 return ff->file_save(jcr, ff, top_level);
470 Dmsg1(dbglvl, "Skip file %s\n", ff->fname);
471 return -1; /* ignore this file */
475 Dmsg1(000, "Unknown FT code %d\n", ff->type);
482 * Terminate find_files() and release
483 * all allocated memory
486 term_find_files(FF_PKT *ff)
490 free_pool_memory(ff->sys_fname);
491 if (ff->fname_save) {
492 free_pool_memory(ff->fname_save);
495 free_pool_memory(ff->link_save);
497 hard_links = term_find_one(ff);