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