2 * Test program for find files
5 Copyright (C) 2000-2006 Kern Sibbald
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.
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.
20 #include "dird/dird.h"
21 #include "findlib/find.h"
24 #if defined(HAVE_WIN32)
25 #define isatty(fd) (fd==0)
29 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
30 int generate_job_event(JCR *jcr, const char *event) { return 1; }
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;
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);
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"
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"
71 main (int argc, char *const *argv)
74 char *configfile = "bacula-dir.conf";
75 char *fileset_name = "Windows-Full-Set";
80 setlocale(LC_ALL, "");
81 bindtextdomain("bacula", LOCALEDIR);
84 while ((ch = getopt(argc, argv, "ac:d:f:?")) != -1) {
86 case 'a': /* print extended attributes *debug* */
90 case 'c': /* set debug level */
94 case 'd': /* set debug level */
95 debug_level = atoi(optarg);
96 if (debug_level <= 0) {
101 case 'f': /* exclude patterns */
102 fileset_name = optarg;
115 parse_config(configfile);
119 foreach_res(msg, R_MSGS)
124 jcr = new_jcr(sizeof(JCR), NULL);
125 jcr->fileset = (FILESET *)GetResWithName(R_FILESET, fileset_name);
127 if (jcr->fileset == NULL) {
128 fprintf(stderr, "%s: Fileset not found\n", fileset_name);
132 fprintf(stderr, "Valid FileSets:\n");
134 foreach_res(var, R_FILESET) {
135 fprintf(stderr, " %s\n", var->hdr.name);
141 ff = init_find_files();
143 copy_fileset(ff, jcr);
145 find_files(jcr, ff, print_file, NULL);
148 free_config_resources();
149 term_last_jobs_list();
151 /* Clean up fileset */
152 findFILESET *fileset = ff->fileset;
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));
165 fo->regexdir.destroy();
166 fo->regexfile.destroy();
168 fo->wilddir.destroy();
169 fo->wildfile.destroy();
170 fo->wildbase.destroy();
171 fo->fstype.destroy();
172 fo->drivetype.destroy();
180 incexe->opts_list.destroy();
181 incexe->name_list.destroy();
183 fileset->include_list.destroy();
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);
191 fo->regexdir.destroy();
192 fo->regexfile.destroy();
194 fo->wilddir.destroy();
195 fo->wildfile.destroy();
196 fo->wildbase.destroy();
197 fo->fstype.destroy();
198 fo->drivetype.destroy();
200 incexe->opts_list.destroy();
201 incexe->name_list.destroy();
203 fileset->exclude_list.destroy();
207 hard_links = term_find_files(ff);
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);
226 static int print_file(FF_PKT *ff, void *pkt, bool top_level)
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);
238 if (debug_level == 1) {
239 printf("%s\n", ff->fname);
240 } else if (debug_level > 1) {
241 printf("Empty: %s\n", ff->fname);
246 if (debug_level == 1) {
247 printf("%s\n", ff->fname);
248 } else if (debug_level > 1) {
249 printf(_("Reg: %s\n"), ff->fname);
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);
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));
279 printf("%s%s%s\n", (debug_level > 1 ? "Dir: " : ""), ff->fname, errmsg);
281 ff->type = FT_DIREND;
285 if (debug_level == 1) {
286 printf("%s\n", ff->fname);
287 } else if (debug_level > 1) {
288 printf("Spec: %s\n", ff->fname);
293 printf(_("Err: Could not access %s: %s\n"), ff->fname, strerror(errno));
296 printf(_("Err: Could not follow ff->link %s: %s\n"), ff->fname, strerror(errno));
299 printf(_("Err: Could not stat %s: %s\n"), ff->fname, strerror(errno));
302 printf(_("Skip: File not saved. No change. %s\n"), ff->fname);
305 printf(_("Err: Attempt to backup archive. Not saved. %s\n"), ff->fname);
308 printf(_("Err: Could not open directory %s: %s\n"), ff->fname, strerror(errno));
311 printf(_("Err: Unknown file ff->type %d: %s\n"), ff->type, ff->fname);
316 encode_attribsEx(NULL, attr, ff);
318 printf("AttrEx=%s\n", attr);
320 // set_attribsEx(NULL, ff->fname, NULL, NULL, ff->type, attr);
325 static void count_files(FF_PKT *ar)
329 char file[MAXSTRING];
330 char spath[MAXSTRING];
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:).
340 for (p=l=ar->fname; *p; p++) {
341 if (IsPathSeparator(*p)) {
342 l = p; /* set pos of last slash */
345 if (IsPathSeparator(*l)) { /* did we find a slash? */
346 l++; /* yes, point to filename */
347 } else { /* no, whole thing must be path name */
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
357 if (fnl > max_file_len) {
361 printf(_("===== Filename truncated to 255 chars: %s\n"), l);
366 strncpy(file, l, fnl); /* copy filename */
369 file[0] = ' '; /* blank filename */
374 if (pnl > max_path_len) {
378 printf(_("========== Path name truncated to 255 chars: %s\n"), ar->fname);
382 strncpy(spath, ar->fname, pnl);
387 printf(_("========== Path length is zero. File=%s\n"), ar->fname);
389 if (debug_level >= 10) {
390 printf(_("Path: %s\n"), spath);
391 printf(_("File: %s\n"), file);
396 bool python_set_prog(JCR*, char const*) { return false; }
398 static bool copy_fileset(FF_PKT *ff, JCR *jcr)
400 FILESET *jcr_fileset = jcr->fileset;
404 findFILESET *fileset;
405 findFOPTS *current_opts;
407 fileset = (findFILESET *)malloc(sizeof(findFILESET));
408 memset(fileset, 0, sizeof(findFILESET));
409 ff->fileset = fileset;
411 fileset->state = state_none;
412 fileset->include_list.init(1, true);
413 fileset->exclude_list.init(1, true);
417 num = jcr_fileset->num_includes;
419 num = jcr_fileset->num_excludes;
421 for (int i=0; i<num; i++) {
426 ie = jcr_fileset->include_items[i];
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);
435 ie = jcr_fileset->exclude_items[i];
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);
445 for (j=0; j<ie->num_opts; j++) {
446 FOPTS *fo = ie->opts_list[j];
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);
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);
463 set_options(current_opts, fo->opts);
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)));
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)));
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)));
477 for (k=0; k<fo->wild.size(); k++) {
478 current_opts->wild.append(bstrdup((const char *)fo->wild.get(k)));
480 for (k=0; k<fo->wilddir.size(); k++) {
481 current_opts->wilddir.append(bstrdup((const char *)fo->wilddir.get(k)));
483 for (k=0; k<fo->wildfile.size(); k++) {
484 current_opts->wildfile.append(bstrdup((const char *)fo->wildfile.get(k)));
486 for (k=0; k<fo->wildbase.size(); k++) {
487 current_opts->wildbase.append(bstrdup((const char *)fo->wildbase.get(k)));
489 for (k=0; k<fo->fstype.size(); k++) {
490 current_opts->fstype.append(bstrdup((const char *)fo->fstype.get(k)));
492 for (k=0; k<fo->drivetype.size(); k++) {
493 current_opts->drivetype.append(bstrdup((const char *)fo->drivetype.get(k)));
496 current_opts->reader = bstrdup(fo->reader);
499 current_opts->writer = bstrdup(fo->writer);
503 for (j=0; j<ie->name_list.size(); j++) {
504 fileset->incexe->name_list.append(bstrdup((const char *)ie->name_list.get(j)));
508 if (!include) { /* If we just did excludes */
509 break; /* all done */
512 include = false; /* Now do excludes */
518 static void set_options(findFOPTS *fo, const char *opts)
523 for (p=opts; *p; p++) {
525 case 'a': /* alway replace */
526 case '0': /* no option */
529 fo->flags |= FO_EXCLUDE;
532 fo->flags |= FO_MULTIFS;
534 case 'h': /* no recursion */
535 fo->flags |= FO_NO_RECURSION;
537 case 'H': /* no hard link handling */
538 fo->flags |= FO_NO_HARDLINK;
541 fo->flags |= FO_IGNORECASE;
547 fo->flags |= FO_NOREPLACE;
549 case 'p': /* use portable data format */
550 fo->flags |= FO_PORTABLE;
552 case 'R': /* Resource forks and Finder Info */
553 fo->flags |= FO_HFSPLUS;
554 case 'r': /* read fifo */
555 fo->flags |= FO_READFIFO;
560 /* Old director did not specify SHA variant */
561 fo->flags |= FO_SHA1;
564 fo->flags |= FO_SHA1;
569 fo->flags |= FO_SHA256;
573 fo->flags |= FO_SHA512;
578 /* Automatically downgrade to SHA-1 if an unsupported
579 * SHA variant is specified */
580 fo->flags |= FO_SHA1;
586 fo->flags |= FO_SPARSE;
589 fo->flags |= FO_MTIMEONLY;
592 fo->flags |= FO_KEEPATIME;
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) {
605 fo->VerifyOpts[j] = 0;
608 fo->flags |= FO_IF_NEWER;
611 fo->flags |= FO_ENHANCEDWILD;
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);
619 Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);