2 * Test program for find files
8 Bacula® - The Network Backup Solution
10 Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
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 plus additions
17 that are listed in the file LICENSE.
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.
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
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.
36 #include "dird/dird.h"
37 #include "findlib/find.h"
40 #if defined(HAVE_WIN32)
41 #define isatty(fd) (fd==0)
45 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
46 int generate_job_event(JCR *jcr, const char *event) { return 1; }
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;
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);
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"
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"
87 main (int argc, char *const *argv)
90 char *configfile = "bacula-dir.conf";
91 char *fileset_name = "Windows-Full-Set";
96 setlocale(LC_ALL, "");
97 bindtextdomain("bacula", LOCALEDIR);
100 while ((ch = getopt(argc, argv, "ac:d:f:?")) != -1) {
102 case 'a': /* print extended attributes *debug* */
106 case 'c': /* set debug level */
110 case 'd': /* set debug level */
111 debug_level = atoi(optarg);
112 if (debug_level <= 0) {
117 case 'f': /* exclude patterns */
118 fileset_name = optarg;
131 parse_config(configfile);
135 foreach_res(msg, R_MSGS)
140 jcr = new_jcr(sizeof(JCR), NULL);
141 jcr->fileset = (FILESET *)GetResWithName(R_FILESET, fileset_name);
143 if (jcr->fileset == NULL) {
144 fprintf(stderr, "%s: Fileset not found\n", fileset_name);
148 fprintf(stderr, "Valid FileSets:\n");
150 foreach_res(var, R_FILESET) {
151 fprintf(stderr, " %s\n", var->hdr.name);
157 ff = init_find_files();
159 copy_fileset(ff, jcr);
161 find_files(jcr, ff, print_file, NULL);
164 free_config_resources();
165 term_last_jobs_list();
167 /* Clean up fileset */
168 findFILESET *fileset = ff->fileset;
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));
181 fo->regexdir.destroy();
182 fo->regexfile.destroy();
184 fo->wilddir.destroy();
185 fo->wildfile.destroy();
186 fo->wildbase.destroy();
187 fo->fstype.destroy();
188 fo->drivetype.destroy();
196 incexe->opts_list.destroy();
197 incexe->name_list.destroy();
199 fileset->include_list.destroy();
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);
207 fo->regexdir.destroy();
208 fo->regexfile.destroy();
210 fo->wilddir.destroy();
211 fo->wildfile.destroy();
212 fo->wildbase.destroy();
213 fo->fstype.destroy();
214 fo->drivetype.destroy();
216 incexe->opts_list.destroy();
217 incexe->name_list.destroy();
219 fileset->exclude_list.destroy();
223 hard_links = term_find_files(ff);
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);
242 static int print_file(FF_PKT *ff, void *pkt, bool top_level)
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);
254 if (debug_level == 1) {
255 printf("%s\n", ff->fname);
256 } else if (debug_level > 1) {
257 printf("Empty: %s\n", ff->fname);
262 if (debug_level == 1) {
263 printf("%s\n", ff->fname);
264 } else if (debug_level > 1) {
265 printf(_("Reg: %s\n"), ff->fname);
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);
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));
295 printf("%s%s%s\n", (debug_level > 1 ? "Dir: " : ""), ff->fname, errmsg);
297 ff->type = FT_DIREND;
301 if (debug_level == 1) {
302 printf("%s\n", ff->fname);
303 } else if (debug_level > 1) {
304 printf("Spec: %s\n", ff->fname);
309 printf(_("Err: Could not access %s: %s\n"), ff->fname, strerror(errno));
312 printf(_("Err: Could not follow ff->link %s: %s\n"), ff->fname, strerror(errno));
315 printf(_("Err: Could not stat %s: %s\n"), ff->fname, strerror(errno));
318 printf(_("Skip: File not saved. No change. %s\n"), ff->fname);
321 printf(_("Err: Attempt to backup archive. Not saved. %s\n"), ff->fname);
324 printf(_("Err: Could not open directory %s: %s\n"), ff->fname, strerror(errno));
327 printf(_("Err: Unknown file ff->type %d: %s\n"), ff->type, ff->fname);
332 encode_attribsEx(NULL, attr, ff);
334 printf("AttrEx=%s\n", attr);
336 // set_attribsEx(NULL, ff->fname, NULL, NULL, ff->type, attr);
341 static void count_files(FF_PKT *ar)
345 char file[MAXSTRING];
346 char spath[MAXSTRING];
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:).
356 for (p=l=ar->fname; *p; p++) {
357 if (IsPathSeparator(*p)) {
358 l = p; /* set pos of last slash */
361 if (IsPathSeparator(*l)) { /* did we find a slash? */
362 l++; /* yes, point to filename */
363 } else { /* no, whole thing must be path name */
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
373 if (fnl > max_file_len) {
377 printf(_("===== Filename truncated to 255 chars: %s\n"), l);
382 strncpy(file, l, fnl); /* copy filename */
385 file[0] = ' '; /* blank filename */
390 if (pnl > max_path_len) {
394 printf(_("========== Path name truncated to 255 chars: %s\n"), ar->fname);
398 strncpy(spath, ar->fname, pnl);
403 printf(_("========== Path length is zero. File=%s\n"), ar->fname);
405 if (debug_level >= 10) {
406 printf(_("Path: %s\n"), spath);
407 printf(_("File: %s\n"), file);
412 bool python_set_prog(JCR*, char const*) { return false; }
414 static bool copy_fileset(FF_PKT *ff, JCR *jcr)
416 FILESET *jcr_fileset = jcr->fileset;
420 findFILESET *fileset;
421 findFOPTS *current_opts;
423 fileset = (findFILESET *)malloc(sizeof(findFILESET));
424 memset(fileset, 0, sizeof(findFILESET));
425 ff->fileset = fileset;
427 fileset->state = state_none;
428 fileset->include_list.init(1, true);
429 fileset->exclude_list.init(1, true);
433 num = jcr_fileset->num_includes;
435 num = jcr_fileset->num_excludes;
437 for (int i=0; i<num; i++) {
442 ie = jcr_fileset->include_items[i];
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);
451 ie = jcr_fileset->exclude_items[i];
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);
461 for (j=0; j<ie->num_opts; j++) {
462 FOPTS *fo = ie->opts_list[j];
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);
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);
479 set_options(current_opts, fo->opts);
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)));
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)));
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)));
493 for (k=0; k<fo->wild.size(); k++) {
494 current_opts->wild.append(bstrdup((const char *)fo->wild.get(k)));
496 for (k=0; k<fo->wilddir.size(); k++) {
497 current_opts->wilddir.append(bstrdup((const char *)fo->wilddir.get(k)));
499 for (k=0; k<fo->wildfile.size(); k++) {
500 current_opts->wildfile.append(bstrdup((const char *)fo->wildfile.get(k)));
502 for (k=0; k<fo->wildbase.size(); k++) {
503 current_opts->wildbase.append(bstrdup((const char *)fo->wildbase.get(k)));
505 for (k=0; k<fo->fstype.size(); k++) {
506 current_opts->fstype.append(bstrdup((const char *)fo->fstype.get(k)));
508 for (k=0; k<fo->drivetype.size(); k++) {
509 current_opts->drivetype.append(bstrdup((const char *)fo->drivetype.get(k)));
512 current_opts->reader = bstrdup(fo->reader);
515 current_opts->writer = bstrdup(fo->writer);
519 for (j=0; j<ie->name_list.size(); j++) {
520 fileset->incexe->name_list.append(bstrdup((const char *)ie->name_list.get(j)));
524 if (!include) { /* If we just did excludes */
525 break; /* all done */
528 include = false; /* Now do excludes */
534 static void set_options(findFOPTS *fo, const char *opts)
539 for (p=opts; *p; p++) {
541 case 'a': /* alway replace */
542 case '0': /* no option */
545 fo->flags |= FO_EXCLUDE;
548 fo->flags |= FO_MULTIFS;
550 case 'h': /* no recursion */
551 fo->flags |= FO_NO_RECURSION;
553 case 'H': /* no hard link handling */
554 fo->flags |= FO_NO_HARDLINK;
557 fo->flags |= FO_IGNORECASE;
563 fo->flags |= FO_NOREPLACE;
565 case 'p': /* use portable data format */
566 fo->flags |= FO_PORTABLE;
568 case 'R': /* Resource forks and Finder Info */
569 fo->flags |= FO_HFSPLUS;
570 case 'r': /* read fifo */
571 fo->flags |= FO_READFIFO;
576 /* Old director did not specify SHA variant */
577 fo->flags |= FO_SHA1;
580 fo->flags |= FO_SHA1;
585 fo->flags |= FO_SHA256;
589 fo->flags |= FO_SHA512;
594 /* Automatically downgrade to SHA-1 if an unsupported
595 * SHA variant is specified */
596 fo->flags |= FO_SHA1;
602 fo->flags |= FO_SPARSE;
605 fo->flags |= FO_MTIMEONLY;
608 fo->flags |= FO_KEEPATIME;
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) {
621 fo->VerifyOpts[j] = 0;
624 fo->flags |= FO_IF_NEWER;
627 fo->flags |= FO_ENHANCEDWILD;
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);
635 Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);