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 int32_t name_max; /* filename max length */
44 int32_t path_max; /* path name max length */
48 #define bmalloc(x) sm_malloc(__FILE__, __LINE__, x)
50 static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level);
51 static bool accept_file(FF_PKT *ff);
53 static const int fnmode = 0;
56 * Initialize the find files "global" variables
58 FF_PKT *init_find_files()
62 ff = (FF_PKT *)bmalloc(sizeof(FF_PKT));
63 memset(ff, 0, sizeof(FF_PKT));
65 ff->sys_fname = get_pool_memory(PM_FNAME);
67 /* Get system path and filename maximum lengths */
68 path_max = pathconf(".", _PC_PATH_MAX);
69 if (path_max < 2048) {
73 name_max = pathconf(".", _PC_NAME_MAX);
74 if (name_max < 2048) {
77 path_max++; /* add for EOS */
78 name_max++; /* add for EOS */
80 Dmsg1(100, "init_find_files ff=%p\n", ff);
85 * Set find_files options. For the moment, we only
86 * provide for full/incremental saves, and setting
87 * of save_time. For additional options, see above
90 set_find_options(FF_PKT *ff, int incremental, time_t save_time)
92 Dmsg0(100, "Enter set_find_options()\n");
93 ff->incremental = incremental;
94 ff->save_time = save_time;
95 Dmsg0(100, "Leave set_find_options()\n");
99 set_find_changed_function(FF_PKT *ff, bool check_fct(JCR *jcr, FF_PKT *ff))
101 Dmsg0(100, "Enter set_find_changed_function()\n");
102 ff->check_fct = check_fct;
106 * For VSS we need to know which windows drives
107 * are used, because we create a snapshot of all used
108 * drives before operation
110 * the function returns the number of used drives and
111 * fills "drives" with up to 26 (A..Z) drive names
115 get_win32_driveletters(FF_PKT *ff, char* szDrives)
117 /* szDrives must be at least 27 bytes long */
119 #if !defined(HAVE_WIN32)
123 szDrives[0] = 0; /* make empty */
126 findFILESET *fileset = ff->fileset;
131 for (i=0; i<fileset->include_list.size(); i++) {
132 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
134 /* look through all files and check */
135 foreach_dlist(node, &incexe->name_list) {
136 char *fname = node->c_str();
137 /* fname should match x:/ */
138 if (strlen(fname) >= 2 && B_ISALPHA(fname[0])
139 && fname[1] == ':') {
141 /* always add in uppercase */
142 char ch = toupper(fname[0]);
143 /* if not found in string, add drive letter */
144 if (!strchr(szDrives,ch)) {
145 szDrives[nCount] = ch;
146 szDrives[nCount+1] = 0;
157 * Call this subroutine with a callback subroutine as the first
158 * argument and a packet as the second argument, this packet
159 * will be passed back to the callback subroutine as the last
164 find_files(JCR *jcr, FF_PKT *ff, int file_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level),
165 int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level))
167 ff->file_save = file_save;
168 ff->plugin_save = plugin_save;
170 /* This is the new way */
171 findFILESET *fileset = ff->fileset;
175 ff->VerifyOpts[0] = 'V';
176 ff->VerifyOpts[1] = 0;
177 strcpy(ff->AccurateOpts, "C:mcs"); /* mtime+ctime+size by default */
178 strcpy(ff->BaseJobOpts, "J:mspug5"); /* mtime+size+perm+user+group+chk */
179 for (i=0; i<fileset->include_list.size(); i++) {
180 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
181 fileset->incexe = incexe;
183 * By setting all options, we in effect or the global options
184 * which is what we want.
186 for (j=0; j<incexe->opts_list.size(); j++) {
187 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
188 ff->flags |= fo->flags;
189 ff->GZIP_level = fo->GZIP_level;
190 ff->strip_path = fo->strip_path;
191 ff->fstypes = fo->fstype;
192 ff->drivetypes = fo->drivetype;
193 bstrncat(ff->VerifyOpts, fo->VerifyOpts, sizeof(ff->VerifyOpts));
194 bstrncat(ff->AccurateOpts, fo->AccurateOpts, sizeof(ff->AccurateOpts));
195 bstrncat(ff->BaseJobOpts, fo->BaseJobOpts, sizeof(ff->BaseJobOpts));
198 foreach_dlist(node, &incexe->name_list) {
199 char *fname = node->c_str();
200 Dmsg1(100, "F %s\n", fname);
201 ff->top_fname = fname;
202 if (find_one_file(jcr, ff, our_callback, ff->top_fname, (dev_t)-1, true) == 0) {
203 return 0; /* error return */
205 if (job_canceled(jcr)) {
209 foreach_dlist(node, &incexe->plugin_list) {
210 char *fname = node->c_str();
212 Jmsg(jcr, M_FATAL, 0, _("Plugin: \"%s\" not found.\n"), fname);
215 Dmsg1(100, "PluginCommand: %s\n", fname);
216 ff->top_fname = fname;
217 ff->cmd_plugin = true;
218 plugin_save(jcr, ff, true);
219 ff->cmd_plugin = false;
220 if (job_canceled(jcr)) {
230 * Test if the currently selected directory (in ff->fname) is
231 * explicitly in the Include list or explicitly in the Exclude
234 bool is_in_fileset(FF_PKT *ff)
240 findFILESET *fileset = ff->fileset;
242 for (i=0; i<fileset->include_list.size(); i++) {
243 incexe = (findINCEXE *)fileset->include_list.get(i);
244 foreach_dlist(node, &incexe->name_list) {
245 fname = node->c_str();
246 Dmsg2(100, "Inc fname=%s ff->fname=%s\n", fname, ff->fname);
247 if (strcmp(fname, ff->fname) == 0) {
252 for (i=0; i<fileset->exclude_list.size(); i++) {
253 incexe = (findINCEXE *)fileset->exclude_list.get(i);
254 foreach_dlist(node, &incexe->name_list) {
255 fname = node->c_str();
256 Dmsg2(100, "Exc fname=%s ff->fname=%s\n", fname, ff->fname);
257 if (strcmp(fname, ff->fname) == 0) {
267 static bool accept_file(FF_PKT *ff)
271 findFILESET *fileset = ff->fileset;
272 findINCEXE *incexe = fileset->incexe;
273 const char *basename;
274 int (*match_func)(const char *pattern, const char *string, int flags);
276 if (ff->flags & FO_ENHANCEDWILD) {
277 // match_func = enh_fnmatch;
278 match_func = fnmatch;
279 if ((basename = last_path_separator(ff->fname)) != NULL)
282 basename = ff->fname;
284 match_func = fnmatch;
285 basename = ff->fname;
288 for (j = 0; j < incexe->opts_list.size(); j++) {
289 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
290 ff->flags = fo->flags;
291 ff->GZIP_level = fo->GZIP_level;
292 ff->fstypes = fo->fstype;
293 ff->drivetypes = fo->drivetype;
295 fnm_flags = (ff->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
296 fnm_flags |= (ff->flags & FO_ENHANCEDWILD) ? FNM_PATHNAME : 0;
298 if (S_ISDIR(ff->statp.st_mode)) {
299 for (k=0; k<fo->wilddir.size(); k++) {
300 if (match_func((char *)fo->wilddir.get(k), ff->fname, fnmode|fnm_flags) == 0) {
301 if (ff->flags & FO_EXCLUDE) {
302 Dmsg2(100, "Exclude wilddir: %s file=%s\n", (char *)fo->wilddir.get(k),
304 return false; /* reject dir */
306 return true; /* accept dir */
310 for (k=0; k<fo->wildfile.size(); k++) {
311 if (match_func((char *)fo->wildfile.get(k), ff->fname, fnmode|fnm_flags) == 0) {
312 if (ff->flags & FO_EXCLUDE) {
313 Dmsg2(100, "Exclude wildfile: %s file=%s\n", (char *)fo->wildfile.get(k),
315 return false; /* reject file */
317 return true; /* accept file */
321 for (k=0; k<fo->wildbase.size(); k++) {
322 if (match_func((char *)fo->wildbase.get(k), basename, fnmode|fnm_flags) == 0) {
323 if (ff->flags & FO_EXCLUDE) {
324 Dmsg2(100, "Exclude wildbase: %s file=%s\n", (char *)fo->wildbase.get(k),
326 return false; /* reject file */
328 return true; /* accept file */
332 for (k=0; k<fo->wild.size(); k++) {
333 if (match_func((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
334 if (ff->flags & FO_EXCLUDE) {
335 Dmsg2(100, "Exclude wild: %s file=%s\n", (char *)fo->wild.get(k),
337 return false; /* reject file */
339 return true; /* accept file */
342 if (S_ISDIR(ff->statp.st_mode)) {
343 for (k=0; k<fo->regexdir.size(); k++) {
344 const int nmatch = 30;
345 regmatch_t pmatch[nmatch];
346 if (regexec((regex_t *)fo->regexdir.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
347 if (ff->flags & FO_EXCLUDE) {
348 return false; /* reject file */
350 return true; /* accept file */
354 for (k=0; k<fo->regexfile.size(); k++) {
355 const int nmatch = 30;
356 regmatch_t pmatch[nmatch];
357 if (regexec((regex_t *)fo->regexfile.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
358 if (ff->flags & FO_EXCLUDE) {
359 return false; /* reject file */
361 return true; /* accept file */
365 for (k=0; k<fo->regex.size(); k++) {
366 const int nmatch = 30;
367 regmatch_t pmatch[nmatch];
368 if (regexec((regex_t *)fo->regex.get(k), ff->fname, nmatch, pmatch, 0) == 0) {
369 if (ff->flags & FO_EXCLUDE) {
370 return false; /* reject file */
372 return true; /* accept file */
376 * If we have an empty Options clause with exclude, then
379 if (ff->flags & FO_EXCLUDE &&
380 fo->regex.size() == 0 && fo->wild.size() == 0 &&
381 fo->regexdir.size() == 0 && fo->wilddir.size() == 0 &&
382 fo->regexfile.size() == 0 && fo->wildfile.size() == 0 &&
383 fo->wildbase.size() == 0) {
384 return false; /* reject file */
388 /* Now apply the Exclude { } directive */
389 for (i=0; i<fileset->exclude_list.size(); i++) {
390 findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
391 for (j=0; j<incexe->opts_list.size(); j++) {
392 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
393 fnm_flags = (fo->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
394 for (k=0; k<fo->wild.size(); k++) {
395 if (fnmatch((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
396 Dmsg1(100, "Reject wild1: %s\n", ff->fname);
397 return false; /* reject file */
401 fnm_flags = (incexe->current_opts != NULL && incexe->current_opts->flags & FO_IGNORECASE)
404 foreach_dlist(node, &incexe->name_list) {
405 char *fname = node->c_str();
406 if (fnmatch(fname, ff->fname, fnmode|fnm_flags) == 0) {
407 Dmsg1(100, "Reject wild2: %s\n", ff->fname);
408 return false; /* reject file */
416 * The code comes here for each file examined.
417 * We filter the files, then call the user's callback if
418 * the file is included.
420 static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level)
423 return ff->file_save(jcr, ff, top_level); /* accept file */
437 // return ff->file_save(jcr, ff, top_level);
439 /* These items can be filtered */
450 if (accept_file(ff)) {
451 return ff->file_save(jcr, ff, top_level);
453 Dmsg1(100, "Skip file %s\n", ff->fname);
454 return -1; /* ignore this file */
458 Dmsg1(000, "Unknown FT code %d\n", ff->type);
465 * Terminate find_files() and release
466 * all allocated memory
469 term_find_files(FF_PKT *ff)
473 free_pool_memory(ff->sys_fname);
474 if (ff->fname_save) {
475 free_pool_memory(ff->fname_save);
478 free_pool_memory(ff->link_save);
480 hard_links = term_find_one(ff);