]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/find.c
Backport from BEE
[bacula/bacula] / bacula / src / findlib / find.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from many
7    others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    Bacula® is a registered trademark of Kern Sibbald.
15 */
16 /*
17  * Main routine for finding files on a file system.
18  *  The heart of the work to find the files on the
19  *    system is done in find_one.c. Here we have the
20  *    higher level control as well as the matching
21  *    routines for the new syntax Options resource.
22  *
23  *  Kern E. Sibbald, MM
24  *
25  */
26
27
28 #include "bacula.h"
29 #include "find.h"
30
31 static const int dbglvl = 450;
32
33 int32_t name_max;              /* filename max length */
34 int32_t path_max;              /* path name max length */
35
36 #ifdef DEBUG
37 #undef bmalloc
38 #define bmalloc(x) sm_malloc(__FILE__, __LINE__, x)
39 #endif
40 static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level);
41
42 static const int fnmode = 0;
43
44 /*
45  * Initialize the find files "global" variables
46  */
47 FF_PKT *init_find_files()
48 {
49   FF_PKT *ff;
50
51   ff = (FF_PKT *)bmalloc(sizeof(FF_PKT));
52   memset(ff, 0, sizeof(FF_PKT));
53
54   ff->sys_fname = get_pool_memory(PM_FNAME);
55
56    /* Get system path and filename maximum lengths */
57    path_max = pathconf(".", _PC_PATH_MAX);
58    if (path_max < 2048) {
59       path_max = 2048;
60    }
61
62    name_max = pathconf(".", _PC_NAME_MAX);
63    if (name_max < 2048) {
64       name_max = 2048;
65    }
66    path_max++;                        /* add for EOS */
67    name_max++;                        /* add for EOS */
68
69   Dmsg1(dbglvl, "init_find_files ff=%p\n", ff);
70   return ff;
71 }
72
73 /*
74  * Set find_files options. For the moment, we only
75  * provide for full/incremental saves, and setting
76  * of save_time. For additional options, see above
77  */
78 void
79 set_find_options(FF_PKT *ff, int incremental, time_t save_time)
80 {
81   Dmsg0(dbglvl, "Enter set_find_options()\n");
82   ff->incremental = incremental;
83   ff->save_time = save_time;
84   Dmsg0(dbglvl, "Leave set_find_options()\n");
85 }
86
87 void
88 set_find_changed_function(FF_PKT *ff, bool check_fct(JCR *jcr, FF_PKT *ff))
89 {
90    Dmsg0(dbglvl, "Enter set_find_changed_function()\n");
91    ff->check_fct = check_fct;
92 }
93
94 /*
95  * For VSS we need to know which windows drives
96  * are used, because we create a snapshot of all used
97  * drives before operation
98  *
99  * the function returns the number of used drives and
100  * fills "drives" with up to 26 (A..Z) drive names
101  *
102  * szDrives must be at least 27 bytes long
103  */
104 int
105 get_win32_driveletters(FF_PKT *ff, char* szDrives)
106 {
107    int nCount = 0;
108    return nCount;
109 }
110
111 /*
112  * Call this subroutine with a callback subroutine as the first
113  * argument and a packet as the second argument, this packet
114  * will be passed back to the callback subroutine as the last
115  * argument.
116  *
117  */
118 int
119 find_files(JCR *jcr, FF_PKT *ff, int file_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level),
120            int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level))
121 {
122    ff->file_save = file_save;
123    ff->plugin_save = plugin_save;
124
125    /* This is the new way */
126    findFILESET *fileset = ff->fileset;
127    if (fileset) {
128       int i, j;
129       /* TODO: We probably need be move the initialization in the fileset loop,
130        * at this place flags options are "concatenated" accross Include {} blocks
131        * (not only Options{} blocks inside a Include{})
132        */
133       ff->flags = 0;
134       for (i=0; i<fileset->include_list.size(); i++) {
135          findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
136          fileset->incexe = incexe;
137
138          /* Here, we reset some values between two different Include{} */
139          strcpy(ff->VerifyOpts, "V");
140          strcpy(ff->AccurateOpts, "Cmcs");  /* mtime+ctime+size by default */
141          strcpy(ff->BaseJobOpts, "Jspug5"); /* size+perm+user+group+chk  */
142          ff->plugin = NULL;
143          ff->opt_plugin = false;
144
145          /*
146           * By setting all options, we in effect OR the global options
147           *   which is what we want.
148           */
149          for (j=0; j<incexe->opts_list.size(); j++) {
150             findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
151             ff->flags |= fo->flags;
152             ff->Compress_algo = fo->Compress_algo;
153             ff->Compress_level = fo->Compress_level;
154             ff->strip_path = fo->strip_path;
155             ff->fstypes = fo->fstype;
156             ff->drivetypes = fo->drivetype;
157             if (fo->plugin != NULL) {
158                ff->plugin = fo->plugin; /* TODO: generate a plugin event ? */
159                ff->opt_plugin = true;
160             }
161             bstrncat(ff->VerifyOpts, fo->VerifyOpts, sizeof(ff->VerifyOpts)); /* TODO: Concat or replace? */
162             if (fo->AccurateOpts[0]) {
163                bstrncpy(ff->AccurateOpts, fo->AccurateOpts, sizeof(ff->AccurateOpts));
164             }
165             if (fo->BaseJobOpts[0]) {
166                bstrncpy(ff->BaseJobOpts, fo->BaseJobOpts, sizeof(ff->BaseJobOpts));
167             }
168          }
169          Dmsg4(50, "Verify=<%s> Accurate=<%s> BaseJob=<%s> flags=<%d>\n",
170                ff->VerifyOpts, ff->AccurateOpts, ff->BaseJobOpts, ff->flags);
171          dlistString *node;
172          foreach_dlist(node, &incexe->name_list) {
173             char *fname = node->c_str();
174             Dmsg1(dbglvl, "F %s\n", fname);
175             ff->top_fname = fname;
176             if (find_one_file(jcr, ff, our_callback, ff->top_fname, (dev_t)-1, true) == 0) {
177                return 0;                  /* error return */
178             }
179             if (job_canceled(jcr)) {
180                return 0;
181             }
182          }
183          foreach_dlist(node, &incexe->plugin_list) {
184             char *fname = node->c_str();
185             if (!plugin_save) {
186                Jmsg(jcr, M_FATAL, 0, _("Plugin: \"%s\" not found.\n"), fname);
187                return 0;
188             }
189             Dmsg1(dbglvl, "PluginCommand: %s\n", fname);
190             ff->top_fname = fname;
191             ff->cmd_plugin = true;
192
193             /* Make sure that opt plugin is not set
194              * The current implementation doesn't allow option plugin
195              * and command plugin to run at the same time
196              */
197             ff->opt_plugin = false;
198             ff->plugin = NULL;
199
200             plugin_save(jcr, ff, true);
201             ff->cmd_plugin = false;
202             if (job_canceled(jcr)) {
203                return 0;
204             }
205          }
206       }
207    }
208    return 1;
209 }
210
211 /*
212  * Test if the currently selected directory (in ff->fname) is
213  *  explicitly in the Include list or explicitly in the Exclude
214  *  list.
215  */
216 bool is_in_fileset(FF_PKT *ff)
217 {
218    dlistString *node;
219    char *fname;
220    int i;
221    findINCEXE *incexe;
222    findFILESET *fileset = ff->fileset;
223    if (fileset) {
224       for (i=0; i<fileset->include_list.size(); i++) {
225          incexe = (findINCEXE *)fileset->include_list.get(i);
226          foreach_dlist(node, &incexe->name_list) {
227             fname = node->c_str();
228             Dmsg2(dbglvl, "Inc fname=%s ff->fname=%s\n", fname, ff->fname);
229             if (strcmp(fname, ff->fname) == 0) {
230                return true;
231             }
232          }
233       }
234       for (i=0; i<fileset->exclude_list.size(); i++) {
235          incexe = (findINCEXE *)fileset->exclude_list.get(i);
236          foreach_dlist(node, &incexe->name_list) {
237             fname = node->c_str();
238             Dmsg2(dbglvl, "Exc fname=%s ff->fname=%s\n", fname, ff->fname);
239             if (strcmp(fname, ff->fname) == 0) {
240                return true;
241             }
242          }
243       }
244    }
245    return false;
246 }
247
248
249 bool accept_file(FF_PKT *ff)
250 {
251    int i, j, k;
252    int fnm_flags;
253    findFILESET *fileset = ff->fileset;
254    findINCEXE *incexe = fileset->incexe;
255    const char *basename;
256    int (*match_func)(const char *pattern, const char *string, int flags);
257
258    Dmsg1(dbglvl, "enter accept_file: fname=%s\n", ff->fname);
259    if (ff->flags & FO_ENHANCEDWILD) {
260 //    match_func = enh_fnmatch;
261       match_func = fnmatch;
262       if ((basename = last_path_separator(ff->fname)) != NULL)
263          basename++;
264       else
265          basename = ff->fname;
266    } else {
267       match_func = fnmatch;
268       basename = ff->fname;
269    }
270
271    for (j = 0; j < incexe->opts_list.size(); j++) {
272       findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
273       ff->flags = fo->flags;
274       ff->Compress_algo = fo->Compress_algo;
275       ff->Compress_level = fo->Compress_level;
276       ff->fstypes = fo->fstype;
277       ff->drivetypes = fo->drivetype;
278
279       fnm_flags = (ff->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
280       fnm_flags |= (ff->flags & FO_ENHANCEDWILD) ? FNM_PATHNAME : 0;
281
282       if (S_ISDIR(ff->statp.st_mode)) {
283          for (k=0; k<fo->wilddir.size(); k++) {
284             if (match_func((char *)fo->wilddir.get(k), ff->fname, fnmode|fnm_flags) == 0) {
285                if (ff->flags & FO_EXCLUDE) {
286                   Dmsg2(dbglvl, "Exclude wilddir: %s file=%s\n", (char *)fo->wilddir.get(k),
287                      ff->fname);
288                   return false;       /* reject dir */
289                }
290                return true;           /* accept dir */
291             }
292          }
293       } else {
294          for (k=0; k<fo->wildfile.size(); k++) {
295             if (match_func((char *)fo->wildfile.get(k), ff->fname, fnmode|fnm_flags) == 0) {
296                if (ff->flags & FO_EXCLUDE) {
297                   Dmsg2(dbglvl, "Exclude wildfile: %s file=%s\n", (char *)fo->wildfile.get(k),
298                      ff->fname);
299                   return false;       /* reject file */
300                }
301                return true;           /* accept file */
302             }
303          }
304
305          for (k=0; k<fo->wildbase.size(); k++) {
306             if (match_func((char *)fo->wildbase.get(k), basename, fnmode|fnm_flags) == 0) {
307                if (ff->flags & FO_EXCLUDE) {
308                   Dmsg2(dbglvl, "Exclude wildbase: %s file=%s\n", (char *)fo->wildbase.get(k),
309                      basename);
310                   return false;       /* reject file */
311                }
312                return true;           /* accept file */
313             }
314          }
315       }
316       for (k=0; k<fo->wild.size(); k++) {
317          if (match_func((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
318             if (ff->flags & FO_EXCLUDE) {
319                Dmsg2(dbglvl, "Exclude wild: %s file=%s\n", (char *)fo->wild.get(k),
320                   ff->fname);
321                return false;          /* reject file */
322             }
323             return true;              /* accept file */
324          }
325       }
326       if (S_ISDIR(ff->statp.st_mode)) {
327          for (k=0; k<fo->regexdir.size(); k++) {
328             const int nmatch = 30;
329             regmatch_t pmatch[nmatch];
330             if (regexec((regex_t *)fo->regexdir.get(k), ff->fname, nmatch, pmatch,  0) == 0) {
331                if (ff->flags & FO_EXCLUDE) {
332                   return false;       /* reject file */
333                }
334                return true;           /* accept file */
335             }
336          }
337       } else {
338          for (k=0; k<fo->regexfile.size(); k++) {
339             const int nmatch = 30;
340             regmatch_t pmatch[nmatch];
341             if (regexec((regex_t *)fo->regexfile.get(k), ff->fname, nmatch, pmatch,  0) == 0) {
342                if (ff->flags & FO_EXCLUDE) {
343                   return false;       /* reject file */
344                }
345                return true;           /* accept file */
346             }
347          }
348       }
349       for (k=0; k<fo->regex.size(); k++) {
350          const int nmatch = 30;
351          regmatch_t pmatch[nmatch];
352          if (regexec((regex_t *)fo->regex.get(k), ff->fname, nmatch, pmatch,  0) == 0) {
353             if (ff->flags & FO_EXCLUDE) {
354                return false;          /* reject file */
355             }
356             return true;              /* accept file */
357          }
358       }
359       /*
360        * If we have an empty Options clause with exclude, then
361        *  exclude the file
362        */
363       if (ff->flags & FO_EXCLUDE &&
364           fo->regex.size() == 0     && fo->wild.size() == 0 &&
365           fo->regexdir.size() == 0  && fo->wilddir.size() == 0 &&
366           fo->regexfile.size() == 0 && fo->wildfile.size() == 0 &&
367           fo->wildbase.size() == 0) {
368          return false;              /* reject file */
369       }
370    }
371
372    /* Now apply the Exclude { } directive */
373    for (i=0; i<fileset->exclude_list.size(); i++) {
374       findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
375       for (j=0; j<incexe->opts_list.size(); j++) {
376          findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
377          fnm_flags = (fo->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
378          for (k=0; k<fo->wild.size(); k++) {
379             if (fnmatch((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
380                Dmsg1(dbglvl, "Reject wild1: %s\n", ff->fname);
381                return false;          /* reject file */
382             }
383          }
384       }
385       fnm_flags = (incexe->current_opts != NULL && incexe->current_opts->flags & FO_IGNORECASE)
386              ? FNM_CASEFOLD : 0;
387       dlistString *node;
388       foreach_dlist(node, &incexe->name_list) {
389          char *fname = node->c_str();
390          if (fnmatch(fname, ff->fname, fnmode|fnm_flags) == 0) {
391             Dmsg1(dbglvl, "Reject wild2: %s\n", ff->fname);
392             return false;          /* reject file */
393          }
394       }
395    }
396    return true;
397 }
398
399 /*
400  * The code comes here for each file examined.
401  * We filter the files, then call the user's callback if
402  *    the file is included.
403  */
404 static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level)
405 {
406    if (top_level) {
407       return ff->file_save(jcr, ff, top_level);   /* accept file */
408    }
409    switch (ff->type) {
410    case FT_NOACCESS:
411    case FT_NOFOLLOW:
412    case FT_NOSTAT:
413    case FT_NOCHG:
414    case FT_ISARCH:
415    case FT_NORECURSE:
416    case FT_NOFSCHG:
417    case FT_INVALIDFS:
418    case FT_INVALIDDT:
419    case FT_NOOPEN:
420 //    return ff->file_save(jcr, ff, top_level);
421
422    /* These items can be filtered */
423    case FT_LNKSAVED:
424    case FT_REGE:
425    case FT_REG:
426    case FT_LNK:
427    case FT_DIRBEGIN:
428    case FT_DIREND:
429    case FT_RAW:
430    case FT_FIFO:
431    case FT_SPEC:
432    case FT_DIRNOCHG:
433    case FT_REPARSE:
434    case FT_JUNCTION:
435       if (accept_file(ff)) {
436          return ff->file_save(jcr, ff, top_level);
437       } else {
438          Dmsg1(dbglvl, "Skip file %s\n", ff->fname);
439          return -1;                   /* ignore this file */
440       }
441
442    default:
443       Dmsg1(000, "Unknown FT code %d\n", ff->type);
444       return 0;
445    }
446 }
447
448
449 /*
450  * Terminate find_files() and release
451  * all allocated memory
452  */
453 int
454 term_find_files(FF_PKT *ff)
455 {
456    int hard_links;
457
458    free_pool_memory(ff->sys_fname);
459    if (ff->fname_save) {
460       free_pool_memory(ff->fname_save);
461    }
462    if (ff->link_save) {
463       free_pool_memory(ff->link_save);
464    }
465    if (ff->ignoredir_fname) {
466       free_pool_memory(ff->ignoredir_fname);
467    }
468    hard_links = term_find_one(ff);
469    free(ff);
470    return hard_links;
471 }