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