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