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
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.
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]
23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
30 static int compare_manifests(FILE *rulesfile
, char *control
, char *test
,
31 boolean_t prog_fmt
, uint_t flags
);
32 static void extract_fname_ftype(char *line
, char *fname
, char *type
);
33 static int report_add(char *fname
, char *type
);
34 static int report_delete(char *fname
, char *type
);
35 static int evaluate_differences(char *control_line
, char *test_line
,
36 boolean_t prog_fmt
, int flags
);
37 static void report_error(char *fname
, char *type
, char *ctrl_val
,
38 char *test_val
, boolean_t prog_fmt
);
39 static int read_manifest_line(FILE *fd
, char *buf
, int buf_size
, int start_pos
,
40 char **line
, char *fname
);
41 static void parse_line(char *line
, char *fname
, char *type
, char *size
,
42 char *mode
, char *acl
, char *mtime
, char *uid
, char *gid
, char *contents
,
43 char *devnode
, char *dest
);
44 static void init_default_flags(uint_t
*flags
);
45 static void get_token(char *line
, int *curr_pos
, int line_len
, char *buf
,
49 bart_compare(int argc
, char **argv
)
51 char *control_fname
, *test_fname
;
53 FILE *rules_fd
= NULL
;
55 boolean_t prog_fmt
= B_FALSE
;
57 init_default_flags(&glob_flags
);
59 while ((c
= getopt(argc
, argv
, "pr:i:")) != EOF
) {
69 if (strcmp(optarg
, "-") == 0)
72 rules_fd
= fopen(optarg
, "r");
73 if (rules_fd
== NULL
) {
80 process_glob_ignores(optarg
, &glob_flags
);
89 /* Make sure we have the right number of args */
90 if ((optind
+ 2) != argc
)
93 control_fname
= argv
[0];
95 /* At this point, the filenames are sane, so do the comparison */
96 return (compare_manifests(rules_fd
, control_fname
, test_fname
,
97 prog_fmt
, glob_flags
));
101 compare_manifests(FILE *rulesfile
, char *control
, char *test
,
102 boolean_t prog_fmt
, uint_t flags
)
104 FILE *control_fd
, *test_fd
;
105 char *control_line
, *test_line
, control_buf
[BUF_SIZE
],
106 test_buf
[BUF_SIZE
], control_fname
[PATH_MAX
],
107 control_type
[TYPE_SIZE
], test_fname
[PATH_MAX
],
108 test_type
[TYPE_SIZE
];
109 int control_pos
, test_pos
, ret
, fname_cmp
, return_status
;
111 return_status
= EXIT
;
113 return_status
= read_rules(rulesfile
, "", flags
, 0);
115 control_fd
= fopen(control
, "r");
116 if (control_fd
== NULL
) {
121 test_fd
= fopen(test
, "r");
122 if (test_fd
== NULL
) {
127 control_pos
= read_manifest_line(control_fd
, control_buf
,
128 BUF_SIZE
, 0, &control_line
, control
);
129 test_pos
= read_manifest_line(test_fd
, test_buf
, BUF_SIZE
, 0,
132 while ((control_pos
!= -1) && (test_pos
!= -1)) {
133 ret
= strcmp(control_line
, test_line
);
135 /* Lines compare OK, just read the next lines.... */
136 control_pos
= read_manifest_line(control_fd
,
137 control_buf
, BUF_SIZE
, control_pos
, &control_line
,
139 test_pos
= read_manifest_line(test_fd
, test_buf
,
140 BUF_SIZE
, test_pos
, &test_line
, test
);
145 * Something didn't compare properly.
147 extract_fname_ftype(control_line
, control_fname
, control_type
);
148 extract_fname_ftype(test_line
, test_fname
, test_type
);
149 fname_cmp
= strcmp(control_fname
, test_fname
);
151 if (fname_cmp
== 0) {
153 * Filenames were the same, see what was
154 * different and continue.
156 if (evaluate_differences(control_line
, test_line
,
157 prog_fmt
, flags
) != 0)
158 return_status
= WARNING_EXIT
;
160 control_pos
= read_manifest_line(control_fd
,
161 control_buf
, BUF_SIZE
, control_pos
, &control_line
,
163 test_pos
= read_manifest_line(test_fd
, test_buf
,
164 BUF_SIZE
, test_pos
, &test_line
, test
);
165 } else if (fname_cmp
> 0) {
166 /* Filenames were different, a files was ADDED */
167 if (report_add(test_fname
, test_type
)) {
168 report_error(test_fname
, ADD_KEYWORD
, NULL
,
170 return_status
= WARNING_EXIT
;
172 test_pos
= read_manifest_line(test_fd
, test_buf
,
173 BUF_SIZE
, test_pos
, &test_line
, test
);
174 } else if (fname_cmp
< 0) {
175 /* Filenames were different, a files was DELETED */
176 if (report_delete(control_fname
, control_type
)) {
177 report_error(control_fname
, DELETE_KEYWORD
,
178 NULL
, NULL
, prog_fmt
);
179 return_status
= WARNING_EXIT
;
181 control_pos
= read_manifest_line(control_fd
,
182 control_buf
, BUF_SIZE
, control_pos
, &control_line
,
188 * Entering this while loop means files were DELETED from the test
191 while (control_pos
!= -1) {
192 (void) sscanf(control_line
, "%1023s", control_fname
);
193 if (report_delete(control_fname
, control_type
)) {
194 report_error(control_fname
, DELETE_KEYWORD
, NULL
,
196 return_status
= WARNING_EXIT
;
198 control_pos
= read_manifest_line(control_fd
, control_buf
,
199 BUF_SIZE
, control_pos
, &control_line
, control
);
203 * Entering this while loop means files were ADDED to the test
206 while (test_pos
!= -1) {
207 (void) sscanf(test_line
, "%1023s", test_fname
);
208 if (report_add(test_fname
, test_type
)) {
209 report_error(test_fname
, ADD_KEYWORD
, NULL
,
211 return_status
= WARNING_EXIT
;
213 test_pos
= read_manifest_line(test_fd
, test_buf
,
214 BUF_SIZE
, test_pos
, &test_line
, test
);
217 (void) fclose(control_fd
);
218 (void) fclose(test_fd
);
220 /* For programmatic mode, add a newline for cosmetic reasons */
221 if (prog_fmt
&& (return_status
!= 0))
224 return (return_status
);
228 parse_line(char *line
, char *fname
, char *type
, char *size
, char *mode
,
229 char *acl
, char *mtime
, char *uid
, char *gid
, char *contents
, char *devnode
,
234 line_len
= strlen(line
);
237 get_token(line
, &pos
, line_len
, fname
, PATH_MAX
);
238 get_token(line
, &pos
, line_len
, type
, TYPE_SIZE
);
239 get_token(line
, &pos
, line_len
, size
, MISC_SIZE
);
240 get_token(line
, &pos
, line_len
, mode
, MISC_SIZE
);
241 get_token(line
, &pos
, line_len
, acl
, ACL_SIZE
);
242 get_token(line
, &pos
, line_len
, mtime
, MISC_SIZE
);
243 get_token(line
, &pos
, line_len
, uid
, MISC_SIZE
);
244 get_token(line
, &pos
, line_len
, gid
, MISC_SIZE
);
246 /* Reset these fields... */
252 /* Handle filetypes which have a last field..... */
254 get_token(line
, &pos
, line_len
, contents
, PATH_MAX
);
255 else if ((type
[0] == 'B') || (type
[0] == 'C'))
256 get_token(line
, &pos
, line_len
, devnode
, PATH_MAX
);
257 else if (type
[0] == 'L')
258 get_token(line
, &pos
, line_len
, dest
, PATH_MAX
);
262 get_token(char *line
, int *curr_pos
, int line_len
, char *buf
, int buf_size
)
266 while (isspace(line
[*curr_pos
]) && (*curr_pos
< line_len
))
269 while (!isspace(line
[*curr_pos
]) &&
270 (*curr_pos
< line_len
) && (cnt
< (buf_size
-1))) {
271 buf
[cnt
] = line
[*curr_pos
];
279 * Utility function: extract fname and type from this line
282 extract_fname_ftype(char *line
, char *fname
, char *type
)
287 line_len
= strlen(line
);
289 get_token(line
, &pos
, line_len
, fname
, PATH_MAX
);
290 get_token(line
, &pos
, line_len
, type
, TYPE_SIZE
);
294 * Utility function: tells us whether or not this addition should be reported
296 * Returns 0 if the discrepancy is ignored, non-zero if the discrepancy is
300 report_add(char *fname
, char *type
)
302 struct rule
*rule_ptr
;
304 rule_ptr
= check_rules(fname
, type
[0]);
305 if ((rule_ptr
!= NULL
) && (rule_ptr
->attr_list
& ATTR_ADD
))
312 * Utility function: tells us whether or not this deletion should be reported
314 * Returns 0 if the discrepancy is ignored, non-zero if the discrepancy is
318 report_delete(char *fname
, char *type
)
320 struct rule
*rule_ptr
;
322 rule_ptr
= check_rules(fname
, type
[0]);
324 if ((rule_ptr
!= NULL
) && (rule_ptr
->attr_list
& ATTR_DELETE
))
331 * This function takes in the two entries, which have been flagged as
332 * different, breaks them up and reports discrepancies. Note, discrepancies
333 * are affected by the 'CHECK' and 'IGNORE' stanzas which may apply to
336 * Returns the number of discrepancies reported.
339 evaluate_differences(char *control_line
, char *test_line
,
340 boolean_t prog_fmt
, int flags
)
342 char ctrl_fname
[PATH_MAX
], test_fname
[PATH_MAX
],
343 ctrl_type
[TYPE_SIZE
], test_type
[TYPE_SIZE
],
344 ctrl_size
[MISC_SIZE
], ctrl_mode
[MISC_SIZE
],
345 ctrl_acl
[ACL_SIZE
], ctrl_mtime
[MISC_SIZE
],
346 ctrl_uid
[MISC_SIZE
], ctrl_gid
[MISC_SIZE
],
347 ctrl_dest
[PATH_MAX
], ctrl_contents
[PATH_MAX
],
348 ctrl_devnode
[PATH_MAX
], test_size
[MISC_SIZE
],
349 test_mode
[MISC_SIZE
], test_acl
[ACL_SIZE
],
350 test_mtime
[MISC_SIZE
], test_uid
[MISC_SIZE
],
351 test_gid
[MISC_SIZE
], test_dest
[PATH_MAX
],
352 test_contents
[PATH_MAX
], test_devnode
[PATH_MAX
],
355 struct rule
*rule_ptr
;
359 parse_line(control_line
, ctrl_fname
, ctrl_type
, ctrl_size
, ctrl_mode
,
360 ctrl_acl
, ctrl_mtime
, ctrl_uid
, ctrl_gid
, ctrl_contents
,
361 ctrl_devnode
, ctrl_dest
);
364 * Now we know the fname and type, let's get the rule that matches this
365 * manifest entry. If there is a match, make sure to setup the
366 * correct reporting flags.
368 rule_ptr
= check_rules(ctrl_fname
, ctrl_type
[0]);
369 if (rule_ptr
!= NULL
)
370 flags
= rule_ptr
->attr_list
;
372 parse_line(test_line
, test_fname
, test_type
, test_size
, test_mode
,
373 test_acl
, test_mtime
, test_uid
, test_gid
, test_contents
,
374 test_devnode
, test_dest
);
377 * Report the errors based upon which keywords have been set by
380 if ((flags
& ATTR_TYPE
) && (ctrl_type
[0] != test_type
[0])) {
381 report_error(ctrl_fname
, TYPE_KEYWORD
, ctrl_type
,
382 test_type
, prog_fmt
);
386 if ((flags
& ATTR_SIZE
) && (strcmp(ctrl_size
, test_size
) != 0)) {
387 report_error(ctrl_fname
, SIZE_KEYWORD
, ctrl_size
,
388 test_size
, prog_fmt
);
392 if ((flags
& ATTR_MODE
) && (strcmp(ctrl_mode
, test_mode
) != 0)) {
393 report_error(ctrl_fname
, MODE_KEYWORD
, ctrl_mode
,
394 test_mode
, prog_fmt
);
398 if ((flags
& ATTR_ACL
) && (strcmp(ctrl_acl
, test_acl
) != 0)) {
399 report_error(ctrl_fname
, ACL_KEYWORD
, ctrl_acl
,
404 if ((flags
& ATTR_MTIME
) && (ctrl_type
[0] == test_type
[0])) {
405 if (strcmp(ctrl_mtime
, test_mtime
) != 0) {
406 switch (ctrl_type
[0]) {
418 report_error(ctrl_fname
, tag
, ctrl_mtime
,
419 test_mtime
, prog_fmt
);
424 if ((ctrl_type
[0] == 'F') && (flags
& ATTR_MTIME
) &&
425 (strcmp(ctrl_mtime
, test_mtime
) != 0)) {
426 report_error(ctrl_fname
, MTIME_KEYWORD
, ctrl_mtime
, test_mtime
,
431 if ((ctrl_type
[0] == 'D') && (flags
& ATTR_DIRMTIME
) &&
432 (strcmp(ctrl_mtime
, test_mtime
) != 0)) {
433 report_error(ctrl_fname
, DIRMTIME_KEYWORD
, ctrl_mtime
,
434 test_mtime
, prog_fmt
);
438 if ((ctrl_type
[0] == 'L') && (flags
& ATTR_LNMTIME
) &&
439 (strcmp(ctrl_mtime
, test_mtime
) != 0)) {
440 report_error(ctrl_fname
, LNMTIME_KEYWORD
, ctrl_mtime
,
441 test_mtime
, prog_fmt
);
444 } else if ((flags
& ATTR_MTIME
) &&
445 (strcmp(ctrl_mtime
, test_mtime
) != 0)) {
446 report_error(ctrl_fname
, MTIME_KEYWORD
, ctrl_mtime
,
447 test_mtime
, prog_fmt
);
451 if ((flags
& ATTR_UID
) && (strcmp(ctrl_uid
, test_uid
) != 0)) {
452 report_error(ctrl_fname
, UID_KEYWORD
, ctrl_uid
,
457 if ((flags
& ATTR_GID
) && (strcmp(ctrl_gid
, test_gid
) != 0)) {
458 report_error(ctrl_fname
, GID_KEYWORD
, ctrl_gid
,
463 if ((flags
& ATTR_DEVNODE
) &&
464 (strcmp(ctrl_devnode
, test_devnode
) != 0)) {
465 report_error(ctrl_fname
, DEVNODE_KEYWORD
, ctrl_devnode
,
466 test_devnode
, prog_fmt
);
470 if ((flags
& ATTR_DEST
) && (strcmp(ctrl_dest
, test_dest
) != 0)) {
471 report_error(ctrl_fname
, DEST_KEYWORD
, ctrl_dest
,
472 test_dest
, prog_fmt
);
476 if ((flags
& ATTR_CONTENTS
) &&
477 (strcmp(ctrl_contents
, test_contents
)) != 0) {
478 report_error(ctrl_fname
, CONTENTS_KEYWORD
, ctrl_contents
,
479 test_contents
, prog_fmt
);
487 * Function responsible for reporting errors.
490 report_error(char *fname
, char *type
, char *ctrl_val
, char *test_val
,
493 static char last_fname
[PATH_MAX
] = "";
497 if (strcmp(fname
, last_fname
) != 0) {
498 (void) printf("%s:\n", fname
);
499 (void) strlcpy(last_fname
, fname
, sizeof (last_fname
));
502 if (strcmp(type
, ADD_KEYWORD
) == 0 ||
503 strcmp(type
, DELETE_KEYWORD
) == 0)
504 (void) printf(" %s\n", type
);
506 (void) printf(" %s control:%s test:%s\n", type
,
509 /* Programmatic mode */
510 if (strcmp(fname
, last_fname
) != 0) {
511 /* Ensure a line is not printed for the initial case */
512 if (strlen(last_fname
) != 0)
514 (void) strlcpy(last_fname
, fname
, sizeof (last_fname
));
515 (void) printf("%s ", fname
);
518 (void) printf("%s ", type
);
519 if (strcmp(type
, ADD_KEYWORD
) != 0 &&
520 strcmp(type
, DELETE_KEYWORD
) != 0) {
521 (void) printf("%s ", ctrl_val
);
522 (void) printf("%s ", test_val
);
528 * Function responsible for reading in a line from the manifest.
529 * Takes in the file ptr and a buffer, parses the buffer and sets the 'line'
530 * ptr correctly. In the case when the buffer is fully parsed, this function
531 * reads more data from the file ptr and refills the buffer.
534 read_manifest_line(FILE *fd
, char *buf
, int buf_size
, int start_pos
,
535 char **line
, char *fname
)
537 int end_pos
, len
, iscomment
= 0, filepos
;
540 * Initialization case: make sure the manifest version is OK
542 if (start_pos
== 0) {
546 (void) fread((void *) buf
, (size_t)buf_size
, (size_t)1, fd
);
551 if (strncmp(buf
, MANIFEST_VER
,
552 strlen(MANIFEST_VER
)) != 0)
553 (void) fprintf(stderr
, MISSING_VER
, fname
);
554 if ((*line
[0] == '!') || (*line
[0] == '#'))
558 while ((buf
[end_pos
] != '\n') &&
559 (buf
[end_pos
] != '\0') &&
560 (end_pos
< buf_size
))
563 if (end_pos
>= buf_size
)
567 *line
= &(buf
[end_pos
]);
569 if ((*line
[0] == '!') || (*line
[0] == '#'))
574 while ((buf
[end_pos
] != '\n') && (buf
[end_pos
] != '\0') &&
575 (end_pos
< buf_size
))
578 if (end_pos
< buf_size
) {
579 if (buf
[end_pos
] == '\n') {
584 if (buf
[end_pos
] == '\0')
588 (void) fprintf(stderr
, MANIFEST_ERR
);
592 end_pos
= (start_pos
+1);
593 *line
= &(buf
[end_pos
]);
595 /* Read the buffer until EOL or the buffer is empty */
596 while ((buf
[end_pos
] != '\n') && (buf
[end_pos
] != '\0') &&
597 (end_pos
< buf_size
))
600 if (end_pos
< buf_size
) {
601 /* Found the end of the line, normal exit */
602 if (buf
[end_pos
] == '\n') {
607 /* No more input to read */
608 if (buf
[end_pos
] == '\0')
613 * The following code takes the remainder of the buffer and
614 * puts it at the beginning. The space after the remainder, which
615 * is now at the beginning, is blanked.
616 * At this point, read in more data and continue to find the EOL....
618 len
= end_pos
- (start_pos
+ 1);
619 (void) memcpy(buf
, &(buf
[start_pos
+1]), (size_t)len
);
620 (void) memset(&buf
[len
], '\0', (buf_size
- len
));
621 (void) fread((void *) &buf
[len
], (size_t)(buf_size
-len
), (size_t)1, fd
);
625 /* Read the buffer until EOL or the buffer is empty */
626 while ((buf
[end_pos
] != '\n') && (buf
[end_pos
] != '\0') &&
627 (end_pos
< buf_size
))
630 if (end_pos
< buf_size
) {
631 /* Found the end of the line, normal exit */
632 if (buf
[end_pos
] == '\n') {
637 /* No more input to read */
638 if (buf
[end_pos
] == '\0')
642 (void) fprintf(stderr
, MANIFEST_ERR
);
649 init_default_flags(uint_t
*flags
)
651 /* Default behavior: everything is checked *except* dirmtime */
652 *flags
= ATTR_ALL
& ~(ATTR_DIRMTIME
);