1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*7c478bd9Sstevel@tonic-gate * with the License. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 12*7c478bd9Sstevel@tonic-gate * and limitations under the License. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*7c478bd9Sstevel@tonic-gate * 20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END 21*7c478bd9Sstevel@tonic-gate */ 22*7c478bd9Sstevel@tonic-gate /* 23*7c478bd9Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 25*7c478bd9Sstevel@tonic-gate * 26*7c478bd9Sstevel@tonic-gate * $Id: pmodes.c,v 1.23 1999/03/22 14:51:16 casper Exp $ 27*7c478bd9Sstevel@tonic-gate * 28*7c478bd9Sstevel@tonic-gate * 29*7c478bd9Sstevel@tonic-gate * Program to list files from packages with modes that are to 30*7c478bd9Sstevel@tonic-gate * permissive. Usage: 31*7c478bd9Sstevel@tonic-gate * 32*7c478bd9Sstevel@tonic-gate * pmodes [options] pkgdir ... 33*7c478bd9Sstevel@tonic-gate * 34*7c478bd9Sstevel@tonic-gate * Pmodes currently has 4 types of modes that are changed: 35*7c478bd9Sstevel@tonic-gate * 36*7c478bd9Sstevel@tonic-gate * m remove group/other write permissions of all files, 37*7c478bd9Sstevel@tonic-gate * except those in the exceptions list. 38*7c478bd9Sstevel@tonic-gate * w remove user write permission for executables that 39*7c478bd9Sstevel@tonic-gate * are not root owned. 40*7c478bd9Sstevel@tonic-gate * s remove g/o read permission for set-uid/set-gid executables 41*7c478bd9Sstevel@tonic-gate * o change the owner of files/directories that can be safely 42*7c478bd9Sstevel@tonic-gate * chowned to root. 43*7c478bd9Sstevel@tonic-gate * 44*7c478bd9Sstevel@tonic-gate * Any combination of changes can be switched of by specifying -X 45*7c478bd9Sstevel@tonic-gate * 46*7c478bd9Sstevel@tonic-gate * The -n option will create a "FILE.new" file for all changed 47*7c478bd9Sstevel@tonic-gate * pkgmap/prototype files. 48*7c478bd9Sstevel@tonic-gate * The -D option will limit changes to directories only. 49*7c478bd9Sstevel@tonic-gate * 50*7c478bd9Sstevel@tonic-gate * output: 51*7c478bd9Sstevel@tonic-gate * 52*7c478bd9Sstevel@tonic-gate * d m oldmode -> newmode pathname 53*7c478bd9Sstevel@tonic-gate * | ^ whether the file/dir is group writable or even world writable 54*7c478bd9Sstevel@tonic-gate * > type of file. 55*7c478bd9Sstevel@tonic-gate * d o owner -> newowner pathname [mode] 56*7c478bd9Sstevel@tonic-gate * 57*7c478bd9Sstevel@tonic-gate * 58*7c478bd9Sstevel@tonic-gate * Casper Dik (Casper.Dik@Holland.Sun.COM) 59*7c478bd9Sstevel@tonic-gate */ 60*7c478bd9Sstevel@tonic-gate 61*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 62*7c478bd9Sstevel@tonic-gate 63*7c478bd9Sstevel@tonic-gate #include <stdio.h> 64*7c478bd9Sstevel@tonic-gate #include <unistd.h> 65*7c478bd9Sstevel@tonic-gate #include <string.h> 66*7c478bd9Sstevel@tonic-gate #include <ctype.h> 67*7c478bd9Sstevel@tonic-gate #include <dirent.h> 68*7c478bd9Sstevel@tonic-gate #include <stdlib.h> 69*7c478bd9Sstevel@tonic-gate #include <errno.h> 70*7c478bd9Sstevel@tonic-gate #include <sys/param.h> 71*7c478bd9Sstevel@tonic-gate #include <sys/stat.h> 72*7c478bd9Sstevel@tonic-gate #include "binsearch.h" 73*7c478bd9Sstevel@tonic-gate 74*7c478bd9Sstevel@tonic-gate static char *exceptions[] = { 75*7c478bd9Sstevel@tonic-gate #include "exceptions.h" 76*7c478bd9Sstevel@tonic-gate }; 77*7c478bd9Sstevel@tonic-gate 78*7c478bd9Sstevel@tonic-gate static char *exempt_pkgs[] = { 79*7c478bd9Sstevel@tonic-gate "SUNWSMSdf", /* "data files" package for SMS */ 80*7c478bd9Sstevel@tonic-gate "SUNWSMSr", /* "root" package for SMS */ 81*7c478bd9Sstevel@tonic-gate "SUNWSMSsu", /* "user" package for SMS */ 82*7c478bd9Sstevel@tonic-gate }; 83*7c478bd9Sstevel@tonic-gate 84*7c478bd9Sstevel@tonic-gate #define NEXEMPT (sizeof (exempt_pkgs) / sizeof (char *)) 85*7c478bd9Sstevel@tonic-gate 86*7c478bd9Sstevel@tonic-gate #define PROTO "prototype_" 87*7c478bd9Sstevel@tonic-gate 88*7c478bd9Sstevel@tonic-gate #define DEFAULT_SU 0 89*7c478bd9Sstevel@tonic-gate #define DEFAULT_OWNER 1 90*7c478bd9Sstevel@tonic-gate #define DEFAULT_MODES 1 91*7c478bd9Sstevel@tonic-gate #define DEFAULT_USERWRITE 1 92*7c478bd9Sstevel@tonic-gate #define DEFAULT_DIRSONLY 0 93*7c478bd9Sstevel@tonic-gate #define DEFAULT_EDITABLE 1 94*7c478bd9Sstevel@tonic-gate 95*7c478bd9Sstevel@tonic-gate static int nexceptions = sizeof (exceptions)/sizeof (char *); 96*7c478bd9Sstevel@tonic-gate static int dosu = DEFAULT_SU; 97*7c478bd9Sstevel@tonic-gate static int doowner = DEFAULT_OWNER; 98*7c478bd9Sstevel@tonic-gate static int domodes = DEFAULT_MODES; 99*7c478bd9Sstevel@tonic-gate static int douserwrite = DEFAULT_USERWRITE; 100*7c478bd9Sstevel@tonic-gate static int dirsonly = DEFAULT_DIRSONLY; 101*7c478bd9Sstevel@tonic-gate static int editable = DEFAULT_EDITABLE; 102*7c478bd9Sstevel@tonic-gate static int makenew = 0; 103*7c478bd9Sstevel@tonic-gate static int installnew = 0; 104*7c478bd9Sstevel@tonic-gate static int diffout = 0; 105*7c478bd9Sstevel@tonic-gate static int proto = 0; 106*7c478bd9Sstevel@tonic-gate static int verbose = 0; 107*7c478bd9Sstevel@tonic-gate static int quiet = 0; 108*7c478bd9Sstevel@tonic-gate static int errors = 0; 109*7c478bd9Sstevel@tonic-gate 110*7c478bd9Sstevel@tonic-gate static void update_map(char *, char *, int); 111*7c478bd9Sstevel@tonic-gate 112*7c478bd9Sstevel@tonic-gate static char *program; 113*7c478bd9Sstevel@tonic-gate 114*7c478bd9Sstevel@tonic-gate itemlist restrictto = NULL; 115*7c478bd9Sstevel@tonic-gate 116*7c478bd9Sstevel@tonic-gate static void 117*7c478bd9Sstevel@tonic-gate usage(void) { 118*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 119*7c478bd9Sstevel@tonic-gate "Usage: %s [-DowsnNmdePvq] [-r file] pkgdir ...\n", program); 120*7c478bd9Sstevel@tonic-gate exit(1); 121*7c478bd9Sstevel@tonic-gate } 122*7c478bd9Sstevel@tonic-gate 123*7c478bd9Sstevel@tonic-gate int 124*7c478bd9Sstevel@tonic-gate main(int argc, char **argv) 125*7c478bd9Sstevel@tonic-gate { 126*7c478bd9Sstevel@tonic-gate char buf[8192]; 127*7c478bd9Sstevel@tonic-gate int c; 128*7c478bd9Sstevel@tonic-gate extern int optind, opterr; 129*7c478bd9Sstevel@tonic-gate 130*7c478bd9Sstevel@tonic-gate opterr = 0; 131*7c478bd9Sstevel@tonic-gate 132*7c478bd9Sstevel@tonic-gate program = argv[0]; 133*7c478bd9Sstevel@tonic-gate 134*7c478bd9Sstevel@tonic-gate while ((c = getopt(argc, argv, "eDowsnNmdPvqr:")) != EOF) { 135*7c478bd9Sstevel@tonic-gate switch (c) { 136*7c478bd9Sstevel@tonic-gate case 's': dosu = !DEFAULT_SU; break; 137*7c478bd9Sstevel@tonic-gate case 'o': doowner = !DEFAULT_OWNER; break; 138*7c478bd9Sstevel@tonic-gate case 'm': domodes = !DEFAULT_MODES; break; 139*7c478bd9Sstevel@tonic-gate case 'w': douserwrite = !DEFAULT_USERWRITE; break; 140*7c478bd9Sstevel@tonic-gate case 'D': dirsonly = !DEFAULT_DIRSONLY; break; 141*7c478bd9Sstevel@tonic-gate case 'e': editable = !DEFAULT_EDITABLE; break; 142*7c478bd9Sstevel@tonic-gate case 'N': installnew = 1; /* FALLTHROUGH */ 143*7c478bd9Sstevel@tonic-gate case 'n': makenew = 1; break; 144*7c478bd9Sstevel@tonic-gate case 'd': diffout = 1; break; 145*7c478bd9Sstevel@tonic-gate case 'P': proto = 1; break; 146*7c478bd9Sstevel@tonic-gate case 'v': verbose = 1; break; 147*7c478bd9Sstevel@tonic-gate case 'q': quiet = 1; break; 148*7c478bd9Sstevel@tonic-gate case 'r': 149*7c478bd9Sstevel@tonic-gate if (restrictto == NULL) 150*7c478bd9Sstevel@tonic-gate restrictto = new_itemlist(); 151*7c478bd9Sstevel@tonic-gate if (item_addfile(restrictto, optarg) != 0) { 152*7c478bd9Sstevel@tonic-gate perror(optarg); 153*7c478bd9Sstevel@tonic-gate exit(1); 154*7c478bd9Sstevel@tonic-gate } 155*7c478bd9Sstevel@tonic-gate break; 156*7c478bd9Sstevel@tonic-gate default: 157*7c478bd9Sstevel@tonic-gate case '?': usage(); break; 158*7c478bd9Sstevel@tonic-gate } 159*7c478bd9Sstevel@tonic-gate } 160*7c478bd9Sstevel@tonic-gate argc -= optind; 161*7c478bd9Sstevel@tonic-gate argv += optind; 162*7c478bd9Sstevel@tonic-gate 163*7c478bd9Sstevel@tonic-gate if (argc < 1) 164*7c478bd9Sstevel@tonic-gate usage(); 165*7c478bd9Sstevel@tonic-gate 166*7c478bd9Sstevel@tonic-gate for (; *argv; argv++) { 167*7c478bd9Sstevel@tonic-gate FILE *info; 168*7c478bd9Sstevel@tonic-gate char name[MAXPATHLEN]; 169*7c478bd9Sstevel@tonic-gate char basedir[MAXPATHLEN] = "/"; 170*7c478bd9Sstevel@tonic-gate int basedir_len; 171*7c478bd9Sstevel@tonic-gate struct stat stb; 172*7c478bd9Sstevel@tonic-gate int isfile = 0; 173*7c478bd9Sstevel@tonic-gate boolean_t exempt = B_FALSE; 174*7c478bd9Sstevel@tonic-gate 175*7c478bd9Sstevel@tonic-gate /* 176*7c478bd9Sstevel@tonic-gate * If a plain file is passed on the command line, we assume 177*7c478bd9Sstevel@tonic-gate * it's a prototype or pkgmap file and try to find the matching 178*7c478bd9Sstevel@tonic-gate * pkginfo file 179*7c478bd9Sstevel@tonic-gate */ 180*7c478bd9Sstevel@tonic-gate if (lstat(*argv, &stb) == 0 && S_ISREG(stb.st_mode)) { 181*7c478bd9Sstevel@tonic-gate char *lastslash = strrchr(*argv, '/'); 182*7c478bd9Sstevel@tonic-gate 183*7c478bd9Sstevel@tonic-gate if (lastslash != NULL) 184*7c478bd9Sstevel@tonic-gate *lastslash = '\0'; 185*7c478bd9Sstevel@tonic-gate (void) sprintf(name, "%s/pkginfo", *argv); 186*7c478bd9Sstevel@tonic-gate if (lastslash != NULL) 187*7c478bd9Sstevel@tonic-gate *lastslash = '/'; 188*7c478bd9Sstevel@tonic-gate isfile = 1; 189*7c478bd9Sstevel@tonic-gate } else 190*7c478bd9Sstevel@tonic-gate (void) sprintf(name, "%s/pkginfo", *argv); 191*7c478bd9Sstevel@tonic-gate 192*7c478bd9Sstevel@tonic-gate /* if there's no pkginfo file, it could be a prototype area */ 193*7c478bd9Sstevel@tonic-gate 194*7c478bd9Sstevel@tonic-gate if (access(name, R_OK) != 0) 195*7c478bd9Sstevel@tonic-gate (void) strcat(name, ".tmpl"); 196*7c478bd9Sstevel@tonic-gate 197*7c478bd9Sstevel@tonic-gate info = fopen(name, "r"); 198*7c478bd9Sstevel@tonic-gate if (info == 0) { 199*7c478bd9Sstevel@tonic-gate if (!quiet) 200*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 201*7c478bd9Sstevel@tonic-gate "Can't open pkginfo file %s\n", name); 202*7c478bd9Sstevel@tonic-gate continue; 203*7c478bd9Sstevel@tonic-gate } 204*7c478bd9Sstevel@tonic-gate 205*7c478bd9Sstevel@tonic-gate while (fgets(buf, sizeof (buf), info) != NULL && !exempt) { 206*7c478bd9Sstevel@tonic-gate if (strncmp(buf, "BASEDIR=", 8) == 0) { 207*7c478bd9Sstevel@tonic-gate (void) strcpy(basedir, buf+8); 208*7c478bd9Sstevel@tonic-gate basedir[strlen(basedir)-1] = '\0'; 209*7c478bd9Sstevel@tonic-gate } else if (strncmp(buf, "PKG=", 4) == 0) { 210*7c478bd9Sstevel@tonic-gate int i; 211*7c478bd9Sstevel@tonic-gate char *str; 212*7c478bd9Sstevel@tonic-gate 213*7c478bd9Sstevel@tonic-gate str = buf + sizeof ("PKG=") - 1; 214*7c478bd9Sstevel@tonic-gate str[strlen(str)-1] = '\0'; 215*7c478bd9Sstevel@tonic-gate for (i = 0; i < NEXEMPT; i++) { 216*7c478bd9Sstevel@tonic-gate if (strcmp(exempt_pkgs[i], str) == 0) { 217*7c478bd9Sstevel@tonic-gate exempt = B_TRUE; 218*7c478bd9Sstevel@tonic-gate break; 219*7c478bd9Sstevel@tonic-gate } 220*7c478bd9Sstevel@tonic-gate } 221*7c478bd9Sstevel@tonic-gate } 222*7c478bd9Sstevel@tonic-gate } 223*7c478bd9Sstevel@tonic-gate 224*7c478bd9Sstevel@tonic-gate (void) fclose(info); 225*7c478bd9Sstevel@tonic-gate 226*7c478bd9Sstevel@tonic-gate /* exempt package */ 227*7c478bd9Sstevel@tonic-gate if (exempt) 228*7c478bd9Sstevel@tonic-gate continue; 229*7c478bd9Sstevel@tonic-gate 230*7c478bd9Sstevel@tonic-gate basedir_len = strlen(basedir); 231*7c478bd9Sstevel@tonic-gate if (basedir_len != 1) 232*7c478bd9Sstevel@tonic-gate basedir[basedir_len++] = '/'; 233*7c478bd9Sstevel@tonic-gate 234*7c478bd9Sstevel@tonic-gate (void) sprintf(name, "%s/pkgmap", *argv); 235*7c478bd9Sstevel@tonic-gate if (isfile) 236*7c478bd9Sstevel@tonic-gate update_map(*argv, basedir, basedir_len); 237*7c478bd9Sstevel@tonic-gate else if (!proto && access(name, R_OK) == 0) 238*7c478bd9Sstevel@tonic-gate update_map(name, basedir, basedir_len); 239*7c478bd9Sstevel@tonic-gate else { 240*7c478bd9Sstevel@tonic-gate DIR *d = opendir(*argv); 241*7c478bd9Sstevel@tonic-gate struct dirent *de; 242*7c478bd9Sstevel@tonic-gate 243*7c478bd9Sstevel@tonic-gate if (d == NULL) { 244*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 245*7c478bd9Sstevel@tonic-gate "Can't read directory \"%s\"\n", *argv); 246*7c478bd9Sstevel@tonic-gate continue; 247*7c478bd9Sstevel@tonic-gate } 248*7c478bd9Sstevel@tonic-gate while (de = readdir(d)) { 249*7c478bd9Sstevel@tonic-gate /* Skip files with .old or .new suffix */ 250*7c478bd9Sstevel@tonic-gate if (strstr(de->d_name, PROTO) != NULL && 251*7c478bd9Sstevel@tonic-gate strncmp(de->d_name, ".del-", 5) != 0 && 252*7c478bd9Sstevel@tonic-gate strstr(de->d_name, ".old") == NULL && 253*7c478bd9Sstevel@tonic-gate strstr(de->d_name, ".new") == NULL) { 254*7c478bd9Sstevel@tonic-gate (void) sprintf(name, "%s/%s", *argv, 255*7c478bd9Sstevel@tonic-gate de->d_name); 256*7c478bd9Sstevel@tonic-gate update_map(name, basedir, basedir_len); 257*7c478bd9Sstevel@tonic-gate } 258*7c478bd9Sstevel@tonic-gate } 259*7c478bd9Sstevel@tonic-gate (void) closedir(d); 260*7c478bd9Sstevel@tonic-gate } 261*7c478bd9Sstevel@tonic-gate } 262*7c478bd9Sstevel@tonic-gate return (errors != 0); 263*7c478bd9Sstevel@tonic-gate } 264*7c478bd9Sstevel@tonic-gate 265*7c478bd9Sstevel@tonic-gate #define NEXTWORD(tmp, end, warnme) \ 266*7c478bd9Sstevel@tonic-gate do { \ 267*7c478bd9Sstevel@tonic-gate tmp = strpbrk(tmp, "\t ");\ 268*7c478bd9Sstevel@tonic-gate if (!tmp) {\ 269*7c478bd9Sstevel@tonic-gate if (warnme)\ 270*7c478bd9Sstevel@tonic-gate warn(name, lineno);\ 271*7c478bd9Sstevel@tonic-gate return (LINE_IGNORE);\ 272*7c478bd9Sstevel@tonic-gate }\ 273*7c478bd9Sstevel@tonic-gate end = tmp++;\ 274*7c478bd9Sstevel@tonic-gate while (*tmp && isspace(*tmp)) tmp++;\ 275*7c478bd9Sstevel@tonic-gate } while (0) 276*7c478bd9Sstevel@tonic-gate 277*7c478bd9Sstevel@tonic-gate static void 278*7c478bd9Sstevel@tonic-gate warn(const char *file, int line) 279*7c478bd9Sstevel@tonic-gate { 280*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "pmodes: %s, line %d: unexpected format\n", 281*7c478bd9Sstevel@tonic-gate file, line); 282*7c478bd9Sstevel@tonic-gate } 283*7c478bd9Sstevel@tonic-gate 284*7c478bd9Sstevel@tonic-gate struct parsed_line { 285*7c478bd9Sstevel@tonic-gate char *start; /* buffer start */ 286*7c478bd9Sstevel@tonic-gate char *rest; /* buffer after owner */ 287*7c478bd9Sstevel@tonic-gate char *owner; /* same size as ut_user */ 288*7c478bd9Sstevel@tonic-gate char *old_owner; /* same size as ut_user */ 289*7c478bd9Sstevel@tonic-gate char group[16]; /* whatever */ 290*7c478bd9Sstevel@tonic-gate int modelen; /* number of mode bytes (3 or 4); */ 291*7c478bd9Sstevel@tonic-gate int mode; /* the complete file mode */ 292*7c478bd9Sstevel@tonic-gate char path[MAXPATHLEN]; /* NUL terminated pathname */ 293*7c478bd9Sstevel@tonic-gate char type; /* */ 294*7c478bd9Sstevel@tonic-gate char realtype; /* */ 295*7c478bd9Sstevel@tonic-gate }; 296*7c478bd9Sstevel@tonic-gate 297*7c478bd9Sstevel@tonic-gate #define LINE_OK 0 298*7c478bd9Sstevel@tonic-gate #define LINE_IGNORE 1 299*7c478bd9Sstevel@tonic-gate #define LINE_ERROR 2 300*7c478bd9Sstevel@tonic-gate 301*7c478bd9Sstevel@tonic-gate static void 302*7c478bd9Sstevel@tonic-gate put_line(FILE *f, struct parsed_line *line) 303*7c478bd9Sstevel@tonic-gate { 304*7c478bd9Sstevel@tonic-gate if (f != NULL) 305*7c478bd9Sstevel@tonic-gate if (line->rest) 306*7c478bd9Sstevel@tonic-gate (void) fprintf(f, "%s%.*o %s %s", line->start, 307*7c478bd9Sstevel@tonic-gate line->modelen, line->mode, line->owner, line->rest); 308*7c478bd9Sstevel@tonic-gate else 309*7c478bd9Sstevel@tonic-gate (void) fputs(line->start, f); 310*7c478bd9Sstevel@tonic-gate } 311*7c478bd9Sstevel@tonic-gate 312*7c478bd9Sstevel@tonic-gate /* 313*7c478bd9Sstevel@tonic-gate * the first field is the path, the second the type, the 314*7c478bd9Sstevel@tonic-gate * third the class, the fourth the mode, when appropriate. 315*7c478bd9Sstevel@tonic-gate * We're interested in 316*7c478bd9Sstevel@tonic-gate * f (file) 317*7c478bd9Sstevel@tonic-gate * e (edited file) 318*7c478bd9Sstevel@tonic-gate * v (volatile file) 319*7c478bd9Sstevel@tonic-gate * d (directory) 320*7c478bd9Sstevel@tonic-gate * c (character devices) 321*7c478bd9Sstevel@tonic-gate * b (block devices) 322*7c478bd9Sstevel@tonic-gate */ 323*7c478bd9Sstevel@tonic-gate 324*7c478bd9Sstevel@tonic-gate static int 325*7c478bd9Sstevel@tonic-gate parse_line(struct parsed_line *parse, char *buf, const char *name, int lineno) 326*7c478bd9Sstevel@tonic-gate { 327*7c478bd9Sstevel@tonic-gate char *tmp; 328*7c478bd9Sstevel@tonic-gate char *p = buf; 329*7c478bd9Sstevel@tonic-gate char *end, *q; 330*7c478bd9Sstevel@tonic-gate 331*7c478bd9Sstevel@tonic-gate parse->start = buf; 332*7c478bd9Sstevel@tonic-gate parse->rest = 0; /* makes put_line work */ 333*7c478bd9Sstevel@tonic-gate 334*7c478bd9Sstevel@tonic-gate /* Trim trailing spaces */ 335*7c478bd9Sstevel@tonic-gate end = buf + strlen(buf); 336*7c478bd9Sstevel@tonic-gate while (end > buf+1 && isspace(end[-2])) { 337*7c478bd9Sstevel@tonic-gate end -= 1; 338*7c478bd9Sstevel@tonic-gate end[-1] = end[0]; 339*7c478bd9Sstevel@tonic-gate end[0] = '\0'; 340*7c478bd9Sstevel@tonic-gate } 341*7c478bd9Sstevel@tonic-gate 342*7c478bd9Sstevel@tonic-gate while (*p && isspace(*p)) 343*7c478bd9Sstevel@tonic-gate p++; 344*7c478bd9Sstevel@tonic-gate 345*7c478bd9Sstevel@tonic-gate if (*p == '#' || *p == ':' || *p == '\0') 346*7c478bd9Sstevel@tonic-gate return (LINE_IGNORE); 347*7c478bd9Sstevel@tonic-gate 348*7c478bd9Sstevel@tonic-gate /* 349*7c478bd9Sstevel@tonic-gate * Special directives; we really should follow the include 350*7c478bd9Sstevel@tonic-gate * directives but we certainly need to look at default 351*7c478bd9Sstevel@tonic-gate */ 352*7c478bd9Sstevel@tonic-gate if (*p == '!') { 353*7c478bd9Sstevel@tonic-gate p++; 354*7c478bd9Sstevel@tonic-gate while (*p && isspace(*p)) 355*7c478bd9Sstevel@tonic-gate p++; 356*7c478bd9Sstevel@tonic-gate 357*7c478bd9Sstevel@tonic-gate if (!*p || *p == '\n') 358*7c478bd9Sstevel@tonic-gate return (LINE_IGNORE); 359*7c478bd9Sstevel@tonic-gate 360*7c478bd9Sstevel@tonic-gate if (strncmp(p, "default", 7) == 0) { 361*7c478bd9Sstevel@tonic-gate NEXTWORD(p, end, 1); 362*7c478bd9Sstevel@tonic-gate parse->type = 'f'; 363*7c478bd9Sstevel@tonic-gate parse->realtype = 'D'; 364*7c478bd9Sstevel@tonic-gate strcpy(parse->path, "(default)"); 365*7c478bd9Sstevel@tonic-gate tmp = p; 366*7c478bd9Sstevel@tonic-gate NEXTWORD(p, end, 1); 367*7c478bd9Sstevel@tonic-gate goto domode; 368*7c478bd9Sstevel@tonic-gate } else if (strncmp(p, "include", 7) == 0) { 369*7c478bd9Sstevel@tonic-gate NEXTWORD(p, end, 1); 370*7c478bd9Sstevel@tonic-gate if (strstr(p, PROTO) == NULL) 371*7c478bd9Sstevel@tonic-gate fprintf(stderr, "including file %s", p); 372*7c478bd9Sstevel@tonic-gate } 373*7c478bd9Sstevel@tonic-gate return (LINE_IGNORE); 374*7c478bd9Sstevel@tonic-gate } 375*7c478bd9Sstevel@tonic-gate 376*7c478bd9Sstevel@tonic-gate /* 377*7c478bd9Sstevel@tonic-gate * Parse the pkgmap line: 378*7c478bd9Sstevel@tonic-gate * [<number>] <type> <class> <path> [<major> <minor>] 379*7c478bd9Sstevel@tonic-gate * [ <mode> <owner> <group> .... ] 380*7c478bd9Sstevel@tonic-gate */ 381*7c478bd9Sstevel@tonic-gate 382*7c478bd9Sstevel@tonic-gate /* Skip first column for non-prototype (i.e., pkgmap) files */ 383*7c478bd9Sstevel@tonic-gate if (isdigit(*p)) 384*7c478bd9Sstevel@tonic-gate NEXTWORD(p, end, 1); 385*7c478bd9Sstevel@tonic-gate 386*7c478bd9Sstevel@tonic-gate parse->realtype = parse->type = *p; 387*7c478bd9Sstevel@tonic-gate 388*7c478bd9Sstevel@tonic-gate switch (parse->type) { 389*7c478bd9Sstevel@tonic-gate case 'i': case 's': case 'l': 390*7c478bd9Sstevel@tonic-gate return (LINE_IGNORE); 391*7c478bd9Sstevel@tonic-gate } 392*7c478bd9Sstevel@tonic-gate 393*7c478bd9Sstevel@tonic-gate NEXTWORD(p, end, 1); 394*7c478bd9Sstevel@tonic-gate 395*7c478bd9Sstevel@tonic-gate /* skip class */ 396*7c478bd9Sstevel@tonic-gate NEXTWORD(p, end, 1); 397*7c478bd9Sstevel@tonic-gate 398*7c478bd9Sstevel@tonic-gate /* 399*7c478bd9Sstevel@tonic-gate * p now points to pathname 400*7c478bd9Sstevel@tonic-gate * At this point, we could have no mode because we are 401*7c478bd9Sstevel@tonic-gate * using a default. 402*7c478bd9Sstevel@tonic-gate */ 403*7c478bd9Sstevel@tonic-gate tmp = p; 404*7c478bd9Sstevel@tonic-gate NEXTWORD(p, end, 0); 405*7c478bd9Sstevel@tonic-gate 406*7c478bd9Sstevel@tonic-gate /* end points to space after name */ 407*7c478bd9Sstevel@tonic-gate (void) strncpy(parse->path, tmp, end - tmp); 408*7c478bd9Sstevel@tonic-gate parse->path[end - tmp] = '\0'; 409*7c478bd9Sstevel@tonic-gate 410*7c478bd9Sstevel@tonic-gate switch (parse->type) { 411*7c478bd9Sstevel@tonic-gate case 'e': 412*7c478bd9Sstevel@tonic-gate case 'v': 413*7c478bd9Sstevel@tonic-gate /* type 'e' and 'v' are files, just like 'f', use 'f' in out */ 414*7c478bd9Sstevel@tonic-gate parse->type = 'f'; 415*7c478bd9Sstevel@tonic-gate /* FALLTHROUGH */ 416*7c478bd9Sstevel@tonic-gate case 'f': 417*7c478bd9Sstevel@tonic-gate case 'd': 418*7c478bd9Sstevel@tonic-gate case 'p': /* FIFO - assume mode is sensible, don't treat as file */ 419*7c478bd9Sstevel@tonic-gate break; 420*7c478bd9Sstevel@tonic-gate 421*7c478bd9Sstevel@tonic-gate case 'x': /* Exclusive directory */ 422*7c478bd9Sstevel@tonic-gate parse->type = 'd'; 423*7c478bd9Sstevel@tonic-gate break; 424*7c478bd9Sstevel@tonic-gate 425*7c478bd9Sstevel@tonic-gate /* device files have class major minor, skip */ 426*7c478bd9Sstevel@tonic-gate case 'c': 427*7c478bd9Sstevel@tonic-gate case 'b': 428*7c478bd9Sstevel@tonic-gate NEXTWORD(p, end, 1); NEXTWORD(p, end, 1); 429*7c478bd9Sstevel@tonic-gate break; 430*7c478bd9Sstevel@tonic-gate 431*7c478bd9Sstevel@tonic-gate default: 432*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "Unknown type '%c', %s:%d\n", 433*7c478bd9Sstevel@tonic-gate parse->type, name, lineno); 434*7c478bd9Sstevel@tonic-gate return (LINE_ERROR); 435*7c478bd9Sstevel@tonic-gate } 436*7c478bd9Sstevel@tonic-gate tmp = p; 437*7c478bd9Sstevel@tonic-gate NEXTWORD(p, end, 1); 438*7c478bd9Sstevel@tonic-gate 439*7c478bd9Sstevel@tonic-gate domode: 440*7c478bd9Sstevel@tonic-gate /* 441*7c478bd9Sstevel@tonic-gate * the mode is either a 4 digit number (file is sticky/set-uid or 442*7c478bd9Sstevel@tonic-gate * set-gid or the mode has a leading 0) or a three digit number 443*7c478bd9Sstevel@tonic-gate * mode has all the mode bits, mode points to the three least 444*7c478bd9Sstevel@tonic-gate * significant bit so fthe mode 445*7c478bd9Sstevel@tonic-gate */ 446*7c478bd9Sstevel@tonic-gate parse->mode = 0; 447*7c478bd9Sstevel@tonic-gate for (q = tmp; q < end; q++) { 448*7c478bd9Sstevel@tonic-gate if (!isdigit(*q) || *q > '7') { 449*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 450*7c478bd9Sstevel@tonic-gate "Warning: Unparseble mode \"%.*s\" at %s:%d\n", 451*7c478bd9Sstevel@tonic-gate end-tmp, tmp, name, lineno); 452*7c478bd9Sstevel@tonic-gate return (LINE_IGNORE); 453*7c478bd9Sstevel@tonic-gate } 454*7c478bd9Sstevel@tonic-gate parse->mode <<= 3; 455*7c478bd9Sstevel@tonic-gate parse->mode += *q - '0'; 456*7c478bd9Sstevel@tonic-gate } 457*7c478bd9Sstevel@tonic-gate parse->modelen = end - tmp; 458*7c478bd9Sstevel@tonic-gate tmp[0] = '\0'; 459*7c478bd9Sstevel@tonic-gate 460*7c478bd9Sstevel@tonic-gate parse->old_owner = parse->owner = p; 461*7c478bd9Sstevel@tonic-gate 462*7c478bd9Sstevel@tonic-gate NEXTWORD(p, end, 1); 463*7c478bd9Sstevel@tonic-gate 464*7c478bd9Sstevel@tonic-gate parse->rest = end+1; 465*7c478bd9Sstevel@tonic-gate *end = '\0'; 466*7c478bd9Sstevel@tonic-gate 467*7c478bd9Sstevel@tonic-gate (void) memset(parse->group, 0, sizeof (parse->group)); 468*7c478bd9Sstevel@tonic-gate (void) strncpy(parse->group, end+1, strcspn(end+1, " \t\n")); 469*7c478bd9Sstevel@tonic-gate 470*7c478bd9Sstevel@tonic-gate return (LINE_OK); 471*7c478bd9Sstevel@tonic-gate } 472*7c478bd9Sstevel@tonic-gate 473*7c478bd9Sstevel@tonic-gate static void 474*7c478bd9Sstevel@tonic-gate update_map(char *name, char *basedir, int basedir_len) 475*7c478bd9Sstevel@tonic-gate { 476*7c478bd9Sstevel@tonic-gate char buf[8192]; 477*7c478bd9Sstevel@tonic-gate int i; 478*7c478bd9Sstevel@tonic-gate FILE *map, *newmap; 479*7c478bd9Sstevel@tonic-gate char newname[MAXPATHLEN]; 480*7c478bd9Sstevel@tonic-gate int nchanges = 0; 481*7c478bd9Sstevel@tonic-gate unsigned int lineno = 0; 482*7c478bd9Sstevel@tonic-gate struct parsed_line line; 483*7c478bd9Sstevel@tonic-gate char *fname; 484*7c478bd9Sstevel@tonic-gate 485*7c478bd9Sstevel@tonic-gate map = fopen(name, "r"); 486*7c478bd9Sstevel@tonic-gate if (map == 0) { 487*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "Can't open \"%s\"\n", name); 488*7c478bd9Sstevel@tonic-gate return; 489*7c478bd9Sstevel@tonic-gate } 490*7c478bd9Sstevel@tonic-gate (void) strcpy(newname, name); 491*7c478bd9Sstevel@tonic-gate (void) strcat(newname, ".new"); 492*7c478bd9Sstevel@tonic-gate if (makenew) { 493*7c478bd9Sstevel@tonic-gate newmap = fopen(newname, "w"); 494*7c478bd9Sstevel@tonic-gate if (newmap == 0) 495*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "Can't open %s for writing\n", 496*7c478bd9Sstevel@tonic-gate name); 497*7c478bd9Sstevel@tonic-gate } else 498*7c478bd9Sstevel@tonic-gate newmap = 0; 499*7c478bd9Sstevel@tonic-gate 500*7c478bd9Sstevel@tonic-gate /* Get last one or two components non-trivial of pathname */ 501*7c478bd9Sstevel@tonic-gate if (verbose) { 502*7c478bd9Sstevel@tonic-gate char *tmp = name + strlen(name); 503*7c478bd9Sstevel@tonic-gate int cnt = 0, first = 0; 504*7c478bd9Sstevel@tonic-gate 505*7c478bd9Sstevel@tonic-gate while (--tmp > name && cnt < 2) { 506*7c478bd9Sstevel@tonic-gate if (*tmp == '/') { 507*7c478bd9Sstevel@tonic-gate if (++cnt == 1) 508*7c478bd9Sstevel@tonic-gate first = tmp - name; 509*7c478bd9Sstevel@tonic-gate else { 510*7c478bd9Sstevel@tonic-gate fname = tmp + 1; 511*7c478bd9Sstevel@tonic-gate /* Triviality check */ 512*7c478bd9Sstevel@tonic-gate if (tmp - name > first - 4) 513*7c478bd9Sstevel@tonic-gate cnt--; 514*7c478bd9Sstevel@tonic-gate } 515*7c478bd9Sstevel@tonic-gate } 516*7c478bd9Sstevel@tonic-gate } 517*7c478bd9Sstevel@tonic-gate if (cnt < 2) 518*7c478bd9Sstevel@tonic-gate fname = name; 519*7c478bd9Sstevel@tonic-gate } 520*7c478bd9Sstevel@tonic-gate 521*7c478bd9Sstevel@tonic-gate nchanges = 0; 522*7c478bd9Sstevel@tonic-gate 523*7c478bd9Sstevel@tonic-gate for (; fgets(buf, sizeof (buf), map) != 0; put_line(newmap, &line)) { 524*7c478bd9Sstevel@tonic-gate 525*7c478bd9Sstevel@tonic-gate int root_owner, mode_diff = 0; 526*7c478bd9Sstevel@tonic-gate int changed = 0; 527*7c478bd9Sstevel@tonic-gate 528*7c478bd9Sstevel@tonic-gate lineno ++; 529*7c478bd9Sstevel@tonic-gate 530*7c478bd9Sstevel@tonic-gate switch (parse_line(&line, buf, name, lineno)) { 531*7c478bd9Sstevel@tonic-gate case LINE_IGNORE: 532*7c478bd9Sstevel@tonic-gate continue; 533*7c478bd9Sstevel@tonic-gate case LINE_ERROR: 534*7c478bd9Sstevel@tonic-gate errors++; 535*7c478bd9Sstevel@tonic-gate continue; 536*7c478bd9Sstevel@tonic-gate } 537*7c478bd9Sstevel@tonic-gate 538*7c478bd9Sstevel@tonic-gate if (restrictto) { 539*7c478bd9Sstevel@tonic-gate char nbuf[MAXPATHLEN]; 540*7c478bd9Sstevel@tonic-gate snprintf(nbuf, sizeof (nbuf), "%.*s%s", basedir_len, 541*7c478bd9Sstevel@tonic-gate basedir, line.path); 542*7c478bd9Sstevel@tonic-gate 543*7c478bd9Sstevel@tonic-gate if (item_search(restrictto, nbuf) == -1) 544*7c478bd9Sstevel@tonic-gate continue; 545*7c478bd9Sstevel@tonic-gate } 546*7c478bd9Sstevel@tonic-gate 547*7c478bd9Sstevel@tonic-gate if (dirsonly && line.type != 'd') 548*7c478bd9Sstevel@tonic-gate continue; 549*7c478bd9Sstevel@tonic-gate 550*7c478bd9Sstevel@tonic-gate root_owner = strcmp(line.owner, "root") == 0; 551*7c478bd9Sstevel@tonic-gate if (dosu && line.type == 'f' && (line.mode & (S_ISUID|S_ISGID))) 552*7c478bd9Sstevel@tonic-gate mode_diff = line.mode & (S_IRGRP|S_IROTH); 553*7c478bd9Sstevel@tonic-gate 554*7c478bd9Sstevel@tonic-gate /* 555*7c478bd9Sstevel@tonic-gate * The following heuristics are used to determine whether a file 556*7c478bd9Sstevel@tonic-gate * can be safely chown'ed to root: 557*7c478bd9Sstevel@tonic-gate * - it's not set-uid. 558*7c478bd9Sstevel@tonic-gate * and one of the following applies: 559*7c478bd9Sstevel@tonic-gate * - it's not writable by the current owner and is 560*7c478bd9Sstevel@tonic-gate * group/world readable 561*7c478bd9Sstevel@tonic-gate * - it's world executable and a file 562*7c478bd9Sstevel@tonic-gate * - owner, group and world permissions are identical 563*7c478bd9Sstevel@tonic-gate * - it's a bin owned directory or a "non-volatile" 564*7c478bd9Sstevel@tonic-gate * file (any owner) for which group and other r-x 565*7c478bd9Sstevel@tonic-gate * permissions are identical, or it's a bin owned 566*7c478bd9Sstevel@tonic-gate * executable or it's a /etc/security/dev/ device 567*7c478bd9Sstevel@tonic-gate */ 568*7c478bd9Sstevel@tonic-gate 569*7c478bd9Sstevel@tonic-gate if (doowner && !(line.mode & S_ISUID) && 570*7c478bd9Sstevel@tonic-gate !root_owner && 571*7c478bd9Sstevel@tonic-gate ((!(line.mode & S_IWUSR) && 572*7c478bd9Sstevel@tonic-gate (line.mode&(S_IRGRP|S_IROTH)) == (S_IRGRP|S_IROTH)) || 573*7c478bd9Sstevel@tonic-gate (line.type == 'f' && (line.mode & S_IXOTH)) || 574*7c478bd9Sstevel@tonic-gate ((line.mode & 07) == ((line.mode>>3) & 07) && 575*7c478bd9Sstevel@tonic-gate (line.mode & 07) == ((line.mode>>6) & 07) && 576*7c478bd9Sstevel@tonic-gate strcmp(line.owner, "uucp") != 0) || 577*7c478bd9Sstevel@tonic-gate ((line.type == 'd' && strcmp(line.owner, "bin") == 0 || 578*7c478bd9Sstevel@tonic-gate (editable && strcmp(line.owner, "bin") == 0 ? 579*7c478bd9Sstevel@tonic-gate line.type : line.realtype) == 'f') && 580*7c478bd9Sstevel@tonic-gate ((line.mode & 05) == ((line.mode>>3) & 05) || 581*7c478bd9Sstevel@tonic-gate (line.mode & 0100) && 582*7c478bd9Sstevel@tonic-gate strcmp(line.owner, "bin") == 0) && 583*7c478bd9Sstevel@tonic-gate ((line.mode & 0105) != 0 || 584*7c478bd9Sstevel@tonic-gate basedir_len < 18 && 585*7c478bd9Sstevel@tonic-gate strncmp(basedir, "/etc/security/dev/", 586*7c478bd9Sstevel@tonic-gate basedir_len) == 0 && 587*7c478bd9Sstevel@tonic-gate strncmp(line.path, "/etc/security/dev/" 588*7c478bd9Sstevel@tonic-gate + basedir_len, 18 - basedir_len) == 0)))) { 589*7c478bd9Sstevel@tonic-gate if (!diffout) { 590*7c478bd9Sstevel@tonic-gate if (!changed && verbose && !nchanges) 591*7c478bd9Sstevel@tonic-gate (void) printf("%s:\n", fname); 592*7c478bd9Sstevel@tonic-gate (void) printf("%c o %s -> root %s%s [%.*o]\n", 593*7c478bd9Sstevel@tonic-gate line.realtype, line.owner, basedir, 594*7c478bd9Sstevel@tonic-gate line.path, line.modelen, line.mode); 595*7c478bd9Sstevel@tonic-gate } 596*7c478bd9Sstevel@tonic-gate line.owner = "root"; 597*7c478bd9Sstevel@tonic-gate root_owner = 1; 598*7c478bd9Sstevel@tonic-gate changed = 1; 599*7c478bd9Sstevel@tonic-gate } 600*7c478bd9Sstevel@tonic-gate /* 601*7c478bd9Sstevel@tonic-gate * Strip user write bit if owner != root and executable by user. 602*7c478bd9Sstevel@tonic-gate * root can write even if no write bits set 603*7c478bd9Sstevel@tonic-gate * Could prevent executables from being overwritten. 604*7c478bd9Sstevel@tonic-gate */ 605*7c478bd9Sstevel@tonic-gate if (douserwrite && line.type == 'f' && !root_owner && 606*7c478bd9Sstevel@tonic-gate (line.mode & (S_IWUSR|S_IXUSR)) == (S_IWUSR|S_IXUSR)) 607*7c478bd9Sstevel@tonic-gate mode_diff |= S_IWUSR; 608*7c478bd9Sstevel@tonic-gate 609*7c478bd9Sstevel@tonic-gate 610*7c478bd9Sstevel@tonic-gate if (domodes && (line.mode & (S_IWGRP|S_IWOTH)) != 0 && 611*7c478bd9Sstevel@tonic-gate (line.mode & S_ISVTX) == 0) { 612*7c478bd9Sstevel@tonic-gate if (basedir_len <= 1) { /* root dir */ 613*7c478bd9Sstevel@tonic-gate for (i = 0; i < nexceptions; i++) { 614*7c478bd9Sstevel@tonic-gate if (strcmp(line.path, 615*7c478bd9Sstevel@tonic-gate exceptions[i]+basedir_len) == 0) 616*7c478bd9Sstevel@tonic-gate break; 617*7c478bd9Sstevel@tonic-gate } 618*7c478bd9Sstevel@tonic-gate } else { 619*7c478bd9Sstevel@tonic-gate for (i = 0; i < nexceptions; i++) { 620*7c478bd9Sstevel@tonic-gate if (strncmp(basedir, exceptions[i], 621*7c478bd9Sstevel@tonic-gate basedir_len) == 0 && 622*7c478bd9Sstevel@tonic-gate strcmp(line.path, 623*7c478bd9Sstevel@tonic-gate exceptions[i]+basedir_len) == 0) 624*7c478bd9Sstevel@tonic-gate break; 625*7c478bd9Sstevel@tonic-gate } 626*7c478bd9Sstevel@tonic-gate } 627*7c478bd9Sstevel@tonic-gate if (i == nexceptions) 628*7c478bd9Sstevel@tonic-gate mode_diff |= line.mode & (S_IWGRP|S_IWOTH); 629*7c478bd9Sstevel@tonic-gate } 630*7c478bd9Sstevel@tonic-gate 631*7c478bd9Sstevel@tonic-gate if (mode_diff) { 632*7c478bd9Sstevel@tonic-gate int oldmode = line.mode; 633*7c478bd9Sstevel@tonic-gate 634*7c478bd9Sstevel@tonic-gate line.mode &= ~mode_diff; 635*7c478bd9Sstevel@tonic-gate 636*7c478bd9Sstevel@tonic-gate if (line.mode != oldmode) { 637*7c478bd9Sstevel@tonic-gate if (!diffout) { 638*7c478bd9Sstevel@tonic-gate if (!changed && verbose && !nchanges) 639*7c478bd9Sstevel@tonic-gate (void) printf("%s:\n", fname); 640*7c478bd9Sstevel@tonic-gate printf("%c %c %04o -> %04o %s%s\n", 641*7c478bd9Sstevel@tonic-gate line.realtype, 642*7c478bd9Sstevel@tonic-gate (mode_diff & (S_IRGRP|S_IROTH)) ? 643*7c478bd9Sstevel@tonic-gate 's' : 'm', 644*7c478bd9Sstevel@tonic-gate oldmode, line.mode, basedir, 645*7c478bd9Sstevel@tonic-gate line.path); 646*7c478bd9Sstevel@tonic-gate } 647*7c478bd9Sstevel@tonic-gate changed = 1; 648*7c478bd9Sstevel@tonic-gate } 649*7c478bd9Sstevel@tonic-gate } 650*7c478bd9Sstevel@tonic-gate nchanges += changed; 651*7c478bd9Sstevel@tonic-gate if (diffout && changed) { 652*7c478bd9Sstevel@tonic-gate if (nchanges == 1 && verbose) 653*7c478bd9Sstevel@tonic-gate (void) printf("%s:\n", fname); 654*7c478bd9Sstevel@tonic-gate 655*7c478bd9Sstevel@tonic-gate (void) printf("< %c %04o %s %s %s%s\n", line.realtype, 656*7c478bd9Sstevel@tonic-gate line.mode | mode_diff, line.old_owner, line.group, 657*7c478bd9Sstevel@tonic-gate basedir, line.path); 658*7c478bd9Sstevel@tonic-gate (void) printf("> %c %04o %s %s %s%s\n", line.realtype, 659*7c478bd9Sstevel@tonic-gate line.mode, line.owner, line.group, basedir, 660*7c478bd9Sstevel@tonic-gate line.path); 661*7c478bd9Sstevel@tonic-gate } 662*7c478bd9Sstevel@tonic-gate } 663*7c478bd9Sstevel@tonic-gate (void) fclose(map); 664*7c478bd9Sstevel@tonic-gate 665*7c478bd9Sstevel@tonic-gate if (newmap != NULL) { 666*7c478bd9Sstevel@tonic-gate (void) fflush(newmap); 667*7c478bd9Sstevel@tonic-gate if (ferror(newmap)) { 668*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "Error writing %s\n", name); 669*7c478bd9Sstevel@tonic-gate return; 670*7c478bd9Sstevel@tonic-gate } 671*7c478bd9Sstevel@tonic-gate (void) fclose(newmap); 672*7c478bd9Sstevel@tonic-gate if (nchanges == 0) 673*7c478bd9Sstevel@tonic-gate (void) unlink(newname); 674*7c478bd9Sstevel@tonic-gate else if (installnew) { 675*7c478bd9Sstevel@tonic-gate char oldname[MAXPATHLEN]; 676*7c478bd9Sstevel@tonic-gate 677*7c478bd9Sstevel@tonic-gate (void) strcpy(oldname, name); 678*7c478bd9Sstevel@tonic-gate (void) strcat(oldname, ".old"); 679*7c478bd9Sstevel@tonic-gate if (rename(name, oldname) == -1 || 680*7c478bd9Sstevel@tonic-gate rename(newname, name) == -1) 681*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 682*7c478bd9Sstevel@tonic-gate "Couldn't install %s: %s\n", 683*7c478bd9Sstevel@tonic-gate newname, strerror(errno)); 684*7c478bd9Sstevel@tonic-gate } 685*7c478bd9Sstevel@tonic-gate } 686*7c478bd9Sstevel@tonic-gate } 687