]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/find.c
Restore win32 dir from Branch-5.2 and update it
[bacula/bacula] / bacula / src / findlib / find.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many 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    This notice must be preserved when any source code is 
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  * Main routine for finding files on a file system.
21  *  The heart of the work to find the files on the
22  *    system is done in find_one.c. Here we have the
23  *    higher level control as well as the matching
24  *    routines for the new syntax Options resource.
25  *
26  *  Kern E. Sibbald, MM
27  *
28  */
29
30
31 #include "bacula.h"
32 #include "find.h"
33
34 static const int dbglvl = 450;
35
36 int32_t name_max;              /* filename max length */
37 int32_t path_max;              /* path name max length */
38
39 #ifdef DEBUG
40 #undef bmalloc
41 #define bmalloc(x) sm_malloc(__FILE__, __LINE__, x)
42 #endif
43 static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level);
44
45 static const int fnmode = 0;
46
47 /*
48  * Initialize the find files "global" variables
49  */
50 FF_PKT *init_find_files()
51 {
52   FF_PKT *ff;
53
54   ff = (FF_PKT *)bmalloc(sizeof(FF_PKT));
55   memset(ff, 0, sizeof(FF_PKT));
56
57   ff->sys_fname = get_pool_memory(PM_FNAME);
58
59    /* Get system path and filename maximum lengths */
60    path_max = pathconf(".", _PC_PATH_MAX);
61    if (path_max < 2048) {
62       path_max = 2048;
63    }
64
65    name_max = pathconf(".", _PC_NAME_MAX);
66    if (name_max < 2048) {
67       name_max = 2048;
68    }
69    path_max++;                        /* add for EOS */
70    name_max++;                        /* add for EOS */
71
72   Dmsg1(dbglvl, "init_find_files ff=%p\n", ff);
73   return ff;
74 }
75
76 /*
77  * Set find_files options. For the moment, we only
78  * provide for full/incremental saves, and setting
79  * of save_time. For additional options, see above
80  */
81 void
82 set_find_options(FF_PKT *ff, int incremental, time_t save_time)
83 {
84   Dmsg0(dbglvl, "Enter set_find_options()\n");
85   ff->incremental = incremental;
86   ff->save_time = save_time;
87   Dmsg0(dbglvl, "Leave set_find_options()\n");
88 }
89
90 void
91 set_find_changed_function(FF_PKT *ff, bool check_fct(JCR *jcr, FF_PKT *ff))
92 {
93    Dmsg0(dbglvl, "Enter set_find_changed_function()\n");
94    ff->check_fct = check_fct;
95 }
96
97 void
98 set_find_snapshot_function(FF_PKT *ff, 
99                            bool convert_path(JCR *jcr, FF_PKT *ff, dlist *filelist, dlistString *node))
100 {
101    ff->snapshot_convert_fct = convert_path;
102 }
103
104 /*
105  * Call this subroutine with a callback subroutine as the first
106  * argument and a packet as the second argument, this packet
107  * will be passed back to the callback subroutine as the last
108  * argument.
109  *
110  */
111 int
112 find_files(JCR *jcr, FF_PKT *ff, int file_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level),
113            int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level))
114 {
115    ff->file_save = file_save;
116    ff->plugin_save = plugin_save;
117
118    /* This is the new way */
119    findFILESET *fileset = ff->fileset;
120    if (fileset) {
121       int i, j;
122       /* TODO: We probably need be move the initialization in the fileset loop,
123        * at this place flags options are "concatenated" accross Include {} blocks
124        * (not only Options{} blocks inside a Include{})
125        */
126       ff->flags = 0;
127       for (i=0; i<fileset->include_list.size(); i++) {
128          findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
129          fileset->incexe = incexe;
130
131          /* Here, we reset some values between two different Include{} */
132          strcpy(ff->VerifyOpts, "V");
133          strcpy(ff->AccurateOpts, "Cmcs");  /* mtime+ctime+size by default */
134          strcpy(ff->BaseJobOpts, "Jspug5"); /* size+perm+user+group+chk  */
135          ff->plugin = NULL;
136          ff->opt_plugin = false;
137
138          /*
139           * By setting all options, we in effect OR the global options
140           *   which is what we want.
141           */
142          for (j=0; j<incexe->opts_list.size(); j++) {
143             findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
144             /* TODO options are "simply" reset by Options block that come next
145              * For example :
146              * Options { IgnoreCase = yes }
147              * ATTN: some plugins use AddOptions() that create extra Option block
148              * Also see accept_file() below that could suffer of the same problem
149              */
150             ff->flags |= fo->flags;
151             /* If the compress option was set in the previous block, overwrite the
152              * algorithm only if defined
153              */
154             if ((ff->flags & FO_COMPRESS) && fo->Compress_algo != 0) {
155                ff->Compress_algo = fo->Compress_algo;
156                ff->Compress_level = fo->Compress_level;
157             }
158             ff->strip_path = fo->strip_path;
159             ff->fstypes = fo->fstype;
160             ff->drivetypes = fo->drivetype;
161             if (fo->plugin != NULL) {
162                ff->plugin = fo->plugin; /* TODO: generate a plugin event ? */
163                ff->opt_plugin = true;
164             }
165             bstrncat(ff->VerifyOpts, fo->VerifyOpts, sizeof(ff->VerifyOpts)); /* TODO: Concat or replace? */
166             if (fo->AccurateOpts[0]) {
167                bstrncpy(ff->AccurateOpts, fo->AccurateOpts, sizeof(ff->AccurateOpts));
168             }
169             if (fo->BaseJobOpts[0]) {
170                bstrncpy(ff->BaseJobOpts, fo->BaseJobOpts, sizeof(ff->BaseJobOpts));
171             }
172          }
173          Dmsg4(50, "Verify=<%s> Accurate=<%s> BaseJob=<%s> flags=<%lld>\n",
174                ff->VerifyOpts, ff->AccurateOpts, ff->BaseJobOpts, ff->flags);
175          dlistString *node;
176          foreach_dlist(node, &incexe->name_list) {
177             char *fname = node->c_str();
178             Dmsg1(dbglvl, "F %s\n", fname);
179
180             ff->top_fname = fname;
181             /* Convert the filename if needed */
182             if (ff->snapshot_convert_fct) {
183                ff->snapshot_convert_fct(jcr, ff, &incexe->name_list, node);
184             }
185
186             if (find_one_file(jcr, ff, our_callback, ff->top_fname, (dev_t)-1, true) == 0) {
187                return 0;                  /* error return */
188             }
189
190             if (job_canceled(jcr)) {
191                return 0;
192             }
193          }
194          foreach_dlist(node, &incexe->plugin_list) {
195             char *fname = node->c_str();
196             if (!plugin_save) {
197                Jmsg(jcr, M_FATAL, 0, _("Plugin: \"%s\" not found.\n"), fname);
198                return 0;
199             }
200             Dmsg1(dbglvl, "PluginCommand: %s\n", fname);
201             ff->top_fname = fname;
202             ff->cmd_plugin = true;
203
204             /* Make sure that opt plugin is not set
205              * The current implementation doesn't allow option plugin
206              * and command plugin to run at the same time
207              */
208             ff->opt_plugin = false;
209             ff->plugin = NULL;
210
211             plugin_save(jcr, ff, true);
212             ff->cmd_plugin = false;
213             if (job_canceled(jcr)) {
214                return 0;
215             }
216          }
217       }
218    }
219    return 1;
220 }
221
222 /*
223  * Test if the currently selected directory (in ff->fname) is
224  *  explicitly in the Include list or explicitly in the Exclude
225  *  list.
226  */
227 bool is_in_fileset(FF_PKT *ff)
228 {
229    dlistString *node;
230    char *fname;
231    int i;
232    findINCEXE *incexe;
233    findFILESET *fileset = ff->fileset;
234    if (fileset) {
235       for (i=0; i<fileset->include_list.size(); i++) {
236          incexe = (findINCEXE *)fileset->include_list.get(i);
237          foreach_dlist(node, &incexe->name_list) {
238             fname = node->c_str();
239             Dmsg2(dbglvl, "Inc fname=%s ff->fname=%s\n", fname, ff->fname);
240             if (strcmp(fname, ff->fname) == 0) {
241                return true;
242             }
243          }
244       }
245       for (i=0; i<fileset->exclude_list.size(); i++) {
246          incexe = (findINCEXE *)fileset->exclude_list.get(i);
247          foreach_dlist(node, &incexe->name_list) {
248             fname = node->c_str();
249             Dmsg2(dbglvl, "Exc fname=%s ff->fname=%s\n", fname, ff->fname);
250             if (strcmp(fname, ff->fname) == 0) {
251                return true;
252             }
253          }
254       }
255    }
256    return false;
257 }
258
259
260 bool accept_file(FF_PKT *ff)
261 {
262    int i, j, k;
263    int fnm_flags;
264    findFILESET *fileset = ff->fileset;
265    findINCEXE *incexe = fileset->incexe;
266    const char *basename;
267    int (*match_func)(const char *pattern, const char *string, int flags);
268
269    Dmsg1(dbglvl, "enter accept_file: fname=%s\n", ff->fname);
270    if (ff->flags & FO_ENHANCEDWILD) {
271 //    match_func = enh_fnmatch;
272       match_func = fnmatch;
273       if ((basename = last_path_separator(ff->fname)) != NULL)
274          basename++;
275       else
276          basename = ff->fname;
277    } else {
278       match_func = fnmatch;
279       basename = ff->fname;
280    }
281
282    for (j = 0; j < incexe->opts_list.size(); j++) {
283       findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
284       ff->flags = fo->flags;
285       ff->Compress_algo = fo->Compress_algo;
286       ff->Compress_level = fo->Compress_level;
287       ff->fstypes = fo->fstype;
288       ff->drivetypes = fo->drivetype;
289
290       fnm_flags = (ff->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
291       fnm_flags |= (ff->flags & FO_ENHANCEDWILD) ? FNM_PATHNAME : 0;
292
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(dbglvl, "Exclude wilddir: %s file=%s\n", (char *)fo->wilddir.get(k),
298                      ff->fname);
299                   return false;       /* reject dir */
300                }
301                return true;           /* accept dir */
302             }
303          }
304       } else {
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(dbglvl, "Exclude wildfile: %s file=%s\n", (char *)fo->wildfile.get(k),
309                      ff->fname);
310                   return false;       /* reject file */
311                }
312                return true;           /* accept file */
313             }
314          }
315
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(dbglvl, "Exclude wildbase: %s file=%s\n", (char *)fo->wildbase.get(k),
320                      basename);
321                   return false;       /* reject file */
322                }
323                return true;           /* accept file */
324             }
325          }
326       }
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(dbglvl, "Exclude wild: %s file=%s\n", (char *)fo->wild.get(k),
331                   ff->fname);
332                return false;          /* reject file */
333             }
334             return true;              /* accept file */
335          }
336       }
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 */
344                }
345                return true;           /* accept file */
346             }
347          }
348       } else {
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 */
355                }
356                return true;           /* accept file */
357             }
358          }
359       }
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 */
366             }
367             return true;              /* accept file */
368          }
369       }
370       /*
371        * If we have an empty Options clause with exclude, then
372        *  exclude the file
373        */
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 */
380       }
381    }
382
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(dbglvl, "Reject wild1: %s\n", ff->fname);
392                return false;          /* reject file */
393             }
394          }
395       }
396       fnm_flags = (incexe->current_opts != NULL && incexe->current_opts->flags & FO_IGNORECASE)
397              ? FNM_CASEFOLD : 0;
398       dlistString *node;
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(dbglvl, "Reject wild2: %s\n", ff->fname);
403             return false;          /* reject file */
404          }
405       }
406    }
407    return true;
408 }
409
410 /*
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.
414  */
415 static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level)
416 {
417    if (top_level) {
418       return ff->file_save(jcr, ff, top_level);   /* accept file */
419    }
420    switch (ff->type) {
421    case FT_NOACCESS:
422    case FT_NOFOLLOW:
423    case FT_NOSTAT:
424    case FT_NOCHG:
425    case FT_ISARCH:
426    case FT_NORECURSE:
427    case FT_NOFSCHG:
428    case FT_INVALIDFS:
429    case FT_INVALIDDT:
430    case FT_NOOPEN:
431 //    return ff->file_save(jcr, ff, top_level);
432
433    /* These items can be filtered */
434    case FT_LNKSAVED:
435    case FT_REGE:
436    case FT_REG:
437    case FT_LNK:
438    case FT_DIRBEGIN:
439    case FT_DIREND:
440    case FT_RAW:
441    case FT_FIFO:
442    case FT_SPEC:
443    case FT_DIRNOCHG:
444    case FT_REPARSE:
445    case FT_JUNCTION:
446       if (accept_file(ff)) {
447          return ff->file_save(jcr, ff, top_level);
448       } else {
449          Dmsg1(dbglvl, "Skip file %s\n", ff->fname);
450          return -1;                   /* ignore this file */
451       }
452
453    default:
454       Dmsg1(000, "Unknown FT code %d\n", ff->type);
455       return 0;
456    }
457 }
458
459
460 /*
461  * Terminate find_files() and release
462  * all allocated memory
463  */
464 int
465 term_find_files(FF_PKT *ff)
466 {
467    int hard_links;
468
469    free_pool_memory(ff->sys_fname);
470    if (ff->fname_save) {
471       free_pool_memory(ff->fname_save);
472    }
473    if (ff->link_save) {
474       free_pool_memory(ff->link_save);
475    }
476    if (ff->ignoredir_fname) {
477       free_pool_memory(ff->ignoredir_fname);
478    }
479    if (ff->snap_fname) {
480       free_pool_memory(ff->snap_fname);
481    }
482    if (ff->snap_top_fname) {
483       free_pool_memory(ff->snap_top_fname);
484    }
485    if (ff->mtab_list) {
486       delete ff->mtab_list;
487    }
488    hard_links = term_find_one(ff);
489    free(ff);
490    return hard_links;
491 }