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