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