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