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 }