1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include <stdio.h>
30 #include <fcntl.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <dirent.h>
36 #include <sys/param.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <ftw.h>
40
41 #include "list.h"
42 #include "protocmp.h"
43 #include "proto_list.h"
44 #include "protodir.h"
45 #include "exception_list.h"
46 #include "stdusers.h"
47
48 #define MAX_PROTO_REFS 5
49 #define MAX_EXCEPTION_FILES 5
50 #define MAX_DEPTH 50
51
52 /*
53 * default flag values
54 */
55 static int check_group = 1;
56 static int set_group = 0;
57 static int check_user = 1;
58 static int set_user = 0;
59 static int check_perm = 1;
60 static int set_perm = 0;
61 static int check_link = 1;
62 static int check_sym = 1;
63 static int check_majmin = 1;
64
65 static elem_list first_list;
66 static char *first_file_name;
67
68 static elem_list second_list;
69 static char *second_file_name;
70
71 static FILE *need_add_fp;
72 static char *need_add_file;
73 static FILE *need_rm_fp;
74 static char *need_rm_file;
75 static FILE *differ_fp;
76 static char *differ_file;
77
78 static char *myname;
79
80 /*
81 * default flag values
82 */
83 static int verbose = 0;
84
85 static void
usage(void)86 usage(void)
87 {
88 (void) fputs("usage: protocmp [-gupGUPlmsLv] "
89 "[-e <exception-list> ...] "
90 "-d <protolist|pkg dir>\n\t[-d <protolist|pkg dir> ...] "
91 "[<protolist|pkg dir>...]|<root>]\n",
92 stderr);
93 (void) fputs(" where:\n", stderr);
94 (void) fputs("\t-g : don't compare group\n", stderr);
95 (void) fputs("\t-u : don't compare owner\n", stderr);
96 (void) fputs("\t-p : don't compare permissions\n", stderr);
97 (void) fputs("\t-G : set group\n", stderr);
98 (void) fputs("\t-U : set owner\n", stderr);
99 (void) fputs("\t-P : set permissions\n", stderr);
100 (void) fputs("\t-l : don't compare link counts\n", stderr);
101 (void) fputs("\t-m : don't compare major/minor numbers\n",
102 stderr);
103 (void) fputs("\t-s : don't compare symlink values\n", stderr);
104 (void) fputs("\t-d <protolist|pkg dir>:\n", stderr);
105 (void) fputs("\t proto list or packaging to check\n", stderr);
106 (void) fputs("\t-e <file>: exceptions file\n", stderr);
107 (void) fputs("\t-L : list filtered exceptions\n", stderr);
108 (void) fputs("\t-v : verbose output\n", stderr);
109 (void) fputs("\n"
110 "If any of the -[GUP] flags are given, then the final argument must be the\n"
111 "proto root directory itself on which to set permissions according to the\n"
112 "packaging data specified via -d options.\n", stderr);
113 }
114
115
116 static void
open_output_files(void)117 open_output_files(void)
118 {
119 if ((need_add_fp =
120 fopen((need_add_file = tempnam(NULL, "add")), "w")) == NULL) {
121 perror(need_add_file);
122 exit(1);
123 }
124
125 if ((need_rm_fp =
126 fopen((need_rm_file = tempnam(NULL, "rm")), "w")) == NULL) {
127 perror(need_rm_file);
128 exit(1);
129 }
130
131 if ((differ_fp =
132 fopen((differ_file = tempnam(NULL, "diff")), "w")) == NULL) {
133 perror(differ_file);
134 exit(1);
135 }
136 }
137
138 static void
close_output_files(void)139 close_output_files(void)
140 {
141 (void) fclose(need_add_fp);
142 (void) fclose(need_rm_fp);
143 (void) fclose(differ_fp);
144 }
145
146 static void
print_file(char * file)147 print_file(char *file)
148 {
149 FILE *fp;
150 int count;
151 char buff[BUF_SIZE];
152
153 if ((fp = fopen(file, "r")) == NULL) {
154 perror(need_add_file);
155 }
156
157 while (count = fread(buff, sizeof (char), BUF_SIZE, fp))
158 (void) fwrite(buff, sizeof (char), count, stdout);
159 (void) fclose(fp);
160 }
161
162 static void
print_header(void)163 print_header(void)
164 {
165 (void) printf("%c %-30s %-20s %-4s %-5s %-5s %-5s %-2s %2s %2s %-9s\n",
166 'T', "File Name", "Reloc/Sym name", "perm", "owner", "group",
167 "inode", "lnk", "maj", "min", "package(s)");
168 (void) puts("-------------------------------------------------------"
169 "-----------------------------------------------------");
170 }
171
172 static void
print_results(void)173 print_results(void)
174 {
175 (void) puts("*******************************************************");
176 (void) puts("*");
177 (void) printf("* Entries found in %s, but not found in %s\n",
178 first_file_name, second_file_name);
179 (void) puts("*");
180 (void) puts("*******************************************************");
181 print_header();
182 print_file(need_add_file);
183 (void) puts("*******************************************************");
184 (void) puts("*");
185 (void) printf("* Entries found in %s, but not found in %s\n",
186 second_file_name, first_file_name);
187 (void) puts("*");
188 (void) puts("*******************************************************");
189 print_header();
190 print_file(need_rm_file);
191 (void) puts("*******************************************************");
192 (void) puts("*");
193 (void) printf("* Entries that differ between %s and %s\n",
194 first_file_name, second_file_name);
195 (void) puts("*");
196 (void) printf("* filea == %s\n", first_file_name);
197 (void) printf("* fileb == %s\n", second_file_name);
198 (void) puts("*");
199 (void) puts("*******************************************************");
200 (void) fputs("Unit ", stdout);
201 print_header();
202 print_file(differ_file);
203 }
204
205 static void
clean_up(void)206 clean_up(void)
207 {
208 (void) unlink(need_add_file);
209 (void) unlink(need_rm_file);
210 (void) unlink(differ_file);
211 }
212
213 /*
214 * elem_compare(a,b)
215 *
216 * Args:
217 * a - element a
218 * b - element b
219 * different_types -
220 * value = 0 -> comparing two elements of same
221 * type (eg: protodir elem vs. protodir elem).
222 * value != 0 -> comparing two elements of different type
223 * (eg: protodir elem vs. protolist elem).
224 *
225 * Returns:
226 * 0 - elements are identical
227 * >0 - elements differ
228 * check flags to see which fields differ.
229 */
230 static int
elem_compare(elem * a,elem * b,int different_types)231 elem_compare(elem *a, elem *b, int different_types)
232 {
233 int res = 0;
234 elem *i, *j;
235
236 /*
237 * if these are hard links to other files - those are the
238 * files that should be compared.
239 */
240 i = a->link_parent ? a->link_parent : a;
241 j = b->link_parent ? b->link_parent : b;
242
243 /*
244 * We do not compare inodes - they always differ.
245 * We do not compare names because we assume that was
246 * checked before.
247 */
248
249 /*
250 * Special rules for comparison:
251 *
252 * 1) if directory - ignore ref_cnt.
253 * 2) if sym_link - only check file_type & symlink
254 * 3) elem type of FILE_T, EDIT_T, & VOLATILE_T are equivilant when
255 * comparing a protodir entry to a protolist entry.
256 */
257 if (i->file_type != j->file_type) {
258 if (different_types) {
259 /*
260 * Check to see if filetypes are FILE_T vs.
261 * EDIT_T/VOLATILE_T/LINK_T comparisons.
262 */
263 if ((i->file_type == FILE_T) &&
264 ((j->file_type == EDIT_T) ||
265 (j->file_type == VOLATILE_T) ||
266 (j->file_type == LINK_T))) {
267 /*EMPTY*/
268 } else if ((j->file_type == FILE_T) &&
269 ((i->file_type == EDIT_T) ||
270 (i->file_type == VOLATILE_T) ||
271 (i->file_type == LINK_T))) {
272 /*EMPTY*/
273 } else
274 res |= TYPE_F;
275 } else
276 res |= TYPE_F;
277 }
278
279 /*
280 * if symlink - check the symlink value and then
281 * return. symlink is the only field of concern
282 * in SYMLINKS.
283 */
284 if (check_sym && ((res == 0) && (i->file_type == SYM_LINK_T))) {
285 if ((!i->symsrc) || (!j->symsrc))
286 res |= SYM_F;
287 else {
288 /*
289 * if either symlink starts with a './' strip it off,
290 * its irrelevant.
291 */
292 if ((i->symsrc[0] == '.') && (i->symsrc[1] == '/'))
293 i->symsrc += 2;
294 if ((j->symsrc[0] == '.') && (j->symsrc[1] == '/'))
295 j->symsrc += 2;
296
297 if (strncmp(i->symsrc, j->symsrc, MAXNAME) != 0)
298 res |= SYM_F;
299 }
300 return (res);
301 }
302
303 if ((i->file_type != DIR_T) && check_link &&
304 (i->ref_cnt != j->ref_cnt))
305 res |= REF_F;
306 if (check_user && (strncmp(i->owner, j->owner, TYPESIZE) != 0))
307 res |= OWNER_F;
308 if (check_group && (strncmp(i->group, j->group, TYPESIZE) != 0))
309 res |= GROUP_F;
310 if (check_perm && (i->perm != j->perm))
311 res |= PERM_F;
312 if (check_majmin && ((i->major != j->major) || (i->minor != j->minor)))
313 res |= MAJMIN_F;
314
315 return (res);
316 }
317
318 static void
print_elem(FILE * fp,elem * e)319 print_elem(FILE *fp, elem *e)
320 {
321 elem p;
322 pkg_list *l;
323 char maj[TYPESIZE], min[TYPESIZE];
324 char perm[12], ref_cnt[12];
325
326 /*
327 * If this is a LINK to another file, then adopt
328 * the permissions of that file.
329 */
330 if (e->link_parent) {
331 p = *((elem *)e->link_parent);
332 (void) strcpy(p.name, e->name);
333 p.symsrc = e->symsrc;
334 p.file_type = e->file_type;
335 e = &p;
336 }
337
338 if (!check_majmin || e->major == -1) {
339 maj[0] = '-';
340 maj[1] = '\0';
341 } else {
342 (void) sprintf(maj, "%d", e->major);
343 }
344
345 if (!check_majmin || e->minor == -1) {
346 min[0] = '-';
347 min[1] = '\0';
348 } else {
349 (void) sprintf(min, "%d", e->minor);
350 }
351
352 if (!check_perm) {
353 perm[0] = '-';
354 perm[1] = '\0';
355 } else {
356 (void) snprintf(perm, sizeof (perm), "%o", e->perm);
357 }
358
359 if (!check_link) {
360 ref_cnt[0] = '-';
361 ref_cnt[1] = '\0';
362 } else {
363 (void) snprintf(ref_cnt, sizeof (ref_cnt), "%d", e->ref_cnt);
364 }
365
366 (void) fprintf(fp, "%c %-30s %-20s %4s %-5s %-5s %6d %2s %2s %2s ",
367 e->file_type, e->name,
368 check_sym && e->symsrc != NULL ? e->symsrc : "-", perm,
369 check_user ? e->owner : "-",
370 check_group ? e->group : "-",
371 e->inode, ref_cnt, maj, min);
372 /*
373 * dump package list - if any.
374 */
375 if (!e->pkgs)
376 (void) fputs(" proto", fp);
377
378 for (l = e->pkgs; l; l = l->next) {
379 (void) fputc(' ', fp);
380 (void) fputs(l->pkg_name, fp);
381 }
382 (void) fputc('\n', fp);
383 }
384
385 /*
386 * do_compare(a,b)
387 *
388 * Args:
389 * different_types - see elem_compare() for explanation.
390 */
391 static void
do_compare(elem * a,elem * b,int different_types)392 do_compare(elem *a, elem *b, int different_types)
393 {
394 int rc;
395
396 if ((rc = elem_compare(a, b, different_types)) != 0) {
397 (void) fputs("filea: ", differ_fp);
398 print_elem(differ_fp, a);
399 (void) fputs("fileb: ", differ_fp);
400 print_elem(differ_fp, b);
401 (void) fputs(" differ: ", differ_fp);
402
403 if (rc & SYM_F)
404 (void) fputs("symlink", differ_fp);
405 if (rc & PERM_F)
406 (void) fputs("perm ", differ_fp);
407 if (rc & REF_F)
408 (void) fputs("ref_cnt ", differ_fp);
409 if (rc & TYPE_F)
410 (void) fputs("file_type ", differ_fp);
411 if (rc & OWNER_F)
412 (void) fputs("owner ", differ_fp);
413 if (rc & GROUP_F)
414 (void) fputs("group ", differ_fp);
415 if (rc & MAJMIN_F)
416 (void) fputs("major/minor ", differ_fp);
417 (void) putc('\n', differ_fp);
418 (void) putc('\n', differ_fp);
419 }
420 }
421
422 static void
check_second_vs_first(int verbose)423 check_second_vs_first(int verbose)
424 {
425 int i;
426 elem *cur;
427
428 for (i = 0; i < second_list.num_of_buckets; i++) {
429 for (cur = second_list.list[i]; cur; cur = cur->next) {
430 if (!(cur->flag & VISITED_F)) {
431 if ((first_list.type != second_list.type) &&
432 find_elem(&exception_list, cur,
433 FOLLOW_LINK)) {
434 /*
435 * this entry is filtered, we don't
436 * need to do any more processing.
437 */
438 if (verbose) {
439 (void) printf(
440 "Filtered: Need Deletion "
441 "of:\n\t");
442 print_elem(stdout, cur);
443 }
444 continue;
445 }
446 /*
447 * It is possible for arch specific files to be
448 * found in a protodir but listed as arch
449 * independent in a protolist file. If this is
450 * a protodir vs. a protolist we will make
451 * that check.
452 */
453 if ((second_list.type == PROTODIR_LIST) &&
454 (cur->arch != P_ISA) &&
455 (first_list.type != PROTODIR_LIST)) {
456 /*
457 * do a lookup for same file, but as
458 * type ISA.
459 */
460 elem *e;
461
462 e = find_elem_isa(&first_list, cur,
463 NO_FOLLOW_LINK);
464 if (e) {
465 do_compare(e, cur,
466 first_list.type -
467 second_list.type);
468 continue;
469 }
470 }
471
472 print_elem(need_rm_fp, cur);
473 }
474 }
475 }
476 }
477
478 static void
check_first_vs_second(int verbose)479 check_first_vs_second(int verbose)
480 {
481 int i;
482 elem *e;
483 elem *cur;
484
485 for (i = 0; i < first_list.num_of_buckets; i++) {
486 for (cur = first_list.list[i]; cur; cur = cur->next) {
487 if ((first_list.type != second_list.type) &&
488 find_elem(&exception_list, cur, FOLLOW_LINK)) {
489 /*
490 * this entry is filtered, we don't need to do
491 * any more processing.
492 */
493 if (verbose) {
494 (void) printf("Filtered: Need "
495 "Addition of:\n\t");
496 print_elem(stdout, cur);
497 }
498 continue;
499 }
500
501 /*
502 * Search package database for file.
503 */
504 e = find_elem(&second_list, cur, NO_FOLLOW_LINK);
505
506 /*
507 * It is possible for arch specific files to be found
508 * in a protodir but listed as arch independent in a
509 * protolist file. If this is a protodir vs. a
510 * protolist we will make that check.
511 */
512 if (!e && (first_list.type == PROTODIR_LIST) &&
513 (cur->arch != P_ISA) &&
514 (second_list.type != PROTODIR_LIST)) {
515 /*
516 * do a lookup for same file, but as type ISA.
517 */
518 e = find_elem_isa(&second_list, cur,
519 NO_FOLLOW_LINK);
520 }
521
522 if (!e && (first_list.type != PROTODIR_LIST) &&
523 (cur->arch == P_ISA) &&
524 (second_list.type == PROTODIR_LIST)) {
525 /*
526 * do a lookup for same file, but as any
527 * type but ISA
528 */
529 e = find_elem_mach(&second_list, cur,
530 NO_FOLLOW_LINK);
531 }
532
533 if (e == NULL)
534 print_elem(need_add_fp, cur);
535 else {
536 do_compare(cur, e,
537 first_list.type - second_list.type);
538 e->flag |= VISITED_F;
539 }
540 }
541 }
542 }
543
544 static int
read_in_file(const char * file_name,elem_list * list)545 read_in_file(const char *file_name, elem_list *list)
546 {
547 struct stat st_buf;
548 int count = 0;
549
550 if (stat(file_name, &st_buf) == 0) {
551 if (S_ISREG(st_buf.st_mode)) {
552 if (verbose) {
553 (void) printf("file(%s): trying to process "
554 "as protolist...\n", file_name);
555 }
556 count = read_in_protolist(file_name, list, verbose);
557 } else if (S_ISDIR(st_buf.st_mode)) {
558 if (verbose)
559 (void) printf("directory(%s): trying to "
560 "process as protodir...\n", file_name);
561 count = read_in_protodir(file_name, list, verbose);
562 } else {
563 (void) fprintf(stderr,
564 "%s not a file or a directory.\n", file_name);
565 usage();
566 exit(1);
567 }
568 } else {
569 perror(file_name);
570 usage();
571 exit(1);
572 }
573
574 return (count);
575 }
576
577 /* ARGSUSED */
578 static int
set_values(const char * fname,const struct stat * sbp,int otype,struct FTW * ftw)579 set_values(const char *fname, const struct stat *sbp, int otype,
580 struct FTW *ftw)
581 {
582 elem *ep;
583 uid_t uid;
584 gid_t gid;
585 elem keyelem;
586 mode_t perm;
587
588 if (fname[0] == '\0' || fname[1] == '\0' || fname[2] == '\0')
589 return (0);
590 /* skip leading "./" */
591 fname += 2;
592 switch (otype) {
593 case FTW_F:
594 case FTW_D:
595 case FTW_DP:
596 if (strlcpy(keyelem.name, fname, sizeof (keyelem.name)) >=
597 sizeof (keyelem.name)) {
598 (void) fprintf(stderr, "%s: %s: name too long\n",
599 myname, fname);
600 return (1);
601 }
602 keyelem.arch = P_ISA;
603 ep = find_elem(&first_list, &keyelem, NO_FOLLOW_LINK);
604 if (ep == NULL) {
605 ep = find_elem_mach(&first_list, &keyelem,
606 NO_FOLLOW_LINK);
607 }
608 /*
609 * Do nothing if this is a hard or symbolic link,
610 * since links don't have this information.
611 *
612 * Assume it's a file on the exception list if it's
613 * not found in the packaging. Those are root:bin 755.
614 */
615 if (ep != NULL &&
616 (ep->file_type == SYM_LINK_T || ep->file_type == LINK_T)) {
617 return (0);
618 }
619 if (!set_group) {
620 gid = -1;
621 } else if (ep == NULL) {
622 gid = 0;
623 } else if ((gid = stdfind(ep->group, groupnames)) == -1) {
624 (void) fprintf(stderr, "%s: %s: group '%s' unknown\n",
625 myname, fname, ep->group);
626 return (1);
627 }
628 if (!set_user) {
629 uid = -1;
630 } else if (ep == NULL) {
631 uid = 2;
632 } else if ((uid = stdfind(ep->owner, usernames)) == -1) {
633 (void) fprintf(stderr, "%s: %s: user '%s' unknown\n",
634 myname, fname, ep->owner);
635 return (1);
636 }
637 if ((set_group && gid != -1 && gid != sbp->st_gid) ||
638 (set_user && uid != -1 && uid != sbp->st_uid)) {
639 if (verbose) {
640 const char *owner, *group;
641
642 owner = ep == NULL ? "root" : ep->owner;
643 group = ep == NULL ? "bin" : ep->group;
644 if (set_group && set_user) {
645 (void) printf("chown %s:%s %s\n",
646 owner, group, fname);
647 } else if (set_user) {
648 (void) printf("chown %s %s\n", owner,
649 fname);
650 } else {
651 (void) printf("chgrp %s %s\n", group,
652 fname);
653 }
654 }
655 if (lchown(fname, uid, gid) == -1) {
656 perror(fname);
657 return (1);
658 }
659 }
660 perm = ep == NULL ? 0755 : ep->perm;
661 if (set_perm && ((perm ^ sbp->st_mode) & ~S_IFMT) != 0) {
662 if (verbose)
663 (void) printf("chmod %lo %s\n", perm, fname);
664 if (chmod(fname, perm) == -1) {
665 perror(fname);
666 return (1);
667 }
668 }
669 return (0);
670 case FTW_DNR:
671 case FTW_NS:
672 (void) fprintf(stderr, "%s: %s: permission denied\n",
673 myname, fname);
674 return (1);
675 case FTW_SL:
676 case FTW_SLN:
677 return (0);
678 default:
679 return (1);
680 }
681 }
682
683 int
main(int argc,char ** argv)684 main(int argc, char **argv)
685 {
686 int errflg = 0;
687 int i, c;
688 int list_filtered_exceptions = NULL;
689 int n_proto_refs = 0;
690 int n_exception_files = 0;
691 char *proto_refs[MAX_PROTO_REFS];
692 char *exception_files[MAX_EXCEPTION_FILES];
693 struct stat st_buf;
694
695 if ((myname = argv[0]) == NULL)
696 myname = "protocmp";
697
698 while ((c = getopt(argc, argv, "gupGUPlmsLe:vd:")) != EOF) {
699 switch (c) {
700 case 's':
701 check_sym = 0;
702 break;
703 case 'm':
704 check_majmin = 0;
705 break;
706 case 'g':
707 check_group = 0;
708 break;
709 case 'G':
710 set_group = 1;
711 break;
712 case 'u':
713 check_user = 0;
714 break;
715 case 'U':
716 set_user = 1;
717 break;
718 case 'l':
719 check_link = 0;
720 break;
721 case 'p':
722 check_perm = 0;
723 break;
724 case 'P':
725 set_perm = 1;
726 break;
727 case 'e':
728 if (n_exception_files >= MAX_EXCEPTION_FILES) {
729 errflg++;
730 (void) fprintf(stderr,
731 "Only %d exception files supported\n",
732 MAX_EXCEPTION_FILES);
733 } else {
734 exception_files[n_exception_files++] = optarg;
735 }
736 break;
737 case 'L':
738 list_filtered_exceptions++;
739 break;
740 case 'v':
741 verbose++;
742 break;
743 case 'd':
744 if (n_proto_refs >= MAX_PROTO_REFS) {
745 errflg++;
746 (void) fprintf(stderr,
747 "Only %d proto references supported\n",
748 MAX_PROTO_REFS);
749 } else {
750 proto_refs[n_proto_refs++] = optarg;
751 }
752 break;
753 case '?':
754 default:
755 errflg++;
756 break;
757 }
758 }
759
760 if (argc == optind || n_proto_refs == 0) {
761 usage();
762 exit(1);
763 }
764
765 if (set_group || set_user || set_perm) {
766 if (optind != argc - 1) {
767 usage();
768 exit(1);
769 }
770 if (stat(argv[optind], &st_buf) == -1) {
771 perror(argv[optind]);
772 exit(1);
773 }
774 if (!S_ISDIR(st_buf.st_mode)) {
775 (void) fprintf(stderr, "%s: %s: not a directory\n",
776 myname, argv[optind]);
777 exit(1);
778 }
779 }
780
781 init_list(&first_list, HASH_SIZE);
782 init_list(&second_list, HASH_SIZE);
783 init_list(&exception_list, HASH_SIZE);
784
785 for (i = 0; i < n_exception_files; i++) {
786 (void) read_in_exceptions(exception_files[i], verbose);
787 }
788
789 for (i = 0; i < n_proto_refs; i++) {
790 first_file_name = proto_refs[i];
791 (void) read_in_file(first_file_name, &first_list);
792 }
793
794 if (set_group || set_user || set_perm) {
795 if (chdir(argv[optind]) == -1) {
796 perror(argv[optind]);
797 exit(1);
798 }
799 i = nftw(".", set_values, MAX_DEPTH, FTW_PHYS|FTW_DEPTH);
800 if (i == -1) {
801 perror("nftw");
802 i = 1;
803 }
804 exit(i);
805 }
806
807 for (i = optind; i < argc; i++) {
808 second_file_name = argv[i];
809 (void) read_in_file(second_file_name, &second_list);
810 }
811
812 open_output_files();
813
814 if (verbose)
815 (void) puts("comparing build to packages...");
816
817 check_first_vs_second(list_filtered_exceptions);
818
819 if (verbose)
820 (void) puts("checking over packages...");
821 check_second_vs_first(list_filtered_exceptions);
822
823 close_output_files();
824
825 print_results();
826
827 clean_up();
828
829 return (0);
830 }
831