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 and included
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 " -d <nn> set debug level to <nn>\n"
70 " -dt print timestamp in debug output\n"
71 " -c specify config file containing FileSet resources\n"
72 " -f specify which FileSet to use\n"
73 " -? print this message.\n"
75 "Patterns are used for file inclusion -- normally directories.\n"
76 "Debug level >= 1 prints each file found.\n"
77 "Debug level >= 10 prints path/file for catalog.\n"
78 "Errors are always printed.\n"
79 "Files/paths truncated is the number of files/paths with len > 255.\n"
80 "Truncation is only in the catalog.\n"
88 main (int argc, char *const *argv)
91 const char *configfile = "bacula-dir.conf";
92 const char *fileset_name = "Windows-Full-Set";
97 setlocale(LC_ALL, "");
98 bindtextdomain("bacula", LOCALEDIR);
101 while ((ch = getopt(argc, argv, "ac:d:f:?")) != -1) {
103 case 'a': /* print extended attributes *debug* */
107 case 'c': /* set debug level */
111 case 'd': /* set debug level */
112 if (*optarg == 't') {
113 dbg_timestamp = true;
115 debug_level = atoi(optarg);
116 if (debug_level <= 0) {
122 case 'f': /* exclude patterns */
123 fileset_name = optarg;
136 parse_config(configfile);
140 foreach_res(msg, R_MSGS)
145 jcr = new_jcr(sizeof(JCR), NULL);
146 jcr->fileset = (FILESET *)GetResWithName(R_FILESET, fileset_name);
148 if (jcr->fileset == NULL) {
149 fprintf(stderr, "%s: Fileset not found\n", fileset_name);
153 fprintf(stderr, "Valid FileSets:\n");
155 foreach_res(var, R_FILESET) {
156 fprintf(stderr, " %s\n", var->hdr.name);
162 ff = init_find_files();
164 copy_fileset(ff, jcr);
166 find_files(jcr, ff, print_file, NULL);
169 free_config_resources();
170 term_last_jobs_list();
172 /* Clean up fileset */
173 findFILESET *fileset = ff->fileset;
177 /* Delete FileSet Include lists */
178 for (i=0; i<fileset->include_list.size(); i++) {
179 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
180 for (j=0; j<incexe->opts_list.size(); j++) {
181 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
182 for (k=0; k<fo->regex.size(); k++) {
183 regfree((regex_t *)fo->regex.get(k));
186 fo->regexdir.destroy();
187 fo->regexfile.destroy();
189 fo->wilddir.destroy();
190 fo->wildfile.destroy();
191 fo->wildbase.destroy();
192 fo->fstype.destroy();
193 fo->drivetype.destroy();
201 incexe->opts_list.destroy();
202 incexe->name_list.destroy();
204 fileset->include_list.destroy();
206 /* Delete FileSet Exclude lists */
207 for (i=0; i<fileset->exclude_list.size(); i++) {
208 findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
209 for (j=0; j<incexe->opts_list.size(); j++) {
210 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
212 fo->regexdir.destroy();
213 fo->regexfile.destroy();
215 fo->wilddir.destroy();
216 fo->wildfile.destroy();
217 fo->wildbase.destroy();
218 fo->fstype.destroy();
219 fo->drivetype.destroy();
221 incexe->opts_list.destroy();
222 incexe->name_list.destroy();
224 fileset->exclude_list.destroy();
228 hard_links = term_find_files(ff);
232 "Max file length: %d\n"
233 "Max path length: %d\n"
234 "Files truncated: %d\n"
235 "Paths truncated: %d\n"
236 "Hard links : %d\n"),
237 num_files, max_file_len, max_path_len,
238 trunc_fname, trunc_path, hard_links);
247 static int print_file(FF_PKT *ff, void *pkt, bool top_level)
252 if (debug_level == 1) {
253 printf("%s\n", ff->fname);
254 } else if (debug_level > 1) {
255 printf("Lnka: %s -> %s\n", ff->fname, ff->link);
259 if (debug_level == 1) {
260 printf("%s\n", ff->fname);
261 } else if (debug_level > 1) {
262 printf("Empty: %s\n", ff->fname);
267 if (debug_level == 1) {
268 printf("%s\n", ff->fname);
269 } else if (debug_level > 1) {
270 printf(_("Reg: %s\n"), ff->fname);
275 if (debug_level == 1) {
276 printf("%s\n", ff->fname);
277 } else if (debug_level > 1) {
278 printf("Lnk: %s -> %s\n", ff->fname, ff->link);
290 char errmsg[100] = "";
291 if (ff->type == FT_NORECURSE) {
292 bstrncpy(errmsg, _("\t[will not descend: recursion turned off]"), sizeof(errmsg));
293 } else if (ff->type == FT_NOFSCHG) {
294 bstrncpy(errmsg, _("\t[will not descend: file system change not allowed]"), sizeof(errmsg));
295 } else if (ff->type == FT_INVALIDFS) {
296 bstrncpy(errmsg, _("\t[will not descend: disallowed file system]"), sizeof(errmsg));
297 } else if (ff->type == FT_INVALIDDT) {
298 bstrncpy(errmsg, _("\t[will not descend: disallowed drive type]"), sizeof(errmsg));
300 printf("%s%s%s\n", (debug_level > 1 ? "Dir: " : ""), ff->fname, errmsg);
302 ff->type = FT_DIREND;
306 if (debug_level == 1) {
307 printf("%s\n", ff->fname);
308 } else if (debug_level > 1) {
309 printf("Spec: %s\n", ff->fname);
314 printf(_("Err: Could not access %s: %s\n"), ff->fname, strerror(errno));
317 printf(_("Err: Could not follow ff->link %s: %s\n"), ff->fname, strerror(errno));
320 printf(_("Err: Could not stat %s: %s\n"), ff->fname, strerror(errno));
323 printf(_("Skip: File not saved. No change. %s\n"), ff->fname);
326 printf(_("Err: Attempt to backup archive. Not saved. %s\n"), ff->fname);
329 printf(_("Err: Could not open directory %s: %s\n"), ff->fname, strerror(errno));
332 printf(_("Err: Unknown file ff->type %d: %s\n"), ff->type, ff->fname);
337 encode_attribsEx(NULL, attr, ff);
339 printf("AttrEx=%s\n", attr);
341 // set_attribsEx(NULL, ff->fname, NULL, NULL, ff->type, attr);
346 static void count_files(FF_PKT *ar)
350 char file[MAXSTRING];
351 char spath[MAXSTRING];
355 /* Find path without the filename.
356 * I.e. everything after the last / is a "filename".
357 * OK, maybe it is a directory name, but we treat it like
358 * a filename. If we don't find a / then the whole name
359 * must be a path name (e.g. c:).
361 for (p=l=ar->fname; *p; p++) {
362 if (IsPathSeparator(*p)) {
363 l = p; /* set pos of last slash */
366 if (IsPathSeparator(*l)) { /* did we find a slash? */
367 l++; /* yes, point to filename */
368 } else { /* no, whole thing must be path name */
372 /* If filename doesn't exist (i.e. root directory), we
373 * simply create a blank name consisting of a single
374 * space. This makes handling zero length filenames
378 if (fnl > max_file_len) {
382 printf(_("===== Filename truncated to 255 chars: %s\n"), l);
387 strncpy(file, l, fnl); /* copy filename */
390 file[0] = ' '; /* blank filename */
395 if (pnl > max_path_len) {
399 printf(_("========== Path name truncated to 255 chars: %s\n"), ar->fname);
403 strncpy(spath, ar->fname, pnl);
408 printf(_("========== Path length is zero. File=%s\n"), ar->fname);
410 if (debug_level >= 10) {
411 printf(_("Path: %s\n"), spath);
412 printf(_("File: %s\n"), file);
417 bool python_set_prog(JCR*, char const*) { return false; }
419 static bool copy_fileset(FF_PKT *ff, JCR *jcr)
421 FILESET *jcr_fileset = jcr->fileset;
425 findFILESET *fileset;
426 findFOPTS *current_opts;
428 fileset = (findFILESET *)malloc(sizeof(findFILESET));
429 memset(fileset, 0, sizeof(findFILESET));
430 ff->fileset = fileset;
432 fileset->state = state_none;
433 fileset->include_list.init(1, true);
434 fileset->exclude_list.init(1, true);
438 num = jcr_fileset->num_includes;
440 num = jcr_fileset->num_excludes;
442 for (int i=0; i<num; i++) {
447 ie = jcr_fileset->include_items[i];
450 fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
451 memset(fileset->incexe, 0, sizeof(findINCEXE));
452 fileset->incexe->opts_list.init(1, true);
453 fileset->incexe->name_list.init(0, 0);
454 fileset->include_list.append(fileset->incexe);
456 ie = jcr_fileset->exclude_items[i];
459 fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
460 memset(fileset->incexe, 0, sizeof(findINCEXE));
461 fileset->incexe->opts_list.init(1, true);
462 fileset->incexe->name_list.init(0, 0);
463 fileset->exclude_list.append(fileset->incexe);
466 for (j=0; j<ie->num_opts; j++) {
467 FOPTS *fo = ie->opts_list[j];
469 current_opts = (findFOPTS *)malloc(sizeof(findFOPTS));
470 memset(current_opts, 0, sizeof(findFOPTS));
471 fileset->incexe->current_opts = current_opts;
472 fileset->incexe->opts_list.append(current_opts);
474 current_opts->regex.init(1, true);
475 current_opts->regexdir.init(1, true);
476 current_opts->regexfile.init(1, true);
477 current_opts->wild.init(1, true);
478 current_opts->wilddir.init(1, true);
479 current_opts->wildfile.init(1, true);
480 current_opts->wildbase.init(1, true);
481 current_opts->fstype.init(1, true);
482 current_opts->drivetype.init(1, true);
484 set_options(current_opts, fo->opts);
486 for (k=0; k<fo->regex.size(); k++) {
487 // bnet_fsend(fd, "R %s\n", fo->regex.get(k));
488 current_opts->regex.append(bstrdup((const char *)fo->regex.get(k)));
490 for (k=0; k<fo->regexdir.size(); k++) {
491 // bnet_fsend(fd, "RD %s\n", fo->regexdir.get(k));
492 current_opts->regexdir.append(bstrdup((const char *)fo->regexdir.get(k)));
494 for (k=0; k<fo->regexfile.size(); k++) {
495 // bnet_fsend(fd, "RF %s\n", fo->regexfile.get(k));
496 current_opts->regexfile.append(bstrdup((const char *)fo->regexfile.get(k)));
498 for (k=0; k<fo->wild.size(); k++) {
499 current_opts->wild.append(bstrdup((const char *)fo->wild.get(k)));
501 for (k=0; k<fo->wilddir.size(); k++) {
502 current_opts->wilddir.append(bstrdup((const char *)fo->wilddir.get(k)));
504 for (k=0; k<fo->wildfile.size(); k++) {
505 current_opts->wildfile.append(bstrdup((const char *)fo->wildfile.get(k)));
507 for (k=0; k<fo->wildbase.size(); k++) {
508 current_opts->wildbase.append(bstrdup((const char *)fo->wildbase.get(k)));
510 for (k=0; k<fo->fstype.size(); k++) {
511 current_opts->fstype.append(bstrdup((const char *)fo->fstype.get(k)));
513 for (k=0; k<fo->drivetype.size(); k++) {
514 current_opts->drivetype.append(bstrdup((const char *)fo->drivetype.get(k)));
517 current_opts->reader = bstrdup(fo->reader);
520 current_opts->writer = bstrdup(fo->writer);
524 for (j=0; j<ie->name_list.size(); j++) {
525 fileset->incexe->name_list.append(bstrdup((const char *)ie->name_list.get(j)));
529 if (!include) { /* If we just did excludes */
530 break; /* all done */
533 include = false; /* Now do excludes */
539 static void set_options(findFOPTS *fo, const char *opts)
544 for (p=opts; *p; p++) {
546 case 'a': /* alway replace */
547 case '0': /* no option */
550 fo->flags |= FO_EXCLUDE;
553 fo->flags |= FO_MULTIFS;
555 case 'h': /* no recursion */
556 fo->flags |= FO_NO_RECURSION;
558 case 'H': /* no hard link handling */
559 fo->flags |= FO_NO_HARDLINK;
562 fo->flags |= FO_IGNORECASE;
568 fo->flags |= FO_NOREPLACE;
570 case 'p': /* use portable data format */
571 fo->flags |= FO_PORTABLE;
573 case 'R': /* Resource forks and Finder Info */
574 fo->flags |= FO_HFSPLUS;
575 case 'r': /* read fifo */
576 fo->flags |= FO_READFIFO;
581 /* Old director did not specify SHA variant */
582 fo->flags |= FO_SHA1;
585 fo->flags |= FO_SHA1;
590 fo->flags |= FO_SHA256;
594 fo->flags |= FO_SHA512;
599 /* Automatically downgrade to SHA-1 if an unsupported
600 * SHA variant is specified */
601 fo->flags |= FO_SHA1;
607 fo->flags |= FO_SPARSE;
610 fo->flags |= FO_MTIMEONLY;
613 fo->flags |= FO_KEEPATIME;
618 case 'V': /* verify options */
619 /* Copy Verify Options */
620 for (j=0; *p && *p != ':'; p++) {
621 fo->VerifyOpts[j] = *p;
622 if (j < (int)sizeof(fo->VerifyOpts) - 1) {
626 fo->VerifyOpts[j] = 0;
629 fo->flags |= FO_IF_NEWER;
632 fo->flags |= FO_ENHANCEDWILD;
634 case 'Z': /* gzip compression */
635 fo->flags |= FO_GZIP;
636 fo->GZIP_level = *++p - '0';
637 Dmsg1(200, "Compression level=%d\n", fo->GZIP_level);
640 Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);