]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/find.c
c8e4e9aeede1c69da0b3a2f957aa3eddfdcd7fec
[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    Copyright (C) 2000-2006 Kern Sibbald
14
15    This program is free software; you can redistribute it and/or
16    modify it under the terms of the GNU General Public License
17    version 2 as amended with additional clauses defined in the
18    file LICENSE in the main source directory.
19
20    This program is distributed in the hope that it will be useful,
21    but WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
23    the file LICENSE for additional details.
24
25  */
26
27
28 #include "bacula.h"
29 #include "find.h"
30
31 int32_t name_max;              /* filename max length */
32 int32_t path_max;              /* path name max length */
33
34 #ifdef DEBUG
35 #undef bmalloc
36 #define bmalloc(x) sm_malloc(__FILE__, __LINE__, x)
37 #endif
38 static int our_callback(FF_PKT *ff, void *hpkt, bool top_level);
39 static bool accept_file(FF_PKT *ff);
40
41 static const int fnmode = 0;
42
43 /*
44  * Initialize the find files "global" variables
45  */
46 FF_PKT *init_find_files()
47 {
48   FF_PKT *ff;
49
50   ff = (FF_PKT *)bmalloc(sizeof(FF_PKT));
51   memset(ff, 0, sizeof(FF_PKT));
52
53   ff->sys_fname = get_pool_memory(PM_FNAME);
54
55    /* Get system path and filename maximum lengths */
56    path_max = pathconf(".", _PC_PATH_MAX);
57    if (path_max < 1024) {
58       path_max = 1024;
59    }
60
61    name_max = pathconf(".", _PC_NAME_MAX);
62    if (name_max < 1024) {
63       name_max = 1024;
64    }
65    path_max++;                        /* add for EOS */
66    name_max++;                        /* add for EOS */
67
68   Dmsg1(100, "init_find_files ff=%p\n", ff);
69   return ff;
70 }
71
72 /*
73  * Set find_files options. For the moment, we only
74  * provide for full/incremental saves, and setting
75  * of save_time. For additional options, see above
76  */
77 void
78 set_find_options(FF_PKT *ff, int incremental, time_t save_time)
79 {
80   Dmsg0(100, "Enter set_find_options()\n");
81   ff->incremental = incremental;
82   ff->save_time = save_time;
83   Dmsg0(100, "Leave set_find_options()\n");
84 }
85
86 /*
87  * For VSS we need to know which windows drives
88  * are used, because we create a snapshot of all used
89  * drives before operation
90  *
91  * the function returns the number of used drives and
92  * fills "drives" with up to 26 (A..Z) drive names
93  *
94  */
95 int
96 get_win32_driveletters(FF_PKT *ff, char* szDrives)
97 {
98    /* szDrives must be at least 27 bytes long */
99
100 #if !defined(HAVE_WIN32)
101    return 0;
102 #endif
103
104    szDrives[0] = 0; /* make empty */
105    int nCount = 0;
106     
107    findFILESET *fileset = ff->fileset;
108    if (fileset) {
109       int i, j;
110       
111       for (i=0; i<fileset->include_list.size(); i++) {
112          findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
113          
114          /* look through all files and check */
115          for (j=0; j<incexe->name_list.size(); j++) {
116             char *fname = (char *)incexe->name_list.get(j);
117             /* fname should match x:/ */
118             if (strlen(fname) >= 2 && B_ISALPHA(fname[0]) 
119                && fname[1] == ':') {
120                
121                /* always add in uppercase */
122                char ch = toupper(fname[0]);
123                /* if not found in string, add drive letter */
124                if (!strchr(szDrives,ch)) {
125                   szDrives[nCount] = ch;
126                   szDrives[nCount+1] = 0;
127                   nCount++;
128                }                                
129             }            
130          }
131       }
132    }
133    return nCount;
134 }
135
136 /*
137  * Find all specified files (determined by calls to name_add()
138  * This routine calls the (handle_file) subroutine with all
139  * sorts of good information for the final disposition of
140  * the file.
141  *
142  * Call this subroutine with a callback subroutine as the first
143  * argument and a packet as the second argument, this packet
144  * will be passed back to the callback subroutine as the last
145  * argument.
146  *
147  * The callback subroutine gets called with:
148  *  arg1 -- the FF_PKT containing filename, link, stat, ftype, flags, etc
149  *  arg2 -- the user supplied packet
150  *
151  */
152 int
153 find_files(JCR *jcr, FF_PKT *ff, int callback(FF_PKT *ff_pkt, void *hpkt, bool top_level), 
154            void *his_pkt)
155 {
156    ff->callback = callback;
157
158    /* This is the new way */
159    findFILESET *fileset = ff->fileset;
160    if (fileset) {
161       int i, j;
162       ff->flags = 0;
163       ff->VerifyOpts[0] = 'V';
164       ff->VerifyOpts[1] = 0;
165       for (i=0; i<fileset->include_list.size(); i++) {
166          findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
167          fileset->incexe = incexe;
168          /*
169           * By setting all options, we in effect or the global options
170           *   which is what we want.
171           */
172          for (j=0; j<incexe->opts_list.size(); j++) {
173             findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
174             ff->flags |= fo->flags;
175             ff->GZIP_level = fo->GZIP_level;
176             ff->fstypes = fo->fstype;
177             ff->drivetypes = fo->drivetype;
178             bstrncat(ff->VerifyOpts, fo->VerifyOpts, sizeof(ff->VerifyOpts));
179          }
180          for (j=0; j<incexe->name_list.size(); j++) {
181             Dmsg1(100, "F %s\n", (char *)incexe->name_list.get(j));
182             char *fname = (char *)incexe->name_list.get(j);
183             if (find_one_file(jcr, ff, our_callback, his_pkt, fname, (dev_t)-1, true) == 0) {
184                return 0;                  /* error return */
185             }
186          }
187       }
188    }
189    return 1;
190 }
191
192 static bool accept_file(FF_PKT *ff)
193 {
194    int i, j, k;
195    int fnm_flags;
196    findFILESET *fileset = ff->fileset;
197    findINCEXE *incexe = fileset->incexe;
198    const char *basename;
199    int (*match_func)(const char *pattern, const char *string, int flags);
200
201    if (ff->flags & FO_ENHANCEDWILD) {
202       match_func = enh_fnmatch;
203       if ((basename = strrchr(ff->fname, '/')) != NULL)
204          basename++;
205       else
206          basename = ff->fname;
207    } else {
208       match_func = fnmatch;
209       basename = ff->fname;
210    }
211
212    for (j=0; j<incexe->opts_list.size(); j++) {
213       findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
214       ff->flags = fo->flags;
215       ff->GZIP_level = fo->GZIP_level;
216       ff->reader = fo->reader;
217       ff->writer = fo->writer;
218       ff->fstypes = fo->fstype;
219       ff->drivetypes = fo->drivetype;
220
221       fnm_flags = (ff->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
222       fnm_flags |= (ff->flags & FO_ENHANCEDWILD) ? FNM_PATHNAME : 0;
223
224       if (S_ISDIR(ff->statp.st_mode)) {
225          for (k=0; k<fo->wilddir.size(); k++) {
226             if (match_func((char *)fo->wilddir.get(k), ff->fname, fnmode|fnm_flags) == 0) {
227                if (ff->flags & FO_EXCLUDE) {
228                   Dmsg2(100, "Exclude wilddir: %s file=%s\n", (char *)fo->wilddir.get(k),
229                      ff->fname);
230                   return false;       /* reject dir */
231                }
232                return true;           /* accept dir */
233             }
234          }
235       } else {
236          for (k=0; k<fo->wildfile.size(); k++) {
237             if (match_func((char *)fo->wildfile.get(k), ff->fname, fnmode|fnm_flags) == 0) {
238                if (ff->flags & FO_EXCLUDE) {
239                   Dmsg2(100, "Exclude wildfile: %s file=%s\n", (char *)fo->wildfile.get(k),
240                      ff->fname);
241                   return false;       /* reject file */
242                }
243                return true;           /* accept file */
244             }
245          }
246
247          for (k=0; k<fo->wildbase.size(); k++) {
248             if (match_func((char *)fo->wildbase.get(k), basename, fnmode|fnm_flags) == 0) {
249                if (ff->flags & FO_EXCLUDE) {
250                   Dmsg2(100, "Exclude wildbase: %s file=%s\n", (char *)fo->wildbase.get(k),
251                      basename);
252                   return false;       /* reject file */
253                }
254                return true;           /* accept file */
255             }
256          }
257       }
258       for (k=0; k<fo->wild.size(); k++) {
259          if (match_func((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
260             if (ff->flags & FO_EXCLUDE) {
261                Dmsg2(100, "Exclude wild: %s file=%s\n", (char *)fo->wild.get(k),
262                   ff->fname);
263                return false;          /* reject file */
264             }
265             return true;              /* accept file */
266          }
267       }
268       if (S_ISDIR(ff->statp.st_mode)) {
269          for (k=0; k<fo->regexdir.size(); k++) {
270             const int nmatch = 30;
271             regmatch_t pmatch[nmatch];
272             if (regexec((regex_t *)fo->regexdir.get(k), ff->fname, nmatch, pmatch,  0) == 0) {
273                if (ff->flags & FO_EXCLUDE) {
274                   return false;       /* reject file */
275                }
276                return true;           /* accept file */
277             }
278          }
279       } else {
280          for (k=0; k<fo->regexfile.size(); k++) {
281             const int nmatch = 30;
282             regmatch_t pmatch[nmatch];
283             if (regexec((regex_t *)fo->regexfile.get(k), ff->fname, nmatch, pmatch,  0) == 0) {
284                if (ff->flags & FO_EXCLUDE) {
285                   return false;       /* reject file */
286                }
287                return true;           /* accept file */
288             }
289          }
290       }
291       for (k=0; k<fo->regex.size(); k++) {
292          const int nmatch = 30;
293          regmatch_t pmatch[nmatch];
294          if (regexec((regex_t *)fo->regex.get(k), ff->fname, nmatch, pmatch,  0) == 0) {
295             if (ff->flags & FO_EXCLUDE) {
296                return false;          /* reject file */
297             }
298             return true;              /* accept file */
299          }
300       }
301       /*
302        * If we have an empty Options clause with exclude, then
303        *  exclude the file
304        */
305       if (ff->flags & FO_EXCLUDE &&
306           fo->regex.size() == 0     && fo->wild.size() == 0 &&
307           fo->regexdir.size() == 0  && fo->wilddir.size() == 0 &&
308           fo->regexfile.size() == 0 && fo->wildfile.size() == 0 &&
309           fo->wildbase.size() == 0) {
310          return false;              /* reject file */
311       }
312    }
313
314    /* Now apply the Exclude { } directive */
315    for (i=0; i<fileset->exclude_list.size(); i++) {
316       findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
317       for (j=0; j<incexe->opts_list.size(); j++) {
318          findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
319          fnm_flags = (fo->flags & FO_IGNORECASE) ? FNM_CASEFOLD : 0;
320          for (k=0; k<fo->wild.size(); k++) {
321             if (fnmatch((char *)fo->wild.get(k), ff->fname, fnmode|fnm_flags) == 0) {
322                Dmsg1(100, "Reject wild1: %s\n", ff->fname);
323                return false;          /* reject file */
324             }
325          }
326       }
327       fnm_flags = (incexe->current_opts != NULL && incexe->current_opts->flags & FO_IGNORECASE)
328              ? FNM_CASEFOLD : 0;
329       for (j=0; j<incexe->name_list.size(); j++) {
330          if (fnmatch((char *)incexe->name_list.get(j), ff->fname, fnmode|fnm_flags) == 0) {
331             Dmsg1(100, "Reject wild2: %s\n", ff->fname);
332             return false;          /* reject file */
333          }
334       }
335    }
336    return true;
337 }
338
339 /*
340  * The code comes here for each file examined.
341  * We filter the files, then call the user's callback if
342  *    the file is included.
343  */
344 static int our_callback(FF_PKT *ff, void *hpkt, bool top_level)
345 {
346    if (top_level) {
347       return ff->callback(ff, hpkt, top_level);   /* accept file */
348    }
349    switch (ff->type) {
350    case FT_NOACCESS:
351    case FT_NOFOLLOW:
352    case FT_NOSTAT:
353    case FT_NOCHG:
354    case FT_ISARCH:
355    case FT_NORECURSE:
356    case FT_NOFSCHG:
357    case FT_INVALIDFS:
358    case FT_INVALIDDT:
359    case FT_NOOPEN:
360 //    return ff->callback(ff, hpkt, top_level);
361
362    /* These items can be filtered */
363    case FT_LNKSAVED:
364    case FT_REGE:
365    case FT_REG:
366    case FT_LNK:
367    case FT_DIRBEGIN:
368    case FT_DIREND:
369    case FT_RAW:
370    case FT_FIFO:
371    case FT_SPEC:
372    case FT_DIRNOCHG:
373       if (accept_file(ff)) {
374 //       Dmsg2(000, "Accept file %s; reader=%s\n", ff->fname, NPRT(ff->reader));
375          return ff->callback(ff, hpkt, top_level);
376       } else {
377          Dmsg1(100, "Skip file %s\n", ff->fname);
378          return -1;                   /* ignore this file */
379       }
380
381    default:
382       Dmsg1(000, "Unknown FT code %d\n", ff->type);
383       return 0;
384    }
385 }
386
387
388 /*
389  * Terminate find_files() and release
390  * all allocated memory
391  */
392 int
393 term_find_files(FF_PKT *ff)
394 {
395   int hard_links;
396
397   free_pool_memory(ff->sys_fname);
398   hard_links = term_find_one(ff);
399   free(ff);
400   return hard_links;
401 }