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