2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * Test program for find files
36 #include "dird/dird.h"
37 #include "findlib/find.h"
38 #include "lib/mntent_cache.h"
41 #if defined(HAVE_WIN32)
42 #define isatty(fd) (fd==0)
46 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
47 int generate_job_event(JCR *jcr, const char *event) { return 1; }
48 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value) { }
49 extern bool parse_dir_config(CONFIG *config, const char *configfile, int exit_code);
51 /* Global variables */
52 static int num_files = 0;
53 static int max_file_len = 0;
54 static int max_path_len = 0;
55 static int trunc_fname = 0;
56 static int trunc_path = 0;
58 static CONFIG *config;
62 static int print_file(JCR *jcr, FF_PKT *ff, bool);
63 static void count_files(FF_PKT *ff);
64 static bool copy_fileset(FF_PKT *ff, JCR *jcr);
65 static void set_options(findFOPTS *fo, const char *opts);
71 "Usage: testfind [-d debug_level] [-] [pattern1 ...]\n"
72 " -a print extended attributes (Win32 debug)\n"
73 " -d <nn> set debug level to <nn>\n"
74 " -dt print timestamp in debug output\n"
75 " -c specify config file containing FileSet resources\n"
76 " -f specify which FileSet to use\n"
77 " -? print this message.\n"
79 "Patterns are used for file inclusion -- normally directories.\n"
80 "Debug level >= 1 prints each file found.\n"
81 "Debug level >= 10 prints path/file for catalog.\n"
82 "Errors are always printed.\n"
83 "Files/paths truncated is the number of files/paths with len > 255.\n"
84 "Truncation is only in the catalog.\n"
92 main (int argc, char *const *argv)
95 const char *configfile = "bacula-dir.conf";
96 const char *fileset_name = "Windows-Full-Set";
101 setlocale(LC_ALL, "");
102 bindtextdomain("bacula", LOCALEDIR);
103 textdomain("bacula");
106 while ((ch = getopt(argc, argv, "ac:d:f:?")) != -1) {
108 case 'a': /* print extended attributes *debug* */
112 case 'c': /* set debug level */
116 case 'd': /* set debug level */
117 if (*optarg == 't') {
118 dbg_timestamp = true;
120 debug_level = atoi(optarg);
121 if (debug_level <= 0) {
127 case 'f': /* exclude patterns */
128 fileset_name = optarg;
141 config = new_config_parser();
142 parse_dir_config(config, configfile, M_ERROR_TERM);
146 foreach_res(msg, R_MSGS)
151 jcr = new_jcr(sizeof(JCR), NULL);
152 jcr->fileset = (FILESET *)GetResWithName(R_FILESET, fileset_name);
154 if (jcr->fileset == NULL) {
155 fprintf(stderr, "%s: Fileset not found\n", fileset_name);
159 fprintf(stderr, "Valid FileSets:\n");
161 foreach_res(var, R_FILESET) {
162 fprintf(stderr, " %s\n", var->hdr.name);
168 ff = init_find_files();
170 copy_fileset(ff, jcr);
172 find_files(jcr, ff, print_file, NULL);
176 config->free_resources();
181 term_last_jobs_list();
183 /* Clean up fileset */
184 findFILESET *fileset = ff->fileset;
188 /* Delete FileSet Include lists */
189 for (i=0; i<fileset->include_list.size(); i++) {
190 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
191 for (j=0; j<incexe->opts_list.size(); j++) {
192 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
193 for (k=0; k<fo->regex.size(); k++) {
194 regfree((regex_t *)fo->regex.get(k));
197 fo->regexdir.destroy();
198 fo->regexfile.destroy();
200 fo->wilddir.destroy();
201 fo->wildfile.destroy();
202 fo->wildbase.destroy();
203 fo->fstype.destroy();
204 fo->drivetype.destroy();
206 incexe->opts_list.destroy();
207 incexe->name_list.destroy();
209 fileset->include_list.destroy();
211 /* Delete FileSet Exclude lists */
212 for (i=0; i<fileset->exclude_list.size(); i++) {
213 findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
214 for (j=0; j<incexe->opts_list.size(); j++) {
215 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
217 fo->regexdir.destroy();
218 fo->regexfile.destroy();
220 fo->wilddir.destroy();
221 fo->wildfile.destroy();
222 fo->wildbase.destroy();
223 fo->fstype.destroy();
224 fo->drivetype.destroy();
226 incexe->opts_list.destroy();
227 incexe->name_list.destroy();
229 fileset->exclude_list.destroy();
233 hard_links = term_find_files(ff);
237 "Max file length: %d\n"
238 "Max path length: %d\n"
239 "Files truncated: %d\n"
240 "Paths truncated: %d\n"
241 "Hard links : %d\n"),
242 num_files, max_file_len, max_path_len,
243 trunc_fname, trunc_path, hard_links);
245 flush_mntent_cache();
255 static int print_file(JCR *jcr, FF_PKT *ff, bool top_level)
260 if (debug_level == 1) {
261 printf("%s\n", ff->fname);
262 } else if (debug_level > 1) {
263 printf("Lnka: %s -> %s\n", ff->fname, ff->link);
267 if (debug_level == 1) {
268 printf("%s\n", ff->fname);
269 } else if (debug_level > 1) {
270 printf("Empty: %s\n", ff->fname);
275 if (debug_level == 1) {
276 printf("%s\n", ff->fname);
277 } else if (debug_level > 1) {
278 printf(_("Reg: %s\n"), ff->fname);
283 if (debug_level == 1) {
284 printf("%s\n", ff->fname);
285 } else if (debug_level > 1) {
286 printf("Lnk: %s -> %s\n", ff->fname, ff->link);
298 char errmsg[100] = "";
299 if (ff->type == FT_NORECURSE) {
300 bstrncpy(errmsg, _("\t[will not descend: recursion turned off]"), sizeof(errmsg));
301 } else if (ff->type == FT_NOFSCHG) {
302 bstrncpy(errmsg, _("\t[will not descend: file system change not allowed]"), sizeof(errmsg));
303 } else if (ff->type == FT_INVALIDFS) {
304 bstrncpy(errmsg, _("\t[will not descend: disallowed file system]"), sizeof(errmsg));
305 } else if (ff->type == FT_INVALIDDT) {
306 bstrncpy(errmsg, _("\t[will not descend: disallowed drive type]"), sizeof(errmsg));
308 printf("%s%s%s\n", (debug_level > 1 ? "Dir: " : ""), ff->fname, errmsg);
310 ff->type = FT_DIREND;
314 if (debug_level == 1) {
315 printf("%s\n", ff->fname);
316 } else if (debug_level > 1) {
317 printf("Spec: %s\n", ff->fname);
322 printf(_("Err: Could not access %s: %s\n"), ff->fname, strerror(errno));
325 printf(_("Err: Could not follow ff->link %s: %s\n"), ff->fname, strerror(errno));
328 printf(_("Err: Could not stat %s: %s\n"), ff->fname, strerror(errno));
331 printf(_("Skip: File not saved. No change. %s\n"), ff->fname);
334 printf(_("Err: Attempt to backup archive. Not saved. %s\n"), ff->fname);
337 printf(_("Err: Could not open directory %s: %s\n"), ff->fname, strerror(errno));
340 printf(_("Err: Unknown file ff->type %d: %s\n"), ff->type, ff->fname);
345 encode_attribsEx(NULL, attr, ff);
347 printf("AttrEx=%s\n", attr);
349 // set_attribsEx(NULL, ff->fname, NULL, NULL, ff->type, attr);
354 static void count_files(FF_PKT *ar)
358 char file[MAXSTRING];
359 char spath[MAXSTRING];
363 /* Find path without the filename.
364 * I.e. everything after the last / is a "filename".
365 * OK, maybe it is a directory name, but we treat it like
366 * a filename. If we don't find a / then the whole name
367 * must be a path name (e.g. c:).
369 for (p=l=ar->fname; *p; p++) {
370 if (IsPathSeparator(*p)) {
371 l = p; /* set pos of last slash */
374 if (IsPathSeparator(*l)) { /* did we find a slash? */
375 l++; /* yes, point to filename */
376 } else { /* no, whole thing must be path name */
380 /* If filename doesn't exist (i.e. root directory), we
381 * simply create a blank name consisting of a single
382 * space. This makes handling zero length filenames
386 if (fnl > max_file_len) {
390 printf(_("===== Filename truncated to 255 chars: %s\n"), l);
395 strncpy(file, l, fnl); /* copy filename */
398 file[0] = ' '; /* blank filename */
403 if (pnl > max_path_len) {
407 printf(_("========== Path name truncated to 255 chars: %s\n"), ar->fname);
411 strncpy(spath, ar->fname, pnl);
416 printf(_("========== Path length is zero. File=%s\n"), ar->fname);
418 if (debug_level >= 10) {
419 printf(_("Path: %s\n"), spath);
420 printf(_("File: %s\n"), file);
425 bool python_set_prog(JCR*, char const*) { return false; }
427 static bool copy_fileset(FF_PKT *ff, JCR *jcr)
429 FILESET *jcr_fileset = jcr->fileset;
433 findFILESET *fileset;
434 findFOPTS *current_opts;
436 fileset = (findFILESET *)malloc(sizeof(findFILESET));
437 memset(fileset, 0, sizeof(findFILESET));
438 ff->fileset = fileset;
440 fileset->state = state_none;
441 fileset->include_list.init(1, true);
442 fileset->exclude_list.init(1, true);
446 num = jcr_fileset->num_includes;
448 num = jcr_fileset->num_excludes;
450 for (int i=0; i<num; i++) {
455 ie = jcr_fileset->include_items[i];
458 fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
459 memset(fileset->incexe, 0, sizeof(findINCEXE));
460 fileset->incexe->opts_list.init(1, true);
461 fileset->incexe->name_list.init(0, 0);
462 fileset->include_list.append(fileset->incexe);
464 ie = jcr_fileset->exclude_items[i];
467 fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
468 memset(fileset->incexe, 0, sizeof(findINCEXE));
469 fileset->incexe->opts_list.init(1, true);
470 fileset->incexe->name_list.init(0, 0);
471 fileset->exclude_list.append(fileset->incexe);
474 for (j=0; j<ie->num_opts; j++) {
475 FOPTS *fo = ie->opts_list[j];
477 current_opts = (findFOPTS *)malloc(sizeof(findFOPTS));
478 memset(current_opts, 0, sizeof(findFOPTS));
479 fileset->incexe->current_opts = current_opts;
480 fileset->incexe->opts_list.append(current_opts);
482 current_opts->regex.init(1, true);
483 current_opts->regexdir.init(1, true);
484 current_opts->regexfile.init(1, true);
485 current_opts->wild.init(1, true);
486 current_opts->wilddir.init(1, true);
487 current_opts->wildfile.init(1, true);
488 current_opts->wildbase.init(1, true);
489 current_opts->fstype.init(1, true);
490 current_opts->drivetype.init(1, true);
492 set_options(current_opts, fo->opts);
494 for (k=0; k<fo->regex.size(); k++) {
495 // fd->fsend("R %s\n", fo->regex.get(k));
496 current_opts->regex.append(bstrdup((const char *)fo->regex.get(k)));
498 for (k=0; k<fo->regexdir.size(); k++) {
499 // fd->fsend("RD %s\n", fo->regexdir.get(k));
500 current_opts->regexdir.append(bstrdup((const char *)fo->regexdir.get(k)));
502 for (k=0; k<fo->regexfile.size(); k++) {
503 // fd->fsend("RF %s\n", fo->regexfile.get(k));
504 current_opts->regexfile.append(bstrdup((const char *)fo->regexfile.get(k)));
506 for (k=0; k<fo->wild.size(); k++) {
507 current_opts->wild.append(bstrdup((const char *)fo->wild.get(k)));
509 for (k=0; k<fo->wilddir.size(); k++) {
510 current_opts->wilddir.append(bstrdup((const char *)fo->wilddir.get(k)));
512 for (k=0; k<fo->wildfile.size(); k++) {
513 current_opts->wildfile.append(bstrdup((const char *)fo->wildfile.get(k)));
515 for (k=0; k<fo->wildbase.size(); k++) {
516 current_opts->wildbase.append(bstrdup((const char *)fo->wildbase.get(k)));
518 for (k=0; k<fo->fstype.size(); k++) {
519 current_opts->fstype.append(bstrdup((const char *)fo->fstype.get(k)));
521 for (k=0; k<fo->drivetype.size(); k++) {
522 current_opts->drivetype.append(bstrdup((const char *)fo->drivetype.get(k)));
526 for (j=0; j<ie->name_list.size(); j++) {
527 fileset->incexe->name_list.append(bstrdup((const char *)ie->name_list.get(j)));
531 if (!include) { /* If we just did excludes */
532 break; /* all done */
535 include = false; /* Now do excludes */
541 static void set_options(findFOPTS *fo, const char *opts)
546 for (p=opts; *p; p++) {
548 case 'a': /* alway replace */
549 case '0': /* no option */
552 fo->flags |= FO_EXCLUDE;
555 fo->flags |= FO_MULTIFS;
557 case 'h': /* no recursion */
558 fo->flags |= FO_NO_RECURSION;
560 case 'H': /* no hard link handling */
561 fo->flags |= FO_NO_HARDLINK;
564 fo->flags |= FO_IGNORECASE;
570 fo->flags |= FO_NOREPLACE;
572 case 'p': /* use portable data format */
573 fo->flags |= FO_PORTABLE;
575 case 'R': /* Resource forks and Finder Info */
576 fo->flags |= FO_HFSPLUS;
577 case 'r': /* read fifo */
578 fo->flags |= FO_READFIFO;
583 /* Old director did not specify SHA variant */
584 fo->flags |= FO_SHA1;
587 fo->flags |= FO_SHA1;
592 fo->flags |= FO_SHA256;
596 fo->flags |= FO_SHA512;
601 /* Automatically downgrade to SHA-1 if an unsupported
602 * SHA variant is specified */
603 fo->flags |= FO_SHA1;
609 fo->flags |= FO_SPARSE;
612 fo->flags |= FO_MTIMEONLY;
615 fo->flags |= FO_KEEPATIME;
620 case 'V': /* verify options */
621 /* Copy Verify Options */
622 for (j=0; *p && *p != ':'; p++) {
623 fo->VerifyOpts[j] = *p;
624 if (j < (int)sizeof(fo->VerifyOpts) - 1) {
628 fo->VerifyOpts[j] = 0;
631 fo->flags |= FO_IF_NEWER;
634 fo->flags |= FO_ENHANCEDWILD;
636 case 'Z': /* compression */
638 if (*p >= '0' && *p <= '9') {
639 fo->flags |= FO_COMPRESS;
640 fo->Compress_algo = COMPRESS_GZIP;
641 fo->Compress_level = *p - '0';
643 else if (*p == 'o') {
644 fo->flags |= FO_COMPRESS;
645 fo->Compress_algo = COMPRESS_LZO1X;
646 fo->Compress_level = 1; /* not used with LZO */
648 Dmsg2(200, "Compression alg=%d level=%d\n", fo->Compress_algo, fo->Compress_level);
651 fo->flags |= FO_XATTR;
654 Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);