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