]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/tools/testfind.c
Backport from Bacula Enterprise
[bacula/bacula] / bacula / src / tools / testfind.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5    Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
6
7    The original author of Bacula is Kern Sibbald, with contributions
8    from many others, a complete list can be found in the file AUTHORS.
9
10    You may use this file and others of this release according to the
11    license defined in the LICENSE file, which includes the Affero General
12    Public License, v3.0 ("AGPLv3") and some additional permissions and
13    terms pursuant to its AGPLv3 Section 7.
14
15    This notice must be preserved when any source code is 
16    conveyed and/or propagated.
17
18    Bacula(R) is a registered trademark of Kern Sibbald.
19 */
20 /*
21  * Test program for find files
22  *
23  *  Kern Sibbald, MM
24  *
25  */
26
27 #include "bacula.h"
28 #include "dird/dird.h"
29 #include "findlib/find.h"
30 #include "ch.h"
31
32 #if defined(HAVE_WIN32)
33 #define isatty(fd) (fd==0)
34 #endif
35
36 /* Dummy functions */
37 int generate_job_event(JCR *jcr, const char *event) { return 1; }
38 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value) { }
39 extern bool parse_dir_config(CONFIG *config, const char *configfile, int exit_code);
40
41 /* Global variables */
42 static int num_files = 0;
43 static int max_file_len = 0;
44 static int max_path_len = 0;
45 static int trunc_fname = 0;
46 static int trunc_path = 0;
47 static int attrs = 0;
48 static CONFIG *config;
49
50 static JCR *jcr;
51
52 static int print_file(JCR *jcr, FF_PKT *ff, bool);
53 static void count_files(FF_PKT *ff);
54 static bool copy_fileset(FF_PKT *ff, JCR *jcr);
55 static void set_options(findFOPTS *fo, const char *opts);
56
57 static void usage()
58 {
59    fprintf(stderr, _(
60 "\n"
61 "Usage: testfind [-d debug_level] [-] [pattern1 ...]\n"
62 "       -a          print extended attributes (Win32 debug)\n"
63 "       -d <nn>     set debug level to <nn>\n"
64 "       -dt         print timestamp in debug output\n"
65 "       -c          specify config file containing FileSet resources\n"
66 "       -f          specify which FileSet to use\n"
67 "       -?          print this message.\n"
68 "\n"
69 "Patterns are used for file inclusion -- normally directories.\n"
70 "Debug level >= 1 prints each file found.\n"
71 "Debug level >= 10 prints path/file for catalog.\n"
72 "Errors are always printed.\n"
73 "Files/paths truncated is the number of files/paths with len > 255.\n"
74 "Truncation is only in the catalog.\n"
75 "\n"));
76
77    exit(1);
78 }
79
80
81 int
82 main (int argc, char *const *argv)
83 {
84    FF_PKT *ff;
85    const char *configfile = "bacula-dir.conf";
86    const char *fileset_name = "Windows-Full-Set";
87    int ch, hard_links;
88
89    OSDependentInit();
90
91    setlocale(LC_ALL, "");
92    bindtextdomain("bacula", LOCALEDIR);
93    textdomain("bacula");
94    lmgr_init_thread();
95
96    while ((ch = getopt(argc, argv, "ac:d:f:?")) != -1) {
97       switch (ch) {
98          case 'a':                    /* print extended attributes *debug* */
99             attrs = 1;
100             break;
101
102          case 'c':                    /* set debug level */
103             configfile = optarg;
104             break;
105
106          case 'd':                    /* set debug level */
107          if (*optarg == 't') {
108             dbg_timestamp = true;
109          } else {
110             debug_level = atoi(optarg);
111             if (debug_level <= 0) {
112                debug_level = 1;
113             }
114          }
115             break;
116
117          case 'f':                    /* exclude patterns */
118             fileset_name = optarg;
119             break;
120
121          case '?':
122          default:
123             usage();
124
125       }
126    }
127
128    argc -= optind;
129    argv += optind;
130
131    config = new_config_parser();
132    parse_dir_config(config, configfile, M_ERROR_TERM);
133
134    MSGS *msg;
135
136    foreach_res(msg, R_MSGS)
137    {
138       init_msg(NULL, msg);
139    }
140
141    jcr = new_jcr(sizeof(JCR), NULL);
142    jcr->fileset = (FILESET *)GetResWithName(R_FILESET, fileset_name);
143
144    if (jcr->fileset == NULL) {
145       fprintf(stderr, "%s: Fileset not found\n", fileset_name);
146
147       FILESET *var;
148
149       fprintf(stderr, "Valid FileSets:\n");
150       
151       foreach_res(var, R_FILESET) {
152          fprintf(stderr, "    %s\n", var->hdr.name);
153       }
154
155       exit(1);
156    }
157
158    ff = init_find_files();
159    
160    copy_fileset(ff, jcr);
161
162    find_files(jcr, ff, print_file, NULL);
163
164    free_jcr(jcr);
165    if (config) {
166       config->free_resources();
167       free(config);
168       config = NULL;
169    }
170    
171    term_last_jobs_list();
172
173    /* Clean up fileset */
174    findFILESET *fileset = ff->fileset;
175
176    if (fileset) {
177       int i, j, k;
178       /* Delete FileSet Include lists */
179       for (i=0; i<fileset->include_list.size(); i++) {
180          findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
181          for (j=0; j<incexe->opts_list.size(); j++) {
182             findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
183             for (k=0; k<fo->regex.size(); k++) {
184                regfree((regex_t *)fo->regex.get(k));
185             }
186             fo->regex.destroy();
187             fo->regexdir.destroy();
188             fo->regexfile.destroy();
189             fo->wild.destroy();
190             fo->wilddir.destroy();
191             fo->wildfile.destroy();
192             fo->wildbase.destroy();
193             fo->fstype.destroy();
194             fo->drivetype.destroy();
195          }
196          incexe->opts_list.destroy();
197          incexe->name_list.destroy();
198       }
199       fileset->include_list.destroy();
200
201       /* Delete FileSet Exclude lists */
202       for (i=0; i<fileset->exclude_list.size(); i++) {
203          findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
204          for (j=0; j<incexe->opts_list.size(); j++) {
205             findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
206             fo->regex.destroy();
207             fo->regexdir.destroy();
208             fo->regexfile.destroy();
209             fo->wild.destroy();
210             fo->wilddir.destroy();
211             fo->wildfile.destroy();
212             fo->wildbase.destroy();
213             fo->fstype.destroy();
214             fo->drivetype.destroy();
215          }
216          incexe->opts_list.destroy();
217          incexe->name_list.destroy();
218       }
219       fileset->exclude_list.destroy();
220       free(fileset);
221    }
222    ff->fileset = NULL;
223    hard_links = term_find_files(ff);
224
225    printf(_("\n"
226 "Total files    : %d\n"
227 "Max file length: %d\n"
228 "Max path length: %d\n"
229 "Files truncated: %d\n"
230 "Paths truncated: %d\n"
231 "Hard links     : %d\n"),
232      num_files, max_file_len, max_path_len,
233      trunc_fname, trunc_path, hard_links);
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 }