xref: /titanic_53/usr/src/tools/pmodes/pmodes.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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