1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 27*0Sstevel@tonic-gate 28*0Sstevel@tonic-gate #include <signal.h> 29*0Sstevel@tonic-gate #include <unistd.h> 30*0Sstevel@tonic-gate #include <sys/acl.h> 31*0Sstevel@tonic-gate #include <sys/statvfs.h> 32*0Sstevel@tonic-gate #include <sys/wait.h> 33*0Sstevel@tonic-gate #include "bart.h" 34*0Sstevel@tonic-gate 35*0Sstevel@tonic-gate static int sanitize_reloc_root(char *root, size_t bufsize); 36*0Sstevel@tonic-gate static int create_manifest_filelist(char **argv, char *reloc_root); 37*0Sstevel@tonic-gate static int create_manifest_rule(char *reloc_root, FILE *rule_fp); 38*0Sstevel@tonic-gate static void output_manifest(void); 39*0Sstevel@tonic-gate static int eval_file(const char *fname, const struct stat64 *statb); 40*0Sstevel@tonic-gate static char *sanitized_fname(const char *, boolean_t); 41*0Sstevel@tonic-gate static char *get_acl_string(const char *fname, const struct stat64 *statb, 42*0Sstevel@tonic-gate int *err_code); 43*0Sstevel@tonic-gate static int generate_hash(int fdin, char *hash_str); 44*0Sstevel@tonic-gate static int read_filelist(char *reloc_root, char **argv, char *buf, 45*0Sstevel@tonic-gate size_t bufsize); 46*0Sstevel@tonic-gate static int walker(const char *name, const struct stat64 *sp, 47*0Sstevel@tonic-gate int type, struct FTW *ftwx); 48*0Sstevel@tonic-gate 49*0Sstevel@tonic-gate /* 50*0Sstevel@tonic-gate * The following globals are necessary due to the "walker" function 51*0Sstevel@tonic-gate * provided by nftw(). Since there is no way to pass them through to the 52*0Sstevel@tonic-gate * walker function, they must be global. 53*0Sstevel@tonic-gate */ 54*0Sstevel@tonic-gate static int compute_chksum = 1, eval_err = 0; 55*0Sstevel@tonic-gate static struct rule *subtree_root; 56*0Sstevel@tonic-gate static char reloc_root[PATH_MAX]; 57*0Sstevel@tonic-gate static struct statvfs parent_vfs; 58*0Sstevel@tonic-gate 59*0Sstevel@tonic-gate int 60*0Sstevel@tonic-gate bart_create(int argc, char **argv) 61*0Sstevel@tonic-gate { 62*0Sstevel@tonic-gate boolean_t filelist_input; 63*0Sstevel@tonic-gate int ret, c, output_pipe[2]; 64*0Sstevel@tonic-gate FILE *rules_fd = NULL; 65*0Sstevel@tonic-gate pid_t pid; 66*0Sstevel@tonic-gate 67*0Sstevel@tonic-gate filelist_input = B_FALSE; 68*0Sstevel@tonic-gate reloc_root[0] = '\0'; 69*0Sstevel@tonic-gate 70*0Sstevel@tonic-gate while ((c = getopt(argc, argv, "Inr:R:")) != EOF) { 71*0Sstevel@tonic-gate switch (c) { 72*0Sstevel@tonic-gate case 'I': 73*0Sstevel@tonic-gate if (rules_fd != NULL) { 74*0Sstevel@tonic-gate (void) fprintf(stderr, "%s", INPUT_ERR); 75*0Sstevel@tonic-gate usage(); 76*0Sstevel@tonic-gate } 77*0Sstevel@tonic-gate filelist_input = B_TRUE; 78*0Sstevel@tonic-gate break; 79*0Sstevel@tonic-gate 80*0Sstevel@tonic-gate case 'n': 81*0Sstevel@tonic-gate compute_chksum = 0; 82*0Sstevel@tonic-gate break; 83*0Sstevel@tonic-gate 84*0Sstevel@tonic-gate case 'r': 85*0Sstevel@tonic-gate if (strcmp(optarg, "-") == 0) 86*0Sstevel@tonic-gate rules_fd = stdin; 87*0Sstevel@tonic-gate else 88*0Sstevel@tonic-gate rules_fd = fopen(optarg, "r"); 89*0Sstevel@tonic-gate if (rules_fd == NULL) { 90*0Sstevel@tonic-gate perror(optarg); 91*0Sstevel@tonic-gate usage(); 92*0Sstevel@tonic-gate } 93*0Sstevel@tonic-gate break; 94*0Sstevel@tonic-gate 95*0Sstevel@tonic-gate case 'R': 96*0Sstevel@tonic-gate (void) strlcpy(reloc_root, optarg, sizeof (reloc_root)); 97*0Sstevel@tonic-gate ret = sanitize_reloc_root(reloc_root, 98*0Sstevel@tonic-gate sizeof (reloc_root)); 99*0Sstevel@tonic-gate if (ret == 0) 100*0Sstevel@tonic-gate usage(); 101*0Sstevel@tonic-gate break; 102*0Sstevel@tonic-gate 103*0Sstevel@tonic-gate case '?': 104*0Sstevel@tonic-gate default : 105*0Sstevel@tonic-gate usage(); 106*0Sstevel@tonic-gate } 107*0Sstevel@tonic-gate } 108*0Sstevel@tonic-gate argv += optind; 109*0Sstevel@tonic-gate 110*0Sstevel@tonic-gate if (pipe(output_pipe) < 0) { 111*0Sstevel@tonic-gate perror(""); 112*0Sstevel@tonic-gate exit(FATAL_EXIT); 113*0Sstevel@tonic-gate } 114*0Sstevel@tonic-gate 115*0Sstevel@tonic-gate pid = fork(); 116*0Sstevel@tonic-gate if (pid < 0) { 117*0Sstevel@tonic-gate perror(NULL); 118*0Sstevel@tonic-gate exit(FATAL_EXIT); 119*0Sstevel@tonic-gate } 120*0Sstevel@tonic-gate 121*0Sstevel@tonic-gate /* 122*0Sstevel@tonic-gate * Break the creation of a manifest into two parts: the parent process 123*0Sstevel@tonic-gate * generated the data whereas the child process sorts the data. 124*0Sstevel@tonic-gate * 125*0Sstevel@tonic-gate * The processes communicate through the pipe. 126*0Sstevel@tonic-gate */ 127*0Sstevel@tonic-gate if (pid > 0) { 128*0Sstevel@tonic-gate /* 129*0Sstevel@tonic-gate * Redirect the stdout of this process so it goes into 130*0Sstevel@tonic-gate * output_pipe[0]. The output of this process will be read 131*0Sstevel@tonic-gate * by the child, which will sort the output. 132*0Sstevel@tonic-gate */ 133*0Sstevel@tonic-gate if (dup2(output_pipe[0], STDOUT_FILENO) != STDOUT_FILENO) { 134*0Sstevel@tonic-gate perror(NULL); 135*0Sstevel@tonic-gate exit(FATAL_EXIT); 136*0Sstevel@tonic-gate } 137*0Sstevel@tonic-gate (void) close(output_pipe[0]); 138*0Sstevel@tonic-gate (void) close(output_pipe[1]); 139*0Sstevel@tonic-gate 140*0Sstevel@tonic-gate if (filelist_input == B_TRUE) { 141*0Sstevel@tonic-gate ret = create_manifest_filelist(argv, reloc_root); 142*0Sstevel@tonic-gate } else { 143*0Sstevel@tonic-gate ret = create_manifest_rule(reloc_root, rules_fd); 144*0Sstevel@tonic-gate } 145*0Sstevel@tonic-gate 146*0Sstevel@tonic-gate /* Close stdout so the sort in the child proc will complete */ 147*0Sstevel@tonic-gate (void) fclose(stdout); 148*0Sstevel@tonic-gate } else { 149*0Sstevel@tonic-gate /* 150*0Sstevel@tonic-gate * Redirect the stdin of this process so its read in from 151*0Sstevel@tonic-gate * the pipe, which is the parent process in this case. 152*0Sstevel@tonic-gate */ 153*0Sstevel@tonic-gate if (dup2(output_pipe[1], STDIN_FILENO) != STDIN_FILENO) { 154*0Sstevel@tonic-gate perror(NULL); 155*0Sstevel@tonic-gate exit(FATAL_EXIT); 156*0Sstevel@tonic-gate } 157*0Sstevel@tonic-gate (void) close(output_pipe[0]); 158*0Sstevel@tonic-gate 159*0Sstevel@tonic-gate output_manifest(); 160*0Sstevel@tonic-gate } 161*0Sstevel@tonic-gate 162*0Sstevel@tonic-gate /* Wait for the child proc (the sort) to complete */ 163*0Sstevel@tonic-gate (void) wait(0); 164*0Sstevel@tonic-gate 165*0Sstevel@tonic-gate return (ret); 166*0Sstevel@tonic-gate } 167*0Sstevel@tonic-gate 168*0Sstevel@tonic-gate /* 169*0Sstevel@tonic-gate * Handle the -R option and sets 'root' to be the absolute path of the 170*0Sstevel@tonic-gate * relocatable root. This is useful when the user specifies '-R ../../foo'. 171*0Sstevel@tonic-gate * 172*0Sstevel@tonic-gate * Return code is whether or not the location spec'd by the -R flag is a 173*0Sstevel@tonic-gate * directory or not. 174*0Sstevel@tonic-gate */ 175*0Sstevel@tonic-gate static int 176*0Sstevel@tonic-gate sanitize_reloc_root(char *root, size_t bufsize) 177*0Sstevel@tonic-gate { 178*0Sstevel@tonic-gate char pwd[PATH_MAX]; 179*0Sstevel@tonic-gate 180*0Sstevel@tonic-gate /* 181*0Sstevel@tonic-gate * First, save the current directory and go to the location 182*0Sstevel@tonic-gate * specified with the -R option. 183*0Sstevel@tonic-gate */ 184*0Sstevel@tonic-gate (void) getcwd(pwd, sizeof (pwd)); 185*0Sstevel@tonic-gate if (chdir(root) < 0) { 186*0Sstevel@tonic-gate /* Failed to change directory, something is wrong.... */ 187*0Sstevel@tonic-gate perror(root); 188*0Sstevel@tonic-gate return (0); 189*0Sstevel@tonic-gate } 190*0Sstevel@tonic-gate 191*0Sstevel@tonic-gate /* 192*0Sstevel@tonic-gate * Save the absolute path of the relocatable root directory. 193*0Sstevel@tonic-gate */ 194*0Sstevel@tonic-gate (void) getcwd(root, bufsize); 195*0Sstevel@tonic-gate 196*0Sstevel@tonic-gate /* 197*0Sstevel@tonic-gate * Now, go back to where we started, necessary for picking up a rules 198*0Sstevel@tonic-gate * file. 199*0Sstevel@tonic-gate */ 200*0Sstevel@tonic-gate if (chdir(pwd) < 0) { 201*0Sstevel@tonic-gate /* Failed to change directory, something is wrong.... */ 202*0Sstevel@tonic-gate perror(root); 203*0Sstevel@tonic-gate return (0); 204*0Sstevel@tonic-gate } 205*0Sstevel@tonic-gate 206*0Sstevel@tonic-gate /* 207*0Sstevel@tonic-gate * Make sure the path returned does not have a trailing /. This 208*0Sstevel@tonic-gate * can only happen when the entire pathname is "/". 209*0Sstevel@tonic-gate */ 210*0Sstevel@tonic-gate if (strcmp(root, "/") == 0) 211*0Sstevel@tonic-gate root[0] = '\0'; 212*0Sstevel@tonic-gate 213*0Sstevel@tonic-gate /* 214*0Sstevel@tonic-gate * Since the earlier chdir() succeeded, return success. 215*0Sstevel@tonic-gate */ 216*0Sstevel@tonic-gate return (1); 217*0Sstevel@tonic-gate } 218*0Sstevel@tonic-gate 219*0Sstevel@tonic-gate /* 220*0Sstevel@tonic-gate * This is the worker bee which creates the manifest based upon the command 221*0Sstevel@tonic-gate * line options supplied by the user. 222*0Sstevel@tonic-gate * 223*0Sstevel@tonic-gate * NOTE: create_manifest() eventually outputs data to a pipe, which is read in 224*0Sstevel@tonic-gate * by the child process. The child process is running output_manifest(), which 225*0Sstevel@tonic-gate * is responsible for generating sorted output. 226*0Sstevel@tonic-gate */ 227*0Sstevel@tonic-gate static int 228*0Sstevel@tonic-gate create_manifest_rule(char *reloc_root, FILE *rule_fp) 229*0Sstevel@tonic-gate { 230*0Sstevel@tonic-gate struct rule *root; 231*0Sstevel@tonic-gate int ret_status = EXIT; 232*0Sstevel@tonic-gate uint_t flags; 233*0Sstevel@tonic-gate 234*0Sstevel@tonic-gate if (compute_chksum) 235*0Sstevel@tonic-gate flags = ATTR_CONTENTS; 236*0Sstevel@tonic-gate else 237*0Sstevel@tonic-gate flags = 0; 238*0Sstevel@tonic-gate ret_status = read_rules(rule_fp, reloc_root, flags, 1); 239*0Sstevel@tonic-gate 240*0Sstevel@tonic-gate /* Loop through every single subtree */ 241*0Sstevel@tonic-gate for (root = get_first_subtree(); root != NULL; 242*0Sstevel@tonic-gate root = get_next_subtree(root)) { 243*0Sstevel@tonic-gate 244*0Sstevel@tonic-gate /* 245*0Sstevel@tonic-gate * This subtree has already been traversed by a 246*0Sstevel@tonic-gate * previous stanza, i.e. this rule is a subset of a 247*0Sstevel@tonic-gate * previous rule. 248*0Sstevel@tonic-gate * 249*0Sstevel@tonic-gate * Subtree has already been handled so move on! 250*0Sstevel@tonic-gate */ 251*0Sstevel@tonic-gate if (root->traversed) 252*0Sstevel@tonic-gate continue; 253*0Sstevel@tonic-gate 254*0Sstevel@tonic-gate /* 255*0Sstevel@tonic-gate * Check to see if this subtree should have contents 256*0Sstevel@tonic-gate * checking turned on or off. 257*0Sstevel@tonic-gate * 258*0Sstevel@tonic-gate * NOTE: The 'compute_chksum' and 'parent_vfs' 259*0Sstevel@tonic-gate * are a necessary hack: the variables are used in 260*0Sstevel@tonic-gate * walker(), both directly and indirectly. Since 261*0Sstevel@tonic-gate * the parameters to walker() are defined by nftw(), 262*0Sstevel@tonic-gate * the globals are really a backdoor mechanism. 263*0Sstevel@tonic-gate */ 264*0Sstevel@tonic-gate ret_status = statvfs(root->subtree, &parent_vfs); 265*0Sstevel@tonic-gate if (ret_status < 0) { 266*0Sstevel@tonic-gate perror(root->subtree); 267*0Sstevel@tonic-gate continue; 268*0Sstevel@tonic-gate } 269*0Sstevel@tonic-gate 270*0Sstevel@tonic-gate /* 271*0Sstevel@tonic-gate * Walk the subtree and invoke the callback function 272*0Sstevel@tonic-gate * walker() 273*0Sstevel@tonic-gate */ 274*0Sstevel@tonic-gate subtree_root = root; 275*0Sstevel@tonic-gate (void) nftw64(root->subtree, &walker, 20, FTW_PHYS); 276*0Sstevel@tonic-gate root->traversed = B_TRUE; 277*0Sstevel@tonic-gate 278*0Sstevel@tonic-gate /* 279*0Sstevel@tonic-gate * Ugly but necessary: 280*0Sstevel@tonic-gate * 281*0Sstevel@tonic-gate * walker() must return 0, or the tree walk will stop, 282*0Sstevel@tonic-gate * so warning flags must be set through a global. 283*0Sstevel@tonic-gate */ 284*0Sstevel@tonic-gate if (eval_err == WARNING_EXIT) 285*0Sstevel@tonic-gate ret_status = WARNING_EXIT; 286*0Sstevel@tonic-gate 287*0Sstevel@tonic-gate } 288*0Sstevel@tonic-gate return (ret_status); 289*0Sstevel@tonic-gate } 290*0Sstevel@tonic-gate 291*0Sstevel@tonic-gate static int 292*0Sstevel@tonic-gate create_manifest_filelist(char **argv, char *reloc_root) 293*0Sstevel@tonic-gate { 294*0Sstevel@tonic-gate int ret_status = EXIT; 295*0Sstevel@tonic-gate char input_fname[PATH_MAX]; 296*0Sstevel@tonic-gate 297*0Sstevel@tonic-gate while (read_filelist(reloc_root, argv, 298*0Sstevel@tonic-gate input_fname, sizeof (input_fname)) != -1) { 299*0Sstevel@tonic-gate 300*0Sstevel@tonic-gate struct stat64 stat_buf; 301*0Sstevel@tonic-gate int ret; 302*0Sstevel@tonic-gate 303*0Sstevel@tonic-gate ret = lstat64(input_fname, &stat_buf); 304*0Sstevel@tonic-gate if (ret < 0) { 305*0Sstevel@tonic-gate ret_status = WARNING_EXIT; 306*0Sstevel@tonic-gate perror(input_fname); 307*0Sstevel@tonic-gate } else { 308*0Sstevel@tonic-gate ret = eval_file(input_fname, &stat_buf); 309*0Sstevel@tonic-gate 310*0Sstevel@tonic-gate if (ret == WARNING_EXIT) 311*0Sstevel@tonic-gate ret_status = WARNING_EXIT; 312*0Sstevel@tonic-gate } 313*0Sstevel@tonic-gate } 314*0Sstevel@tonic-gate 315*0Sstevel@tonic-gate return (ret_status); 316*0Sstevel@tonic-gate } 317*0Sstevel@tonic-gate 318*0Sstevel@tonic-gate /* 319*0Sstevel@tonic-gate * output_manifest() the child process. It reads in the output from 320*0Sstevel@tonic-gate * create_manifest() and sorts it. 321*0Sstevel@tonic-gate */ 322*0Sstevel@tonic-gate static void 323*0Sstevel@tonic-gate output_manifest(void) 324*0Sstevel@tonic-gate { 325*0Sstevel@tonic-gate char *env[] = {"LC_CTYPE=C", "LC_COLLATE=C", "LC_NUMERIC=C", NULL}; 326*0Sstevel@tonic-gate time_t time_val; 327*0Sstevel@tonic-gate struct tm *tm; 328*0Sstevel@tonic-gate char time_buf[1024]; 329*0Sstevel@tonic-gate 330*0Sstevel@tonic-gate (void) printf("%s", MANIFEST_VER); 331*0Sstevel@tonic-gate time_val = time((time_t)0); 332*0Sstevel@tonic-gate tm = localtime(&time_val); 333*0Sstevel@tonic-gate (void) strftime(time_buf, sizeof (time_buf), "%A, %B %d, %Y (%T)", tm); 334*0Sstevel@tonic-gate (void) printf("! %s\n", time_buf); 335*0Sstevel@tonic-gate (void) printf("%s", FORMAT_STR); 336*0Sstevel@tonic-gate (void) fflush(stdout); 337*0Sstevel@tonic-gate /* 338*0Sstevel@tonic-gate * Simply run sort and read from the the current stdin, which is really 339*0Sstevel@tonic-gate * the output of create_manifest(). 340*0Sstevel@tonic-gate * Also, make sure the output is unique, since a given file may be 341*0Sstevel@tonic-gate * included by several stanzas. 342*0Sstevel@tonic-gate */ 343*0Sstevel@tonic-gate if (execle("/usr/bin/sort", "sort", NULL, env) < 0) { 344*0Sstevel@tonic-gate perror(""); 345*0Sstevel@tonic-gate exit(FATAL_EXIT); 346*0Sstevel@tonic-gate } 347*0Sstevel@tonic-gate 348*0Sstevel@tonic-gate /*NOTREACHED*/ 349*0Sstevel@tonic-gate } 350*0Sstevel@tonic-gate 351*0Sstevel@tonic-gate /* 352*0Sstevel@tonic-gate * Callback function for nftw() 353*0Sstevel@tonic-gate */ 354*0Sstevel@tonic-gate static int 355*0Sstevel@tonic-gate walker(const char *name, const struct stat64 *sp, int type, struct FTW *ftwx) 356*0Sstevel@tonic-gate { 357*0Sstevel@tonic-gate int ret; 358*0Sstevel@tonic-gate struct statvfs path_vfs; 359*0Sstevel@tonic-gate boolean_t dir_flag = B_FALSE; 360*0Sstevel@tonic-gate struct rule *rule; 361*0Sstevel@tonic-gate 362*0Sstevel@tonic-gate switch (type) { 363*0Sstevel@tonic-gate case FTW_F: /* file */ 364*0Sstevel@tonic-gate rule = check_rules(name, 'F'); 365*0Sstevel@tonic-gate if (rule != NULL) { 366*0Sstevel@tonic-gate if (rule->attr_list & ATTR_CONTENTS) 367*0Sstevel@tonic-gate compute_chksum = 1; 368*0Sstevel@tonic-gate else 369*0Sstevel@tonic-gate compute_chksum = 0; 370*0Sstevel@tonic-gate } 371*0Sstevel@tonic-gate break; 372*0Sstevel@tonic-gate case FTW_SL: /* symbolic link */ 373*0Sstevel@tonic-gate case FTW_DP: /* end of directory */ 374*0Sstevel@tonic-gate case FTW_DNR: /* unreadable directory */ 375*0Sstevel@tonic-gate case FTW_NS: /* unstatable file */ 376*0Sstevel@tonic-gate break; 377*0Sstevel@tonic-gate case FTW_D: /* enter directory */ 378*0Sstevel@tonic-gate 379*0Sstevel@tonic-gate /* 380*0Sstevel@tonic-gate * Check to see if any subsequent rules are a subset 381*0Sstevel@tonic-gate * of this rule; if they are, then mark them as 382*0Sstevel@tonic-gate * "traversed". 383*0Sstevel@tonic-gate */ 384*0Sstevel@tonic-gate rule = subtree_root->next; 385*0Sstevel@tonic-gate while (rule != NULL) { 386*0Sstevel@tonic-gate if (strcmp(name, rule->subtree) == 0) 387*0Sstevel@tonic-gate rule->traversed = B_TRUE; 388*0Sstevel@tonic-gate 389*0Sstevel@tonic-gate rule = rule->next; 390*0Sstevel@tonic-gate } 391*0Sstevel@tonic-gate dir_flag = B_TRUE; 392*0Sstevel@tonic-gate ret = statvfs(name, &path_vfs); 393*0Sstevel@tonic-gate if (ret < 0) 394*0Sstevel@tonic-gate eval_err = WARNING_EXIT; 395*0Sstevel@tonic-gate break; 396*0Sstevel@tonic-gate default: 397*0Sstevel@tonic-gate (void) fprintf(stderr, INVALID_FILE, name); 398*0Sstevel@tonic-gate eval_err = WARNING_EXIT; 399*0Sstevel@tonic-gate break; 400*0Sstevel@tonic-gate } 401*0Sstevel@tonic-gate 402*0Sstevel@tonic-gate /* This is the function which really processes the file */ 403*0Sstevel@tonic-gate ret = eval_file(name, sp); 404*0Sstevel@tonic-gate 405*0Sstevel@tonic-gate /* 406*0Sstevel@tonic-gate * Since the parameters to walker() are constrained by nftw(), 407*0Sstevel@tonic-gate * need to use a global to reflect a WARNING. Sigh. 408*0Sstevel@tonic-gate */ 409*0Sstevel@tonic-gate if (ret == WARNING_EXIT) 410*0Sstevel@tonic-gate eval_err = WARNING_EXIT; 411*0Sstevel@tonic-gate 412*0Sstevel@tonic-gate /* 413*0Sstevel@tonic-gate * This is a case of a directory which crosses into a mounted 414*0Sstevel@tonic-gate * filesystem of a different type, e.g., UFS -> NFS. 415*0Sstevel@tonic-gate * BART should not walk the new filesystem (by specification), so 416*0Sstevel@tonic-gate * set this consolidation-private flag so the rest of the subtree 417*0Sstevel@tonic-gate * under this directory is not waled. 418*0Sstevel@tonic-gate */ 419*0Sstevel@tonic-gate if (dir_flag && 420*0Sstevel@tonic-gate (strcmp(parent_vfs.f_basetype, path_vfs.f_basetype) != 0)) 421*0Sstevel@tonic-gate ftwx->quit = FTW_PRUNE; 422*0Sstevel@tonic-gate 423*0Sstevel@tonic-gate return (0); 424*0Sstevel@tonic-gate } 425*0Sstevel@tonic-gate 426*0Sstevel@tonic-gate /* 427*0Sstevel@tonic-gate * This file does the per-file evaluation and is run to generate every entry 428*0Sstevel@tonic-gate * in the manifest. 429*0Sstevel@tonic-gate * 430*0Sstevel@tonic-gate * All output is written to a pipe which is read by the child process, 431*0Sstevel@tonic-gate * which is running output_manifest(). 432*0Sstevel@tonic-gate */ 433*0Sstevel@tonic-gate static int 434*0Sstevel@tonic-gate eval_file(const char *fname, const struct stat64 *statb) 435*0Sstevel@tonic-gate { 436*0Sstevel@tonic-gate int fd, ret, err_code, i; 437*0Sstevel@tonic-gate char last_field[PATH_MAX], ftype, *acl_str, 438*0Sstevel@tonic-gate *quoted_name; 439*0Sstevel@tonic-gate 440*0Sstevel@tonic-gate err_code = EXIT; 441*0Sstevel@tonic-gate 442*0Sstevel@tonic-gate switch (statb->st_mode & S_IFMT) { 443*0Sstevel@tonic-gate /* Regular file */ 444*0Sstevel@tonic-gate case S_IFREG: ftype = 'F'; break; 445*0Sstevel@tonic-gate 446*0Sstevel@tonic-gate /* Directory */ 447*0Sstevel@tonic-gate case S_IFDIR: ftype = 'D'; break; 448*0Sstevel@tonic-gate 449*0Sstevel@tonic-gate /* Block Device */ 450*0Sstevel@tonic-gate case S_IFBLK: ftype = 'B'; break; 451*0Sstevel@tonic-gate 452*0Sstevel@tonic-gate /* Character Device */ 453*0Sstevel@tonic-gate case S_IFCHR: ftype = 'C'; break; 454*0Sstevel@tonic-gate 455*0Sstevel@tonic-gate /* Named Pipe */ 456*0Sstevel@tonic-gate case S_IFIFO: ftype = 'P'; break; 457*0Sstevel@tonic-gate 458*0Sstevel@tonic-gate /* Socket */ 459*0Sstevel@tonic-gate case S_IFSOCK: ftype = 'S'; break; 460*0Sstevel@tonic-gate 461*0Sstevel@tonic-gate /* Door */ 462*0Sstevel@tonic-gate case S_IFDOOR: ftype = 'O'; break; 463*0Sstevel@tonic-gate 464*0Sstevel@tonic-gate /* Symbolic link */ 465*0Sstevel@tonic-gate case S_IFLNK: ftype = 'L'; break; 466*0Sstevel@tonic-gate 467*0Sstevel@tonic-gate default: ftype = '-'; break; 468*0Sstevel@tonic-gate } 469*0Sstevel@tonic-gate 470*0Sstevel@tonic-gate /* First, make sure this file should be cataloged */ 471*0Sstevel@tonic-gate 472*0Sstevel@tonic-gate if ((subtree_root != NULL) && 473*0Sstevel@tonic-gate (exclude_fname(fname, ftype, subtree_root))) 474*0Sstevel@tonic-gate return (err_code); 475*0Sstevel@tonic-gate 476*0Sstevel@tonic-gate for (i = 0; i < PATH_MAX; i++) 477*0Sstevel@tonic-gate last_field[i] = '\0'; 478*0Sstevel@tonic-gate 479*0Sstevel@tonic-gate /* 480*0Sstevel@tonic-gate * Regular files, compute the MD5 checksum and put it into 'last_field' 481*0Sstevel@tonic-gate * UNLESS instructed to ignore the checksums. 482*0Sstevel@tonic-gate */ 483*0Sstevel@tonic-gate if (ftype == 'F') { 484*0Sstevel@tonic-gate if (compute_chksum) { 485*0Sstevel@tonic-gate fd = open(fname, O_RDONLY|O_LARGEFILE); 486*0Sstevel@tonic-gate if (fd < 0) { 487*0Sstevel@tonic-gate err_code = WARNING_EXIT; 488*0Sstevel@tonic-gate perror(fname); 489*0Sstevel@tonic-gate 490*0Sstevel@tonic-gate /* default value since the computution failed */ 491*0Sstevel@tonic-gate (void) strcpy(last_field, "-"); 492*0Sstevel@tonic-gate } else { 493*0Sstevel@tonic-gate if (generate_hash(fd, last_field) != 0) { 494*0Sstevel@tonic-gate err_code = WARNING_EXIT; 495*0Sstevel@tonic-gate (void) fprintf(stderr, CONTENTS_WARN, 496*0Sstevel@tonic-gate fname); 497*0Sstevel@tonic-gate (void) strcpy(last_field, "-"); 498*0Sstevel@tonic-gate } 499*0Sstevel@tonic-gate } 500*0Sstevel@tonic-gate (void) close(fd); 501*0Sstevel@tonic-gate } 502*0Sstevel@tonic-gate /* Instructed to ignore checksums, just put in a '-' */ 503*0Sstevel@tonic-gate else 504*0Sstevel@tonic-gate (void) strcpy(last_field, "-"); 505*0Sstevel@tonic-gate } 506*0Sstevel@tonic-gate 507*0Sstevel@tonic-gate /* 508*0Sstevel@tonic-gate * For symbolic links, put the destination of the symbolic link into 509*0Sstevel@tonic-gate * 'last_field' 510*0Sstevel@tonic-gate */ 511*0Sstevel@tonic-gate if (ftype == 'L') { 512*0Sstevel@tonic-gate ret = readlink(fname, last_field, sizeof (last_field)); 513*0Sstevel@tonic-gate if (ret < 0) { 514*0Sstevel@tonic-gate err_code = WARNING_EXIT; 515*0Sstevel@tonic-gate perror(fname); 516*0Sstevel@tonic-gate 517*0Sstevel@tonic-gate /* default value since the computation failed */ 518*0Sstevel@tonic-gate (void) strcpy(last_field, "-"); 519*0Sstevel@tonic-gate } 520*0Sstevel@tonic-gate else 521*0Sstevel@tonic-gate (void) strlcpy(last_field, 522*0Sstevel@tonic-gate sanitized_fname(last_field, B_FALSE), 523*0Sstevel@tonic-gate sizeof (last_field)); 524*0Sstevel@tonic-gate 525*0Sstevel@tonic-gate /* 526*0Sstevel@tonic-gate * Boundary condition: possible for a symlink to point to 527*0Sstevel@tonic-gate * nothing [ ln -s '' link_name ]. For this case, set the 528*0Sstevel@tonic-gate * destination to "\000". 529*0Sstevel@tonic-gate */ 530*0Sstevel@tonic-gate if (strlen(last_field) == 0) 531*0Sstevel@tonic-gate (void) strcpy(last_field, "\\000"); 532*0Sstevel@tonic-gate } 533*0Sstevel@tonic-gate 534*0Sstevel@tonic-gate acl_str = get_acl_string(fname, statb, &err_code); 535*0Sstevel@tonic-gate 536*0Sstevel@tonic-gate /* Sanitize 'fname', so its in the proper format for the manifest */ 537*0Sstevel@tonic-gate quoted_name = sanitized_fname(fname, B_TRUE); 538*0Sstevel@tonic-gate 539*0Sstevel@tonic-gate /* Start to build the entry.... */ 540*0Sstevel@tonic-gate (void) printf("%s %c %d %o %s %x %d %d", quoted_name, ftype, 541*0Sstevel@tonic-gate (int)statb->st_size, (int)statb->st_mode, acl_str, 542*0Sstevel@tonic-gate (int)statb->st_mtime, (int)statb->st_uid, (int)statb->st_gid); 543*0Sstevel@tonic-gate 544*0Sstevel@tonic-gate /* Finish it off based upon whether or not it's a device node */ 545*0Sstevel@tonic-gate if ((ftype == 'B') && (ftype == 'C')) 546*0Sstevel@tonic-gate (void) printf(" %x\n", (int)statb->st_rdev); 547*0Sstevel@tonic-gate else if (strlen(last_field) > 0) 548*0Sstevel@tonic-gate (void) printf(" %s\n", last_field); 549*0Sstevel@tonic-gate else 550*0Sstevel@tonic-gate (void) printf("\n"); 551*0Sstevel@tonic-gate 552*0Sstevel@tonic-gate /* free the memory consumed */ 553*0Sstevel@tonic-gate free(acl_str); 554*0Sstevel@tonic-gate free(quoted_name); 555*0Sstevel@tonic-gate 556*0Sstevel@tonic-gate return (err_code); 557*0Sstevel@tonic-gate } 558*0Sstevel@tonic-gate 559*0Sstevel@tonic-gate /* 560*0Sstevel@tonic-gate * When creating a manifest, make sure all '?', tabs, space, newline, '/' 561*0Sstevel@tonic-gate * and '[' are all properly quoted. Convert them to a "\ooo" where the 'ooo' 562*0Sstevel@tonic-gate * represents their octal value. For filesystem objects, as opposed to symlink 563*0Sstevel@tonic-gate * targets, also canonicalize the pathname. 564*0Sstevel@tonic-gate */ 565*0Sstevel@tonic-gate static char * 566*0Sstevel@tonic-gate sanitized_fname(const char *fname, boolean_t canon_path) 567*0Sstevel@tonic-gate { 568*0Sstevel@tonic-gate const char *ip; 569*0Sstevel@tonic-gate unsigned char ch; 570*0Sstevel@tonic-gate char *op, *quoted_name; 571*0Sstevel@tonic-gate 572*0Sstevel@tonic-gate /* Initialize everything */ 573*0Sstevel@tonic-gate quoted_name = safe_calloc((4 * PATH_MAX) + 1); 574*0Sstevel@tonic-gate ip = fname; 575*0Sstevel@tonic-gate op = quoted_name; 576*0Sstevel@tonic-gate 577*0Sstevel@tonic-gate if (canon_path) { 578*0Sstevel@tonic-gate /* 579*0Sstevel@tonic-gate * In the case when a relocatable root was used, the relocatable 580*0Sstevel@tonic-gate * root should *not* be part of the manifest. 581*0Sstevel@tonic-gate */ 582*0Sstevel@tonic-gate ip += strlen(reloc_root); 583*0Sstevel@tonic-gate 584*0Sstevel@tonic-gate /* 585*0Sstevel@tonic-gate * In the case when the '-I' option was used, make sure 586*0Sstevel@tonic-gate * the quoted_name starts with a '/'. 587*0Sstevel@tonic-gate */ 588*0Sstevel@tonic-gate if (*ip != '/') 589*0Sstevel@tonic-gate *op++ = '/'; 590*0Sstevel@tonic-gate } 591*0Sstevel@tonic-gate 592*0Sstevel@tonic-gate /* Now walk through 'fname' and build the quoted string */ 593*0Sstevel@tonic-gate while ((ch = *ip++) != 0) { 594*0Sstevel@tonic-gate switch (ch) { 595*0Sstevel@tonic-gate /* Quote the following characters */ 596*0Sstevel@tonic-gate case ' ': 597*0Sstevel@tonic-gate case '*': 598*0Sstevel@tonic-gate case '\n': 599*0Sstevel@tonic-gate case '?': 600*0Sstevel@tonic-gate case '[': 601*0Sstevel@tonic-gate case '\\': 602*0Sstevel@tonic-gate case '\t': 603*0Sstevel@tonic-gate op += sprintf(op, "\\%.3o", (unsigned char)ch); 604*0Sstevel@tonic-gate break; 605*0Sstevel@tonic-gate 606*0Sstevel@tonic-gate /* Otherwise, simply append them */ 607*0Sstevel@tonic-gate default: 608*0Sstevel@tonic-gate *op++ = ch; 609*0Sstevel@tonic-gate break; 610*0Sstevel@tonic-gate } 611*0Sstevel@tonic-gate } 612*0Sstevel@tonic-gate 613*0Sstevel@tonic-gate *op = 0; 614*0Sstevel@tonic-gate 615*0Sstevel@tonic-gate return (quoted_name); 616*0Sstevel@tonic-gate } 617*0Sstevel@tonic-gate 618*0Sstevel@tonic-gate /* 619*0Sstevel@tonic-gate * Function responsible for generating the ACL information for a given 620*0Sstevel@tonic-gate * file. Note, the string is put into buffer malloc'd by this function. 621*0Sstevel@tonic-gate * Its the responsibility of the caller to free the buffer. 622*0Sstevel@tonic-gate */ 623*0Sstevel@tonic-gate static char * 624*0Sstevel@tonic-gate get_acl_string(const char *fname, const struct stat64 *statb, int *err_code) 625*0Sstevel@tonic-gate { 626*0Sstevel@tonic-gate aclent_t *aclbuf; 627*0Sstevel@tonic-gate int num_acls, ret; 628*0Sstevel@tonic-gate char *acl_info; 629*0Sstevel@tonic-gate 630*0Sstevel@tonic-gate if (S_ISLNK(statb->st_mode)) { 631*0Sstevel@tonic-gate return (safe_strdup("-")); 632*0Sstevel@tonic-gate } 633*0Sstevel@tonic-gate 634*0Sstevel@tonic-gate /* First, figure out how many ACL entries this file has */ 635*0Sstevel@tonic-gate num_acls = acl(fname, GETACLCNT, 0, NULL); 636*0Sstevel@tonic-gate if (num_acls < 0) { 637*0Sstevel@tonic-gate *err_code = WARNING_EXIT; 638*0Sstevel@tonic-gate perror(fname); 639*0Sstevel@tonic-gate return (safe_strdup("-")); 640*0Sstevel@tonic-gate } 641*0Sstevel@tonic-gate 642*0Sstevel@tonic-gate /* 643*0Sstevel@tonic-gate * Next, create a buffer which is big enough for all the ACL entries. 644*0Sstevel@tonic-gate * Then go get the raw data. 645*0Sstevel@tonic-gate */ 646*0Sstevel@tonic-gate aclbuf = (aclent_t *)safe_calloc(sizeof (aclent_t) * num_acls); 647*0Sstevel@tonic-gate ret = acl(fname, GETACL, num_acls, aclbuf); 648*0Sstevel@tonic-gate if (ret < 0) { 649*0Sstevel@tonic-gate *err_code = WARNING_EXIT; 650*0Sstevel@tonic-gate perror(fname); 651*0Sstevel@tonic-gate return (safe_strdup("-")); 652*0Sstevel@tonic-gate } 653*0Sstevel@tonic-gate 654*0Sstevel@tonic-gate /* Convert the raw entries to text */ 655*0Sstevel@tonic-gate acl_info = acltotext(aclbuf, num_acls); 656*0Sstevel@tonic-gate 657*0Sstevel@tonic-gate /* Free up the buffer which held the raw ACL entries */ 658*0Sstevel@tonic-gate free(aclbuf); 659*0Sstevel@tonic-gate 660*0Sstevel@tonic-gate if (acl_info == NULL) { 661*0Sstevel@tonic-gate *err_code = WARNING_EXIT; 662*0Sstevel@tonic-gate perror(fname); 663*0Sstevel@tonic-gate return (safe_strdup("-")); 664*0Sstevel@tonic-gate } else 665*0Sstevel@tonic-gate return (acl_info); 666*0Sstevel@tonic-gate } 667*0Sstevel@tonic-gate 668*0Sstevel@tonic-gate 669*0Sstevel@tonic-gate /* 670*0Sstevel@tonic-gate * 671*0Sstevel@tonic-gate * description: This routine reads stdin in BUF_SIZE chunks, uses the bits 672*0Sstevel@tonic-gate * to update the md5 hash buffer, and outputs the chunks 673*0Sstevel@tonic-gate * to stdout. When stdin is exhausted, the hash is computed, 674*0Sstevel@tonic-gate * converted to a hexadecimal string, and returned. 675*0Sstevel@tonic-gate * 676*0Sstevel@tonic-gate * returns: The md5 hash of stdin, or NULL if unsuccessful for any reason. 677*0Sstevel@tonic-gate */ 678*0Sstevel@tonic-gate static int 679*0Sstevel@tonic-gate generate_hash(int fdin, char *hash_str) 680*0Sstevel@tonic-gate { 681*0Sstevel@tonic-gate unsigned char buf[BUF_SIZE]; 682*0Sstevel@tonic-gate unsigned char hash[MD5_DIGEST_LENGTH]; 683*0Sstevel@tonic-gate int i, amtread; 684*0Sstevel@tonic-gate MD5_CTX ctx; 685*0Sstevel@tonic-gate 686*0Sstevel@tonic-gate MD5Init(&ctx); 687*0Sstevel@tonic-gate 688*0Sstevel@tonic-gate for (;;) { 689*0Sstevel@tonic-gate amtread = read(fdin, buf, sizeof (buf)); 690*0Sstevel@tonic-gate if (amtread == 0) 691*0Sstevel@tonic-gate break; 692*0Sstevel@tonic-gate if (amtread < 0) 693*0Sstevel@tonic-gate return (1); 694*0Sstevel@tonic-gate 695*0Sstevel@tonic-gate /* got some data. Now update hash */ 696*0Sstevel@tonic-gate MD5Update(&ctx, buf, amtread); 697*0Sstevel@tonic-gate } 698*0Sstevel@tonic-gate 699*0Sstevel@tonic-gate /* done passing through data, calculate hash */ 700*0Sstevel@tonic-gate MD5Final(hash, &ctx); 701*0Sstevel@tonic-gate 702*0Sstevel@tonic-gate for (i = 0; i < MD5_DIGEST_LENGTH; i++) 703*0Sstevel@tonic-gate (void) sprintf(hash_str + (i*2), "%2.2x", hash[i]); 704*0Sstevel@tonic-gate 705*0Sstevel@tonic-gate return (0); 706*0Sstevel@tonic-gate } 707*0Sstevel@tonic-gate 708*0Sstevel@tonic-gate /* 709*0Sstevel@tonic-gate * Used by 'bart create' with the '-I' option. Return each entry into a 'buf' 710*0Sstevel@tonic-gate * with the appropriate exit code: '0' for success and '-1' for failure. 711*0Sstevel@tonic-gate */ 712*0Sstevel@tonic-gate static int 713*0Sstevel@tonic-gate read_filelist(char *reloc_root, char **argv, char *buf, size_t bufsize) 714*0Sstevel@tonic-gate { 715*0Sstevel@tonic-gate static int argv_index = -1; 716*0Sstevel@tonic-gate static boolean_t read_stdinput = B_FALSE; 717*0Sstevel@tonic-gate char temp_buf[PATH_MAX]; 718*0Sstevel@tonic-gate char *cp; 719*0Sstevel@tonic-gate 720*0Sstevel@tonic-gate /* 721*0Sstevel@tonic-gate * INITIALIZATION: 722*0Sstevel@tonic-gate * Setup this code so it knows whether or not to read sdtin. 723*0Sstevel@tonic-gate * Also, if reading from argv, setup the index, "argv_index" 724*0Sstevel@tonic-gate */ 725*0Sstevel@tonic-gate if (argv_index == -1) { 726*0Sstevel@tonic-gate argv_index = 0; 727*0Sstevel@tonic-gate 728*0Sstevel@tonic-gate /* In this case, no args after '-I', so read stdin */ 729*0Sstevel@tonic-gate if (argv[0] == NULL) 730*0Sstevel@tonic-gate read_stdinput = B_TRUE; 731*0Sstevel@tonic-gate } 732*0Sstevel@tonic-gate 733*0Sstevel@tonic-gate buf[0] = '\0'; 734*0Sstevel@tonic-gate 735*0Sstevel@tonic-gate if (read_stdinput) { 736*0Sstevel@tonic-gate if (fgets(temp_buf, PATH_MAX, stdin) == NULL) 737*0Sstevel@tonic-gate return (-1); 738*0Sstevel@tonic-gate cp = strtok(temp_buf, "\n"); 739*0Sstevel@tonic-gate } else { 740*0Sstevel@tonic-gate cp = argv[argv_index++]; 741*0Sstevel@tonic-gate } 742*0Sstevel@tonic-gate 743*0Sstevel@tonic-gate if (cp == NULL) 744*0Sstevel@tonic-gate return (-1); 745*0Sstevel@tonic-gate 746*0Sstevel@tonic-gate /* 747*0Sstevel@tonic-gate * Unlike similar code elsewhere, avoid adding a leading 748*0Sstevel@tonic-gate * slash for relative pathnames. 749*0Sstevel@tonic-gate */ 750*0Sstevel@tonic-gate (void) snprintf(buf, bufsize, 751*0Sstevel@tonic-gate (reloc_root[0] == '\0' || cp[0] == '/') ? "%s%s" : "%s/%s", 752*0Sstevel@tonic-gate reloc_root, cp); 753*0Sstevel@tonic-gate 754*0Sstevel@tonic-gate return (0); 755*0Sstevel@tonic-gate } 756