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