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