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