]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/match.c
9b187c0f968d81e960838d16fe28706746409f03
[bacula/bacula] / bacula / src / findlib / match.c
1 /*
2  *     Old style 
3  *
4  *  Routines used to keep and match include and exclude
5  *   filename/pathname patterns.
6  *
7  *  Note, this file is used for the old style include and
8  *   excludes, so is deprecated. The new style code is
9  *   found in find.c.   
10  *  This code is still used for lists in testls and bextract.
11  *
12  *   Kern E. Sibbald, December MMI
13  *
14  */
15 /*
16    Copyright (C) 2001-2005 Kern Sibbald
17
18    This program is free software; you can redistribute it and/or
19    modify it under the terms of the GNU General Public License
20    version 2 as amended with additional clauses defined in the
21    file LICENSE in the main source directory.
22
23    This program is distributed in the hope that it will be useful,
24    but WITHOUT ANY WARRANTY; without even the implied warranty of
25    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
26    the file LICENSE for additional details.
27
28  */
29
30 #include "bacula.h"
31 #include "find.h"
32
33 #include <pwd.h>
34 #include <grp.h>
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 callback(FF_PKT *ff_pkt, void *hpkt, bool), void *his_pkt)
55 {
56    ff->callback = callback;
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, callback, his_pkt, 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':                 /* gzip compression */
178             inc->options |= FO_GZIP;
179             inc->level = *++rp - '0';
180             Dmsg1(200, "Compression level=%d\n", inc->level);
181             break;
182          case 'K':
183             inc->options |= FO_NOATIME;
184             break;
185          default:
186             Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *rp);
187             break;
188          }
189       }
190       /* Skip past space(s) */
191       for ( ; *rp == ' '; rp++)
192          {}
193    } else {
194       rp = fname;
195    }
196
197    strcpy(inc->fname, rp);
198    p = inc->fname;
199    len = strlen(p);
200    /* Zap trailing slashes.  */
201    p += len - 1;
202    while (p > inc->fname && IsPathSeparator(*p)) {
203       *p-- = 0;
204       len--;
205    }
206    inc->len = len;
207    /* Check for wild cards */
208    inc->pattern = 0;
209    for (p=inc->fname; *p; p++) {
210       if (*p == '*' || *p == '[' || *p == '?') {
211          inc->pattern = 1;
212          break;
213       }
214    }
215 #if defined(HAVE_WIN32)
216    /* Convert any \'s into /'s */
217    for (p=inc->fname; *p; p++) {
218       if (*p == '\\') {
219          *p = '/';
220       }
221    }
222 #endif
223    inc->next = NULL;
224    /* Chain this one on the end of the list */
225    if (!ff->included_files_list) {
226       /* First one, so set head */
227       ff->included_files_list = inc;
228    } else {
229       struct s_included_file *next;
230       /* Walk to end of list */
231       for (next=ff->included_files_list; next->next; next=next->next)
232          { }
233       next->next = inc;
234    }
235    Dmsg1(50, "add_fname_to_include fname=%s\n", inc->fname);
236 }
237
238 /*
239  * We add an exclude name to either the exclude path
240  *  list or the exclude filename list.
241  */
242 void add_fname_to_exclude_list(FF_PKT *ff, const char *fname)
243 {
244    int len;
245    struct s_excluded_file *exc, **list;
246
247    Dmsg1(20, "Add name to exclude: %s\n", fname);
248
249    if (first_path_separator(fname) != NULL) {
250       list = &ff->excluded_paths_list;
251    } else {
252       list = &ff->excluded_files_list;
253    }
254
255    len = strlen(fname);
256
257    exc = (struct s_excluded_file *)bmalloc(sizeof(struct s_excluded_file) + len + 1);
258    exc->next = *list;
259    exc->len = len;
260    strcpy(exc->fname, fname);
261 #if defined(HAVE_WIN32)
262    /* Convert any \'s into /'s */
263    for (char *p=exc->fname; *p; p++) {
264       if (*p == '\\') {
265          *p = '/';
266       }
267    }
268 #endif
269    *list = exc;
270 }
271
272
273 /*
274  * Get next included file
275  */
276 struct s_included_file *get_next_included_file(FF_PKT *ff, struct s_included_file *ainc)
277 {
278    struct s_included_file *inc;
279
280    if (ainc == NULL) {
281       inc = ff->included_files_list;
282    } else {
283       inc = ainc->next;
284    }
285    /*
286     * copy inc_options for this file into the ff packet
287     */
288    if (inc) {
289       ff->flags = inc->options;
290       ff->GZIP_level = inc->level;
291    }
292    return inc;
293 }
294
295 /*
296  * Walk through the included list to see if this
297  *  file is included possibly with wild-cards.
298  */
299
300 int file_is_included(FF_PKT *ff, const char *file)
301 {
302    struct s_included_file *inc = ff->included_files_list;
303    int len;
304
305    for ( ; inc; inc=inc->next ) {
306       if (inc->pattern) {
307          if (fnmatch(inc->fname, file, fnmode|FNM_LEADING_DIR) == 0) {
308             return 1;
309          }
310          continue;
311       }
312       /*
313        * No wild cards. We accept a match to the
314        *  end of any component.
315        */
316       Dmsg2(900, "pat=%s file=%s\n", inc->fname, file);
317       len = strlen(file);
318       if (inc->len == len && strcmp(inc->fname, file) == 0) {
319          return 1;
320       }
321       if (inc->len < len && IsPathSeparator(file[inc->len]) &&
322           strncmp(inc->fname, file, inc->len) == 0) {
323          return 1;
324       }
325       if (inc->len == 1 && IsPathSeparator(inc->fname[0])) {
326          return 1;
327       }
328    }
329    return 0;
330 }
331
332
333 /*
334  * This is the workhorse of excluded_file().
335  * Determine if the file is excluded or not.
336  */
337 static int
338 file_in_excluded_list(struct s_excluded_file *exc, const char *file)
339 {
340    if (exc == NULL) {
341       Dmsg0(900, "exc is NULL\n");
342    }
343    for ( ; exc; exc=exc->next ) {
344       if (fnmatch(exc->fname, file, fnmode|FNM_PATHNAME) == 0) {
345          Dmsg2(900, "Match exc pat=%s: file=%s:\n", exc->fname, file);
346          return 1;
347       }
348       Dmsg2(900, "No match exc pat=%s: file=%s:\n", exc->fname, file);
349    }
350    return 0;
351 }
352
353
354 /*
355  * Walk through the excluded lists to see if this
356  *  file is excluded, or if it matches a component
357  *  of an excluded directory.
358  */
359
360 int file_is_excluded(FF_PKT *ff, const char *file)
361 {
362    const char *p;
363
364 #if defined(HAVE_WIN32)
365    /*
366     *  ***NB*** this removes the drive from the exclude
367     *  rule.  Why?????
368     */
369    if (file[1] == ':') {
370       file += 2;
371    }
372 #endif
373
374    if (file_in_excluded_list(ff->excluded_paths_list, file)) {
375       return 1;
376    }
377
378    /* Try each component */
379    for (p = file; *p; p++) {
380       /* Match from the beginning of a component only */
381       if ((p == file || (!IsPathSeparator(*p) && IsPathSeparator(p[-1])))
382            && file_in_excluded_list(ff->excluded_files_list, p)) {
383          return 1;
384       }
385    }
386    return 0;
387 }