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