2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * Test program for find files
27 #include "dird/dird.h"
28 #include "findlib/find.h"
31 #if defined(HAVE_WIN32)
32 #define isatty(fd) (fd==0)
36 int generate_job_event(JCR *jcr, const char *event) { return 1; }
37 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value) { }
38 extern bool parse_dir_config(CONFIG *config, const char *configfile, int exit_code);
40 /* Global variables */
41 static int num_files = 0;
42 static int max_file_len = 0;
43 static int max_path_len = 0;
44 static int trunc_fname = 0;
45 static int trunc_path = 0;
47 static CONFIG *config;
51 static int print_file(JCR *jcr, FF_PKT *ff, bool);
52 static void count_files(FF_PKT *ff);
53 static bool copy_fileset(FF_PKT *ff, JCR *jcr);
54 static void set_options(findFOPTS *fo, const char *opts);
60 "Usage: testfind [-d debug_level] [-] [pattern1 ...]\n"
61 " -a print extended attributes (Win32 debug)\n"
62 " -d <nn> set debug level to <nn>\n"
63 " -dt print timestamp in debug output\n"
64 " -c specify config file containing FileSet resources\n"
65 " -f specify which FileSet to use\n"
66 " -? print this message.\n"
68 "Patterns are used for file inclusion -- normally directories.\n"
69 "Debug level >= 1 prints each file found.\n"
70 "Debug level >= 10 prints path/file for catalog.\n"
71 "Errors are always printed.\n"
72 "Files/paths truncated is the number of files/paths with len > 255.\n"
73 "Truncation is only in the catalog.\n"
81 main (int argc, char *const *argv)
84 const char *configfile = "bacula-dir.conf";
85 const char *fileset_name = "Windows-Full-Set";
90 setlocale(LC_ALL, "");
91 bindtextdomain("bacula", LOCALEDIR);
95 while ((ch = getopt(argc, argv, "ac:d:f:?")) != -1) {
97 case 'a': /* print extended attributes *debug* */
101 case 'c': /* set debug level */
105 case 'd': /* set debug level */
106 if (*optarg == 't') {
107 dbg_timestamp = true;
109 debug_level = atoi(optarg);
110 if (debug_level <= 0) {
116 case 'f': /* exclude patterns */
117 fileset_name = optarg;
130 config = new_config_parser();
131 parse_dir_config(config, configfile, M_ERROR_TERM);
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);
165 config->free_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();
195 incexe->opts_list.destroy();
196 incexe->name_list.destroy();
198 fileset->include_list.destroy();
200 /* Delete FileSet Exclude lists */
201 for (i=0; i<fileset->exclude_list.size(); i++) {
202 findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
203 for (j=0; j<incexe->opts_list.size(); j++) {
204 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
206 fo->regexdir.destroy();
207 fo->regexfile.destroy();
209 fo->wilddir.destroy();
210 fo->wildfile.destroy();
211 fo->wildbase.destroy();
212 fo->fstype.destroy();
213 fo->drivetype.destroy();
215 incexe->opts_list.destroy();
216 incexe->name_list.destroy();
218 fileset->exclude_list.destroy();
222 hard_links = term_find_files(ff);
226 "Max file length: %d\n"
227 "Max path length: %d\n"
228 "Files truncated: %d\n"
229 "Paths truncated: %d\n"
230 "Hard links : %d\n"),
231 num_files, max_file_len, max_path_len,
232 trunc_fname, trunc_path, hard_links);
242 static int print_file(JCR *jcr, FF_PKT *ff, 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 // fd->fsend("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 // fd->fsend("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 // fd->fsend("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)));
513 for (j=0; j<ie->name_list.size(); j++) {
514 fileset->incexe->name_list.append(bstrdup((const char *)ie->name_list.get(j)));
518 if (!include) { /* If we just did excludes */
519 break; /* all done */
522 include = false; /* Now do excludes */
528 static void set_options(findFOPTS *fo, const char *opts)
533 for (p=opts; *p; p++) {
535 case 'a': /* alway replace */
536 case '0': /* no option */
539 fo->flags |= FO_EXCLUDE;
542 fo->flags |= FO_MULTIFS;
544 case 'h': /* no recursion */
545 fo->flags |= FO_NO_RECURSION;
547 case 'H': /* no hard link handling */
548 fo->flags |= FO_NO_HARDLINK;
551 fo->flags |= FO_IGNORECASE;
557 fo->flags |= FO_NOREPLACE;
559 case 'p': /* use portable data format */
560 fo->flags |= FO_PORTABLE;
562 case 'R': /* Resource forks and Finder Info */
563 fo->flags |= FO_HFSPLUS;
564 case 'r': /* read fifo */
565 fo->flags |= FO_READFIFO;
570 /* Old director did not specify SHA variant */
571 fo->flags |= FO_SHA1;
574 fo->flags |= FO_SHA1;
579 fo->flags |= FO_SHA256;
583 fo->flags |= FO_SHA512;
588 /* Automatically downgrade to SHA-1 if an unsupported
589 * SHA variant is specified */
590 fo->flags |= FO_SHA1;
596 fo->flags |= FO_SPARSE;
599 fo->flags |= FO_MTIMEONLY;
602 fo->flags |= FO_KEEPATIME;
607 case 'V': /* verify options */
608 /* Copy Verify Options */
609 for (j=0; *p && *p != ':'; p++) {
610 fo->VerifyOpts[j] = *p;
611 if (j < (int)sizeof(fo->VerifyOpts) - 1) {
615 fo->VerifyOpts[j] = 0;
618 fo->flags |= FO_IF_NEWER;
621 fo->flags |= FO_ENHANCEDWILD;
623 case 'Z': /* compression */
625 if (*p >= '0' && *p <= '9') {
626 fo->flags |= FO_COMPRESS;
627 fo->Compress_algo = COMPRESS_GZIP;
628 fo->Compress_level = *p - '0';
630 else if (*p == 'o') {
631 fo->flags |= FO_COMPRESS;
632 fo->Compress_algo = COMPRESS_LZO1X;
633 fo->Compress_level = 1; /* not used with LZO */
635 Dmsg2(200, "Compression alg=%d level=%d\n", fo->Compress_algo, fo->Compress_level);
638 fo->flags |= FO_XATTR;
641 Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);