]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/find.c
Update copyright
[bacula/bacula] / bacula / src / findlib / find.c
1 /*
2  * Main routine for finding files on a file system.
3  *  The heart of the work to find the files on the
4  *    system is done in find_one.c. Here we have the
5  *    higher level control as well as the matching
6  *    routines for the new syntax Options resource.
7  *
8  *  Kern E. Sibbald, MM
9  *
10  *   Version $Id$
11  */
12 /*
13    Bacula® - The Network Backup Solution
14
15    Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
16
17    The main author of Bacula is Kern Sibbald, with contributions from
18    many others, a complete list can be found in the file AUTHORS.
19    This program is Free Software; you can redistribute it and/or
20    modify it under the terms of version two of the GNU General Public
21    License as published by the Free Software Foundation plus additions
22    that are listed in the file LICENSE.
23
24    This program is distributed in the hope that it will be useful, but
25    WITHOUT ANY WARRANTY; without even the implied warranty of
26    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27    General Public License for more details.
28
29    You should have received a copy of the GNU General Public License
30    along with this program; if not, write to the Free Software
31    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
32    02110-1301, USA.
33
34    Bacula® is a registered trademark of John Walker.
35    The licensor of Bacula is the Free Software Foundation Europe
36    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
37    Switzerland, email:ftf@fsfeurope.org.
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, j;
123       
124       for (i=0; i<fileset->include_list.size(); i++) {
125          findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
126          
127          /* look through all files and check */
128          for (j=0; j<incexe->name_list.size(); j++) {
129             char *fname = (char *)incexe->name_list.get(j);
130             /* fname should match x:/ */
131             if (strlen(fname) >= 2 && B_ISALPHA(fname[0]) 
132                && fname[1] == ':') {
133                
134                /* always add in uppercase */
135                char ch = toupper(fname[0]);
136                /* if not found in string, add drive letter */
137                if (!strchr(szDrives,ch)) {
138                   szDrives[nCount] = ch;
139                   szDrives[nCount+1] = 0;
140                   nCount++;
141                }                                
142             }            
143          }
144       }
145    }
146    return nCount;
147 }
148
149 /*
150  * Find all specified files (determined by calls to name_add()
151  * This routine calls the (handle_file) subroutine with all
152  * sorts of good information for the final disposition of
153  * the file.
154  *
155  * Call this subroutine with a callback subroutine as the first
156  * argument and a packet as the second argument, this packet
157  * will be passed back to the callback subroutine as the last
158  * argument.
159  *
160  * The callback subroutine gets called with:
161  *  arg1 -- the FF_PKT containing filename, link, stat, ftype, flags, etc
162  *  arg2 -- the user supplied packet
163  *
164  */
165 int
166 find_files(JCR *jcr, FF_PKT *ff, int callback(FF_PKT *ff_pkt, void *hpkt, bool top_level), 
167            void *his_pkt)
168 {
169    ff->callback = callback;
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       for (i=0; i<fileset->include_list.size(); i++) {
179          findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
180          fileset->incexe = incexe;
181          /*
182           * By setting all options, we in effect or the global options
183           *   which is what we want.
184           */
185          for (j=0; j<incexe->opts_list.size(); j++) {
186             findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
187             ff->flags |= fo->flags;
188             ff->GZIP_level = fo->GZIP_level;
189             ff->fstypes = fo->fstype;
190             ff->drivetypes = fo->drivetype;
191             bstrncat(ff->VerifyOpts, fo->VerifyOpts, sizeof(ff->VerifyOpts));
192          }
193          for (j=0; j<incexe->name_list.size(); j++) {
194             Dmsg1(100, "F %s\n", (char *)incexe->name_list.get(j));
195             ff->top_fname = (char *)incexe->name_list.get(j);
196             if (find_one_file(jcr, ff, our_callback, his_pkt, ff->top_fname, (dev_t)-1, true) == 0) {
197                return 0;                  /* error return */
198             }
199          }
200       }
201    }
202    return 1;
203 }
204
205 static bool accept_file(FF_PKT *ff)
206 {
207    int i, j, k;
208    int fnm_flags;
209    findFILESET *fileset = ff->fileset;
210    findINCEXE *incexe = fileset->incexe;
211    const char *basename;
212    int (*match_func)(const char *pattern, const char *string, int flags);
213
214    if (ff->flags & FO_ENHANCEDWILD) {
215       match_func = enh_fnmatch;
216       if ((basename = last_path_separator(ff->fname)) != NULL)
217          basename++;
218       else
219          basename = ff->fname;
220    } else {
221       match_func = fnmatch;
222       basename = ff->fname;
223    }
224
225    for (j = 0; j < incexe->opts_list.size(); j++) {
226       findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
227       ff->flags = fo->flags;
228       ff->GZIP_level = fo->GZIP_level;
229       ff->reader = fo->reader;
230       ff->writer = fo->writer;
231       ff->fstypes = fo->fstype;
232       ff->drivetypes = fo->drivetype;
233
234       fnm_flags = (ff->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
235       fnm_flags |= (ff->flags & FO_ENHANCEDWILD) ? FNM_PATHNAME : 0;
236
237       if (S_ISDIR(ff->statp.st_mode)) {
238          for (k=0; k<fo->wilddir.size(); k++) {
239             if (match_func((char *)fo->wilddir.get(k), ff->fname, fnmode|fnm_flags) == 0) {
240                if (ff->flags & FO_EXCLUDE) {
241                   Dmsg2(100, "Exclude wilddir: %s file=%s\n", (char *)fo->wilddir.get(k),
242                      ff->fname);
243                   return false;       /* reject dir */
244                }
245                return true;           /* accept dir */
246             }
247          }
248       } else {
249          for (k=0; k<fo->wildfile.size(); k++) {
250             if (match_func((char *)fo->wildfile.get(k), ff->fname, fnmode|fnm_flags) == 0) {
251                if (ff->flags & FO_EXCLUDE) {
252                   Dmsg2(100, "Exclude wildfile: %s file=%s\n", (char *)fo->wildfile.get(k),
253                      ff->fname);
254                   return false;       /* reject file */
255                }
256                return true;           /* accept file */
257             }
258          }
259
260          for (k=0; k<fo->wildbase.size(); k++) {
261             if (match_func((char *)fo->wildbase.get(k), basename, fnmode|fnm_flags) == 0) {
262                if (ff->flags & FO_EXCLUDE) {
263                   Dmsg2(100, "Exclude wildbase: %s file=%s\n", (char *)fo->wildbase.get(k),
264                      basename);
265                   return false;       /* reject file */
266                }
267                return true;           /* accept file */
268             }
269          }
270       }
271       for (k=0; k<fo->wild.size(); k++) {
272          if (match_func((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
273             if (ff->flags & FO_EXCLUDE) {
274                Dmsg2(100, "Exclude wild: %s file=%s\n", (char *)fo->wild.get(k),
275                   ff->fname);
276                return false;          /* reject file */
277             }
278             return true;              /* accept file */
279          }
280       }
281       if (S_ISDIR(ff->statp.st_mode)) {
282          for (k=0; k<fo->regexdir.size(); k++) {
283             const int nmatch = 30;
284             regmatch_t pmatch[nmatch];
285             if (regexec((regex_t *)fo->regexdir.get(k), ff->fname, nmatch, pmatch,  0) == 0) {
286                if (ff->flags & FO_EXCLUDE) {
287                   return false;       /* reject file */
288                }
289                return true;           /* accept file */
290             }
291          }
292       } else {
293          for (k=0; k<fo->regexfile.size(); k++) {
294             const int nmatch = 30;
295             regmatch_t pmatch[nmatch];
296             if (regexec((regex_t *)fo->regexfile.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       }
304       for (k=0; k<fo->regex.size(); k++) {
305          const int nmatch = 30;
306          regmatch_t pmatch[nmatch];
307          if (regexec((regex_t *)fo->regex.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        * If we have an empty Options clause with exclude, then
316        *  exclude the file
317        */
318       if (ff->flags & FO_EXCLUDE &&
319           fo->regex.size() == 0     && fo->wild.size() == 0 &&
320           fo->regexdir.size() == 0  && fo->wilddir.size() == 0 &&
321           fo->regexfile.size() == 0 && fo->wildfile.size() == 0 &&
322           fo->wildbase.size() == 0) {
323          return false;              /* reject file */
324       }
325    }
326
327    /* Now apply the Exclude { } directive */
328    for (i=0; i<fileset->exclude_list.size(); i++) {
329       findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
330       for (j=0; j<incexe->opts_list.size(); j++) {
331          findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
332          fnm_flags = (fo->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
333          for (k=0; k<fo->wild.size(); k++) {
334             if (fnmatch((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
335                Dmsg1(100, "Reject wild1: %s\n", ff->fname);
336                return false;          /* reject file */
337             }
338          }
339       }
340       fnm_flags = (incexe->current_opts != NULL && incexe->current_opts->flags & FO_IGNORECASE)
341              ? FNM_CASEFOLD : 0;
342       for (j=0; j<incexe->name_list.size(); j++) {
343          if (fnmatch((char *)incexe->name_list.get(j), ff->fname, fnmode|fnm_flags) == 0) {
344             Dmsg1(100, "Reject wild2: %s\n", ff->fname);
345             return false;          /* reject file */
346          }
347       }
348    }
349    return true;
350 }
351
352 /*
353  * The code comes here for each file examined.
354  * We filter the files, then call the user's callback if
355  *    the file is included.
356  */
357 static int our_callback(FF_PKT *ff, void *hpkt, bool top_level)
358 {
359    if (top_level) {
360       return ff->callback(ff, hpkt, top_level);   /* accept file */
361    }
362    switch (ff->type) {
363    case FT_NOACCESS:
364    case FT_NOFOLLOW:
365    case FT_NOSTAT:
366    case FT_NOCHG:
367    case FT_ISARCH:
368    case FT_NORECURSE:
369    case FT_NOFSCHG:
370    case FT_INVALIDFS:
371    case FT_INVALIDDT:
372    case FT_NOOPEN:
373 //    return ff->callback(ff, hpkt, top_level);
374
375    /* These items can be filtered */
376    case FT_LNKSAVED:
377    case FT_REGE:
378    case FT_REG:
379    case FT_LNK:
380    case FT_DIRBEGIN:
381    case FT_DIREND:
382    case FT_RAW:
383    case FT_FIFO:
384    case FT_SPEC:
385    case FT_DIRNOCHG:
386       if (accept_file(ff)) {
387 //       Dmsg2(000, "Accept file %s; reader=%s\n", ff->fname, NPRT(ff->reader));
388          return ff->callback(ff, hpkt, top_level);
389       } else {
390          Dmsg1(100, "Skip file %s\n", ff->fname);
391          return -1;                   /* ignore this file */
392       }
393
394    default:
395       Dmsg1(000, "Unknown FT code %d\n", ff->type);
396       return 0;
397    }
398 }
399
400
401 /*
402  * Terminate find_files() and release
403  * all allocated memory
404  */
405 int
406 term_find_files(FF_PKT *ff)
407 {
408   int hard_links;
409
410   free_pool_memory(ff->sys_fname);
411   hard_links = term_find_one(ff);
412   free(ff);
413   return hard_links;
414 }