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