Home | History | Annotate | Download | only in bart
      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 2003 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 #include <unistd.h>
     29 #include "bart.h"
     30 
     31 static int compare_manifests(FILE *rulesfile, char *control, char *test,
     32     boolean_t prog_fmt, uint_t flags);
     33 static void extract_fname_ftype(char *line, char *fname, char *type);
     34 static int report_add(char *fname, char *type);
     35 static int report_delete(char *fname, char *type);
     36 static int evaluate_differences(char *control_line, char *test_line,
     37     boolean_t prog_fmt, int flags);
     38 static void report_error(char *fname, char *type, char *ctrl_val,
     39     char *test_val, boolean_t prog_fmt);
     40 static int read_manifest_line(FILE *fd, char *buf, int buf_size, int start_pos,
     41     char **line, char *fname);
     42 static void parse_line(char *line, char *fname, char *type, char *size,
     43     char *mode, char *acl, char *mtime, char *uid, char *gid, char *contents,
     44     char *devnode, char *dest);
     45 static void init_default_flags(uint_t *flags);
     46 static void get_token(char *line, int *curr_pos, int line_len, char *buf,
     47     int buf_size);
     48 
     49 int
     50 bart_compare(int argc, char **argv)
     51 {
     52 	char			*control_fname, *test_fname;
     53 	int			c;
     54 	FILE			*rules_fd = NULL;
     55 	uint_t 			glob_flags;
     56 	boolean_t		prog_fmt = B_FALSE;
     57 
     58 	init_default_flags(&glob_flags);
     59 
     60 	while ((c = getopt(argc, argv, "pr:i:")) != EOF) {
     61 		switch (c) {
     62 		case 'p':
     63 			prog_fmt = B_TRUE;
     64 			break;
     65 
     66 		case 'r':
     67 			if (optarg == NULL)
     68 				usage();
     69 
     70 			if (strcmp(optarg, "-") == 0)
     71 				rules_fd = stdin;
     72 			else
     73 				rules_fd = fopen(optarg, "r");
     74 			if (rules_fd == NULL) {
     75 				perror(optarg);
     76 				usage();
     77 			}
     78 			break;
     79 
     80 		case 'i':
     81 			process_glob_ignores(optarg, &glob_flags);
     82 			break;
     83 
     84 		case '?':
     85 		default:
     86 			usage();
     87 		}
     88 	}
     89 
     90 	/* Make sure we have the right number of args */
     91 	if ((optind + 2) != argc)
     92 		usage();
     93 	argv += optind;
     94 	control_fname = argv[0];
     95 	test_fname = argv[1];
     96 	/* At this point, the filenames are sane, so do the comparison */
     97 	return (compare_manifests(rules_fd, control_fname, test_fname,
     98 	    prog_fmt, glob_flags));
     99 }
    100 
    101 static int
    102 compare_manifests(FILE *rulesfile, char *control, char *test,
    103 boolean_t prog_fmt, uint_t flags)
    104 {
    105 	FILE	*control_fd, *test_fd;
    106 	char	*control_line, *test_line, control_buf[BUF_SIZE],
    107 		test_buf[BUF_SIZE], control_fname[PATH_MAX],
    108 		control_type[TYPE_SIZE], test_fname[PATH_MAX],
    109 		test_type[TYPE_SIZE];
    110 	int	control_pos, test_pos, ret, fname_cmp, return_status;
    111 
    112 	return_status = EXIT;
    113 
    114 	return_status = read_rules(rulesfile, "", flags, 0);
    115 
    116 	control_fd = fopen(control, "r");
    117 	if (control_fd == NULL) {
    118 		perror(control);
    119 		return (FATAL_EXIT);
    120 	}
    121 
    122 	test_fd = fopen(test, "r");
    123 	if (test_fd == NULL) {
    124 		perror(test);
    125 		return (FATAL_EXIT);
    126 	}
    127 
    128 	control_pos = read_manifest_line(control_fd, control_buf,
    129 	    BUF_SIZE, 0, &control_line, control);
    130 	test_pos = read_manifest_line(test_fd, test_buf, BUF_SIZE, 0,
    131 	    &test_line, test);
    132 
    133 	while ((control_pos != -1) && (test_pos != -1)) {
    134 		ret = strcmp(control_line, test_line);
    135 		if (ret == 0) {
    136 			/* Lines compare OK, just read the next lines.... */
    137 			control_pos = read_manifest_line(control_fd,
    138 			    control_buf, BUF_SIZE, control_pos, &control_line,
    139 			    control);
    140 			test_pos = read_manifest_line(test_fd, test_buf,
    141 			    BUF_SIZE, test_pos, &test_line, test);
    142 			continue;
    143 		}
    144 
    145 		/*
    146 		 * Something didn't compare properly.
    147 		 */
    148 		extract_fname_ftype(control_line, control_fname, control_type);
    149 		extract_fname_ftype(test_line, test_fname, test_type);
    150 		fname_cmp = strcmp(control_fname, test_fname);
    151 
    152 		if (fname_cmp == 0) {
    153 			/*
    154 			 * Filenames were the same, see what was
    155 			 * different and continue.
    156 			 */
    157 			if (evaluate_differences(control_line, test_line,
    158 			    prog_fmt, flags) != 0)
    159 				return_status = WARNING_EXIT;
    160 
    161 			control_pos = read_manifest_line(control_fd,
    162 			    control_buf, BUF_SIZE, control_pos, &control_line,
    163 			    control);
    164 			test_pos = read_manifest_line(test_fd, test_buf,
    165 			    BUF_SIZE, test_pos, &test_line, test);
    166 		} else if (fname_cmp > 0) {
    167 			/* Filenames were different, a files was ADDED */
    168 			if (report_add(test_fname, test_type)) {
    169 				report_error(test_fname, ADD_KEYWORD, NULL,
    170 				    NULL, prog_fmt);
    171 				return_status = WARNING_EXIT;
    172 			}
    173 			test_pos = read_manifest_line(test_fd, test_buf,
    174 			    BUF_SIZE, test_pos, &test_line, test);
    175 		} else if (fname_cmp < 0) {
    176 			/* Filenames were different, a files was DELETED */
    177 			if (report_delete(control_fname, control_type)) {
    178 				report_error(control_fname, DELETE_KEYWORD,
    179 				    NULL, NULL, prog_fmt);
    180 				return_status = WARNING_EXIT;
    181 			}
    182 			control_pos = read_manifest_line(control_fd,
    183 			    control_buf, BUF_SIZE, control_pos, &control_line,
    184 			    control);
    185 		}
    186 	}
    187 
    188 	/*
    189 	 * Entering this while loop means files were DELETED from the test
    190 	 * manifest.
    191 	 */
    192 	while (control_pos != -1) {
    193 		(void) sscanf(control_line, "%1023s", control_fname);
    194 		if (report_delete(control_fname, control_type)) {
    195 			report_error(control_fname, DELETE_KEYWORD, NULL,
    196 			    NULL, prog_fmt);
    197 			return_status = WARNING_EXIT;
    198 		}
    199 		control_pos = read_manifest_line(control_fd, control_buf,
    200 		    BUF_SIZE, control_pos, &control_line, control);
    201 	}
    202 
    203 	/*
    204 	 * Entering this while loop means files were ADDED to the test
    205 	 * manifest.
    206 	 */
    207 	while (test_pos != -1) {
    208 		(void) sscanf(test_line, "%1023s", test_fname);
    209 		if (report_add(test_fname, test_type)) {
    210 			report_error(test_fname, ADD_KEYWORD, NULL,
    211 			    NULL, prog_fmt);
    212 			return_status = WARNING_EXIT;
    213 		}
    214 		test_pos = read_manifest_line(test_fd, test_buf,
    215 		    BUF_SIZE, test_pos, &test_line, test);
    216 	}
    217 
    218 	(void) fclose(control_fd);
    219 	(void) fclose(test_fd);
    220 
    221 	/* For programmatic mode, add a newline for cosmetic reasons */
    222 	if (prog_fmt && (return_status != 0))
    223 		(void) printf("\n");
    224 
    225 	return (return_status);
    226 }
    227 
    228 static void
    229 parse_line(char *line, char *fname, char *type, char *size, char *mode,
    230 char *acl, char *mtime, char *uid, char *gid, char *contents, char *devnode,
    231 char *dest)
    232 {
    233 	int		pos, line_len;
    234 
    235 	line_len = strlen(line);
    236 	pos = 0;
    237 
    238 	get_token(line, &pos, line_len, fname, PATH_MAX);
    239 	get_token(line, &pos, line_len, type, TYPE_SIZE);
    240 	get_token(line, &pos, line_len, size, MISC_SIZE);
    241 	get_token(line, &pos, line_len, mode, MISC_SIZE);
    242 	get_token(line, &pos, line_len, acl, ACL_SIZE);
    243 	get_token(line, &pos, line_len, mtime, MISC_SIZE);
    244 	get_token(line, &pos, line_len, uid, MISC_SIZE);
    245 	get_token(line, &pos, line_len, gid, MISC_SIZE);
    246 
    247 	/* Reset these fields... */
    248 
    249 	*contents = NULL;
    250 	*devnode = '\0';
    251 	*dest = '\0';
    252 
    253 	/* Handle filetypes which have a last field..... */
    254 	if (type[0] == 'F')
    255 		get_token(line, &pos, line_len, contents, PATH_MAX);
    256 	else if ((type[0] == 'B') || (type[0] == 'C'))
    257 		get_token(line, &pos, line_len, devnode, PATH_MAX);
    258 	else if (type[0] == 'L')
    259 		get_token(line, &pos, line_len, dest, PATH_MAX);
    260 }
    261 
    262 static void
    263 get_token(char *line, int *curr_pos, int line_len, char *buf, int buf_size)
    264 {
    265 	int	cnt = 0;
    266 
    267 	while (isspace(line[*curr_pos]) && (*curr_pos < line_len))
    268 		(*curr_pos)++;
    269 
    270 	while (!isspace(line[*curr_pos]) &&
    271 	    (*curr_pos < line_len) && (cnt < (buf_size-1))) {
    272 		buf[cnt] = line[*curr_pos];
    273 		(*curr_pos)++;
    274 		cnt++;
    275 	}
    276 	buf[cnt] = '\0';
    277 }
    278 
    279 /*
    280  * Utility function: extract fname and type from this line
    281  */
    282 static void
    283 extract_fname_ftype(char *line, char *fname, char *type)
    284 {
    285 	int		line_len, pos;
    286 
    287 	pos = 0;
    288 	line_len = strlen(line);
    289 
    290 	get_token(line, &pos, line_len, fname, PATH_MAX);
    291 	get_token(line, &pos, line_len, type, TYPE_SIZE);
    292 }
    293 
    294 /*
    295  * Utility function: tells us whether or not this addition should be reported
    296  *
    297  * Returns 0 if the discrepancy is ignored, non-zero if the discrepancy is
    298  * reported.
    299  */
    300 static int
    301 report_add(char *fname, char *type)
    302 {
    303 	struct rule	*rule_ptr;
    304 
    305 	rule_ptr = check_rules(fname, type[0]);
    306 	if ((rule_ptr != NULL) && (rule_ptr->attr_list & ATTR_ADD))
    307 		return (1);
    308 	else
    309 		return (0);
    310 }
    311 
    312 /*
    313  * Utility function: tells us whether or not this deletion should be reported
    314  *
    315  * Returns 0 if the discrepancy is ignored, non-zero if the discrepancy is
    316  * reported.
    317  */
    318 static int
    319 report_delete(char *fname, char *type)
    320 {
    321 	struct rule	*rule_ptr;
    322 
    323 	rule_ptr = check_rules(fname, type[0]);
    324 
    325 	if ((rule_ptr != NULL) && (rule_ptr->attr_list & ATTR_DELETE))
    326 		return (1);
    327 	else
    328 		return (0);
    329 }
    330 
    331 /*
    332  * This function takes in the two entries, which have been flagged as
    333  * different, breaks them up and reports discrepancies.  Note, discrepancies
    334  * are affected by the 'CHECK' and 'IGNORE' stanzas which may apply to
    335  * these entries.
    336  *
    337  * Returns the number of discrepancies reported.
    338  */
    339 static int
    340 evaluate_differences(char *control_line, char *test_line,
    341     boolean_t prog_fmt, int flags)
    342 {
    343 	char		ctrl_fname[PATH_MAX], test_fname[PATH_MAX],
    344 			ctrl_type[TYPE_SIZE], test_type[TYPE_SIZE],
    345 			ctrl_size[MISC_SIZE], ctrl_mode[MISC_SIZE],
    346 			ctrl_acl[ACL_SIZE], ctrl_mtime[MISC_SIZE],
    347 			ctrl_uid[MISC_SIZE], ctrl_gid[MISC_SIZE],
    348 			ctrl_dest[PATH_MAX], ctrl_contents[PATH_MAX],
    349 			ctrl_devnode[PATH_MAX], test_size[MISC_SIZE],
    350 			test_mode[MISC_SIZE], test_acl[ACL_SIZE],
    351 			test_mtime[MISC_SIZE], test_uid[MISC_SIZE],
    352 			test_gid[MISC_SIZE], test_dest[PATH_MAX],
    353 			test_contents[PATH_MAX], test_devnode[PATH_MAX],
    354 			*tag;
    355 	int		ret_val;
    356 	struct rule	*rule_ptr;
    357 
    358 	ret_val = 0;
    359 
    360 	parse_line(control_line, ctrl_fname, ctrl_type, ctrl_size, ctrl_mode,
    361 	    ctrl_acl, ctrl_mtime, ctrl_uid, ctrl_gid, ctrl_contents,
    362 	    ctrl_devnode, ctrl_dest);
    363 
    364 	/*
    365 	 * Now we know the fname and type, let's get the rule that matches this
    366 	 * manifest entry.  If there is a match, make sure to setup the
    367 	 * correct reporting flags.
    368 	 */
    369 	rule_ptr = check_rules(ctrl_fname, ctrl_type[0]);
    370 	if (rule_ptr != NULL)
    371 		flags = rule_ptr->attr_list;
    372 
    373 	parse_line(test_line, test_fname, test_type, test_size, test_mode,
    374 	    test_acl, test_mtime, test_uid, test_gid, test_contents,
    375 	    test_devnode, test_dest);
    376 
    377 	/*
    378 	 * Report the errors based upon which keywords have been set by
    379 	 * the user.
    380 	 */
    381 	if ((flags & ATTR_TYPE) && (ctrl_type[0] != test_type[0])) {
    382 		report_error(ctrl_fname, TYPE_KEYWORD, ctrl_type,
    383 		    test_type, prog_fmt);
    384 		ret_val++;
    385 	}
    386 
    387 	if ((flags & ATTR_SIZE) && (strcmp(ctrl_size, test_size) != 0)) {
    388 		report_error(ctrl_fname, SIZE_KEYWORD, ctrl_size,
    389 		    test_size, prog_fmt);
    390 		ret_val++;
    391 	}
    392 
    393 	if ((flags & ATTR_MODE) && (strcmp(ctrl_mode, test_mode) != 0)) {
    394 		report_error(ctrl_fname, MODE_KEYWORD, ctrl_mode,
    395 		    test_mode, prog_fmt);
    396 		ret_val++;
    397 	}
    398 
    399 	if ((flags & ATTR_ACL) && (strcmp(ctrl_acl, test_acl) != 0)) {
    400 		report_error(ctrl_fname, ACL_KEYWORD, ctrl_acl,
    401 		    test_acl, prog_fmt);
    402 		ret_val++;
    403 	}
    404 
    405 	if ((flags & ATTR_MTIME) && (ctrl_type[0] == test_type[0])) {
    406 		if (strcmp(ctrl_mtime, test_mtime) != 0) {
    407 			switch (ctrl_type[0]) {
    408 			case 'D':
    409 				tag = "dirmtime";
    410 				break;
    411 			case 'L':
    412 				tag = "lnmtime";
    413 				break;
    414 			default:
    415 				tag = "mtime";
    416 				break;
    417 			}
    418 			if (flags == 0) {
    419 				report_error(ctrl_fname, tag, ctrl_mtime,
    420 				    test_mtime, prog_fmt);
    421 			ret_val++;
    422 		}
    423 	}
    424 
    425 	if ((ctrl_type[0] == 'F') && (flags & ATTR_MTIME) &&
    426 	    (strcmp(ctrl_mtime, test_mtime) != 0)) {
    427 		report_error(ctrl_fname, MTIME_KEYWORD, ctrl_mtime, test_mtime,
    428 		    prog_fmt);
    429 		ret_val++;
    430 	}
    431 
    432 	if ((ctrl_type[0] == 'D') && (flags & ATTR_DIRMTIME) &&
    433 	    (strcmp(ctrl_mtime, test_mtime) != 0)) {
    434 		report_error(ctrl_fname, DIRMTIME_KEYWORD, ctrl_mtime,
    435 		    test_mtime, prog_fmt);
    436 		ret_val++;
    437 	}
    438 
    439 	if ((ctrl_type[0] == 'L') && (flags & ATTR_LNMTIME) &&
    440 	    (strcmp(ctrl_mtime, test_mtime) != 0)) {
    441 		report_error(ctrl_fname, LNMTIME_KEYWORD, ctrl_mtime,
    442 		    test_mtime, prog_fmt);
    443 		ret_val++;
    444 	}
    445 	} else if ((flags & ATTR_MTIME) &&
    446 	    (strcmp(ctrl_mtime, test_mtime) != 0)) {
    447 		report_error(ctrl_fname, MTIME_KEYWORD, ctrl_mtime,
    448 		    test_mtime, prog_fmt);
    449 		ret_val++;
    450 	}
    451 
    452 	if ((flags & ATTR_UID) && (strcmp(ctrl_uid, test_uid) != 0)) {
    453 		report_error(ctrl_fname, UID_KEYWORD, ctrl_uid,
    454 		    test_uid, prog_fmt);
    455 		ret_val++;
    456 	}
    457 
    458 	if ((flags & ATTR_GID) && (strcmp(ctrl_gid, test_gid) != 0)) {
    459 		report_error(ctrl_fname, GID_KEYWORD, ctrl_gid,
    460 		    test_gid, prog_fmt);
    461 		ret_val++;
    462 	}
    463 
    464 	if ((flags & ATTR_DEVNODE) &&
    465 	    (strcmp(ctrl_devnode, test_devnode) != 0)) {
    466 		report_error(ctrl_fname, DEVNODE_KEYWORD, ctrl_devnode,
    467 		    test_devnode, prog_fmt);
    468 		ret_val++;
    469 	}
    470 
    471 	if ((flags & ATTR_DEST) && (strcmp(ctrl_dest, test_dest) != 0)) {
    472 		report_error(ctrl_fname, DEST_KEYWORD, ctrl_dest,
    473 		    test_dest, prog_fmt);
    474 		ret_val++;
    475 	}
    476 
    477 	if ((flags & ATTR_CONTENTS) &&
    478 	    (strcmp(ctrl_contents, test_contents)) != 0) {
    479 		report_error(ctrl_fname, CONTENTS_KEYWORD, ctrl_contents,
    480 		    test_contents, prog_fmt);
    481 		ret_val++;
    482 	}
    483 
    484 	return (ret_val);
    485 }
    486 
    487 /*
    488  * Function responsible for reporting errors.
    489  */
    490 static void
    491 report_error(char *fname, char *type, char *ctrl_val, char *test_val,
    492     boolean_t prog_fmt)
    493 {
    494 	static char	last_fname[PATH_MAX] = "";
    495 
    496 	if (!prog_fmt) {
    497 		/* Verbose mode */
    498 		if (strcmp(fname, last_fname) != 0) {
    499 			(void) printf("%s:\n", fname);
    500 			(void) strlcpy(last_fname, fname, sizeof (last_fname));
    501 		}
    502 
    503 		if (strcmp(type, ADD_KEYWORD) == 0 ||
    504 		    strcmp(type, DELETE_KEYWORD) == 0)
    505 			(void) printf("  %s\n", type);
    506 		else
    507 			(void) printf("  %s  control:%s  test:%s\n", type,
    508 			    ctrl_val, test_val);
    509 	} else {
    510 		/* Programmatic mode */
    511 		if (strcmp(fname, last_fname) != 0) {
    512 			/* Ensure a line is not printed for the initial case */
    513 			if (strlen(last_fname) != 0)
    514 				(void) printf("\n");
    515 			(void) strlcpy(last_fname, fname, sizeof (last_fname));
    516 			(void) printf("%s ", fname);
    517 		}
    518 
    519 		(void) printf("%s ", type);
    520 		if (strcmp(type, ADD_KEYWORD) != 0 &&
    521 		    strcmp(type, DELETE_KEYWORD) != 0) {
    522 			(void) printf("%s ", ctrl_val);
    523 			(void) printf("%s ", test_val);
    524 		}
    525 	}
    526 }
    527 
    528 /*
    529  * Function responsible for reading in a line from the manifest.
    530  * Takes in the file ptr and a buffer, parses the buffer  and sets the 'line'
    531  * ptr correctly.  In the case when the buffer is fully parsed, this function
    532  * reads more data from the file ptr and refills the buffer.
    533  */
    534 static int
    535 read_manifest_line(FILE *fd, char *buf, int buf_size, int start_pos,
    536     char **line, char *fname)
    537 {
    538 	int	end_pos, len, iscomment = 0, filepos;
    539 
    540 	/*
    541 	 * Initialization case: make sure the manifest version is OK
    542 	 */
    543 	if (start_pos == 0) {
    544 		end_pos = 0;
    545 		buf[0] = '\0';
    546 		filepos = ftell(fd);
    547 		(void) fread((void *) buf, (size_t)buf_size, (size_t)1, fd);
    548 
    549 		*line = buf;
    550 
    551 		if (filepos == 0) {
    552 			if (strncmp(buf, MANIFEST_VER,
    553 			    strlen(MANIFEST_VER)) != 0)
    554 				(void) fprintf(stderr, MISSING_VER, fname);
    555 			if ((*line[0] == '!') || (*line[0] == '#'))
    556 				iscomment++;
    557 
    558 			while (iscomment) {
    559 				while ((buf[end_pos] != '\n') &&
    560 				    (buf[end_pos] != '\0') &&
    561 				    (end_pos < buf_size))
    562 					end_pos++;
    563 
    564 				if (end_pos >= buf_size)
    565 					return (-1);
    566 
    567 				end_pos++;
    568 				*line = &(buf[end_pos]);
    569 				iscomment = 0;
    570 				if ((*line[0] == '!') || (*line[0] == '#'))
    571 					iscomment++;
    572 			}
    573 		}
    574 
    575 		while ((buf[end_pos] != '\n') && (buf[end_pos] != '\0') &&
    576 		    (end_pos < buf_size))
    577 			end_pos++;
    578 
    579 		if (end_pos < buf_size) {
    580 			if (buf[end_pos] == '\n') {
    581 				buf[end_pos] = '\0';
    582 				return (end_pos);
    583 			}
    584 
    585 			if (buf[end_pos] == '\0')
    586 				return (-1);
    587 		}
    588 
    589 		(void) fprintf(stderr, MANIFEST_ERR);
    590 		exit(FATAL_EXIT);
    591 	}
    592 
    593 	end_pos = (start_pos+1);
    594 	*line = &(buf[end_pos]);
    595 
    596 	/* Read the buffer until EOL or the buffer is empty */
    597 	while ((buf[end_pos] != '\n') && (buf[end_pos] != '\0') &&
    598 	    (end_pos < buf_size))
    599 		end_pos++;
    600 
    601 	if (end_pos < buf_size) {
    602 		/* Found the end of the line, normal exit */
    603 		if (buf[end_pos] == '\n') {
    604 			buf[end_pos] = '\0';
    605 			return (end_pos);
    606 		}
    607 
    608 		/* No more input to read */
    609 		if (buf[end_pos] == '\0')
    610 			return (-1);
    611 	}
    612 
    613 	/*
    614 	 * The following code takes the remainder of the buffer and
    615 	 * puts it at the beginning.  The space after the remainder, which
    616 	 * is now at the beginning, is blanked.
    617 	 * At this point, read in more data and continue to find the EOL....
    618 	 */
    619 	len = end_pos - (start_pos + 1);
    620 	(void) memcpy(buf, &(buf[start_pos+1]), (size_t)len);
    621 	(void) memset(&buf[len], '\0', (buf_size - len));
    622 	(void) fread((void *) &buf[len], (size_t)(buf_size-len), (size_t)1, fd);
    623 	*line = buf;
    624 	end_pos = len;
    625 
    626 	/* Read the buffer until EOL or the buffer is empty */
    627 	while ((buf[end_pos] != '\n') && (buf[end_pos] != '\0') &&
    628 	    (end_pos < buf_size))
    629 		end_pos++;
    630 
    631 	if (end_pos < buf_size) {
    632 		/* Found the end of the line, normal exit */
    633 		if (buf[end_pos] == '\n') {
    634 			buf[end_pos] = '\0';
    635 			return (end_pos);
    636 		}
    637 
    638 		/* No more input to read */
    639 		if (buf[end_pos] == '\0')
    640 			return (-1);
    641 	}
    642 
    643 	(void) fprintf(stderr, MANIFEST_ERR);
    644 	exit(FATAL_EXIT);
    645 
    646 	/* NOTREACHED */
    647 }
    648 
    649 static void
    650 init_default_flags(uint_t *flags)
    651 {
    652 	/* Default behavior: everything is checked *except* dirmtime */
    653 	*flags = ATTR_ALL & ~(ATTR_DIRMTIME);
    654 }
    655