]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/match.c
Tweak
[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          default:
199             Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *rp);
200             break;
201          }
202       }
203       /* Skip past space(s) */
204       for ( ; *rp == ' '; rp++)
205          {}
206    } else {
207       rp = fname;
208    }
209
210    strcpy(inc->fname, rp);
211    p = inc->fname;
212    len = strlen(p);
213    /* Zap trailing slashes.  */
214    p += len - 1;
215    while (p > inc->fname && IsPathSeparator(*p)) {
216       *p-- = 0;
217       len--;
218    }
219    inc->len = len;
220    /* Check for wild cards */
221    inc->pattern = 0;
222    for (p=inc->fname; *p; p++) {
223       if (*p == '*' || *p == '[' || *p == '?') {
224          inc->pattern = 1;
225          break;
226       }
227    }
228 #if defined(HAVE_WIN32)
229    /* Convert any \'s into /'s */
230    for (p=inc->fname; *p; p++) {
231       if (*p == '\\') {
232          *p = '/';
233       }
234    }
235 #endif
236    inc->next = NULL;
237    /* Chain this one on the end of the list */
238    if (!ff->included_files_list) {
239       /* First one, so set head */
240       ff->included_files_list = inc;
241    } else {
242       struct s_included_file *next;
243       /* Walk to end of list */
244       for (next=ff->included_files_list; next->next; next=next->next)
245          { }
246       next->next = inc;
247    }
248    Dmsg3(100, "add_fname_to_include prefix=%d gzip=%d fname=%s\n", 
249          prefixed, !!(inc->options & FO_GZIP), inc->fname);
250 }
251
252 /*
253  * We add an exclude name to either the exclude path
254  *  list or the exclude filename list.
255  */
256 void add_fname_to_exclude_list(FF_PKT *ff, const char *fname)
257 {
258    int len;
259    struct s_excluded_file *exc, **list;
260
261    Dmsg1(20, "Add name to exclude: %s\n", fname);
262
263    if (first_path_separator(fname) != NULL) {
264       list = &ff->excluded_paths_list;
265    } else {
266       list = &ff->excluded_files_list;
267    }
268
269    len = strlen(fname);
270
271    exc = (struct s_excluded_file *)bmalloc(sizeof(struct s_excluded_file) + len + 1);
272    exc->next = *list;
273    exc->len = len;
274    strcpy(exc->fname, fname);
275 #if defined(HAVE_WIN32)
276    /* Convert any \'s into /'s */
277    for (char *p=exc->fname; *p; p++) {
278       if (*p == '\\') {
279          *p = '/';
280       }
281    }
282 #endif
283    *list = exc;
284 }
285
286
287 /*
288  * Get next included file
289  */
290 struct s_included_file *get_next_included_file(FF_PKT *ff, struct s_included_file *ainc)
291 {
292    struct s_included_file *inc;
293
294    if (ainc == NULL) {
295       inc = ff->included_files_list;
296    } else {
297       inc = ainc->next;
298    }
299    /*
300     * copy inc_options for this file into the ff packet
301     */
302    if (inc) {
303       ff->flags = inc->options;
304       ff->GZIP_level = inc->level;
305    }
306    return inc;
307 }
308
309 /*
310  * Walk through the included list to see if this
311  *  file is included possibly with wild-cards.
312  */
313
314 int file_is_included(FF_PKT *ff, const char *file)
315 {
316    struct s_included_file *inc = ff->included_files_list;
317    int len;
318
319    for ( ; inc; inc=inc->next ) {
320       if (inc->pattern) {
321          if (fnmatch(inc->fname, file, fnmode|FNM_LEADING_DIR) == 0) {
322             return 1;
323          }
324          continue;
325       }
326       /*
327        * No wild cards. We accept a match to the
328        *  end of any component.
329        */
330       Dmsg2(900, "pat=%s file=%s\n", inc->fname, file);
331       len = strlen(file);
332       if (inc->len == len && strcmp(inc->fname, file) == 0) {
333          return 1;
334       }
335       if (inc->len < len && IsPathSeparator(file[inc->len]) &&
336           strncmp(inc->fname, file, inc->len) == 0) {
337          return 1;
338       }
339       if (inc->len == 1 && IsPathSeparator(inc->fname[0])) {
340          return 1;
341       }
342    }
343    return 0;
344 }
345
346
347 /*
348  * This is the workhorse of excluded_file().
349  * Determine if the file is excluded or not.
350  */
351 static int
352 file_in_excluded_list(struct s_excluded_file *exc, const char *file)
353 {
354    if (exc == NULL) {
355       Dmsg0(900, "exc is NULL\n");
356    }
357    for ( ; exc; exc=exc->next ) {
358       if (fnmatch(exc->fname, file, fnmode|FNM_PATHNAME) == 0) {
359          Dmsg2(900, "Match exc pat=%s: file=%s:\n", exc->fname, file);
360          return 1;
361       }
362       Dmsg2(900, "No match exc pat=%s: file=%s:\n", exc->fname, file);
363    }
364    return 0;
365 }
366
367
368 /*
369  * Walk through the excluded lists to see if this
370  *  file is excluded, or if it matches a component
371  *  of an excluded directory.
372  */
373
374 int file_is_excluded(FF_PKT *ff, const char *file)
375 {
376    const char *p;
377
378 #if defined(HAVE_WIN32)
379    /*
380     *  ***NB*** this removes the drive from the exclude
381     *  rule.  Why?????
382     */
383    if (file[1] == ':') {
384       file += 2;
385    }
386 #endif
387
388    if (file_in_excluded_list(ff->excluded_paths_list, file)) {
389       return 1;
390    }
391
392    /* Try each component */
393    for (p = file; *p; p++) {
394       /* Match from the beginning of a component only */
395       if ((p == file || (!IsPathSeparator(*p) && IsPathSeparator(p[-1])))
396            && file_in_excluded_list(ff->excluded_files_list, p)) {
397          return 1;
398       }
399    }
400    return 0;
401 }