]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/find.c
Allow plugins to add drives to vss snapshot
[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 three of the GNU Affero 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 Affero 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 = 450;
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    int nCount;
125    /* Can be already filled by plugin, so check that everything
126     * is on upper case. TODO: can check for dupplicate?
127     */
128    for (nCount = 0; nCount < 27 && szDrives[nCount] ; nCount++) {
129       szDrives[nCount] = toupper(szDrives[nCount]);
130    }
131
132    findFILESET *fileset = ff->fileset;
133    if (fileset) {
134       int i;
135       dlistString *node;
136       
137       for (i=0; i<fileset->include_list.size(); i++) {
138          findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
139          
140          /* look through all files and check */
141          foreach_dlist(node, &incexe->name_list) {
142             char *fname = node->c_str();
143             /* fname should match x:/ */
144             if (strlen(fname) >= 2 && B_ISALPHA(fname[0]) 
145                && fname[1] == ':') {
146                
147                /* always add in uppercase */
148                char ch = toupper(fname[0]);
149                /* if not found in string, add drive letter */
150                if (!strchr(szDrives,ch)) {
151                   szDrives[nCount] = ch;
152                   szDrives[nCount+1] = 0;
153                   nCount++;
154                }                                
155             }            
156          }
157       }
158    }
159    return nCount;
160 }
161
162 /*
163  * Call this subroutine with a callback subroutine as the first
164  * argument and a packet as the second argument, this packet
165  * will be passed back to the callback subroutine as the last
166  * argument.
167  *
168  */
169 int
170 find_files(JCR *jcr, FF_PKT *ff, int file_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level),
171            int plugin_save(JCR *jcr, FF_PKT *ff_pkt, bool top_level)) 
172 {
173    ff->file_save = file_save;
174    ff->plugin_save = plugin_save;
175
176    /* This is the new way */
177    findFILESET *fileset = ff->fileset;
178    if (fileset) {
179       int i, j;
180       ff->flags = 0;
181       ff->VerifyOpts[0] = 'V';
182       ff->VerifyOpts[1] = 0;
183       strcpy(ff->AccurateOpts, "Cmcs");  /* mtime+ctime+size by default */
184       strcpy(ff->BaseJobOpts, "Jspug5"); /* size+perm+user+group+chk  */
185       for (i=0; i<fileset->include_list.size(); i++) {
186          findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
187          fileset->incexe = incexe;
188          /*
189           * By setting all options, we in effect OR the global options
190           *   which is what we want.
191           */
192          for (j=0; j<incexe->opts_list.size(); j++) {
193             findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
194             ff->flags |= fo->flags;
195             ff->GZIP_level = fo->GZIP_level;
196             ff->strip_path = fo->strip_path;
197             ff->fstypes = fo->fstype;
198             ff->drivetypes = fo->drivetype;
199             bstrncat(ff->VerifyOpts, fo->VerifyOpts, sizeof(ff->VerifyOpts));
200             if (fo->AccurateOpts[0]) {
201                bstrncpy(ff->AccurateOpts, fo->AccurateOpts, sizeof(ff->AccurateOpts));
202             }
203             if (fo->BaseJobOpts[0]) {
204                bstrncpy(ff->BaseJobOpts, fo->BaseJobOpts, sizeof(ff->BaseJobOpts));
205             }
206          }
207          Dmsg3(50, "Verify=<%s> Accurate=<%s> BaseJob=<%s>\n", ff->VerifyOpts, ff->AccurateOpts, ff->BaseJobOpts);
208          dlistString *node;
209          foreach_dlist(node, &incexe->name_list) {
210             char *fname = node->c_str();
211             Dmsg1(dbglvl, "F %s\n", fname);
212             ff->top_fname = fname;
213             if (find_one_file(jcr, ff, our_callback, ff->top_fname, (dev_t)-1, true) == 0) {
214                return 0;                  /* error return */
215             }
216             if (job_canceled(jcr)) {
217                return 0;
218             }
219          }
220          foreach_dlist(node, &incexe->plugin_list) {
221             char *fname = node->c_str();
222             if (!plugin_save) {
223                Jmsg(jcr, M_FATAL, 0, _("Plugin: \"%s\" not found.\n"), fname);
224                return 0;
225             }
226             Dmsg1(dbglvl, "PluginCommand: %s\n", fname);
227             ff->top_fname = fname;
228             ff->cmd_plugin = true;
229             plugin_save(jcr, ff, true);
230             ff->cmd_plugin = false;
231             if (job_canceled(jcr)) {
232                return 0;
233             }
234          }
235       }
236    }
237    return 1;
238 }
239
240 /*
241  * Test if the currently selected directory (in ff->fname) is
242  *  explicitly in the Include list or explicitly in the Exclude 
243  *  list.
244  */
245 bool is_in_fileset(FF_PKT *ff)
246 {
247    dlistString *node;
248    char *fname;
249    int i;
250    findINCEXE *incexe;
251    findFILESET *fileset = ff->fileset;
252    if (fileset) {
253       for (i=0; i<fileset->include_list.size(); i++) {
254          incexe = (findINCEXE *)fileset->include_list.get(i);
255          foreach_dlist(node, &incexe->name_list) {
256             fname = node->c_str();
257             Dmsg2(dbglvl, "Inc fname=%s ff->fname=%s\n", fname, ff->fname);
258             if (strcmp(fname, ff->fname) == 0) {
259                return true;
260             }
261          }
262       }
263       for (i=0; i<fileset->exclude_list.size(); i++) {
264          incexe = (findINCEXE *)fileset->exclude_list.get(i);
265          foreach_dlist(node, &incexe->name_list) {
266             fname = node->c_str();
267             Dmsg2(dbglvl, "Exc fname=%s ff->fname=%s\n", fname, ff->fname);
268             if (strcmp(fname, ff->fname) == 0) {
269                return true;
270             }
271          }
272       }
273    }
274    return false;
275 }
276
277
278 static bool accept_file(FF_PKT *ff)
279 {
280    int i, j, k;
281    int fnm_flags;
282    findFILESET *fileset = ff->fileset;
283    findINCEXE *incexe = fileset->incexe;
284    const char *basename;
285    int (*match_func)(const char *pattern, const char *string, int flags);
286
287    Dmsg1(dbglvl, "enter accept_file: fname=%s\n", ff->fname);
288    if (ff->flags & FO_ENHANCEDWILD) {
289 //    match_func = enh_fnmatch;
290       match_func = fnmatch;
291       if ((basename = last_path_separator(ff->fname)) != NULL)
292          basename++;
293       else
294          basename = ff->fname;
295    } else {
296       match_func = fnmatch;
297       basename = ff->fname;
298    }
299
300    for (j = 0; j < incexe->opts_list.size(); j++) {
301       findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
302       ff->flags = fo->flags;
303       ff->GZIP_level = fo->GZIP_level;
304       ff->fstypes = fo->fstype;
305       ff->drivetypes = fo->drivetype;
306
307       fnm_flags = (ff->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
308       fnm_flags |= (ff->flags & FO_ENHANCEDWILD) ? FNM_PATHNAME : 0;
309
310       if (S_ISDIR(ff->statp.st_mode)) {
311          for (k=0; k<fo->wilddir.size(); k++) {
312             if (match_func((char *)fo->wilddir.get(k), ff->fname, fnmode|fnm_flags) == 0) {
313                if (ff->flags & FO_EXCLUDE) {
314                   Dmsg2(dbglvl, "Exclude wilddir: %s file=%s\n", (char *)fo->wilddir.get(k),
315                      ff->fname);
316                   return false;       /* reject dir */
317                }
318                return true;           /* accept dir */
319             }
320          }
321       } else {
322          for (k=0; k<fo->wildfile.size(); k++) {
323             if (match_func((char *)fo->wildfile.get(k), ff->fname, fnmode|fnm_flags) == 0) {
324                if (ff->flags & FO_EXCLUDE) {
325                   Dmsg2(dbglvl, "Exclude wildfile: %s file=%s\n", (char *)fo->wildfile.get(k),
326                      ff->fname);
327                   return false;       /* reject file */
328                }
329                return true;           /* accept file */
330             }
331          }
332
333          for (k=0; k<fo->wildbase.size(); k++) {
334             if (match_func((char *)fo->wildbase.get(k), basename, fnmode|fnm_flags) == 0) {
335                if (ff->flags & FO_EXCLUDE) {
336                   Dmsg2(dbglvl, "Exclude wildbase: %s file=%s\n", (char *)fo->wildbase.get(k),
337                      basename);
338                   return false;       /* reject file */
339                }
340                return true;           /* accept file */
341             }
342          }
343       }
344       for (k=0; k<fo->wild.size(); k++) {
345          if (match_func((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
346             if (ff->flags & FO_EXCLUDE) {
347                Dmsg2(dbglvl, "Exclude wild: %s file=%s\n", (char *)fo->wild.get(k),
348                   ff->fname);
349                return false;          /* reject file */
350             }
351             return true;              /* accept file */
352          }
353       }
354       if (S_ISDIR(ff->statp.st_mode)) {
355          for (k=0; k<fo->regexdir.size(); k++) {
356             const int nmatch = 30;
357             regmatch_t pmatch[nmatch];
358             if (regexec((regex_t *)fo->regexdir.get(k), ff->fname, nmatch, pmatch,  0) == 0) {
359                if (ff->flags & FO_EXCLUDE) {
360                   return false;       /* reject file */
361                }
362                return true;           /* accept file */
363             }
364          }
365       } else {
366          for (k=0; k<fo->regexfile.size(); k++) {
367             const int nmatch = 30;
368             regmatch_t pmatch[nmatch];
369             if (regexec((regex_t *)fo->regexfile.get(k), ff->fname, nmatch, pmatch,  0) == 0) {
370                if (ff->flags & FO_EXCLUDE) {
371                   return false;       /* reject file */
372                }
373                return true;           /* accept file */
374             }
375          }
376       }
377       for (k=0; k<fo->regex.size(); k++) {
378          const int nmatch = 30;
379          regmatch_t pmatch[nmatch];
380          if (regexec((regex_t *)fo->regex.get(k), ff->fname, nmatch, pmatch,  0) == 0) {
381             if (ff->flags & FO_EXCLUDE) {
382                return false;          /* reject file */
383             }
384             return true;              /* accept file */
385          }
386       }
387       /*
388        * If we have an empty Options clause with exclude, then
389        *  exclude the file
390        */
391       if (ff->flags & FO_EXCLUDE &&
392           fo->regex.size() == 0     && fo->wild.size() == 0 &&
393           fo->regexdir.size() == 0  && fo->wilddir.size() == 0 &&
394           fo->regexfile.size() == 0 && fo->wildfile.size() == 0 &&
395           fo->wildbase.size() == 0) {
396          return false;              /* reject file */
397       }
398    }
399
400    /* Now apply the Exclude { } directive */
401    for (i=0; i<fileset->exclude_list.size(); i++) {
402       findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
403       for (j=0; j<incexe->opts_list.size(); j++) {
404          findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
405          fnm_flags = (fo->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
406          for (k=0; k<fo->wild.size(); k++) {
407             if (fnmatch((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
408                Dmsg1(dbglvl, "Reject wild1: %s\n", ff->fname);
409                return false;          /* reject file */
410             }
411          }
412       }
413       fnm_flags = (incexe->current_opts != NULL && incexe->current_opts->flags & FO_IGNORECASE)
414              ? FNM_CASEFOLD : 0;
415       dlistString *node;
416       foreach_dlist(node, &incexe->name_list) {
417          char *fname = node->c_str();
418          if (fnmatch(fname, ff->fname, fnmode|fnm_flags) == 0) {
419             Dmsg1(dbglvl, "Reject wild2: %s\n", ff->fname);
420             return false;          /* reject file */
421          }
422       }
423    }
424    return true;
425 }
426
427 /*
428  * The code comes here for each file examined.
429  * We filter the files, then call the user's callback if
430  *    the file is included.
431  */
432 static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level)
433 {
434    if (top_level) {
435       return ff->file_save(jcr, ff, top_level);   /* accept file */
436    }
437    switch (ff->type) {
438    case FT_NOACCESS:
439    case FT_NOFOLLOW:
440    case FT_NOSTAT:
441    case FT_NOCHG:
442    case FT_ISARCH:
443    case FT_NORECURSE:
444    case FT_NOFSCHG:
445    case FT_INVALIDFS:
446    case FT_INVALIDDT:
447    case FT_NOOPEN:
448    case FT_REPARSE:
449 //    return ff->file_save(jcr, ff, top_level);
450
451    /* These items can be filtered */
452    case FT_LNKSAVED:
453    case FT_REGE:
454    case FT_REG:
455    case FT_LNK:
456    case FT_DIRBEGIN:
457    case FT_DIREND:
458    case FT_RAW:
459    case FT_FIFO:
460    case FT_SPEC:
461    case FT_DIRNOCHG:
462       if (accept_file(ff)) {
463          return ff->file_save(jcr, ff, top_level);
464       } else {
465          Dmsg1(dbglvl, "Skip file %s\n", ff->fname);
466          return -1;                   /* ignore this file */
467       }
468
469    default:
470       Dmsg1(000, "Unknown FT code %d\n", ff->type);
471       return 0;
472    }
473 }
474
475
476 /*
477  * Terminate find_files() and release
478  * all allocated memory
479  */
480 int
481 term_find_files(FF_PKT *ff)
482 {
483    int hard_links;
484
485    free_pool_memory(ff->sys_fname);
486    if (ff->fname_save) {
487       free_pool_memory(ff->fname_save);
488    }
489    if (ff->link_save) {
490       free_pool_memory(ff->link_save);
491    }
492    hard_links = term_find_one(ff);
493    free(ff);
494    return hard_links;
495 }