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