1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
5 Copyright (C) 2016-2016 Bareos GmbH & Co. KG
6
7 This program is Free Software; you can redistribute it and/or
8 modify it under the terms of version three of the GNU Affero General Public
9 License as published by the Free Software Foundation and included
10 in the file LICENSE.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Affero General Public License for more details.
16
17 You should have received a copy of the GNU Affero General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301, USA.
21 */
22 /*
23 * Kern Sibbald, MM
24 */
25 /**
26 * @file
27 * Test program for find files
28 */
29
30 #include "include/bareos.h"
31 #include "dird/dird.h"
32 #include "findlib/find.h"
33 #include "lib/mntent_cache.h"
34 #include "ch.h"
35
36 #if defined(HAVE_WIN32)
37 #define isatty(fd) (fd == 0)
38 #endif
39
40 using namespace directordaemon;
41
42 /* Dummy functions */
GeneratePluginEvent(JobControlRecord * jcr,bEventType eventType,void * value)43 void GeneratePluginEvent(JobControlRecord* jcr,
44 bEventType eventType,
45 void* value)
46 {
47 }
48 extern bool ParseDirConfig(const char* configfile, int exit_code);
49
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;
56 static int attrs = 0;
57
58 static JobControlRecord* jcr;
59
60 static int PrintFile(JobControlRecord* jcr, FindFilesPacket* ff, bool);
61 static void CountFiles(FindFilesPacket* ff);
62 static bool CopyFileset(FindFilesPacket* ff, JobControlRecord* jcr);
63 static void SetOptions(findFOPTS* fo, const char* opts);
64
usage()65 static void usage()
66 {
67 fprintf(
68 stderr,
69 _("\n"
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"
77 "\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"
84 "\n"));
85
86 exit(1);
87 }
88
89
main(int argc,char * const * argv)90 int main(int argc, char* const* argv)
91 {
92 FindFilesPacket* ff;
93 const char* configfile = "bareos-dir.conf";
94 const char* fileset_name = "Windows-Full-Set";
95 int ch, hard_links;
96
97 OSDependentInit();
98
99 setlocale(LC_ALL, "");
100 tzset();
101 bindtextdomain("bareos", LOCALEDIR);
102 textdomain("bareos");
103
104 while ((ch = getopt(argc, argv, "ac:d:f:?")) != -1) {
105 switch (ch) {
106 case 'a': /* print extended attributes *debug* */
107 attrs = 1;
108 break;
109
110 case 'c': /* set debug level */
111 configfile = optarg;
112 break;
113
114 case 'd': /* set debug level */
115 if (*optarg == 't') {
116 dbg_timestamp = true;
117 } else {
118 debug_level = atoi(optarg);
119 if (debug_level <= 0) { debug_level = 1; }
120 }
121 break;
122
123 case 'f': /* exclude patterns */
124 fileset_name = optarg;
125 break;
126
127 case '?':
128 default:
129 usage();
130 }
131 }
132
133 argc -= optind;
134 argv += optind;
135
136 my_config = InitDirConfig(configfile, M_ERROR_TERM);
137 my_config->ParseConfig();
138
139 MessagesResource* msg;
140
141 foreach_res (msg, R_MSGS) {
142 InitMsg(NULL, msg);
143 }
144
145 jcr = NewDirectorJcr(); // Ueb: null
146 jcr->impl->res.fileset =
147 (FilesetResource*)my_config->GetResWithName(R_FILESET, fileset_name);
148
149 if (jcr->impl->res.fileset == NULL) {
150 fprintf(stderr, "%s: Fileset not found\n", fileset_name);
151
152 FilesetResource* var;
153
154 fprintf(stderr, "Valid FileSets:\n");
155
156 foreach_res (var, R_FILESET) {
157 fprintf(stderr, " %s\n", var->resource_name_);
158 }
159
160 exit(1);
161 }
162
163 ff = init_find_files();
164
165 CopyFileset(ff, jcr);
166
167 FindFiles(jcr, ff, PrintFile, NULL);
168
169 FreeJcr(jcr);
170 if (my_config) {
171 delete my_config;
172 my_config = NULL;
173 }
174
175 RecentJobResultsList::Cleanup();
176 CleanupJcrChain();
177
178 /* Clean up fileset */
179 findFILESET* fileset = ff->fileset;
180
181 if (fileset) {
182 int i, j, k;
183 /* Delete FileSet Include lists */
184 for (i = 0; i < fileset->include_list.size(); i++) {
185 findIncludeExcludeItem* incexe =
186 (findIncludeExcludeItem*)fileset->include_list.get(i);
187 for (j = 0; j < incexe->opts_list.size(); j++) {
188 findFOPTS* fo = (findFOPTS*)incexe->opts_list.get(j);
189 for (k = 0; k < fo->regex.size(); k++) {
190 regfree((regex_t*)fo->regex.get(k));
191 }
192 fo->regex.destroy();
193 fo->regexdir.destroy();
194 fo->regexfile.destroy();
195 fo->wild.destroy();
196 fo->wilddir.destroy();
197 fo->wildfile.destroy();
198 fo->wildbase.destroy();
199 fo->fstype.destroy();
200 fo->Drivetype.destroy();
201 }
202 incexe->opts_list.destroy();
203 incexe->name_list.destroy();
204 }
205 fileset->include_list.destroy();
206
207 /* Delete FileSet Exclude lists */
208 for (i = 0; i < fileset->exclude_list.size(); i++) {
209 findIncludeExcludeItem* incexe =
210 (findIncludeExcludeItem*)fileset->exclude_list.get(i);
211 for (j = 0; j < incexe->opts_list.size(); j++) {
212 findFOPTS* fo = (findFOPTS*)incexe->opts_list.get(j);
213 fo->regex.destroy();
214 fo->regexdir.destroy();
215 fo->regexfile.destroy();
216 fo->wild.destroy();
217 fo->wilddir.destroy();
218 fo->wildfile.destroy();
219 fo->wildbase.destroy();
220 fo->fstype.destroy();
221 fo->Drivetype.destroy();
222 }
223 incexe->opts_list.destroy();
224 incexe->name_list.destroy();
225 }
226 fileset->exclude_list.destroy();
227 free(fileset);
228 }
229 ff->fileset = NULL;
230 hard_links = TermFindFiles(ff);
231
232 printf(_("\n"
233 "Total files : %d\n"
234 "Max file length: %d\n"
235 "Max path length: %d\n"
236 "Files truncated: %d\n"
237 "Paths truncated: %d\n"
238 "Hard links : %d\n"),
239 num_files, max_file_len, max_path_len, trunc_fname, trunc_path,
240 hard_links);
241
242 FlushMntentCache();
243
244 TermMsg();
245
246 CloseMemoryPool();
247 exit(0);
248 }
249
PrintFile(JobControlRecord * jcr,FindFilesPacket * ff,bool top_level)250 static int PrintFile(JobControlRecord* jcr, FindFilesPacket* ff, bool top_level)
251 {
252 switch (ff->type) {
253 case FT_LNKSAVED:
254 if (debug_level == 1) {
255 printf("%s\n", ff->fname);
256 } else if (debug_level > 1) {
257 printf("Lnka: %s -> %s\n", ff->fname, ff->link);
258 }
259 break;
260 case FT_REGE:
261 if (debug_level == 1) {
262 printf("%s\n", ff->fname);
263 } else if (debug_level > 1) {
264 printf("Empty: %s\n", ff->fname);
265 }
266 CountFiles(ff);
267 break;
268 case FT_REG:
269 if (debug_level == 1) {
270 printf("%s\n", ff->fname);
271 } else if (debug_level > 1) {
272 printf(_("Reg: %s\n"), ff->fname);
273 }
274 CountFiles(ff);
275 break;
276 case FT_LNK:
277 if (debug_level == 1) {
278 printf("%s\n", ff->fname);
279 } else if (debug_level > 1) {
280 printf("Lnk: %s -> %s\n", ff->fname, ff->link);
281 }
282 CountFiles(ff);
283 break;
284 case FT_DIRBEGIN:
285 return 1;
286 case FT_NORECURSE:
287 case FT_NOFSCHG:
288 case FT_INVALIDFS:
289 case FT_INVALIDDT:
290 case FT_DIREND:
291 if (debug_level) {
292 char errmsg[100] = "";
293 if (ff->type == FT_NORECURSE) {
294 bstrncpy(errmsg, _("\t[will not descend: recursion turned off]"),
295 sizeof(errmsg));
296 } else if (ff->type == FT_NOFSCHG) {
297 bstrncpy(errmsg,
298 _("\t[will not descend: file system change not allowed]"),
299 sizeof(errmsg));
300 } else if (ff->type == FT_INVALIDFS) {
301 bstrncpy(errmsg, _("\t[will not descend: disallowed file system]"),
302 sizeof(errmsg));
303 } else if (ff->type == FT_INVALIDDT) {
304 bstrncpy(errmsg, _("\t[will not descend: disallowed drive type]"),
305 sizeof(errmsg));
306 }
307 printf("%s%s%s\n", (debug_level > 1 ? "Dir: " : ""), ff->fname, errmsg);
308 }
309 ff->type = FT_DIREND;
310 CountFiles(ff);
311 break;
312 case FT_SPEC:
313 if (debug_level == 1) {
314 printf("%s\n", ff->fname);
315 } else if (debug_level > 1) {
316 printf("Spec: %s\n", ff->fname);
317 }
318 CountFiles(ff);
319 break;
320 case FT_NOACCESS:
321 printf(_("Err: Could not access %s: %s\n"), ff->fname, strerror(errno));
322 break;
323 case FT_NOFOLLOW:
324 printf(_("Err: Could not follow ff->link %s: %s\n"), ff->fname,
325 strerror(errno));
326 break;
327 case FT_NOSTAT:
328 printf(_("Err: Could not stat %s: %s\n"), ff->fname, strerror(errno));
329 break;
330 case FT_NOCHG:
331 printf(_("Skip: File not saved. No change. %s\n"), ff->fname);
332 break;
333 case FT_ISARCH:
334 printf(_("Err: Attempt to backup archive. Not saved. %s\n"), ff->fname);
335 break;
336 case FT_NOOPEN:
337 printf(_("Err: Could not open directory %s: %s\n"), ff->fname,
338 strerror(errno));
339 break;
340 default:
341 printf(_("Err: Unknown file ff->type %d: %s\n"), ff->type, ff->fname);
342 break;
343 }
344 if (attrs) {
345 char attr[200];
346 encode_attribsEx(NULL, attr, ff);
347 if (*attr != 0) { printf("AttrEx=%s\n", attr); }
348 // set_attribsEx(NULL, ff->fname, NULL, NULL, ff->type, attr);
349 }
350 return 1;
351 }
352
CountFiles(FindFilesPacket * ar)353 static void CountFiles(FindFilesPacket* ar)
354 {
355 int fnl, pnl;
356 char *l, *p;
357 PoolMem file(PM_FNAME);
358 PoolMem spath(PM_FNAME);
359
360 num_files++;
361
362 /* Find path without the filename.
363 * I.e. everything after the last / is a "filename".
364 * OK, maybe it is a directory name, but we treat it like
365 * a filename. If we don't find a / then the whole name
366 * must be a path name (e.g. c:).
367 */
368 for (p = l = ar->fname; *p; p++) {
369 if (IsPathSeparator(*p)) { l = p; /* set pos of last slash */ }
370 }
371 if (IsPathSeparator(*l)) { /* did we find a slash? */
372 l++; /* yes, point to filename */
373 } else { /* no, whole thing must be path name */
374 l = p;
375 }
376
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
380 * easier.
381 */
382 fnl = p - l;
383 if (fnl > max_file_len) { max_file_len = fnl; }
384 if (fnl > 255) {
385 printf(_("===== Filename truncated to 255 chars: %s\n"), l);
386 fnl = 255;
387 trunc_fname++;
388 }
389
390 if (fnl > 0) {
391 PmStrcpy(file, l); /* copy filename */
392 } else {
393 PmStrcpy(file, " "); /* blank filename */
394 }
395
396 pnl = l - ar->fname;
397 if (pnl > max_path_len) { max_path_len = pnl; }
398 if (pnl > 255) {
399 printf(_("========== Path name truncated to 255 chars: %s\n"), ar->fname);
400 pnl = 255;
401 trunc_path++;
402 }
403
404 PmStrcpy(spath, ar->fname);
405 if (pnl == 0) {
406 PmStrcpy(spath, " ");
407 printf(_("========== Path length is zero. File=%s\n"), ar->fname);
408 }
409 if (debug_level >= 10) {
410 printf(_("Path: %s\n"), spath.c_str());
411 printf(_("File: %s\n"), file.c_str());
412 }
413 }
414
CopyFileset(FindFilesPacket * ff,JobControlRecord * jcr)415 static bool CopyFileset(FindFilesPacket* ff, JobControlRecord* jcr)
416 {
417 FilesetResource* jcr_fileset = jcr->impl->res.fileset;
418 int num;
419 bool include = true;
420
421 findFILESET* fileset;
422 findFOPTS* current_opts;
423
424 fileset = (findFILESET*)malloc(sizeof(findFILESET));
425 memset(fileset, 0, sizeof(findFILESET));
426 ff->fileset = fileset;
427
428 fileset->state = state_none;
429 fileset->include_list.init(1, true);
430 fileset->exclude_list.init(1, true);
431
432 for (;;) {
433 if (include) {
434 num = jcr_fileset->num_includes;
435 } else {
436 num = jcr_fileset->num_excludes;
437 }
438 for (int i = 0; i < num; i++) {
439 IncludeExcludeItem* ie;
440 int j, k;
441
442 if (include) {
443 ie = jcr_fileset->include_items[i];
444
445 /* New include */
446 fileset->incexe =
447 (findIncludeExcludeItem*)malloc(sizeof(findIncludeExcludeItem));
448 memset(fileset->incexe, 0, sizeof(findIncludeExcludeItem));
449 fileset->incexe->opts_list.init(1, true);
450 fileset->incexe->name_list.init(0, 0);
451 fileset->include_list.append(fileset->incexe);
452 } else {
453 ie = jcr_fileset->exclude_items[i];
454
455 /* New exclude */
456 fileset->incexe =
457 (findIncludeExcludeItem*)malloc(sizeof(findIncludeExcludeItem));
458 memset(fileset->incexe, 0, sizeof(findIncludeExcludeItem));
459 fileset->incexe->opts_list.init(1, true);
460 fileset->incexe->name_list.init(0, 0);
461 fileset->exclude_list.append(fileset->incexe);
462 }
463
464 for (j = 0; j < ie->num_opts; j++) {
465 FileOptions* fo = ie->opts_list[j];
466
467 current_opts = (findFOPTS*)malloc(sizeof(findFOPTS));
468 memset(current_opts, 0, sizeof(findFOPTS));
469 fileset->incexe->current_opts = current_opts;
470 fileset->incexe->opts_list.append(current_opts);
471
472 current_opts->regex.init(1, true);
473 current_opts->regexdir.init(1, true);
474 current_opts->regexfile.init(1, true);
475 current_opts->wild.init(1, true);
476 current_opts->wilddir.init(1, true);
477 current_opts->wildfile.init(1, true);
478 current_opts->wildbase.init(1, true);
479 current_opts->fstype.init(1, true);
480 current_opts->Drivetype.init(1, true);
481
482 SetOptions(current_opts, fo->opts);
483
484 for (k = 0; k < fo->regex.size(); k++) {
485 // fd->fsend("R %s\n", fo->regex.get(k));
486 current_opts->regex.append(strdup((const char*)fo->regex.get(k)));
487 }
488 for (k = 0; k < fo->regexdir.size(); k++) {
489 // fd->fsend("RD %s\n", fo->regexdir.get(k));
490 current_opts->regexdir.append(
491 strdup((const char*)fo->regexdir.get(k)));
492 }
493 for (k = 0; k < fo->regexfile.size(); k++) {
494 // fd->fsend("RF %s\n", fo->regexfile.get(k));
495 current_opts->regexfile.append(
496 strdup((const char*)fo->regexfile.get(k)));
497 }
498 for (k = 0; k < fo->wild.size(); k++) {
499 current_opts->wild.append(strdup((const char*)fo->wild.get(k)));
500 }
501 for (k = 0; k < fo->wilddir.size(); k++) {
502 current_opts->wilddir.append(strdup((const char*)fo->wilddir.get(k)));
503 }
504 for (k = 0; k < fo->wildfile.size(); k++) {
505 current_opts->wildfile.append(
506 strdup((const char*)fo->wildfile.get(k)));
507 }
508 for (k = 0; k < fo->wildbase.size(); k++) {
509 current_opts->wildbase.append(
510 strdup((const char*)fo->wildbase.get(k)));
511 }
512 for (k = 0; k < fo->fstype.size(); k++) {
513 current_opts->fstype.append(strdup((const char*)fo->fstype.get(k)));
514 }
515 for (k = 0; k < fo->Drivetype.size(); k++) {
516 current_opts->Drivetype.append(
517 strdup((const char*)fo->Drivetype.get(k)));
518 }
519 }
520
521 for (j = 0; j < ie->name_list.size(); j++) {
522 fileset->incexe->name_list.append(
523 strdup((const char*)ie->name_list.get(j)));
524 }
525 }
526
527 if (!include) { /* If we just did excludes */
528 break; /* all done */
529 }
530
531 include = false; /* Now do excludes */
532 }
533
534 return true;
535 }
536
SetOptions(findFOPTS * fo,const char * opts)537 static void SetOptions(findFOPTS* fo, const char* opts)
538 {
539 int j;
540 const char* p;
541
542 for (p = opts; *p; p++) {
543 switch (*p) {
544 case 'a': /* alway replace */
545 case '0': /* no option */
546 break;
547 case 'e':
548 SetBit(FO_EXCLUDE, fo->flags);
549 break;
550 case 'f':
551 SetBit(FO_MULTIFS, fo->flags);
552 break;
553 case 'h': /* no recursion */
554 SetBit(FO_NO_RECURSION, fo->flags);
555 break;
556 case 'H': /* no hard link handling */
557 SetBit(FO_NO_HARDLINK, fo->flags);
558 break;
559 case 'i':
560 SetBit(FO_IGNORECASE, fo->flags);
561 break;
562 case 'M': /* MD5 */
563 SetBit(FO_MD5, fo->flags);
564 break;
565 case 'n':
566 SetBit(FO_NOREPLACE, fo->flags);
567 break;
568 case 'p': /* use portable data format */
569 SetBit(FO_PORTABLE, fo->flags);
570 break;
571 case 'R': /* Resource forks and Finder Info */
572 SetBit(FO_HFSPLUS, fo->flags);
573 case 'r': /* read fifo */
574 SetBit(FO_READFIFO, fo->flags);
575 break;
576 case 'S':
577 switch (*(p + 1)) {
578 case ' ':
579 /* Old director did not specify SHA variant */
580 SetBit(FO_SHA1, fo->flags);
581 break;
582 case '1':
583 SetBit(FO_SHA1, fo->flags);
584 p++;
585 break;
586 #ifdef HAVE_SHA2
587 case '2':
588 SetBit(FO_SHA256, fo->flags);
589 p++;
590 break;
591 case '3':
592 SetBit(FO_SHA512, fo->flags);
593 p++;
594 break;
595 #endif
596 default:
597 /* Automatically downgrade to SHA-1 if an unsupported
598 * SHA variant is specified */
599 SetBit(FO_SHA1, fo->flags);
600 p++;
601 break;
602 }
603 break;
604 case 's':
605 SetBit(FO_SPARSE, fo->flags);
606 break;
607 case 'm':
608 SetBit(FO_MTIMEONLY, fo->flags);
609 break;
610 case 'k':
611 SetBit(FO_KEEPATIME, fo->flags);
612 break;
613 case 'A':
614 SetBit(FO_ACL, fo->flags);
615 break;
616 case 'V': /* verify options */
617 /* Copy Verify Options */
618 for (j = 0; *p && *p != ':'; p++) {
619 fo->VerifyOpts[j] = *p;
620 if (j < (int)sizeof(fo->VerifyOpts) - 1) { j++; }
621 }
622 fo->VerifyOpts[j] = 0;
623 break;
624 case 'w':
625 SetBit(FO_IF_NEWER, fo->flags);
626 break;
627 case 'W':
628 SetBit(FO_ENHANCEDWILD, fo->flags);
629 break;
630 case 'Z': /* compression */
631 p++; /* skip Z */
632 if (*p >= '0' && *p <= '9') {
633 SetBit(FO_COMPRESS, fo->flags);
634 fo->Compress_algo = COMPRESS_GZIP;
635 fo->Compress_level = *p - '0';
636 } else if (*p == 'o') {
637 SetBit(FO_COMPRESS, fo->flags);
638 fo->Compress_algo = COMPRESS_LZO1X;
639 fo->Compress_level = 1; /* not used with LZO */
640 }
641 Dmsg2(200, "Compression alg=%d level=%d\n", fo->Compress_algo,
642 fo->Compress_level);
643 break;
644 case 'x':
645 SetBit(FO_NO_AUTOEXCL, fo->flags);
646 break;
647 case 'X':
648 SetBit(FO_XATTR, fo->flags);
649 break;
650 default:
651 Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);
652 break;
653 }
654 }
655 }
656