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