]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/find.c
FD plugin enhancements
[bacula/bacula] / bacula / src / findlib / find.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2008 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 John Walker.
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(FF_PKT *ff, void *hpkt, 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 /*
100  * For VSS we need to know which windows drives
101  * are used, because we create a snapshot of all used
102  * drives before operation
103  *
104  * the function returns the number of used drives and
105  * fills "drives" with up to 26 (A..Z) drive names
106  *
107  */
108 int
109 get_win32_driveletters(FF_PKT *ff, char* szDrives)
110 {
111    /* szDrives must be at least 27 bytes long */
112
113 #if !defined(HAVE_WIN32)
114    return 0;
115 #endif
116
117    szDrives[0] = 0; /* make empty */
118    int nCount = 0;
119     
120    findFILESET *fileset = ff->fileset;
121    if (fileset) {
122       int i;
123       dlistString *node;
124       
125       for (i=0; i<fileset->include_list.size(); i++) {
126          findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
127          
128          /* look through all files and check */
129          foreach_dlist(node, &incexe->name_list) {
130             char *fname = node->c_str();
131             /* fname should match x:/ */
132             if (strlen(fname) >= 2 && B_ISALPHA(fname[0]) 
133                && fname[1] == ':') {
134                
135                /* always add in uppercase */
136                char ch = toupper(fname[0]);
137                /* if not found in string, add drive letter */
138                if (!strchr(szDrives,ch)) {
139                   szDrives[nCount] = ch;
140                   szDrives[nCount+1] = 0;
141                   nCount++;
142                }                                
143             }            
144          }
145       }
146    }
147    return nCount;
148 }
149
150 /*
151  * Find all specified files (determined by calls to name_add()
152  * This routine calls the (handle_file) subroutine with all
153  * sorts of good information for the final disposition of
154  * the file.
155  *
156  * Call this subroutine with a callback subroutine as the first
157  * argument and a packet as the second argument, this packet
158  * will be passed back to the callback subroutine as the last
159  * argument.
160  *
161  * The callback subroutine gets called with:
162  *  arg1 -- the FF_PKT containing filename, link, stat, ftype, flags, etc
163  *  arg2 -- the user supplied packet
164  *
165  */
166 int
167 find_files(JCR *jcr, FF_PKT *ff, int callback(FF_PKT *ff_pkt, void *hpkt, bool top_level), 
168            void *his_pkt)
169 {
170    ff->callback = callback;
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       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          }
195          dlistString *node;
196          foreach_dlist(node, &incexe->name_list) {
197             char *fname = node->c_str();
198             Dmsg1(100, "F %s\n", fname);
199             ff->top_fname = fname;
200             if (find_one_file(jcr, ff, our_callback, his_pkt, ff->top_fname, (dev_t)-1, true) == 0) {
201                return 0;                  /* error return */
202             }
203          }
204          foreach_dlist(node, &incexe->plugin_list) {
205             char *fname = node->c_str();
206             Dmsg1(100, "P %s\n", fname);
207             ff->top_fname = fname;
208             generate_plugin_event(jcr, bEventPluginCommand, (void *)fname);
209          }
210       }
211    }
212    return 1;
213 }
214
215 static bool accept_file(FF_PKT *ff)
216 {
217    int i, j, k;
218    int fnm_flags;
219    findFILESET *fileset = ff->fileset;
220    findINCEXE *incexe = fileset->incexe;
221    const char *basename;
222    int (*match_func)(const char *pattern, const char *string, int flags);
223
224    if (ff->flags & FO_ENHANCEDWILD) {
225 //    match_func = enh_fnmatch;
226       match_func = fnmatch;
227       if ((basename = last_path_separator(ff->fname)) != NULL)
228          basename++;
229       else
230          basename = ff->fname;
231    } else {
232       match_func = fnmatch;
233       basename = ff->fname;
234    }
235
236    for (j = 0; j < incexe->opts_list.size(); j++) {
237       findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
238       ff->flags = fo->flags;
239       ff->GZIP_level = fo->GZIP_level;
240       ff->reader = fo->reader;
241       ff->writer = fo->writer;
242       ff->fstypes = fo->fstype;
243       ff->drivetypes = fo->drivetype;
244
245       fnm_flags = (ff->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
246       fnm_flags |= (ff->flags & FO_ENHANCEDWILD) ? FNM_PATHNAME : 0;
247
248       if (S_ISDIR(ff->statp.st_mode)) {
249          for (k=0; k<fo->wilddir.size(); k++) {
250             if (match_func((char *)fo->wilddir.get(k), ff->fname, fnmode|fnm_flags) == 0) {
251                if (ff->flags & FO_EXCLUDE) {
252                   Dmsg2(100, "Exclude wilddir: %s file=%s\n", (char *)fo->wilddir.get(k),
253                      ff->fname);
254                   return false;       /* reject dir */
255                }
256                return true;           /* accept dir */
257             }
258          }
259       } else {
260          for (k=0; k<fo->wildfile.size(); k++) {
261             if (match_func((char *)fo->wildfile.get(k), ff->fname, fnmode|fnm_flags) == 0) {
262                if (ff->flags & FO_EXCLUDE) {
263                   Dmsg2(100, "Exclude wildfile: %s file=%s\n", (char *)fo->wildfile.get(k),
264                      ff->fname);
265                   return false;       /* reject file */
266                }
267                return true;           /* accept file */
268             }
269          }
270
271          for (k=0; k<fo->wildbase.size(); k++) {
272             if (match_func((char *)fo->wildbase.get(k), basename, fnmode|fnm_flags) == 0) {
273                if (ff->flags & FO_EXCLUDE) {
274                   Dmsg2(100, "Exclude wildbase: %s file=%s\n", (char *)fo->wildbase.get(k),
275                      basename);
276                   return false;       /* reject file */
277                }
278                return true;           /* accept file */
279             }
280          }
281       }
282       for (k=0; k<fo->wild.size(); k++) {
283          if (match_func((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
284             if (ff->flags & FO_EXCLUDE) {
285                Dmsg2(100, "Exclude wild: %s file=%s\n", (char *)fo->wild.get(k),
286                   ff->fname);
287                return false;          /* reject file */
288             }
289             return true;              /* accept file */
290          }
291       }
292       if (S_ISDIR(ff->statp.st_mode)) {
293          for (k=0; k<fo->regexdir.size(); k++) {
294             const int nmatch = 30;
295             regmatch_t pmatch[nmatch];
296             if (regexec((regex_t *)fo->regexdir.get(k), ff->fname, nmatch, pmatch,  0) == 0) {
297                if (ff->flags & FO_EXCLUDE) {
298                   return false;       /* reject file */
299                }
300                return true;           /* accept file */
301             }
302          }
303       } else {
304          for (k=0; k<fo->regexfile.size(); k++) {
305             const int nmatch = 30;
306             regmatch_t pmatch[nmatch];
307             if (regexec((regex_t *)fo->regexfile.get(k), ff->fname, nmatch, pmatch,  0) == 0) {
308                if (ff->flags & FO_EXCLUDE) {
309                   return false;       /* reject file */
310                }
311                return true;           /* accept file */
312             }
313          }
314       }
315       for (k=0; k<fo->regex.size(); k++) {
316          const int nmatch = 30;
317          regmatch_t pmatch[nmatch];
318          if (regexec((regex_t *)fo->regex.get(k), ff->fname, nmatch, pmatch,  0) == 0) {
319             if (ff->flags & FO_EXCLUDE) {
320                return false;          /* reject file */
321             }
322             return true;              /* accept file */
323          }
324       }
325       /*
326        * If we have an empty Options clause with exclude, then
327        *  exclude the file
328        */
329       if (ff->flags & FO_EXCLUDE &&
330           fo->regex.size() == 0     && fo->wild.size() == 0 &&
331           fo->regexdir.size() == 0  && fo->wilddir.size() == 0 &&
332           fo->regexfile.size() == 0 && fo->wildfile.size() == 0 &&
333           fo->wildbase.size() == 0) {
334          return false;              /* reject file */
335       }
336    }
337
338    /* Now apply the Exclude { } directive */
339    for (i=0; i<fileset->exclude_list.size(); i++) {
340       findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
341       for (j=0; j<incexe->opts_list.size(); j++) {
342          findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
343          fnm_flags = (fo->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
344          for (k=0; k<fo->wild.size(); k++) {
345             if (fnmatch((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
346                Dmsg1(100, "Reject wild1: %s\n", ff->fname);
347                return false;          /* reject file */
348             }
349          }
350       }
351       fnm_flags = (incexe->current_opts != NULL && incexe->current_opts->flags & FO_IGNORECASE)
352              ? FNM_CASEFOLD : 0;
353       dlistString *node;
354       foreach_dlist(node, &incexe->name_list) {
355          char *fname = node->c_str();
356          if (fnmatch(fname, ff->fname, fnmode|fnm_flags) == 0) {
357             Dmsg1(100, "Reject wild2: %s\n", ff->fname);
358             return false;          /* reject file */
359          }
360       }
361    }
362    return true;
363 }
364
365 /*
366  * The code comes here for each file examined.
367  * We filter the files, then call the user's callback if
368  *    the file is included.
369  */
370 static int our_callback(FF_PKT *ff, void *hpkt, bool top_level)
371 {
372    if (top_level) {
373       return ff->callback(ff, hpkt, top_level);   /* accept file */
374    }
375    switch (ff->type) {
376    case FT_NOACCESS:
377    case FT_NOFOLLOW:
378    case FT_NOSTAT:
379    case FT_NOCHG:
380    case FT_ISARCH:
381    case FT_NORECURSE:
382    case FT_NOFSCHG:
383    case FT_INVALIDFS:
384    case FT_INVALIDDT:
385    case FT_NOOPEN:
386    case FT_REPARSE:
387 //    return ff->callback(ff, hpkt, top_level);
388
389    /* These items can be filtered */
390    case FT_LNKSAVED:
391    case FT_REGE:
392    case FT_REG:
393    case FT_LNK:
394    case FT_DIRBEGIN:
395    case FT_DIREND:
396    case FT_RAW:
397    case FT_FIFO:
398    case FT_SPEC:
399    case FT_DIRNOCHG:
400       if (accept_file(ff)) {
401 //       Dmsg2(000, "Accept file %s; reader=%s\n", ff->fname, NPRT(ff->reader));
402          return ff->callback(ff, hpkt, top_level);
403       } else {
404          Dmsg1(100, "Skip file %s\n", ff->fname);
405          return -1;                   /* ignore this file */
406       }
407
408    default:
409       Dmsg1(000, "Unknown FT code %d\n", ff->type);
410       return 0;
411    }
412 }
413
414
415 /*
416  * Terminate find_files() and release
417  * all allocated memory
418  */
419 int
420 term_find_files(FF_PKT *ff)
421 {
422    int hard_links;
423
424    free_pool_memory(ff->sys_fname);
425    if (ff->fname_save) {
426       free_pool_memory(ff->fname_save);
427    }
428    if (ff->link_save) {
429       free_pool_memory(ff->link_save);
430    }
431    hard_links = term_find_one(ff);
432    free(ff);
433    return hard_links;
434 }