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