2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 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 Bacula® is a registered trademark of Kern Sibbald.
17 * Test program for find files
24 #include "dird/dird.h"
25 #include "findlib/find.h"
26 #include "lib/mntent_cache.h"
29 #if defined(HAVE_WIN32)
30 #define isatty(fd) (fd==0)
34 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
35 int generate_job_event(JCR *jcr, const char *event) { return 1; }
36 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value) { }
37 extern bool parse_dir_config(CONFIG *config, const char *configfile, int exit_code);
39 /* Global variables */
40 static int num_files = 0;
41 static int max_file_len = 0;
42 static int max_path_len = 0;
43 static int trunc_fname = 0;
44 static int trunc_path = 0;
46 static CONFIG *config;
50 static int print_file(JCR *jcr, FF_PKT *ff, bool);
51 static void count_files(FF_PKT *ff);
52 static bool copy_fileset(FF_PKT *ff, JCR *jcr);
53 static void set_options(findFOPTS *fo, const char *opts);
59 "Usage: testfind [-d debug_level] [-] [pattern1 ...]\n"
60 " -a print extended attributes (Win32 debug)\n"
61 " -d <nn> set debug level to <nn>\n"
62 " -dt print timestamp in debug output\n"
63 " -c specify config file containing FileSet resources\n"
64 " -f specify which FileSet to use\n"
65 " -? print this message.\n"
67 "Patterns are used for file inclusion -- normally directories.\n"
68 "Debug level >= 1 prints each file found.\n"
69 "Debug level >= 10 prints path/file for catalog.\n"
70 "Errors are always printed.\n"
71 "Files/paths truncated is the number of files/paths with len > 255.\n"
72 "Truncation is only in the catalog.\n"
80 main (int argc, char *const *argv)
83 const char *configfile = "bacula-dir.conf";
84 const char *fileset_name = "Windows-Full-Set";
89 setlocale(LC_ALL, "");
90 bindtextdomain("bacula", LOCALEDIR);
94 while ((ch = getopt(argc, argv, "ac:d:f:?")) != -1) {
96 case 'a': /* print extended attributes *debug* */
100 case 'c': /* set debug level */
104 case 'd': /* set debug level */
105 if (*optarg == 't') {
106 dbg_timestamp = true;
108 debug_level = atoi(optarg);
109 if (debug_level <= 0) {
115 case 'f': /* exclude patterns */
116 fileset_name = optarg;
129 config = new_config_parser();
130 parse_dir_config(config, configfile, M_ERROR_TERM);
134 foreach_res(msg, R_MSGS)
139 jcr = new_jcr(sizeof(JCR), NULL);
140 jcr->fileset = (FILESET *)GetResWithName(R_FILESET, fileset_name);
142 if (jcr->fileset == NULL) {
143 fprintf(stderr, "%s: Fileset not found\n", fileset_name);
147 fprintf(stderr, "Valid FileSets:\n");
149 foreach_res(var, R_FILESET) {
150 fprintf(stderr, " %s\n", var->hdr.name);
156 ff = init_find_files();
158 copy_fileset(ff, jcr);
160 find_files(jcr, ff, print_file, NULL);
164 config->free_resources();
169 term_last_jobs_list();
171 /* Clean up fileset */
172 findFILESET *fileset = ff->fileset;
176 /* Delete FileSet Include lists */
177 for (i=0; i<fileset->include_list.size(); i++) {
178 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
179 for (j=0; j<incexe->opts_list.size(); j++) {
180 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
181 for (k=0; k<fo->regex.size(); k++) {
182 regfree((regex_t *)fo->regex.get(k));
185 fo->regexdir.destroy();
186 fo->regexfile.destroy();
188 fo->wilddir.destroy();
189 fo->wildfile.destroy();
190 fo->wildbase.destroy();
191 fo->fstype.destroy();
192 fo->drivetype.destroy();
194 incexe->opts_list.destroy();
195 incexe->name_list.destroy();
197 fileset->include_list.destroy();
199 /* Delete FileSet Exclude lists */
200 for (i=0; i<fileset->exclude_list.size(); i++) {
201 findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
202 for (j=0; j<incexe->opts_list.size(); j++) {
203 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
205 fo->regexdir.destroy();
206 fo->regexfile.destroy();
208 fo->wilddir.destroy();
209 fo->wildfile.destroy();
210 fo->wildbase.destroy();
211 fo->fstype.destroy();
212 fo->drivetype.destroy();
214 incexe->opts_list.destroy();
215 incexe->name_list.destroy();
217 fileset->exclude_list.destroy();
221 hard_links = term_find_files(ff);
225 "Max file length: %d\n"
226 "Max path length: %d\n"
227 "Files truncated: %d\n"
228 "Paths truncated: %d\n"
229 "Hard links : %d\n"),
230 num_files, max_file_len, max_path_len,
231 trunc_fname, trunc_path, hard_links);
233 flush_mntent_cache();
243 static int print_file(JCR *jcr, FF_PKT *ff, bool top_level)
248 if (debug_level == 1) {
249 printf("%s\n", ff->fname);
250 } else if (debug_level > 1) {
251 printf("Lnka: %s -> %s\n", ff->fname, ff->link);
255 if (debug_level == 1) {
256 printf("%s\n", ff->fname);
257 } else if (debug_level > 1) {
258 printf("Empty: %s\n", ff->fname);
263 if (debug_level == 1) {
264 printf("%s\n", ff->fname);
265 } else if (debug_level > 1) {
266 printf(_("Reg: %s\n"), ff->fname);
271 if (debug_level == 1) {
272 printf("%s\n", ff->fname);
273 } else if (debug_level > 1) {
274 printf("Lnk: %s -> %s\n", ff->fname, ff->link);
286 char errmsg[100] = "";
287 if (ff->type == FT_NORECURSE) {
288 bstrncpy(errmsg, _("\t[will not descend: recursion turned off]"), sizeof(errmsg));
289 } else if (ff->type == FT_NOFSCHG) {
290 bstrncpy(errmsg, _("\t[will not descend: file system change not allowed]"), sizeof(errmsg));
291 } else if (ff->type == FT_INVALIDFS) {
292 bstrncpy(errmsg, _("\t[will not descend: disallowed file system]"), sizeof(errmsg));
293 } else if (ff->type == FT_INVALIDDT) {
294 bstrncpy(errmsg, _("\t[will not descend: disallowed drive type]"), sizeof(errmsg));
296 printf("%s%s%s\n", (debug_level > 1 ? "Dir: " : ""), ff->fname, errmsg);
298 ff->type = FT_DIREND;
302 if (debug_level == 1) {
303 printf("%s\n", ff->fname);
304 } else if (debug_level > 1) {
305 printf("Spec: %s\n", ff->fname);
310 printf(_("Err: Could not access %s: %s\n"), ff->fname, strerror(errno));
313 printf(_("Err: Could not follow ff->link %s: %s\n"), ff->fname, strerror(errno));
316 printf(_("Err: Could not stat %s: %s\n"), ff->fname, strerror(errno));
319 printf(_("Skip: File not saved. No change. %s\n"), ff->fname);
322 printf(_("Err: Attempt to backup archive. Not saved. %s\n"), ff->fname);
325 printf(_("Err: Could not open directory %s: %s\n"), ff->fname, strerror(errno));
328 printf(_("Err: Unknown file ff->type %d: %s\n"), ff->type, ff->fname);
333 encode_attribsEx(NULL, attr, ff);
335 printf("AttrEx=%s\n", attr);
337 // set_attribsEx(NULL, ff->fname, NULL, NULL, ff->type, attr);
342 static void count_files(FF_PKT *ar)
346 char file[MAXSTRING];
347 char spath[MAXSTRING];
351 /* Find path without the filename.
352 * I.e. everything after the last / is a "filename".
353 * OK, maybe it is a directory name, but we treat it like
354 * a filename. If we don't find a / then the whole name
355 * must be a path name (e.g. c:).
357 for (p=l=ar->fname; *p; p++) {
358 if (IsPathSeparator(*p)) {
359 l = p; /* set pos of last slash */
362 if (IsPathSeparator(*l)) { /* did we find a slash? */
363 l++; /* yes, point to filename */
364 } else { /* no, whole thing must be path name */
368 /* If filename doesn't exist (i.e. root directory), we
369 * simply create a blank name consisting of a single
370 * space. This makes handling zero length filenames
374 if (fnl > max_file_len) {
378 printf(_("===== Filename truncated to 255 chars: %s\n"), l);
383 strncpy(file, l, fnl); /* copy filename */
386 file[0] = ' '; /* blank filename */
391 if (pnl > max_path_len) {
395 printf(_("========== Path name truncated to 255 chars: %s\n"), ar->fname);
399 strncpy(spath, ar->fname, pnl);
404 printf(_("========== Path length is zero. File=%s\n"), ar->fname);
406 if (debug_level >= 10) {
407 printf(_("Path: %s\n"), spath);
408 printf(_("File: %s\n"), file);
413 bool python_set_prog(JCR*, char const*) { return false; }
415 static bool copy_fileset(FF_PKT *ff, JCR *jcr)
417 FILESET *jcr_fileset = jcr->fileset;
421 findFILESET *fileset;
422 findFOPTS *current_opts;
424 fileset = (findFILESET *)malloc(sizeof(findFILESET));
425 memset(fileset, 0, sizeof(findFILESET));
426 ff->fileset = fileset;
428 fileset->state = state_none;
429 fileset->include_list.init(1, true);
430 fileset->exclude_list.init(1, true);
434 num = jcr_fileset->num_includes;
436 num = jcr_fileset->num_excludes;
438 for (int i=0; i<num; i++) {
443 ie = jcr_fileset->include_items[i];
446 fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
447 memset(fileset->incexe, 0, sizeof(findINCEXE));
448 fileset->incexe->opts_list.init(1, true);
449 fileset->incexe->name_list.init(0, 0);
450 fileset->include_list.append(fileset->incexe);
452 ie = jcr_fileset->exclude_items[i];
455 fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
456 memset(fileset->incexe, 0, sizeof(findINCEXE));
457 fileset->incexe->opts_list.init(1, true);
458 fileset->incexe->name_list.init(0, 0);
459 fileset->exclude_list.append(fileset->incexe);
462 for (j=0; j<ie->num_opts; j++) {
463 FOPTS *fo = ie->opts_list[j];
465 current_opts = (findFOPTS *)malloc(sizeof(findFOPTS));
466 memset(current_opts, 0, sizeof(findFOPTS));
467 fileset->incexe->current_opts = current_opts;
468 fileset->incexe->opts_list.append(current_opts);
470 current_opts->regex.init(1, true);
471 current_opts->regexdir.init(1, true);
472 current_opts->regexfile.init(1, true);
473 current_opts->wild.init(1, true);
474 current_opts->wilddir.init(1, true);
475 current_opts->wildfile.init(1, true);
476 current_opts->wildbase.init(1, true);
477 current_opts->fstype.init(1, true);
478 current_opts->drivetype.init(1, true);
480 set_options(current_opts, fo->opts);
482 for (k=0; k<fo->regex.size(); k++) {
483 // fd->fsend("R %s\n", fo->regex.get(k));
484 current_opts->regex.append(bstrdup((const char *)fo->regex.get(k)));
486 for (k=0; k<fo->regexdir.size(); k++) {
487 // fd->fsend("RD %s\n", fo->regexdir.get(k));
488 current_opts->regexdir.append(bstrdup((const char *)fo->regexdir.get(k)));
490 for (k=0; k<fo->regexfile.size(); k++) {
491 // fd->fsend("RF %s\n", fo->regexfile.get(k));
492 current_opts->regexfile.append(bstrdup((const char *)fo->regexfile.get(k)));
494 for (k=0; k<fo->wild.size(); k++) {
495 current_opts->wild.append(bstrdup((const char *)fo->wild.get(k)));
497 for (k=0; k<fo->wilddir.size(); k++) {
498 current_opts->wilddir.append(bstrdup((const char *)fo->wilddir.get(k)));
500 for (k=0; k<fo->wildfile.size(); k++) {
501 current_opts->wildfile.append(bstrdup((const char *)fo->wildfile.get(k)));
503 for (k=0; k<fo->wildbase.size(); k++) {
504 current_opts->wildbase.append(bstrdup((const char *)fo->wildbase.get(k)));
506 for (k=0; k<fo->fstype.size(); k++) {
507 current_opts->fstype.append(bstrdup((const char *)fo->fstype.get(k)));
509 for (k=0; k<fo->drivetype.size(); k++) {
510 current_opts->drivetype.append(bstrdup((const char *)fo->drivetype.get(k)));
514 for (j=0; j<ie->name_list.size(); j++) {
515 fileset->incexe->name_list.append(bstrdup((const char *)ie->name_list.get(j)));
519 if (!include) { /* If we just did excludes */
520 break; /* all done */
523 include = false; /* Now do excludes */
529 static void set_options(findFOPTS *fo, const char *opts)
534 for (p=opts; *p; p++) {
536 case 'a': /* alway replace */
537 case '0': /* no option */
540 fo->flags |= FO_EXCLUDE;
543 fo->flags |= FO_MULTIFS;
545 case 'h': /* no recursion */
546 fo->flags |= FO_NO_RECURSION;
548 case 'H': /* no hard link handling */
549 fo->flags |= FO_NO_HARDLINK;
552 fo->flags |= FO_IGNORECASE;
558 fo->flags |= FO_NOREPLACE;
560 case 'p': /* use portable data format */
561 fo->flags |= FO_PORTABLE;
563 case 'R': /* Resource forks and Finder Info */
564 fo->flags |= FO_HFSPLUS;
565 case 'r': /* read fifo */
566 fo->flags |= FO_READFIFO;
571 /* Old director did not specify SHA variant */
572 fo->flags |= FO_SHA1;
575 fo->flags |= FO_SHA1;
580 fo->flags |= FO_SHA256;
584 fo->flags |= FO_SHA512;
589 /* Automatically downgrade to SHA-1 if an unsupported
590 * SHA variant is specified */
591 fo->flags |= FO_SHA1;
597 fo->flags |= FO_SPARSE;
600 fo->flags |= FO_MTIMEONLY;
603 fo->flags |= FO_KEEPATIME;
608 case 'V': /* verify options */
609 /* Copy Verify Options */
610 for (j=0; *p && *p != ':'; p++) {
611 fo->VerifyOpts[j] = *p;
612 if (j < (int)sizeof(fo->VerifyOpts) - 1) {
616 fo->VerifyOpts[j] = 0;
619 fo->flags |= FO_IF_NEWER;
622 fo->flags |= FO_ENHANCEDWILD;
624 case 'Z': /* compression */
626 if (*p >= '0' && *p <= '9') {
627 fo->flags |= FO_COMPRESS;
628 fo->Compress_algo = COMPRESS_GZIP;
629 fo->Compress_level = *p - '0';
631 else if (*p == 'o') {
632 fo->flags |= FO_COMPRESS;
633 fo->Compress_algo = COMPRESS_LZO1X;
634 fo->Compress_level = 1; /* not used with LZO */
636 Dmsg2(200, "Compression alg=%d level=%d\n", fo->Compress_algo, fo->Compress_level);
639 fo->flags |= FO_XATTR;
642 Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);