]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/find.c
Make a missing plugin during backup fatal
[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 < 2048) {
71       path_max = 2048;
72    }
73
74    name_max = pathconf(".", _PC_NAME_MAX);
75    if (name_max < 2048) {
76       name_max = 2048;
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       strcpy(ff->BaseJobOpts, "J:mspug5"); /* mtime+size+perm+user+group+chk  */
180       for (i=0; i<fileset->include_list.size(); i++) {
181          findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
182          fileset->incexe = incexe;
183          /*
184           * By setting all options, we in effect or the global options
185           *   which is what we want.
186           */
187          for (j=0; j<incexe->opts_list.size(); j++) {
188             findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
189             ff->flags |= fo->flags;
190             ff->GZIP_level = fo->GZIP_level;
191             ff->strip_path = fo->strip_path;
192             ff->fstypes = fo->fstype;
193             ff->drivetypes = fo->drivetype;
194             bstrncat(ff->VerifyOpts, fo->VerifyOpts, sizeof(ff->VerifyOpts));
195             bstrncat(ff->AccurateOpts, fo->AccurateOpts, sizeof(ff->AccurateOpts));
196             bstrncat(ff->BaseJobOpts, fo->BaseJobOpts, sizeof(ff->BaseJobOpts));
197          }
198          dlistString *node;
199          foreach_dlist(node, &incexe->name_list) {
200             char *fname = node->c_str();
201             Dmsg1(100, "F %s\n", fname);
202             ff->top_fname = fname;
203             if (find_one_file(jcr, ff, our_callback, ff->top_fname, (dev_t)-1, true) == 0) {
204                return 0;                  /* error return */
205             }
206          }
207          foreach_dlist(node, &incexe->plugin_list) {
208             char *fname = node->c_str();
209             if (!plugin_save) {
210                Jmsg(jcr, M_FATAL, 0, _("Plugin: \"%s\" not found.\n"), fname);
211                return 0;
212             }
213             Dmsg1(100, "PluginCommand: %s\n", fname);
214             ff->top_fname = fname;
215             ff->cmd_plugin = true;
216             plugin_save(jcr, ff, true);
217             ff->cmd_plugin = false;
218          }
219       }
220    }
221    return 1;
222 }
223
224 /*
225  * Test if the currently selected directory (in ff->fname) is
226  *  explicitly in the Include list or explicitly in the Exclude 
227  *  list.
228  */
229 bool is_in_fileset(FF_PKT *ff)
230 {
231    dlistString *node;
232    char *fname;
233    int i;
234    findINCEXE *incexe;
235    findFILESET *fileset = ff->fileset;
236    if (fileset) {
237       for (i=0; i<fileset->include_list.size(); i++) {
238          incexe = (findINCEXE *)fileset->include_list.get(i);
239          foreach_dlist(node, &incexe->name_list) {
240             fname = node->c_str();
241             Dmsg2(100, "Inc fname=%s ff->fname=%s\n", fname, ff->fname);
242             if (strcmp(fname, ff->fname) == 0) {
243                return true;
244             }
245          }
246       }
247       for (i=0; i<fileset->exclude_list.size(); i++) {
248          incexe = (findINCEXE *)fileset->exclude_list.get(i);
249          foreach_dlist(node, &incexe->name_list) {
250             fname = node->c_str();
251             Dmsg2(100, "Exc fname=%s ff->fname=%s\n", fname, ff->fname);
252             if (strcmp(fname, ff->fname) == 0) {
253                return true;
254             }
255          }
256       }
257    }
258    return false;
259 }
260
261
262 static bool accept_file(FF_PKT *ff)
263 {
264    int i, j, k;
265    int fnm_flags;
266    findFILESET *fileset = ff->fileset;
267    findINCEXE *incexe = fileset->incexe;
268    const char *basename;
269    int (*match_func)(const char *pattern, const char *string, int flags);
270
271    if (ff->flags & FO_ENHANCEDWILD) {
272 //    match_func = enh_fnmatch;
273       match_func = fnmatch;
274       if ((basename = last_path_separator(ff->fname)) != NULL)
275          basename++;
276       else
277          basename = ff->fname;
278    } else {
279       match_func = fnmatch;
280       basename = ff->fname;
281    }
282
283    for (j = 0; j < incexe->opts_list.size(); j++) {
284       findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
285       ff->flags = fo->flags;
286       ff->GZIP_level = fo->GZIP_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(100, "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(100, "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(100, "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(100, "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(100, "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(100, "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    case FT_REPARSE:
432 //    return ff->file_save(jcr, ff, top_level);
433
434    /* These items can be filtered */
435    case FT_LNKSAVED:
436    case FT_REGE:
437    case FT_REG:
438    case FT_LNK:
439    case FT_DIRBEGIN:
440    case FT_DIREND:
441    case FT_RAW:
442    case FT_FIFO:
443    case FT_SPEC:
444    case FT_DIRNOCHG:
445       if (accept_file(ff)) {
446          return ff->file_save(jcr, ff, top_level);
447       } else {
448          Dmsg1(100, "Skip file %s\n", ff->fname);
449          return -1;                   /* ignore this file */
450       }
451
452    default:
453       Dmsg1(000, "Unknown FT code %d\n", ff->type);
454       return 0;
455    }
456 }
457
458
459 /*
460  * Terminate find_files() and release
461  * all allocated memory
462  */
463 int
464 term_find_files(FF_PKT *ff)
465 {
466    int hard_links;
467
468    free_pool_memory(ff->sys_fname);
469    if (ff->fname_save) {
470       free_pool_memory(ff->fname_save);
471    }
472    if (ff->link_save) {
473       free_pool_memory(ff->link_save);
474    }
475    hard_links = term_find_one(ff);
476    free(ff);
477    return hard_links;
478 }