1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
5    Copyright (C) 2016-2016 Bareos GmbH & Co. KG
6 
7    This program is Free Software; you can redistribute it and/or
8    modify it under the terms of version three of the GNU Affero General Public
9    License as published by the Free Software Foundation and included
10    in the file LICENSE.
11 
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15    Affero General Public License for more details.
16 
17    You should have received a copy of the GNU Affero General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20    02110-1301, USA.
21 */
22 /*
23  * Kern Sibbald, MM
24  */
25 /**
26  * @file
27  * Test program for find files
28  */
29 
30 #include "include/bareos.h"
31 #include "dird/dird.h"
32 #include "findlib/find.h"
33 #include "lib/mntent_cache.h"
34 #include "ch.h"
35 
36 #if defined(HAVE_WIN32)
37 #define isatty(fd) (fd==0)
38 #endif
39 
40 using namespace directordaemon;
41 
42 /* Dummy functions */
GeneratePluginEvent(JobControlRecord * jcr,bEventType eventType,void * value)43 void GeneratePluginEvent(JobControlRecord *jcr, bEventType eventType, void *value) { }
44 extern bool ParseDirConfig(const char *configfile, int exit_code);
45 
46 /* Global variables */
47 static int num_files = 0;
48 static int max_file_len = 0;
49 static int max_path_len = 0;
50 static int trunc_fname = 0;
51 static int trunc_path = 0;
52 static int attrs = 0;
53 
54 static JobControlRecord *jcr;
55 
56 static int PrintFile(JobControlRecord *jcr, FindFilesPacket *ff, bool);
57 static void CountFiles(FindFilesPacket *ff);
58 static bool CopyFileset(FindFilesPacket *ff, JobControlRecord *jcr);
59 static void SetOptions(findFOPTS *fo, const char *opts);
60 
usage()61 static void usage()
62 {
63    fprintf(stderr, _(
64 "\n"
65 "Usage: testfind [-d debug_level] [-] [pattern1 ...]\n"
66 "       -a          print extended attributes (Win32 debug)\n"
67 "       -d <nn>     set debug level to <nn>\n"
68 "       -dt         print timestamp in debug output\n"
69 "       -c          specify config file containing FileSet resources\n"
70 "       -f          specify which FileSet to use\n"
71 "       -?          print this message.\n"
72 "\n"
73 "Patterns are used for file inclusion -- normally directories.\n"
74 "Debug level >= 1 prints each file found.\n"
75 "Debug level >= 10 prints path/file for catalog.\n"
76 "Errors are always printed.\n"
77 "Files/paths truncated is the number of files/paths with len > 255.\n"
78 "Truncation is only in the catalog.\n"
79 "\n"));
80 
81    exit(1);
82 }
83 
84 
85 int
main(int argc,char * const * argv)86 main (int argc, char *const *argv)
87 {
88    FindFilesPacket *ff;
89    const char *configfile = "bareos-dir.conf";
90    const char *fileset_name = "Windows-Full-Set";
91    int ch, hard_links;
92 
93    OSDependentInit();
94 
95    setlocale(LC_ALL, "");
96    bindtextdomain("bareos", LOCALEDIR);
97    textdomain("bareos");
98    LmgrInitThread();
99 
100    while ((ch = getopt(argc, argv, "ac:d:f:?")) != -1) {
101       switch (ch) {
102          case 'a':                    /* print extended attributes *debug* */
103             attrs = 1;
104             break;
105 
106          case 'c':                    /* set debug level */
107             configfile = optarg;
108             break;
109 
110          case 'd':                    /* set debug level */
111          if (*optarg == 't') {
112             dbg_timestamp = true;
113          } else {
114             debug_level = atoi(optarg);
115             if (debug_level <= 0) {
116                debug_level = 1;
117             }
118          }
119             break;
120 
121          case 'f':                    /* exclude patterns */
122             fileset_name = optarg;
123             break;
124 
125          case '?':
126          default:
127             usage();
128 
129       }
130    }
131 
132    argc -= optind;
133    argv += optind;
134 
135    my_config = InitDirConfig(configfile, M_ERROR_TERM);
136    my_config->ParseConfig();
137 
138    MessagesResource *msg;
139 
140    foreach_res(msg, R_MSGS)
141    {
142       InitMsg(NULL, msg);
143    }
144 
145    jcr = new_jcr(sizeof(JobControlRecord), NULL);
146    jcr->res.fileset = (FilesetResource *)my_config->GetResWithName(R_FILESET, fileset_name);
147 
148    if (jcr->res.fileset == NULL) {
149       fprintf(stderr, "%s: Fileset not found\n", fileset_name);
150 
151       FilesetResource *var;
152 
153       fprintf(stderr, "Valid FileSets:\n");
154 
155       foreach_res(var, R_FILESET) {
156          fprintf(stderr, "    %s\n", var->hdr.name);
157       }
158 
159       exit(1);
160    }
161 
162    ff = init_find_files();
163 
164    CopyFileset(ff, jcr);
165 
166    FindFiles(jcr, ff, PrintFile, NULL);
167 
168    FreeJcr(jcr);
169    if (my_config) {
170       delete my_config;
171       my_config = NULL;
172    }
173 
174    TermLastJobsList();
175 
176    /* Clean up fileset */
177    findFILESET *fileset = ff->fileset;
178 
179    if (fileset) {
180       int i, j, k;
181       /* Delete FileSet Include lists */
182       for (i=0; i<fileset->include_list.size(); i++) {
183          findIncludeExcludeItem *incexe = (findIncludeExcludeItem *)fileset->include_list.get(i);
184          for (j=0; j<incexe->opts_list.size(); j++) {
185             findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
186             for (k=0; k<fo->regex.size(); k++) {
187                regfree((regex_t *)fo->regex.get(k));
188             }
189             fo->regex.destroy();
190             fo->regexdir.destroy();
191             fo->regexfile.destroy();
192             fo->wild.destroy();
193             fo->wilddir.destroy();
194             fo->wildfile.destroy();
195             fo->wildbase.destroy();
196             fo->fstype.destroy();
197             fo->Drivetype.destroy();
198          }
199          incexe->opts_list.destroy();
200          incexe->name_list.destroy();
201       }
202       fileset->include_list.destroy();
203 
204       /* Delete FileSet Exclude lists */
205       for (i=0; i<fileset->exclude_list.size(); i++) {
206          findIncludeExcludeItem *incexe = (findIncludeExcludeItem *)fileset->exclude_list.get(i);
207          for (j=0; j<incexe->opts_list.size(); j++) {
208             findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
209             fo->regex.destroy();
210             fo->regexdir.destroy();
211             fo->regexfile.destroy();
212             fo->wild.destroy();
213             fo->wilddir.destroy();
214             fo->wildfile.destroy();
215             fo->wildbase.destroy();
216             fo->fstype.destroy();
217             fo->Drivetype.destroy();
218          }
219          incexe->opts_list.destroy();
220          incexe->name_list.destroy();
221       }
222       fileset->exclude_list.destroy();
223       free(fileset);
224    }
225    ff->fileset = NULL;
226    hard_links = TermFindFiles(ff);
227 
228    printf(_("\n"
229 "Total files    : %d\n"
230 "Max file length: %d\n"
231 "Max path length: %d\n"
232 "Files truncated: %d\n"
233 "Paths truncated: %d\n"
234 "Hard links     : %d\n"),
235      num_files, max_file_len, max_path_len,
236      trunc_fname, trunc_path, hard_links);
237 
238    FlushMntentCache();
239 
240    TermMsg();
241 
242    CloseMemoryPool();
243    LmgrCleanupMain();
244    sm_dump(false);
245    exit(0);
246 }
247 
PrintFile(JobControlRecord * jcr,FindFilesPacket * ff,bool top_level)248 static int PrintFile(JobControlRecord *jcr, FindFilesPacket *ff, bool top_level)
249 {
250 
251    switch (ff->type) {
252    case FT_LNKSAVED:
253       if (debug_level == 1) {
254          printf("%s\n", ff->fname);
255       } else if (debug_level > 1) {
256          printf("Lnka: %s -> %s\n", ff->fname, ff->link);
257       }
258       break;
259    case FT_REGE:
260       if (debug_level == 1) {
261          printf("%s\n", ff->fname);
262       } else if (debug_level > 1) {
263          printf("Empty: %s\n", ff->fname);
264       }
265       CountFiles(ff);
266       break;
267    case FT_REG:
268       if (debug_level == 1) {
269          printf("%s\n", ff->fname);
270       } else if (debug_level > 1) {
271          printf(_("Reg: %s\n"), ff->fname);
272       }
273       CountFiles(ff);
274       break;
275    case FT_LNK:
276       if (debug_level == 1) {
277          printf("%s\n", ff->fname);
278       } else if (debug_level > 1) {
279          printf("Lnk: %s -> %s\n", ff->fname, ff->link);
280       }
281       CountFiles(ff);
282       break;
283    case FT_DIRBEGIN:
284       return 1;
285    case FT_NORECURSE:
286    case FT_NOFSCHG:
287    case FT_INVALIDFS:
288    case FT_INVALIDDT:
289    case FT_DIREND:
290       if (debug_level) {
291          char errmsg[100] = "";
292          if (ff->type == FT_NORECURSE) {
293             bstrncpy(errmsg, _("\t[will not descend: recursion turned off]"), sizeof(errmsg));
294          } else if (ff->type == FT_NOFSCHG) {
295             bstrncpy(errmsg, _("\t[will not descend: file system change not allowed]"), sizeof(errmsg));
296          } else if (ff->type == FT_INVALIDFS) {
297             bstrncpy(errmsg, _("\t[will not descend: disallowed file system]"), sizeof(errmsg));
298          } else if (ff->type == FT_INVALIDDT) {
299             bstrncpy(errmsg, _("\t[will not descend: disallowed drive type]"), sizeof(errmsg));
300          }
301          printf("%s%s%s\n", (debug_level > 1 ? "Dir: " : ""), ff->fname, errmsg);
302       }
303       ff->type = FT_DIREND;
304       CountFiles(ff);
305       break;
306    case FT_SPEC:
307       if (debug_level == 1) {
308          printf("%s\n", ff->fname);
309       } else if (debug_level > 1) {
310          printf("Spec: %s\n", ff->fname);
311       }
312       CountFiles(ff);
313       break;
314    case FT_NOACCESS:
315       printf(_("Err: Could not access %s: %s\n"), ff->fname, strerror(errno));
316       break;
317    case FT_NOFOLLOW:
318       printf(_("Err: Could not follow ff->link %s: %s\n"), ff->fname, strerror(errno));
319       break;
320    case FT_NOSTAT:
321       printf(_("Err: Could not stat %s: %s\n"), ff->fname, strerror(errno));
322       break;
323    case FT_NOCHG:
324       printf(_("Skip: File not saved. No change. %s\n"), ff->fname);
325       break;
326    case FT_ISARCH:
327       printf(_("Err: Attempt to backup archive. Not saved. %s\n"), ff->fname);
328       break;
329    case FT_NOOPEN:
330       printf(_("Err: Could not open directory %s: %s\n"), ff->fname, strerror(errno));
331       break;
332    default:
333       printf(_("Err: Unknown file ff->type %d: %s\n"), ff->type, ff->fname);
334       break;
335    }
336    if (attrs) {
337       char attr[200];
338       encode_attribsEx(NULL, attr, ff);
339       if (*attr != 0) {
340          printf("AttrEx=%s\n", attr);
341       }
342 //    set_attribsEx(NULL, ff->fname, NULL, NULL, ff->type, attr);
343    }
344    return 1;
345 }
346 
CountFiles(FindFilesPacket * ar)347 static void CountFiles(FindFilesPacket *ar)
348 {
349    int fnl, pnl;
350    char *l, *p;
351    PoolMem file(PM_FNAME);
352    PoolMem spath(PM_FNAME);
353 
354    num_files++;
355 
356    /* Find path without the filename.
357     * I.e. everything after the last / is a "filename".
358     * OK, maybe it is a directory name, but we treat it like
359     * a filename. If we don't find a / then the whole name
360     * must be a path name (e.g. c:).
361     */
362    for (p=l=ar->fname; *p; p++) {
363       if (IsPathSeparator(*p)) {
364          l = p;                       /* set pos of last slash */
365       }
366    }
367    if (IsPathSeparator(*l)) {                   /* did we find a slash? */
368       l++;                            /* yes, point to filename */
369    } else {                           /* no, whole thing must be path name */
370       l = p;
371    }
372 
373    /* If filename doesn't exist (i.e. root directory), we
374     * simply create a blank name consisting of a single
375     * space. This makes handling zero length filenames
376     * easier.
377     */
378    fnl = p - l;
379    if (fnl > max_file_len) {
380       max_file_len = fnl;
381    }
382    if (fnl > 255) {
383       printf(_("===== Filename truncated to 255 chars: %s\n"), l);
384       fnl = 255;
385       trunc_fname++;
386    }
387 
388    if (fnl > 0) {
389       PmStrcpy(file, l);             /* copy filename */
390    } else {
391       PmStrcpy(file, " ");           /* blank filename */
392    }
393 
394    pnl = l - ar->fname;
395    if (pnl > max_path_len) {
396       max_path_len = pnl;
397    }
398    if (pnl > 255) {
399       printf(_("========== Path name truncated to 255 chars: %s\n"), ar->fname);
400       pnl = 255;
401       trunc_path++;
402    }
403 
404    PmStrcpy(spath, ar->fname);
405    if (pnl == 0) {
406       PmStrcpy(spath, " ");
407       printf(_("========== Path length is zero. File=%s\n"), ar->fname);
408    }
409    if (debug_level >= 10) {
410       printf(_("Path: %s\n"), spath.c_str());
411       printf(_("File: %s\n"), file.c_str());
412    }
413 
414 }
415 
CopyFileset(FindFilesPacket * ff,JobControlRecord * jcr)416 static bool CopyFileset(FindFilesPacket *ff, JobControlRecord *jcr)
417 {
418    FilesetResource *jcr_fileset = jcr->res.fileset;
419    int num;
420    bool include = true;
421 
422    findFILESET *fileset;
423    findFOPTS *current_opts;
424 
425    fileset = (findFILESET *)malloc(sizeof(findFILESET));
426    memset(fileset, 0, sizeof(findFILESET));
427    ff->fileset = fileset;
428 
429    fileset->state = state_none;
430    fileset->include_list.init(1, true);
431    fileset->exclude_list.init(1, true);
432 
433    for ( ;; ) {
434       if (include) {
435          num = jcr_fileset->num_includes;
436       } else {
437          num = jcr_fileset->num_excludes;
438       }
439       for (int i=0; i<num; i++) {
440          IncludeExcludeItem *ie;
441          int j, k;
442 
443          if (include) {
444             ie = jcr_fileset->include_items[i];
445 
446             /* New include */
447             fileset->incexe = (findIncludeExcludeItem *)malloc(sizeof(findIncludeExcludeItem));
448             memset(fileset->incexe, 0, sizeof(findIncludeExcludeItem));
449             fileset->incexe->opts_list.init(1, true);
450             fileset->incexe->name_list.init(0, 0);
451             fileset->include_list.append(fileset->incexe);
452          } else {
453             ie = jcr_fileset->exclude_items[i];
454 
455             /* New exclude */
456             fileset->incexe = (findIncludeExcludeItem *)malloc(sizeof(findIncludeExcludeItem));
457             memset(fileset->incexe, 0, sizeof(findIncludeExcludeItem));
458             fileset->incexe->opts_list.init(1, true);
459             fileset->incexe->name_list.init(0, 0);
460             fileset->exclude_list.append(fileset->incexe);
461          }
462 
463          for (j=0; j<ie->num_opts; j++) {
464             FileOptions *fo = ie->opts_list[j];
465 
466             current_opts = (findFOPTS *)malloc(sizeof(findFOPTS));
467             memset(current_opts, 0, sizeof(findFOPTS));
468             fileset->incexe->current_opts = current_opts;
469             fileset->incexe->opts_list.append(current_opts);
470 
471             current_opts->regex.init(1, true);
472             current_opts->regexdir.init(1, true);
473             current_opts->regexfile.init(1, true);
474             current_opts->wild.init(1, true);
475             current_opts->wilddir.init(1, true);
476             current_opts->wildfile.init(1, true);
477             current_opts->wildbase.init(1, true);
478             current_opts->fstype.init(1, true);
479             current_opts->Drivetype.init(1, true);
480 
481             SetOptions(current_opts, fo->opts);
482 
483             for (k=0; k<fo->regex.size(); k++) {
484                // fd->fsend("R %s\n", fo->regex.get(k));
485                current_opts->regex.append(bstrdup((const char *)fo->regex.get(k)));
486             }
487             for (k=0; k<fo->regexdir.size(); k++) {
488                // fd->fsend("RD %s\n", fo->regexdir.get(k));
489                current_opts->regexdir.append(bstrdup((const char *)fo->regexdir.get(k)));
490             }
491             for (k=0; k<fo->regexfile.size(); k++) {
492                // fd->fsend("RF %s\n", fo->regexfile.get(k));
493                current_opts->regexfile.append(bstrdup((const char *)fo->regexfile.get(k)));
494             }
495             for (k=0; k<fo->wild.size(); k++) {
496                current_opts->wild.append(bstrdup((const char *)fo->wild.get(k)));
497             }
498             for (k=0; k<fo->wilddir.size(); k++) {
499                current_opts->wilddir.append(bstrdup((const char *)fo->wilddir.get(k)));
500             }
501             for (k=0; k<fo->wildfile.size(); k++) {
502                current_opts->wildfile.append(bstrdup((const char *)fo->wildfile.get(k)));
503             }
504             for (k=0; k<fo->wildbase.size(); k++) {
505                current_opts->wildbase.append(bstrdup((const char *)fo->wildbase.get(k)));
506             }
507             for (k=0; k<fo->fstype.size(); k++) {
508                current_opts->fstype.append(bstrdup((const char *)fo->fstype.get(k)));
509             }
510             for (k=0; k<fo->Drivetype.size(); k++) {
511                current_opts->Drivetype.append(bstrdup((const char *)fo->Drivetype.get(k)));
512             }
513          }
514 
515          for (j=0; j<ie->name_list.size(); j++) {
516             fileset->incexe->name_list.append(bstrdup((const char *)ie->name_list.get(j)));
517          }
518       }
519 
520       if (!include) {                 /* If we just did excludes */
521          break;                       /*   all done */
522       }
523 
524       include = false;                /* Now do excludes */
525    }
526 
527    return true;
528 }
529 
SetOptions(findFOPTS * fo,const char * opts)530 static void SetOptions(findFOPTS *fo, const char *opts)
531 {
532    int j;
533    const char *p;
534 
535    for (p=opts; *p; p++) {
536       switch (*p) {
537       case 'a':                 /* alway replace */
538       case '0':                 /* no option */
539          break;
540       case 'e':
541          SetBit(FO_EXCLUDE, fo->flags);
542          break;
543       case 'f':
544          SetBit(FO_MULTIFS, fo->flags);
545          break;
546       case 'h':                 /* no recursion */
547          SetBit(FO_NO_RECURSION, fo->flags);
548          break;
549       case 'H':                 /* no hard link handling */
550          SetBit(FO_NO_HARDLINK, fo->flags);
551          break;
552       case 'i':
553          SetBit(FO_IGNORECASE, fo->flags);
554          break;
555       case 'M':                 /* MD5 */
556          SetBit(FO_MD5, fo->flags);
557          break;
558       case 'n':
559          SetBit(FO_NOREPLACE, fo->flags);
560          break;
561       case 'p':                 /* use portable data format */
562          SetBit(FO_PORTABLE, fo->flags);
563          break;
564       case 'R':                 /* Resource forks and Finder Info */
565          SetBit(FO_HFSPLUS, fo->flags);
566       case 'r':                 /* read fifo */
567          SetBit(FO_READFIFO, fo->flags);
568          break;
569       case 'S':
570          switch(*(p + 1)) {
571          case ' ':
572             /* Old director did not specify SHA variant */
573             SetBit(FO_SHA1, fo->flags);
574             break;
575          case '1':
576             SetBit(FO_SHA1, fo->flags);
577             p++;
578             break;
579 #ifdef HAVE_SHA2
580          case '2':
581             SetBit(FO_SHA256, fo->flags);
582             p++;
583             break;
584          case '3':
585             SetBit(FO_SHA512, fo->flags);
586             p++;
587             break;
588 #endif
589          default:
590             /* Automatically downgrade to SHA-1 if an unsupported
591              * SHA variant is specified */
592             SetBit(FO_SHA1, fo->flags);
593             p++;
594             break;
595          }
596          break;
597       case 's':
598          SetBit(FO_SPARSE, fo->flags);
599          break;
600       case 'm':
601          SetBit(FO_MTIMEONLY, fo->flags);
602          break;
603       case 'k':
604          SetBit(FO_KEEPATIME, fo->flags);
605          break;
606       case 'A':
607          SetBit(FO_ACL, fo->flags);
608          break;
609       case 'V':                  /* verify options */
610          /* Copy Verify Options */
611          for (j=0; *p && *p != ':'; p++) {
612             fo->VerifyOpts[j] = *p;
613             if (j < (int)sizeof(fo->VerifyOpts) - 1) {
614                j++;
615             }
616          }
617          fo->VerifyOpts[j] = 0;
618          break;
619       case 'w':
620          SetBit(FO_IF_NEWER, fo->flags);
621          break;
622       case 'W':
623          SetBit(FO_ENHANCEDWILD, fo->flags);
624          break;
625       case 'Z':                 /* compression */
626          p++;                   /* skip Z */
627          if (*p >= '0' && *p <= '9') {
628             SetBit(FO_COMPRESS, fo->flags);
629             fo->Compress_algo = COMPRESS_GZIP;
630             fo->Compress_level = *p - '0';
631          }
632          else if (*p == 'o') {
633             SetBit(FO_COMPRESS, fo->flags);
634             fo->Compress_algo = COMPRESS_LZO1X;
635             fo->Compress_level = 1; /* not used with LZO */
636          }
637          Dmsg2(200, "Compression alg=%d level=%d\n", fo->Compress_algo, fo->Compress_level);
638          break;
639       case 'x':
640          SetBit(FO_NO_AUTOEXCL, fo->flags);
641          break;
642       case 'X':
643          SetBit(FO_XATTR, fo->flags);
644          break;
645       default:
646          Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);
647          break;
648       }
649    }
650 }
651