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