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
match_files(JCR * jcr,FF_PKT * ff,int file_save (JCR *,FF_PKT * ff_pkt,bool))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 */
term_include_exclude_files(FF_PKT * ff)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 */
add_fname_to_include_list(FF_PKT * ff,int prefixed,const char * fname)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 */
add_fname_to_exclude_list(FF_PKT * ff,const char * fname)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 */
get_next_included_file(FF_PKT * ff,struct s_included_file * ainc)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
file_is_included(FF_PKT * ff,const char * file)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
file_in_excluded_list(struct s_excluded_file * exc,const char * file)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
file_is_excluded(FF_PKT * ff,const char * file)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 }
405