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"
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; }
47 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value) { }
48 extern bool parse_dir_config(CONFIG *config, const char *configfile, int exit_code);
50 /* Global variables */
51 static int num_files = 0;
52 static int max_file_len = 0;
53 static int max_path_len = 0;
54 static int trunc_fname = 0;
55 static int trunc_path = 0;
57 static CONFIG *config;
61 static int print_file(JCR *jcr, FF_PKT *ff, bool);
62 static void count_files(FF_PKT *ff);
63 static bool copy_fileset(FF_PKT *ff, JCR *jcr);
64 static void set_options(findFOPTS *fo, const char *opts);
70 "Usage: testfind [-d debug_level] [-] [pattern1 ...]\n"
71 " -a print extended attributes (Win32 debug)\n"
72 " -d <nn> set debug level to <nn>\n"
73 " -dt print timestamp in debug output\n"
74 " -c specify config file containing FileSet resources\n"
75 " -f specify which FileSet to use\n"
76 " -? print this message.\n"
78 "Patterns are used for file inclusion -- normally directories.\n"
79 "Debug level >= 1 prints each file found.\n"
80 "Debug level >= 10 prints path/file for catalog.\n"
81 "Errors are always printed.\n"
82 "Files/paths truncated is the number of files/paths with len > 255.\n"
83 "Truncation is only in the catalog.\n"
91 main (int argc, char *const *argv)
94 const char *configfile = "bacula-dir.conf";
95 const char *fileset_name = "Windows-Full-Set";
100 setlocale(LC_ALL, "");
101 bindtextdomain("bacula", LOCALEDIR);
102 textdomain("bacula");
105 while ((ch = getopt(argc, argv, "ac:d:f:?")) != -1) {
107 case 'a': /* print extended attributes *debug* */
111 case 'c': /* set debug level */
115 case 'd': /* set debug level */
116 if (*optarg == 't') {
117 dbg_timestamp = true;
119 debug_level = atoi(optarg);
120 if (debug_level <= 0) {
126 case 'f': /* exclude patterns */
127 fileset_name = optarg;
140 config = new_config_parser();
141 parse_dir_config(config, configfile, M_ERROR_TERM);
145 foreach_res(msg, R_MSGS)
150 jcr = new_jcr(sizeof(JCR), NULL);
151 jcr->fileset = (FILESET *)GetResWithName(R_FILESET, fileset_name);
153 if (jcr->fileset == NULL) {
154 fprintf(stderr, "%s: Fileset not found\n", fileset_name);
158 fprintf(stderr, "Valid FileSets:\n");
160 foreach_res(var, R_FILESET) {
161 fprintf(stderr, " %s\n", var->hdr.name);
167 ff = init_find_files();
169 copy_fileset(ff, jcr);
171 find_files(jcr, ff, print_file, NULL);
175 config->free_resources();
180 term_last_jobs_list();
182 /* Clean up fileset */
183 findFILESET *fileset = ff->fileset;
187 /* Delete FileSet Include lists */
188 for (i=0; i<fileset->include_list.size(); i++) {
189 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
190 for (j=0; j<incexe->opts_list.size(); j++) {
191 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
192 for (k=0; k<fo->regex.size(); k++) {
193 regfree((regex_t *)fo->regex.get(k));
196 fo->regexdir.destroy();
197 fo->regexfile.destroy();
199 fo->wilddir.destroy();
200 fo->wildfile.destroy();
201 fo->wildbase.destroy();
202 fo->fstype.destroy();
203 fo->drivetype.destroy();
205 incexe->opts_list.destroy();
206 incexe->name_list.destroy();
208 fileset->include_list.destroy();
210 /* Delete FileSet Exclude lists */
211 for (i=0; i<fileset->exclude_list.size(); i++) {
212 findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
213 for (j=0; j<incexe->opts_list.size(); j++) {
214 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
216 fo->regexdir.destroy();
217 fo->regexfile.destroy();
219 fo->wilddir.destroy();
220 fo->wildfile.destroy();
221 fo->wildbase.destroy();
222 fo->fstype.destroy();
223 fo->drivetype.destroy();
225 incexe->opts_list.destroy();
226 incexe->name_list.destroy();
228 fileset->exclude_list.destroy();
232 hard_links = term_find_files(ff);
236 "Max file length: %d\n"
237 "Max path length: %d\n"
238 "Files truncated: %d\n"
239 "Paths truncated: %d\n"
240 "Hard links : %d\n"),
241 num_files, max_file_len, max_path_len,
242 trunc_fname, trunc_path, hard_links);
252 static int print_file(JCR *jcr, FF_PKT *ff, bool top_level)
257 if (debug_level == 1) {
258 printf("%s\n", ff->fname);
259 } else if (debug_level > 1) {
260 printf("Lnka: %s -> %s\n", ff->fname, ff->link);
264 if (debug_level == 1) {
265 printf("%s\n", ff->fname);
266 } else if (debug_level > 1) {
267 printf("Empty: %s\n", ff->fname);
272 if (debug_level == 1) {
273 printf("%s\n", ff->fname);
274 } else if (debug_level > 1) {
275 printf(_("Reg: %s\n"), ff->fname);
280 if (debug_level == 1) {
281 printf("%s\n", ff->fname);
282 } else if (debug_level > 1) {
283 printf("Lnk: %s -> %s\n", ff->fname, ff->link);
295 char errmsg[100] = "";
296 if (ff->type == FT_NORECURSE) {
297 bstrncpy(errmsg, _("\t[will not descend: recursion turned off]"), sizeof(errmsg));
298 } else if (ff->type == FT_NOFSCHG) {
299 bstrncpy(errmsg, _("\t[will not descend: file system change not allowed]"), sizeof(errmsg));
300 } else if (ff->type == FT_INVALIDFS) {
301 bstrncpy(errmsg, _("\t[will not descend: disallowed file system]"), sizeof(errmsg));
302 } else if (ff->type == FT_INVALIDDT) {
303 bstrncpy(errmsg, _("\t[will not descend: disallowed drive type]"), sizeof(errmsg));
305 printf("%s%s%s\n", (debug_level > 1 ? "Dir: " : ""), ff->fname, errmsg);
307 ff->type = FT_DIREND;
311 if (debug_level == 1) {
312 printf("%s\n", ff->fname);
313 } else if (debug_level > 1) {
314 printf("Spec: %s\n", ff->fname);
319 printf(_("Err: Could not access %s: %s\n"), ff->fname, strerror(errno));
322 printf(_("Err: Could not follow ff->link %s: %s\n"), ff->fname, strerror(errno));
325 printf(_("Err: Could not stat %s: %s\n"), ff->fname, strerror(errno));
328 printf(_("Skip: File not saved. No change. %s\n"), ff->fname);
331 printf(_("Err: Attempt to backup archive. Not saved. %s\n"), ff->fname);
334 printf(_("Err: Could not open directory %s: %s\n"), ff->fname, strerror(errno));
337 printf(_("Err: Unknown file ff->type %d: %s\n"), ff->type, ff->fname);
342 encode_attribsEx(NULL, attr, ff);
344 printf("AttrEx=%s\n", attr);
346 // set_attribsEx(NULL, ff->fname, NULL, NULL, ff->type, attr);
351 static void count_files(FF_PKT *ar)
355 char file[MAXSTRING];
356 char spath[MAXSTRING];
360 /* Find path without the filename.
361 * I.e. everything after the last / is a "filename".
362 * OK, maybe it is a directory name, but we treat it like
363 * a filename. If we don't find a / then the whole name
364 * must be a path name (e.g. c:).
366 for (p=l=ar->fname; *p; p++) {
367 if (IsPathSeparator(*p)) {
368 l = p; /* set pos of last slash */
371 if (IsPathSeparator(*l)) { /* did we find a slash? */
372 l++; /* yes, point to filename */
373 } else { /* no, whole thing must be path name */
377 /* If filename doesn't exist (i.e. root directory), we
378 * simply create a blank name consisting of a single
379 * space. This makes handling zero length filenames
383 if (fnl > max_file_len) {
387 printf(_("===== Filename truncated to 255 chars: %s\n"), l);
392 strncpy(file, l, fnl); /* copy filename */
395 file[0] = ' '; /* blank filename */
400 if (pnl > max_path_len) {
404 printf(_("========== Path name truncated to 255 chars: %s\n"), ar->fname);
408 strncpy(spath, ar->fname, pnl);
413 printf(_("========== Path length is zero. File=%s\n"), ar->fname);
415 if (debug_level >= 10) {
416 printf(_("Path: %s\n"), spath);
417 printf(_("File: %s\n"), file);
422 bool python_set_prog(JCR*, char const*) { return false; }
424 static bool copy_fileset(FF_PKT *ff, JCR *jcr)
426 FILESET *jcr_fileset = jcr->fileset;
430 findFILESET *fileset;
431 findFOPTS *current_opts;
433 fileset = (findFILESET *)malloc(sizeof(findFILESET));
434 memset(fileset, 0, sizeof(findFILESET));
435 ff->fileset = fileset;
437 fileset->state = state_none;
438 fileset->include_list.init(1, true);
439 fileset->exclude_list.init(1, true);
443 num = jcr_fileset->num_includes;
445 num = jcr_fileset->num_excludes;
447 for (int i=0; i<num; i++) {
452 ie = jcr_fileset->include_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->include_list.append(fileset->incexe);
461 ie = jcr_fileset->exclude_items[i];
464 fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
465 memset(fileset->incexe, 0, sizeof(findINCEXE));
466 fileset->incexe->opts_list.init(1, true);
467 fileset->incexe->name_list.init(0, 0);
468 fileset->exclude_list.append(fileset->incexe);
471 for (j=0; j<ie->num_opts; j++) {
472 FOPTS *fo = ie->opts_list[j];
474 current_opts = (findFOPTS *)malloc(sizeof(findFOPTS));
475 memset(current_opts, 0, sizeof(findFOPTS));
476 fileset->incexe->current_opts = current_opts;
477 fileset->incexe->opts_list.append(current_opts);
479 current_opts->regex.init(1, true);
480 current_opts->regexdir.init(1, true);
481 current_opts->regexfile.init(1, true);
482 current_opts->wild.init(1, true);
483 current_opts->wilddir.init(1, true);
484 current_opts->wildfile.init(1, true);
485 current_opts->wildbase.init(1, true);
486 current_opts->fstype.init(1, true);
487 current_opts->drivetype.init(1, true);
489 set_options(current_opts, fo->opts);
491 for (k=0; k<fo->regex.size(); k++) {
492 // fd->fsend("R %s\n", fo->regex.get(k));
493 current_opts->regex.append(bstrdup((const char *)fo->regex.get(k)));
495 for (k=0; k<fo->regexdir.size(); k++) {
496 // fd->fsend("RD %s\n", fo->regexdir.get(k));
497 current_opts->regexdir.append(bstrdup((const char *)fo->regexdir.get(k)));
499 for (k=0; k<fo->regexfile.size(); k++) {
500 // fd->fsend("RF %s\n", fo->regexfile.get(k));
501 current_opts->regexfile.append(bstrdup((const char *)fo->regexfile.get(k)));
503 for (k=0; k<fo->wild.size(); k++) {
504 current_opts->wild.append(bstrdup((const char *)fo->wild.get(k)));
506 for (k=0; k<fo->wilddir.size(); k++) {
507 current_opts->wilddir.append(bstrdup((const char *)fo->wilddir.get(k)));
509 for (k=0; k<fo->wildfile.size(); k++) {
510 current_opts->wildfile.append(bstrdup((const char *)fo->wildfile.get(k)));
512 for (k=0; k<fo->wildbase.size(); k++) {
513 current_opts->wildbase.append(bstrdup((const char *)fo->wildbase.get(k)));
515 for (k=0; k<fo->fstype.size(); k++) {
516 current_opts->fstype.append(bstrdup((const char *)fo->fstype.get(k)));
518 for (k=0; k<fo->drivetype.size(); k++) {
519 current_opts->drivetype.append(bstrdup((const char *)fo->drivetype.get(k)));
523 for (j=0; j<ie->name_list.size(); j++) {
524 fileset->incexe->name_list.append(bstrdup((const char *)ie->name_list.get(j)));
528 if (!include) { /* If we just did excludes */
529 break; /* all done */
532 include = false; /* Now do excludes */
538 static void set_options(findFOPTS *fo, const char *opts)
543 for (p=opts; *p; p++) {
545 case 'a': /* alway replace */
546 case '0': /* no option */
549 fo->flags |= FO_EXCLUDE;
552 fo->flags |= FO_MULTIFS;
554 case 'h': /* no recursion */
555 fo->flags |= FO_NO_RECURSION;
557 case 'H': /* no hard link handling */
558 fo->flags |= FO_NO_HARDLINK;
561 fo->flags |= FO_IGNORECASE;
567 fo->flags |= FO_NOREPLACE;
569 case 'p': /* use portable data format */
570 fo->flags |= FO_PORTABLE;
572 case 'R': /* Resource forks and Finder Info */
573 fo->flags |= FO_HFSPLUS;
574 case 'r': /* read fifo */
575 fo->flags |= FO_READFIFO;
580 /* Old director did not specify SHA variant */
581 fo->flags |= FO_SHA1;
584 fo->flags |= FO_SHA1;
589 fo->flags |= FO_SHA256;
593 fo->flags |= FO_SHA512;
598 /* Automatically downgrade to SHA-1 if an unsupported
599 * SHA variant is specified */
600 fo->flags |= FO_SHA1;
606 fo->flags |= FO_SPARSE;
609 fo->flags |= FO_MTIMEONLY;
612 fo->flags |= FO_KEEPATIME;
617 case 'V': /* verify options */
618 /* Copy Verify Options */
619 for (j=0; *p && *p != ':'; p++) {
620 fo->VerifyOpts[j] = *p;
621 if (j < (int)sizeof(fo->VerifyOpts) - 1) {
625 fo->VerifyOpts[j] = 0;
628 fo->flags |= FO_IF_NEWER;
631 fo->flags |= FO_ENHANCEDWILD;
633 case 'Z': /* compression */
635 if (*p >= '0' && *p <= '9') {
636 fo->flags |= FO_COMPRESS;
637 fo->Compress_algo = COMPRESS_GZIP;
638 fo->Compress_level = *p - '0';
640 else if (*p == 'o') {
641 fo->flags |= FO_COMPRESS;
642 fo->Compress_algo = COMPRESS_LZO1X;
643 fo->Compress_level = 1; /* not used with LZO */
645 Dmsg2(200, "Compression alg=%d level=%d\n", fo->Compress_algo, fo->Compress_level);
648 fo->flags |= FO_XATTR;
651 Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);