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