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