1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2012 Planets Communications B.V.
6 Copyright (C) 2013-2019 Bareos GmbH & Co. KG
7
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 Affero 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 /*
24 * Kern E. Sibbald, December MMI
25 */
26 /**
27 * @file
28 * Old style
29 *
30 * Routines used to keep and match include and exclude
31 * filename/pathname patterns.
32 *
33 * Note, this file is used for the old style include and
34 * excludes, so is deprecated. The new style code is found in
35 * src/filed/fileset.c.
36 *
37 * This code is still used for lists in testls and bextract.
38 */
39
40 #include "include/bareos.h"
41 #include "include/jcr.h"
42 #include "find.h"
43 #include "include/ch.h"
44
45 #include <sys/types.h>
46 #include "findlib/match.h"
47 #include "findlib/find_one.h"
48 #include "lib/edit.h"
49
50 #ifndef FNM_LEADING_DIR
51 # define FNM_LEADING_DIR 0
52 #endif
53
54 /* Fold case in fnmatch() on Win32 */
55 #ifdef HAVE_WIN32
56 static const int fnmode = FNM_CASEFOLD;
57 #else
58 static const int fnmode = 0;
59 #endif
60
MatchFiles(JobControlRecord * jcr,FindFilesPacket * ff,int FileSave (JobControlRecord *,FindFilesPacket * ff_pkt,bool))61 bool MatchFiles(JobControlRecord* jcr,
62 FindFilesPacket* ff,
63 int FileSave(JobControlRecord*, FindFilesPacket* ff_pkt, bool))
64 {
65 ff->FileSave = FileSave;
66
67 struct s_included_file* inc = NULL;
68
69 /* This is the old deprecated way */
70 while (!JobCanceled(jcr) && (inc = get_next_included_file(ff, inc))) {
71 /* Copy options for this file */
72 bstrncat(ff->VerifyOpts, inc->VerifyOpts, sizeof(ff->VerifyOpts));
73 Dmsg1(100, "FindFiles: file=%s\n", inc->fname);
74 if (!FileIsExcluded(ff, inc->fname)) {
75 if (FindOneFile(jcr, ff, FileSave, inc->fname, (dev_t)-1, 1) == 0) {
76 return false; /* error return */
77 }
78 }
79 }
80 return true;
81 }
82
83 /**
84 * Done doing filename matching, release all
85 * resources used.
86 */
TermIncludeExcludeFiles(FindFilesPacket * ff)87 void TermIncludeExcludeFiles(FindFilesPacket* ff)
88 {
89 struct s_included_file *inc, *next_inc;
90 struct s_excluded_file *exc, *next_exc;
91
92 for (inc = ff->included_files_list; inc;) {
93 next_inc = inc->next;
94 if (inc->size_match) { free(inc->size_match); }
95 free(inc);
96 inc = next_inc;
97 }
98 ff->included_files_list = NULL;
99
100 for (exc = ff->excluded_files_list; exc;) {
101 next_exc = exc->next;
102 free(exc);
103 exc = next_exc;
104 }
105 ff->excluded_files_list = NULL;
106
107 for (exc = ff->excluded_paths_list; exc;) {
108 next_exc = exc->next;
109 free(exc);
110 exc = next_exc;
111 }
112 ff->excluded_paths_list = NULL;
113 }
114
115 /**
116 * Add a filename to list of included files
117 */
AddFnameToIncludeList(FindFilesPacket * ff,int prefixed,const char * fname)118 void AddFnameToIncludeList(FindFilesPacket* ff, int prefixed, const char* fname)
119 {
120 int len, j;
121 struct s_included_file* inc;
122 char* p;
123 const char* rp;
124 char size[50];
125
126 len = strlen(fname);
127
128 inc = (struct s_included_file*)malloc(sizeof(struct s_included_file) + len
129 + 1);
130 memset(inc, 0, sizeof(struct s_included_file) + len + 1);
131 inc->VerifyOpts[0] = 'V';
132 inc->VerifyOpts[1] = ':';
133 inc->VerifyOpts[2] = 0;
134
135 /* prefixed = preceded with options */
136 if (prefixed) {
137 for (rp = fname; *rp && *rp != ' '; rp++) {
138 switch (*rp) {
139 case 'A':
140 SetBit(FO_ACL, inc->options);
141 break;
142 case 'a': /* alway replace */
143 case '0': /* no option */
144 break;
145 case 'c':
146 SetBit(FO_CHKCHANGES, inc->options);
147 break;
148 case 'd':
149 switch (*(rp + 1)) {
150 case '1':
151 inc->shadow_type = check_shadow_local_warn;
152 rp++;
153 break;
154 case '2':
155 inc->shadow_type = check_shadow_local_remove;
156 rp++;
157 break;
158 case '3':
159 inc->shadow_type = check_shadow_global_warn;
160 rp++;
161 break;
162 case '4':
163 inc->shadow_type = check_shadow_global_remove;
164 rp++;
165 break;
166 }
167 break;
168 case 'e':
169 SetBit(FO_EXCLUDE, inc->options);
170 break;
171 case 'E':
172 switch (*(rp + 1)) {
173 case '3':
174 inc->cipher = CRYPTO_CIPHER_3DES_CBC;
175 rp++;
176 break;
177 case 'a':
178 switch (*(rp + 2)) {
179 case '1':
180 inc->cipher = CRYPTO_CIPHER_AES_128_CBC;
181 rp += 2;
182 break;
183 case '2':
184 inc->cipher = CRYPTO_CIPHER_AES_192_CBC;
185 rp += 2;
186 break;
187 case '3':
188 inc->cipher = CRYPTO_CIPHER_AES_256_CBC;
189 rp += 2;
190 break;
191 }
192 break;
193 case 'b':
194 inc->cipher = CRYPTO_CIPHER_BLOWFISH_CBC;
195 rp++;
196 break;
197 case 'c':
198 switch (*(rp + 2)) {
199 case '1':
200 inc->cipher = CRYPTO_CIPHER_CAMELLIA_128_CBC;
201 rp += 2;
202 break;
203 case '2':
204 inc->cipher = CRYPTO_CIPHER_CAMELLIA_192_CBC;
205 rp += 2;
206 break;
207 case '3':
208 inc->cipher = CRYPTO_CIPHER_CAMELLIA_256_CBC;
209 rp += 2;
210 break;
211 }
212 break;
213 case 'f':
214 SetBit(FO_FORCE_ENCRYPT, inc->options);
215 rp++;
216 break;
217 case 'h':
218 switch (*(rp + 2)) {
219 case '1':
220 inc->cipher = CRYPTO_CIPHER_AES_128_CBC_HMAC_SHA1;
221 rp += 2;
222 break;
223 case '2':
224 inc->cipher = CRYPTO_CIPHER_AES_256_CBC_HMAC_SHA1;
225 rp += 2;
226 break;
227 }
228 }
229 break;
230 case 'f':
231 SetBit(FO_MULTIFS, inc->options);
232 break;
233 case 'H': /* no hard link handling */
234 SetBit(FO_NO_HARDLINK, inc->options);
235 break;
236 case 'h': /* no recursion */
237 SetBit(FO_NO_RECURSION, inc->options);
238 break;
239 case 'i':
240 SetBit(FO_IGNORECASE, inc->options);
241 break;
242 case 'K':
243 SetBit(FO_NOATIME, inc->options);
244 break;
245 case 'k':
246 SetBit(FO_KEEPATIME, inc->options);
247 break;
248 case 'M': /* MD5 */
249 SetBit(FO_MD5, inc->options);
250 break;
251 case 'm':
252 SetBit(FO_MTIMEONLY, inc->options);
253 break;
254 case 'N':
255 SetBit(FO_HONOR_NODUMP, inc->options);
256 break;
257 case 'n':
258 SetBit(FO_NOREPLACE, inc->options);
259 break;
260 case 'p': /* use portable data format */
261 SetBit(FO_PORTABLE, inc->options);
262 break;
263 case 'R': /* Resource forks and Finder Info */
264 SetBit(FO_HFSPLUS, inc->options);
265 break;
266 case 'r': /* read fifo */
267 SetBit(FO_READFIFO, inc->options);
268 break;
269 case 'S':
270 switch (*(rp + 1)) {
271 case '1':
272 SetBit(FO_SHA1, inc->options);
273 rp++;
274 break;
275 #ifdef HAVE_SHA2
276 case '2':
277 SetBit(FO_SHA256, inc->options);
278 rp++;
279 break;
280 case '3':
281 SetBit(FO_SHA512, inc->options);
282 rp++;
283 break;
284 #endif
285 default:
286 /*
287 * If 2 or 3 is seen here, SHA2 is not configured, so
288 * eat the option, and drop back to SHA-1.
289 */
290 if (rp[1] == '2' || rp[1] == '3') { rp++; }
291 SetBit(FO_SHA1, inc->options);
292 break;
293 }
294 break;
295 case 's':
296 SetBit(FO_SPARSE, inc->options);
297 break;
298 case 'V': /* verify options */
299 /* Copy Verify Options */
300 for (j = 0; *rp && *rp != ':'; rp++) {
301 inc->VerifyOpts[j] = *rp;
302 if (j < (int)sizeof(inc->VerifyOpts) - 1) { j++; }
303 }
304 inc->VerifyOpts[j] = 0;
305 break;
306 case 'W':
307 SetBit(FO_ENHANCEDWILD, inc->options);
308 break;
309 case 'w':
310 SetBit(FO_IF_NEWER, inc->options);
311 break;
312 case 'x':
313 SetBit(FO_NO_AUTOEXCL, inc->options);
314 break;
315 case 'X':
316 SetBit(FO_XATTR, inc->options);
317 break;
318 case 'Z': /* Compression */
319 rp++; /* Skip Z */
320 if (*rp >= '0' && *rp <= '9') {
321 SetBit(FO_COMPRESS, inc->options);
322 inc->algo = COMPRESS_GZIP;
323 inc->level = *rp - '0';
324 } else if (*rp == 'o') {
325 SetBit(FO_COMPRESS, inc->options);
326 inc->algo = COMPRESS_LZO1X;
327 inc->level = 1; /* Not used with LZO */
328 } else if (*rp == 'f') {
329 if (rp[1] == 'f') {
330 rp++; /* Skip f */
331 SetBit(FO_COMPRESS, inc->options);
332 inc->algo = COMPRESS_FZFZ;
333 inc->level = 1; /* Not used with libfzlib */
334 } else if (rp[1] == '4') {
335 rp++; /* Skip f */
336 SetBit(FO_COMPRESS, inc->options);
337 inc->algo = COMPRESS_FZ4L;
338 inc->level = 1; /* Not used with libfzlib */
339 } else if (rp[1] == 'h') {
340 rp++; /* Skip f */
341 SetBit(FO_COMPRESS, inc->options);
342 inc->algo = COMPRESS_FZ4H;
343 inc->level = 1; /* Not used with libfzlib */
344 }
345 }
346 Dmsg2(200, "Compression alg=%d level=%d\n", inc->algo, inc->level);
347 break;
348 case 'z': /* Min, Max or Approx size or Size range */
349 rp++; /* Skip z */
350 for (j = 0; *rp && *rp != ':'; rp++) {
351 size[j] = *rp;
352 if (j < (int)sizeof(size) - 1) { j++; }
353 }
354 size[j] = 0;
355 if (!inc->size_match) {
356 inc->size_match
357 = (struct s_sz_matching*)malloc(sizeof(struct s_sz_matching));
358 }
359 if (!ParseSizeMatch(size, inc->size_match)) {
360 Emsg1(M_ERROR, 0, _("Unparseable size option: %s\n"), size);
361 }
362 break;
363 default:
364 Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *rp);
365 break;
366 }
367 }
368 /* Skip past space(s) */
369 for (; *rp == ' '; rp++) {}
370 } else {
371 rp = fname;
372 }
373
374 strcpy(inc->fname, rp);
375 p = inc->fname;
376 len = strlen(p);
377 /* Zap trailing slashes. */
378 p += len - 1;
379 while (p > inc->fname && IsPathSeparator(*p)) {
380 *p-- = 0;
381 len--;
382 }
383 inc->len = len;
384 /* Check for wild cards */
385 inc->pattern = 0;
386 for (p = inc->fname; *p; p++) {
387 if (*p == '*' || *p == '[' || *p == '?') {
388 inc->pattern = 1;
389 break;
390 }
391 }
392 #if defined(HAVE_WIN32)
393 /* Convert any \'s into /'s */
394 for (p = inc->fname; *p; p++) {
395 if (*p == '\\') { *p = '/'; }
396 }
397 #endif
398 inc->next = NULL;
399 /* Chain this one on the end of the list */
400 if (!ff->included_files_list) {
401 /* First one, so set head */
402 ff->included_files_list = inc;
403 } else {
404 struct s_included_file* next;
405 /* Walk to end of list */
406 for (next = ff->included_files_list; next->next; next = next->next) {}
407 next->next = inc;
408 }
409 Dmsg4(100, "add_fname_to_include prefix=%d compres=%d alg= %d fname=%s\n",
410 prefixed, BitIsSet(FO_COMPRESS, inc->options), inc->algo, inc->fname);
411 }
412
413 /**
414 * We add an exclude name to either the exclude path
415 * list or the exclude filename list.
416 */
AddFnameToExcludeList(FindFilesPacket * ff,const char * fname)417 void AddFnameToExcludeList(FindFilesPacket* ff, const char* fname)
418 {
419 int len;
420 struct s_excluded_file *exc, **list;
421
422 Dmsg1(20, "Add name to exclude: %s\n", fname);
423
424 if (first_path_separator(fname) != NULL) {
425 list = &ff->excluded_paths_list;
426 } else {
427 list = &ff->excluded_files_list;
428 }
429
430 len = strlen(fname);
431
432 exc = (struct s_excluded_file*)malloc(sizeof(struct s_excluded_file) + len
433 + 1);
434 memset(exc, 0, sizeof(struct s_excluded_file) + len + 1);
435 exc->next = *list;
436 exc->len = len;
437 strcpy(exc->fname, fname);
438 #if defined(HAVE_WIN32)
439 /* Convert any \'s into /'s */
440 for (char* p = exc->fname; *p; p++) {
441 if (*p == '\\') { *p = '/'; }
442 }
443 #endif
444 *list = exc;
445 }
446
447
448 /**
449 * Get next included file
450 */
get_next_included_file(FindFilesPacket * ff,struct s_included_file * ainc)451 struct s_included_file* get_next_included_file(FindFilesPacket* ff,
452 struct s_included_file* ainc)
453 {
454 struct s_included_file* inc;
455
456 if (ainc == NULL) {
457 inc = ff->included_files_list;
458 } else {
459 inc = ainc->next;
460 }
461 /*
462 * copy inc_options for this file into the ff packet
463 */
464 if (inc) {
465 CopyBits(FO_MAX, inc->options, ff->flags);
466 ff->Compress_algo = inc->algo;
467 ff->Compress_level = inc->level;
468 }
469 return inc;
470 }
471
472 /**
473 * Walk through the included list to see if this
474 * file is included possibly with wild-cards.
475 */
FileIsIncluded(FindFilesPacket * ff,const char * file)476 bool FileIsIncluded(FindFilesPacket* ff, const char* file)
477 {
478 struct s_included_file* inc = ff->included_files_list;
479 int len;
480
481 for (; inc; inc = inc->next) {
482 if (inc->pattern) {
483 if (fnmatch(inc->fname, file, fnmode | FNM_LEADING_DIR) == 0) {
484 return true;
485 }
486 continue;
487 }
488 /*
489 * No wild cards. We accept a match to the
490 * end of any component.
491 */
492 Dmsg2(900, "pat=%s file=%s\n", inc->fname, file);
493 len = strlen(file);
494 if (inc->len == len && bstrcmp(inc->fname, file)) { return true; }
495 if (inc->len < len && IsPathSeparator(file[inc->len])
496 && bstrncmp(inc->fname, file, inc->len)) {
497 return true;
498 }
499 if (inc->len == 1 && IsPathSeparator(inc->fname[0])) { return true; }
500 }
501 return false;
502 }
503
504 /**
505 * This is the workhorse of excluded_file().
506 * Determine if the file is excluded or not.
507 */
FileInExcludedList(struct s_excluded_file * exc,const char * file)508 static bool FileInExcludedList(struct s_excluded_file* exc, const char* file)
509 {
510 if (exc == NULL) { Dmsg0(900, "exc is NULL\n"); }
511 for (; exc; exc = exc->next) {
512 if (fnmatch(exc->fname, file, fnmode | FNM_PATHNAME) == 0) {
513 Dmsg2(900, "Match exc pat=%s: file=%s:\n", exc->fname, file);
514 return true;
515 }
516 Dmsg2(900, "No match exc pat=%s: file=%s:\n", exc->fname, file);
517 }
518 return false;
519 }
520
521 /**
522 * Walk through the excluded lists to see if this
523 * file is excluded, or if it matches a component
524 * of an excluded directory.
525 */
FileIsExcluded(FindFilesPacket * ff,const char * file)526 bool FileIsExcluded(FindFilesPacket* ff, const char* file)
527 {
528 const char* p;
529
530 #if defined(HAVE_WIN32)
531 /*
532 * ***NB*** this removes the drive from the exclude
533 * rule. Why?????
534 */
535 if (file[1] == ':') { file += 2; }
536 #endif
537
538 if (FileInExcludedList(ff->excluded_paths_list, file)) { return true; }
539
540 /* Try each component */
541 for (p = file; *p; p++) {
542 /* Match from the beginning of a component only */
543 if ((p == file || (!IsPathSeparator(*p) && IsPathSeparator(p[-1])))
544 && FileInExcludedList(ff->excluded_files_list, p)) {
545 return true;
546 }
547 }
548 return false;
549 }
550
551 /**
552 * Parse a size matching fileset option.
553 */
ParseSizeMatch(const char * size_match_pattern,struct s_sz_matching * size_matching)554 bool ParseSizeMatch(const char* size_match_pattern,
555 struct s_sz_matching* size_matching)
556 {
557 bool retval = false;
558 char *private_copy, *bp;
559
560 /*
561 * Make a private copy of the input string.
562 * As we manipulate the input and size_to_uint64
563 * eats its input.
564 */
565 private_copy = strdup(size_match_pattern);
566
567 /*
568 * Empty the matching arguments.
569 */
570 *size_matching = s_sz_matching{};
571
572 /*
573 * See if the size is a range e.g. there is a - in the
574 * match pattern. As a size of a file can never be negative
575 * this is a workable solution.
576 */
577 if ((bp = strchr(private_copy, '-')) != NULL) {
578 *bp++ = '\0';
579 size_matching->type = size_match_range;
580 if (!size_to_uint64(private_copy, &size_matching->begin_size)) {
581 goto bail_out;
582 }
583 if (!size_to_uint64(bp, &size_matching->end_size)) { goto bail_out; }
584 } else {
585 switch (*private_copy) {
586 case '<':
587 size_matching->type = size_match_smaller;
588 if (!size_to_uint64(private_copy + 1, &size_matching->begin_size)) {
589 goto bail_out;
590 }
591 break;
592 case '>':
593 size_matching->type = size_match_greater;
594 if (!size_to_uint64(private_copy + 1, &size_matching->begin_size)) {
595 goto bail_out;
596 }
597 break;
598 default:
599 size_matching->type = size_match_approx;
600 if (!size_to_uint64(private_copy, &size_matching->begin_size)) {
601 goto bail_out;
602 }
603 break;
604 }
605 }
606
607 retval = true;
608
609 bail_out:
610 free(private_copy);
611 return retval;
612 }
613