]> 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       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          if (plugin_save) {
208             foreach_dlist(node, &incexe->plugin_list) {
209                char *fname = node->c_str();
210                Dmsg1(100, "PluginCommand: %s\n", fname);
211                ff->top_fname = fname;
212                ff->cmd_plugin = true;
213                plugin_save(jcr, ff, true);
214                ff->cmd_plugin = false;
215             }
216          }
217       }
218    }
219    return 1;
220 }
221
222 /*
223  * Test if the currently selected directory (in ff->fname) is
224  *  explicitly in the Include list or explicitly in the Exclude 
225  *  list.
226  */
227 bool is_in_fileset(FF_PKT *ff)
228 {
229    dlistString *node;
230    char *fname;
231    int i;
232    findINCEXE *incexe;
233    findFILESET *fileset = ff->fileset;
234    if (fileset) {
235       for (i=0; i<fileset->include_list.size(); i++) {
236          incexe = (findINCEXE *)fileset->include_list.get(i);
237          foreach_dlist(node, &incexe->name_list) {
238             fname = node->c_str();
239             Dmsg2(100, "Inc fname=%s ff->fname=%s\n", fname, ff->fname);
240             if (strcmp(fname, ff->fname) == 0) {
241                return true;
242             }
243          }
244       }
245       for (i=0; i<fileset->exclude_list.size(); i++) {
246          incexe = (findINCEXE *)fileset->exclude_list.get(i);
247          foreach_dlist(node, &incexe->name_list) {
248             fname = node->c_str();
249             Dmsg2(100, "Exc fname=%s ff->fname=%s\n", fname, ff->fname);
250             if (strcmp(fname, ff->fname) == 0) {
251                return true;
252             }
253          }
254       }
255    }
256    return false;
257 }
258
259
260 static bool accept_file(FF_PKT *ff)
261 {
262    int i, j, k;
263    int fnm_flags;
264    findFILESET *fileset = ff->fileset;
265    findINCEXE *incexe = fileset->incexe;
266    const char *basename;
267    int (*match_func)(const char *pattern, const char *string, int flags);
268
269    if (ff->flags & FO_ENHANCEDWILD) {
270 //    match_func = enh_fnmatch;
271       match_func = fnmatch;
272       if ((basename = last_path_separator(ff->fname)) != NULL)
273          basename++;
274       else
275          basename = ff->fname;
276    } else {
277       match_func = fnmatch;
278       basename = ff->fname;
279    }
280
281    for (j = 0; j < incexe->opts_list.size(); j++) {
282       findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
283       ff->flags = fo->flags;
284       ff->GZIP_level = fo->GZIP_level;
285       ff->ignoredir = fo->ignoredir;
286       ff->fstypes = fo->fstype;
287       ff->drivetypes = fo->drivetype;
288
289       fnm_flags = (ff->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
290       fnm_flags |= (ff->flags & FO_ENHANCEDWILD) ? FNM_PATHNAME : 0;
291
292       if (S_ISDIR(ff->statp.st_mode)) {
293          for (k=0; k<fo->wilddir.size(); k++) {
294             if (match_func((char *)fo->wilddir.get(k), ff->fname, fnmode|fnm_flags) == 0) {
295                if (ff->flags & FO_EXCLUDE) {
296                   Dmsg2(100, "Exclude wilddir: %s file=%s\n", (char *)fo->wilddir.get(k),
297                      ff->fname);
298                   return false;       /* reject dir */
299                }
300                return true;           /* accept dir */
301             }
302          }
303       } else {
304          for (k=0; k<fo->wildfile.size(); k++) {
305             if (match_func((char *)fo->wildfile.get(k), ff->fname, fnmode|fnm_flags) == 0) {
306                if (ff->flags & FO_EXCLUDE) {
307                   Dmsg2(100, "Exclude wildfile: %s file=%s\n", (char *)fo->wildfile.get(k),
308                      ff->fname);
309                   return false;       /* reject file */
310                }
311                return true;           /* accept file */
312             }
313          }
314
315          for (k=0; k<fo->wildbase.size(); k++) {
316             if (match_func((char *)fo->wildbase.get(k), basename, fnmode|fnm_flags) == 0) {
317                if (ff->flags & FO_EXCLUDE) {
318                   Dmsg2(100, "Exclude wildbase: %s file=%s\n", (char *)fo->wildbase.get(k),
319                      basename);
320                   return false;       /* reject file */
321                }
322                return true;           /* accept file */
323             }
324          }
325       }
326       for (k=0; k<fo->wild.size(); k++) {
327          if (match_func((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
328             if (ff->flags & FO_EXCLUDE) {
329                Dmsg2(100, "Exclude wild: %s file=%s\n", (char *)fo->wild.get(k),
330                   ff->fname);
331                return false;          /* reject file */
332             }
333             return true;              /* accept file */
334          }
335       }
336       if (S_ISDIR(ff->statp.st_mode)) {
337          for (k=0; k<fo->regexdir.size(); k++) {
338             const int nmatch = 30;
339             regmatch_t pmatch[nmatch];
340             if (regexec((regex_t *)fo->regexdir.get(k), ff->fname, nmatch, pmatch,  0) == 0) {
341                if (ff->flags & FO_EXCLUDE) {
342                   return false;       /* reject file */
343                }
344                return true;           /* accept file */
345             }
346          }
347       } else {
348          for (k=0; k<fo->regexfile.size(); k++) {
349             const int nmatch = 30;
350             regmatch_t pmatch[nmatch];
351             if (regexec((regex_t *)fo->regexfile.get(k), ff->fname, nmatch, pmatch,  0) == 0) {
352                if (ff->flags & FO_EXCLUDE) {
353                   return false;       /* reject file */
354                }
355                return true;           /* accept file */
356             }
357          }
358       }
359       for (k=0; k<fo->regex.size(); k++) {
360          const int nmatch = 30;
361          regmatch_t pmatch[nmatch];
362          if (regexec((regex_t *)fo->regex.get(k), ff->fname, nmatch, pmatch,  0) == 0) {
363             if (ff->flags & FO_EXCLUDE) {
364                return false;          /* reject file */
365             }
366             return true;              /* accept file */
367          }
368       }
369       /*
370        * If we have an empty Options clause with exclude, then
371        *  exclude the file
372        */
373       if (ff->flags & FO_EXCLUDE &&
374           fo->regex.size() == 0     && fo->wild.size() == 0 &&
375           fo->regexdir.size() == 0  && fo->wilddir.size() == 0 &&
376           fo->regexfile.size() == 0 && fo->wildfile.size() == 0 &&
377           fo->wildbase.size() == 0) {
378          return false;              /* reject file */
379       }
380    }
381
382    /* Now apply the Exclude { } directive */
383    for (i=0; i<fileset->exclude_list.size(); i++) {
384       findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
385       for (j=0; j<incexe->opts_list.size(); j++) {
386          findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
387          fnm_flags = (fo->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
388          for (k=0; k<fo->wild.size(); k++) {
389             if (fnmatch((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
390                Dmsg1(100, "Reject wild1: %s\n", ff->fname);
391                return false;          /* reject file */
392             }
393          }
394       }
395       fnm_flags = (incexe->current_opts != NULL && incexe->current_opts->flags & FO_IGNORECASE)
396              ? FNM_CASEFOLD : 0;
397       dlistString *node;
398       foreach_dlist(node, &incexe->name_list) {
399          char *fname = node->c_str();
400          if (fnmatch(fname, ff->fname, fnmode|fnm_flags) == 0) {
401             Dmsg1(100, "Reject wild2: %s\n", ff->fname);
402             return false;          /* reject file */
403          }
404       }
405    }
406    return true;
407 }
408
409 /*
410  * The code comes here for each file examined.
411  * We filter the files, then call the user's callback if
412  *    the file is included.
413  */
414 static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level)
415 {
416    if (top_level) {
417       return ff->file_save(jcr, ff, top_level);   /* accept file */
418    }
419    switch (ff->type) {
420    case FT_NOACCESS:
421    case FT_NOFOLLOW:
422    case FT_NOSTAT:
423    case FT_NOCHG:
424    case FT_ISARCH:
425    case FT_NORECURSE:
426    case FT_NOFSCHG:
427    case FT_INVALIDFS:
428    case FT_INVALIDDT:
429    case FT_NOOPEN:
430    case FT_REPARSE:
431 //    return ff->file_save(jcr, ff, top_level);
432
433    /* These items can be filtered */
434    case FT_LNKSAVED:
435    case FT_REGE:
436    case FT_REG:
437    case FT_LNK:
438    case FT_DIRBEGIN:
439    case FT_DIREND:
440    case FT_RAW:
441    case FT_FIFO:
442    case FT_SPEC:
443    case FT_DIRNOCHG:
444       if (accept_file(ff)) {
445          return ff->file_save(jcr, ff, top_level);
446       } else {
447          Dmsg1(100, "Skip file %s\n", ff->fname);
448          return -1;                   /* ignore this file */
449       }
450
451    default:
452       Dmsg1(000, "Unknown FT code %d\n", ff->type);
453       return 0;
454    }
455 }
456
457
458 /*
459  * Terminate find_files() and release
460  * all allocated memory
461  */
462 int
463 term_find_files(FF_PKT *ff)
464 {
465    int hard_links;
466
467    free_pool_memory(ff->sys_fname);
468    if (ff->fname_save) {
469       free_pool_memory(ff->fname_save);
470    }
471    if (ff->link_save) {
472       free_pool_memory(ff->link_save);
473    }
474    hard_links = term_find_one(ff);
475    free(ff);
476    return hard_links;
477 }