]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/match.c
Fix #1370 about the implementation of the "Exclude Dir Containing" option on FD.
[bacula/bacula] / bacula / src / findlib / match.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 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  *     Old style 
30  *
31  *  Routines used to keep and match include and exclude
32  *   filename/pathname patterns.
33  *
34  *  Note, this file is used for the old style include and
35  *   excludes, so is deprecated. The new style code is
36  *   found in find.c.   
37  *  This code is still used for lists in testls and bextract.
38  *
39  *   Kern E. Sibbald, December MMI
40  *
41  */
42
43 #include "bacula.h"
44 #include "find.h"
45
46 #include <pwd.h>
47 #include <grp.h>
48 #include <sys/types.h>
49
50 #ifndef FNM_LEADING_DIR
51 #define FNM_LEADING_DIR 0
52 #endif
53
54 /* Fold case in fnmatch() on Win32 */
55 #ifdef HAVE_WIN32
56 static const int fnmode = FNM_CASEFOLD;
57 #else
58 static const int fnmode = 0;
59 #endif
60
61
62 #undef bmalloc
63 #define bmalloc(x) sm_malloc(__FILE__, __LINE__, x)
64
65
66 int
67 match_files(JCR *jcr, FF_PKT *ff, int file_save(JCR *, FF_PKT *ff_pkt, bool))
68 {
69    ff->file_save = file_save;
70
71    struct s_included_file *inc = NULL;
72
73    /* This is the old deprecated way */
74    while (!job_canceled(jcr) && (inc = get_next_included_file(ff, inc))) {
75       /* Copy options for this file */
76       bstrncat(ff->VerifyOpts, inc->VerifyOpts, sizeof(ff->VerifyOpts));
77       Dmsg1(100, "find_files: file=%s\n", inc->fname);
78       if (!file_is_excluded(ff, inc->fname)) {
79          if (find_one_file(jcr, ff, file_save, inc->fname, (dev_t)-1, 1) ==0) {
80             return 0;                  /* error return */
81          }
82       }
83    }
84    return 1;
85 }
86
87
88 /*
89  * Done doing filename matching, release all
90  *  resources used.
91  */
92 void term_include_exclude_files(FF_PKT *ff)
93 {
94    struct s_included_file *inc, *next_inc;
95    struct s_excluded_file *exc, *next_exc;
96
97    for (inc=ff->included_files_list; inc; ) {
98       next_inc = inc->next;
99       free(inc);
100       inc = next_inc;
101    }
102    ff->included_files_list = NULL;
103
104    for (exc=ff->excluded_files_list; exc; ) {
105       next_exc = exc->next;
106       free(exc);
107       exc = next_exc;
108    }
109    ff->excluded_files_list = NULL;
110
111    for (exc=ff->excluded_paths_list; exc; ) {
112       next_exc = exc->next;
113       free(exc);
114       exc = next_exc;
115    }
116    ff->excluded_paths_list = NULL;
117 }
118
119 /*
120  * Add a filename to list of included files
121  */
122 void add_fname_to_include_list(FF_PKT *ff, int prefixed, const char *fname)
123 {
124    int len, j;
125    struct s_included_file *inc;
126    char *p;
127    const char *rp;
128
129    len = strlen(fname);
130
131    inc =(struct s_included_file *)bmalloc(sizeof(struct s_included_file) + len + 1);
132    inc->options = 0;
133    inc->VerifyOpts[0] = 'V';
134    inc->VerifyOpts[1] = ':';
135    inc->VerifyOpts[2] = 0;
136
137    /* prefixed = preceded with options */
138    if (prefixed) {
139       for (rp=fname; *rp && *rp != ' '; rp++) {
140          switch (*rp) {
141          case 'a':                 /* alway replace */
142          case '0':                 /* no option */
143             break;
144          case 'f':
145             inc->options |= FO_MULTIFS;
146             break;
147          case 'h':                 /* no recursion */
148             inc->options |= FO_NO_RECURSION;
149             break;
150          case 'M':                 /* MD5 */
151             inc->options |= FO_MD5;
152             break;
153          case 'n':
154             inc->options |= FO_NOREPLACE;
155             break;
156          case 'p':                 /* use portable data format */
157             inc->options |= FO_PORTABLE;
158             break;
159          case 'r':                 /* read fifo */
160             inc->options |= FO_READFIFO;
161             break;
162          case 'S':
163             inc->options |= FO_SHA1;
164             break;
165          case 's':
166             inc->options |= FO_SPARSE;
167             break;
168          case 'm':
169             inc->options |= FO_MTIMEONLY;
170             break;
171          case 'k':
172             inc->options |= FO_KEEPATIME;
173             break;
174          case 'V':                  /* verify options */
175             /* Copy Verify Options */
176             for (j=0; *rp && *rp != ':'; rp++) {
177                inc->VerifyOpts[j] = *rp;
178                if (j < (int)sizeof(inc->VerifyOpts) - 1) {
179                   j++;
180                }
181             }
182             inc->VerifyOpts[j] = 0;
183             break;
184          case 'w':
185             inc->options |= FO_IF_NEWER;
186             break;
187          case 'A':
188             inc->options |= FO_ACL;
189             break;
190          case 'Z':                 /* gzip compression */
191             inc->options |= FO_GZIP;
192             inc->level = *++rp - '0';
193             Dmsg1(200, "Compression level=%d\n", inc->level);
194             break;
195          case 'K':
196             inc->options |= FO_NOATIME;
197             break;
198          case 'X':
199             inc->options |= FO_XATTR;
200             break;
201          default:
202             Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *rp);
203             break;
204          }
205       }
206       /* Skip past space(s) */
207       for ( ; *rp == ' '; rp++)
208          {}
209    } else {
210       rp = fname;
211    }
212
213    strcpy(inc->fname, rp);
214    p = inc->fname;
215    len = strlen(p);
216    /* Zap trailing slashes.  */
217    p += len - 1;
218    while (p > inc->fname && IsPathSeparator(*p)) {
219       *p-- = 0;
220       len--;
221    }
222    inc->len = len;
223    /* Check for wild cards */
224    inc->pattern = 0;
225    for (p=inc->fname; *p; p++) {
226       if (*p == '*' || *p == '[' || *p == '?') {
227          inc->pattern = 1;
228          break;
229       }
230    }
231 #if defined(HAVE_WIN32)
232    /* Convert any \'s into /'s */
233    for (p=inc->fname; *p; p++) {
234       if (*p == '\\') {
235          *p = '/';
236       }
237    }
238 #endif
239    inc->next = NULL;
240    /* Chain this one on the end of the list */
241    if (!ff->included_files_list) {
242       /* First one, so set head */
243       ff->included_files_list = inc;
244    } else {
245       struct s_included_file *next;
246       /* Walk to end of list */
247       for (next=ff->included_files_list; next->next; next=next->next)
248          { }
249       next->next = inc;
250    }
251    Dmsg3(100, "add_fname_to_include prefix=%d gzip=%d fname=%s\n", 
252          prefixed, !!(inc->options & FO_GZIP), inc->fname);
253 }
254
255 /*
256  * We add an exclude name to either the exclude path
257  *  list or the exclude filename list.
258  */
259 void add_fname_to_exclude_list(FF_PKT *ff, const char *fname)
260 {
261    int len;
262    struct s_excluded_file *exc, **list;
263
264    Dmsg1(20, "Add name to exclude: %s\n", fname);
265
266    if (first_path_separator(fname) != NULL) {
267       list = &ff->excluded_paths_list;
268    } else {
269       list = &ff->excluded_files_list;
270    }
271
272    len = strlen(fname);
273
274    exc = (struct s_excluded_file *)bmalloc(sizeof(struct s_excluded_file) + len + 1);
275    exc->next = *list;
276    exc->len = len;
277    strcpy(exc->fname, fname);
278 #if defined(HAVE_WIN32)
279    /* Convert any \'s into /'s */
280    for (char *p=exc->fname; *p; p++) {
281       if (*p == '\\') {
282          *p = '/';
283       }
284    }
285 #endif
286    *list = exc;
287 }
288
289
290 /*
291  * Get next included file
292  */
293 struct s_included_file *get_next_included_file(FF_PKT *ff, struct s_included_file *ainc)
294 {
295    struct s_included_file *inc;
296
297    if (ainc == NULL) {
298       inc = ff->included_files_list;
299    } else {
300       inc = ainc->next;
301    }
302    /*
303     * copy inc_options for this file into the ff packet
304     */
305    if (inc) {
306       ff->flags = inc->options;
307       ff->GZIP_level = inc->level;
308    }
309    return inc;
310 }
311
312 /*
313  * Walk through the included list to see if this
314  *  file is included possibly with wild-cards.
315  */
316
317 int file_is_included(FF_PKT *ff, const char *file)
318 {
319    struct s_included_file *inc = ff->included_files_list;
320    int len;
321
322    for ( ; inc; inc=inc->next ) {
323       if (inc->pattern) {
324          if (fnmatch(inc->fname, file, fnmode|FNM_LEADING_DIR) == 0) {
325             return 1;
326          }
327          continue;
328       }
329       /*
330        * No wild cards. We accept a match to the
331        *  end of any component.
332        */
333       Dmsg2(900, "pat=%s file=%s\n", inc->fname, file);
334       len = strlen(file);
335       if (inc->len == len && strcmp(inc->fname, file) == 0) {
336          return 1;
337       }
338       if (inc->len < len && IsPathSeparator(file[inc->len]) &&
339           strncmp(inc->fname, file, inc->len) == 0) {
340          return 1;
341       }
342       if (inc->len == 1 && IsPathSeparator(inc->fname[0])) {
343          return 1;
344       }
345    }
346    return 0;
347 }
348
349
350 /*
351  * This is the workhorse of excluded_file().
352  * Determine if the file is excluded or not.
353  */
354 static int
355 file_in_excluded_list(struct s_excluded_file *exc, const char *file)
356 {
357    if (exc == NULL) {
358       Dmsg0(900, "exc is NULL\n");
359    }
360    for ( ; exc; exc=exc->next ) {
361       if (fnmatch(exc->fname, file, fnmode|FNM_PATHNAME) == 0) {
362          Dmsg2(900, "Match exc pat=%s: file=%s:\n", exc->fname, file);
363          return 1;
364       }
365       Dmsg2(900, "No match exc pat=%s: file=%s:\n", exc->fname, file);
366    }
367    return 0;
368 }
369
370
371 /*
372  * Walk through the excluded lists to see if this
373  *  file is excluded, or if it matches a component
374  *  of an excluded directory.
375  */
376
377 int file_is_excluded(FF_PKT *ff, const char *file)
378 {
379    const char *p;
380
381 #if defined(HAVE_WIN32)
382    /*
383     *  ***NB*** this removes the drive from the exclude
384     *  rule.  Why?????
385     */
386    if (file[1] == ':') {
387       file += 2;
388    }
389 #endif
390
391    if (file_in_excluded_list(ff->excluded_paths_list, file)) {
392       return 1;
393    }
394
395    /* Try each component */
396    for (p = file; *p; p++) {
397       /* Match from the beginning of a component only */
398       if ((p == file || (!IsPathSeparator(*p) && IsPathSeparator(p[-1])))
399            && file_in_excluded_list(ff->excluded_files_list, p)) {
400          return 1;
401       }
402    }
403    return 0;
404 }