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