xref: /titanic_50/usr/src/tools/pmodes/pmodes.c (revision 88422796fedbe7211a00b0f6e35a9ab2e2b7c8ab)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5985f0d0fSdduvall  * Common Development and Distribution License (the "License").
6985f0d0fSdduvall  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21*88422796SMark J. Nelson 
227c478bd9Sstevel@tonic-gate /*
23*88422796SMark J. Nelson  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  *
267c478bd9Sstevel@tonic-gate  * $Id: pmodes.c,v 1.23 1999/03/22 14:51:16 casper Exp $
277c478bd9Sstevel@tonic-gate  *
287c478bd9Sstevel@tonic-gate  *
297c478bd9Sstevel@tonic-gate  * Program to list files from packages with modes that are to
307c478bd9Sstevel@tonic-gate  * permissive.  Usage:
317c478bd9Sstevel@tonic-gate  *
327c478bd9Sstevel@tonic-gate  *	pmodes [options] pkgdir ...
337c478bd9Sstevel@tonic-gate  *
347c478bd9Sstevel@tonic-gate  * Pmodes currently has 4 types of modes that are changed:
357c478bd9Sstevel@tonic-gate  *
367c478bd9Sstevel@tonic-gate  *	m	remove group/other write permissions of all files,
377c478bd9Sstevel@tonic-gate  *		except those in the exceptions list.
387c478bd9Sstevel@tonic-gate  *	w	remove user write permission for executables that
397c478bd9Sstevel@tonic-gate  *		are not root owned.
407c478bd9Sstevel@tonic-gate  *	s	remove g/o read permission for set-uid/set-gid executables
417c478bd9Sstevel@tonic-gate  *	o	change the owner of files/directories that can be safely
427c478bd9Sstevel@tonic-gate  *		chowned to root.
437c478bd9Sstevel@tonic-gate  *
447c478bd9Sstevel@tonic-gate  *	Any combination of changes can be switched of by specifying -X
457c478bd9Sstevel@tonic-gate  *
467c478bd9Sstevel@tonic-gate  *	The -n option will create a "FILE.new" file for all changed
477c478bd9Sstevel@tonic-gate  *	pkgmap/prototype files.
487c478bd9Sstevel@tonic-gate  *	The -D option will limit changes to directories only.
497c478bd9Sstevel@tonic-gate  *
507c478bd9Sstevel@tonic-gate  * output:
517c478bd9Sstevel@tonic-gate  *
527c478bd9Sstevel@tonic-gate  * d m oldmode -> newmode pathname
537c478bd9Sstevel@tonic-gate  * | ^ whether the file/dir is group writable or even world writable
547c478bd9Sstevel@tonic-gate  * > type of file.
557c478bd9Sstevel@tonic-gate  * d o owner -> newowner pathname [mode]
567c478bd9Sstevel@tonic-gate  *
577c478bd9Sstevel@tonic-gate  *
587c478bd9Sstevel@tonic-gate  * Casper Dik (Casper.Dik@Holland.Sun.COM)
597c478bd9Sstevel@tonic-gate  */
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate #include <stdio.h>
627c478bd9Sstevel@tonic-gate #include <unistd.h>
637c478bd9Sstevel@tonic-gate #include <string.h>
647c478bd9Sstevel@tonic-gate #include <ctype.h>
657c478bd9Sstevel@tonic-gate #include <dirent.h>
667c478bd9Sstevel@tonic-gate #include <stdlib.h>
677c478bd9Sstevel@tonic-gate #include <errno.h>
687c478bd9Sstevel@tonic-gate #include <sys/param.h>
697c478bd9Sstevel@tonic-gate #include <sys/stat.h>
707c478bd9Sstevel@tonic-gate #include "binsearch.h"
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate static char *exceptions[] = {
73*88422796SMark J. Nelson 	"/etc/lp",
74*88422796SMark J. Nelson 	"/var/cache/cups",
757c478bd9Sstevel@tonic-gate };
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate static char *exempt_pkgs[] = {
787c478bd9Sstevel@tonic-gate 	"SUNWSMSdf",	/* "data files" package for SMS */
797c478bd9Sstevel@tonic-gate 	"SUNWSMSr",	/* "root" package for SMS */
807c478bd9Sstevel@tonic-gate 	"SUNWSMSsu",	/* "user" package for SMS */
81985f0d0fSdduvall 	"SUNWnethackr",	/* "root" package for nethack */
827c478bd9Sstevel@tonic-gate };
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate #define	NEXEMPT	(sizeof (exempt_pkgs) / sizeof (char *))
857c478bd9Sstevel@tonic-gate 
867c478bd9Sstevel@tonic-gate #define	PROTO "prototype_"
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate #define	DEFAULT_SU 0
897c478bd9Sstevel@tonic-gate #define	DEFAULT_OWNER 1
907c478bd9Sstevel@tonic-gate #define	DEFAULT_MODES 1
917c478bd9Sstevel@tonic-gate #define	DEFAULT_USERWRITE 1
927c478bd9Sstevel@tonic-gate #define	DEFAULT_DIRSONLY 0
937c478bd9Sstevel@tonic-gate #define	DEFAULT_EDITABLE 1
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate static int nexceptions = sizeof (exceptions)/sizeof (char *);
967c478bd9Sstevel@tonic-gate static int dosu = DEFAULT_SU;
977c478bd9Sstevel@tonic-gate static int doowner = DEFAULT_OWNER;
987c478bd9Sstevel@tonic-gate static int domodes = DEFAULT_MODES;
997c478bd9Sstevel@tonic-gate static int douserwrite = DEFAULT_USERWRITE;
1007c478bd9Sstevel@tonic-gate static int dirsonly = DEFAULT_DIRSONLY;
1017c478bd9Sstevel@tonic-gate static int editable = DEFAULT_EDITABLE;
1027c478bd9Sstevel@tonic-gate static int makenew = 0;
1037c478bd9Sstevel@tonic-gate static int installnew = 0;
1047c478bd9Sstevel@tonic-gate static int diffout = 0;
1057c478bd9Sstevel@tonic-gate static int proto = 0;
1067c478bd9Sstevel@tonic-gate static int verbose = 0;
1077c478bd9Sstevel@tonic-gate static int quiet = 0;
1087c478bd9Sstevel@tonic-gate static int errors = 0;
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate static void update_map(char *, char *, int);
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate static char *program;
1137c478bd9Sstevel@tonic-gate 
1147c478bd9Sstevel@tonic-gate itemlist restrictto = NULL;
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate static void
usage(void)1177c478bd9Sstevel@tonic-gate usage(void) {
1187c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr,
1197c478bd9Sstevel@tonic-gate 	    "Usage: %s [-DowsnNmdePvq] [-r file] pkgdir ...\n", program);
1207c478bd9Sstevel@tonic-gate 	exit(1);
1217c478bd9Sstevel@tonic-gate }
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate int
main(int argc,char ** argv)1247c478bd9Sstevel@tonic-gate main(int argc, char **argv)
1257c478bd9Sstevel@tonic-gate {
1267c478bd9Sstevel@tonic-gate 	char buf[8192];
1277c478bd9Sstevel@tonic-gate 	int c;
1287c478bd9Sstevel@tonic-gate 	extern int optind, opterr;
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate 	opterr = 0;
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate 	program = argv[0];
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate 	while ((c = getopt(argc, argv, "eDowsnNmdPvqr:")) != EOF) {
1357c478bd9Sstevel@tonic-gate 		switch (c) {
1367c478bd9Sstevel@tonic-gate 		case 's': dosu = !DEFAULT_SU; break;
1377c478bd9Sstevel@tonic-gate 		case 'o': doowner = !DEFAULT_OWNER; break;
1387c478bd9Sstevel@tonic-gate 		case 'm': domodes = !DEFAULT_MODES; break;
1397c478bd9Sstevel@tonic-gate 		case 'w': douserwrite = !DEFAULT_USERWRITE; break;
1407c478bd9Sstevel@tonic-gate 		case 'D': dirsonly = !DEFAULT_DIRSONLY; break;
1417c478bd9Sstevel@tonic-gate 		case 'e': editable = !DEFAULT_EDITABLE; break;
1427c478bd9Sstevel@tonic-gate 		case 'N': installnew = 1; /* FALLTHROUGH */
1437c478bd9Sstevel@tonic-gate 		case 'n': makenew = 1; break;
1447c478bd9Sstevel@tonic-gate 		case 'd': diffout = 1; break;
1457c478bd9Sstevel@tonic-gate 		case 'P': proto = 1; break;
1467c478bd9Sstevel@tonic-gate 		case 'v': verbose = 1; break;
1477c478bd9Sstevel@tonic-gate 		case 'q': quiet = 1; break;
1487c478bd9Sstevel@tonic-gate 		case 'r':
1497c478bd9Sstevel@tonic-gate 			if (restrictto == NULL)
1507c478bd9Sstevel@tonic-gate 				restrictto = new_itemlist();
1517c478bd9Sstevel@tonic-gate 			if (item_addfile(restrictto, optarg) != 0) {
1527c478bd9Sstevel@tonic-gate 				perror(optarg);
1537c478bd9Sstevel@tonic-gate 				exit(1);
1547c478bd9Sstevel@tonic-gate 			}
1557c478bd9Sstevel@tonic-gate 			break;
1567c478bd9Sstevel@tonic-gate 		default:
1577c478bd9Sstevel@tonic-gate 		case '?': usage(); break;
1587c478bd9Sstevel@tonic-gate 		}
1597c478bd9Sstevel@tonic-gate 	}
1607c478bd9Sstevel@tonic-gate 	argc -= optind;
1617c478bd9Sstevel@tonic-gate 	argv += optind;
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate 	if (argc < 1)
1647c478bd9Sstevel@tonic-gate 		usage();
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate 	for (; *argv; argv++) {
1677c478bd9Sstevel@tonic-gate 		FILE *info;
1687c478bd9Sstevel@tonic-gate 		char name[MAXPATHLEN];
1697c478bd9Sstevel@tonic-gate 		char basedir[MAXPATHLEN] = "/";
1707c478bd9Sstevel@tonic-gate 		int basedir_len;
1717c478bd9Sstevel@tonic-gate 		struct stat stb;
1727c478bd9Sstevel@tonic-gate 		int isfile = 0;
1737c478bd9Sstevel@tonic-gate 		boolean_t exempt = B_FALSE;
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate 		/*
1767c478bd9Sstevel@tonic-gate 		 * If a plain file is passed on the command line, we assume
1777c478bd9Sstevel@tonic-gate 		 * it's a prototype or pkgmap file and try to find the matching
1787c478bd9Sstevel@tonic-gate 		 * pkginfo file
1797c478bd9Sstevel@tonic-gate 		 */
1807c478bd9Sstevel@tonic-gate 		if (lstat(*argv, &stb) == 0 && S_ISREG(stb.st_mode)) {
1817c478bd9Sstevel@tonic-gate 			char *lastslash = strrchr(*argv, '/');
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate 			if (lastslash != NULL)
1847c478bd9Sstevel@tonic-gate 				*lastslash = '\0';
1857c478bd9Sstevel@tonic-gate 			(void) sprintf(name, "%s/pkginfo", *argv);
1867c478bd9Sstevel@tonic-gate 			if (lastslash != NULL)
1877c478bd9Sstevel@tonic-gate 				*lastslash = '/';
1887c478bd9Sstevel@tonic-gate 			isfile = 1;
1897c478bd9Sstevel@tonic-gate 		} else
1907c478bd9Sstevel@tonic-gate 			(void) sprintf(name, "%s/pkginfo", *argv);
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 		/* if there's no pkginfo file, it could be a prototype area */
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate 		if (access(name, R_OK) != 0)
1957c478bd9Sstevel@tonic-gate 			(void) strcat(name, ".tmpl");
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate 		info = fopen(name, "r");
1987c478bd9Sstevel@tonic-gate 		if (info == 0) {
1997c478bd9Sstevel@tonic-gate 			if (!quiet)
2007c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
2017c478bd9Sstevel@tonic-gate 				    "Can't open pkginfo file %s\n", name);
2027c478bd9Sstevel@tonic-gate 			continue;
2037c478bd9Sstevel@tonic-gate 		}
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate 		while (fgets(buf, sizeof (buf), info) != NULL && !exempt) {
2067c478bd9Sstevel@tonic-gate 			if (strncmp(buf, "BASEDIR=", 8) == 0) {
2077c478bd9Sstevel@tonic-gate 				(void) strcpy(basedir, buf+8);
2087c478bd9Sstevel@tonic-gate 				basedir[strlen(basedir)-1] = '\0';
2097c478bd9Sstevel@tonic-gate 			} else if (strncmp(buf, "PKG=", 4) == 0) {
2107c478bd9Sstevel@tonic-gate 				int i;
2117c478bd9Sstevel@tonic-gate 				char *str;
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 				str = buf + sizeof ("PKG=") - 1;
2147c478bd9Sstevel@tonic-gate 				str[strlen(str)-1] = '\0';
215985f0d0fSdduvall 				if (str[0] == '"')
216985f0d0fSdduvall 					str++;
217985f0d0fSdduvall 				if (str[strlen(str)-1] == '"')
218985f0d0fSdduvall 					str[strlen(str)-1] = '\0';
2197c478bd9Sstevel@tonic-gate 				for (i = 0; i < NEXEMPT; i++) {
2207c478bd9Sstevel@tonic-gate 					if (strcmp(exempt_pkgs[i], str) == 0) {
2217c478bd9Sstevel@tonic-gate 						exempt = B_TRUE;
2227c478bd9Sstevel@tonic-gate 						break;
2237c478bd9Sstevel@tonic-gate 					}
2247c478bd9Sstevel@tonic-gate 				}
2257c478bd9Sstevel@tonic-gate 			}
2267c478bd9Sstevel@tonic-gate 		}
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate 		(void) fclose(info);
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate 		/* exempt package */
2317c478bd9Sstevel@tonic-gate 		if (exempt)
2327c478bd9Sstevel@tonic-gate 			continue;
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate 		basedir_len = strlen(basedir);
2357c478bd9Sstevel@tonic-gate 		if (basedir_len != 1)
2367c478bd9Sstevel@tonic-gate 			basedir[basedir_len++] = '/';
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate 		(void) sprintf(name, "%s/pkgmap", *argv);
2397c478bd9Sstevel@tonic-gate 		if (isfile)
2407c478bd9Sstevel@tonic-gate 			update_map(*argv, basedir, basedir_len);
2417c478bd9Sstevel@tonic-gate 		else if (!proto && access(name, R_OK) == 0)
2427c478bd9Sstevel@tonic-gate 			update_map(name, basedir, basedir_len);
2437c478bd9Sstevel@tonic-gate 		else {
2447c478bd9Sstevel@tonic-gate 			DIR *d = opendir(*argv);
2457c478bd9Sstevel@tonic-gate 			struct dirent *de;
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate 			if (d == NULL) {
2487c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
2497c478bd9Sstevel@tonic-gate 					"Can't read directory \"%s\"\n", *argv);
2507c478bd9Sstevel@tonic-gate 				continue;
2517c478bd9Sstevel@tonic-gate 			}
2527c478bd9Sstevel@tonic-gate 			while (de = readdir(d)) {
2537c478bd9Sstevel@tonic-gate 				/* Skip files with .old or .new suffix */
2547c478bd9Sstevel@tonic-gate 				if (strstr(de->d_name, PROTO) != NULL &&
2557c478bd9Sstevel@tonic-gate 				    strncmp(de->d_name, ".del-", 5) != 0 &&
2567c478bd9Sstevel@tonic-gate 				    strstr(de->d_name, ".old") == NULL &&
2577c478bd9Sstevel@tonic-gate 				    strstr(de->d_name, ".new") == NULL) {
2587c478bd9Sstevel@tonic-gate 					(void) sprintf(name, "%s/%s", *argv,
2597c478bd9Sstevel@tonic-gate 					    de->d_name);
2607c478bd9Sstevel@tonic-gate 					update_map(name, basedir, basedir_len);
2617c478bd9Sstevel@tonic-gate 				}
2627c478bd9Sstevel@tonic-gate 			}
2637c478bd9Sstevel@tonic-gate 			(void) closedir(d);
2647c478bd9Sstevel@tonic-gate 		}
2657c478bd9Sstevel@tonic-gate 	}
2667c478bd9Sstevel@tonic-gate 	return (errors != 0);
2677c478bd9Sstevel@tonic-gate }
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate #define	NEXTWORD(tmp, end, warnme) \
2707c478bd9Sstevel@tonic-gate 	do { \
2717c478bd9Sstevel@tonic-gate 		tmp = strpbrk(tmp, "\t ");\
2727c478bd9Sstevel@tonic-gate 		if (!tmp) {\
2737c478bd9Sstevel@tonic-gate 			if (warnme)\
2747c478bd9Sstevel@tonic-gate 				warn(name, lineno);\
2757c478bd9Sstevel@tonic-gate 			return (LINE_IGNORE);\
2767c478bd9Sstevel@tonic-gate 		}\
2777c478bd9Sstevel@tonic-gate 		end = tmp++;\
2787c478bd9Sstevel@tonic-gate 		while (*tmp && isspace(*tmp)) tmp++;\
2797c478bd9Sstevel@tonic-gate 	} while (0)
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate static void
warn(const char * file,int line)2827c478bd9Sstevel@tonic-gate warn(const char *file, int line)
2837c478bd9Sstevel@tonic-gate {
2847c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, "pmodes: %s, line %d: unexpected format\n",
2857c478bd9Sstevel@tonic-gate 		file, line);
2867c478bd9Sstevel@tonic-gate }
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate struct parsed_line {
2897c478bd9Sstevel@tonic-gate 	char *start;		/* buffer start */
2907c478bd9Sstevel@tonic-gate 	char *rest;		/* buffer after owner */
2917c478bd9Sstevel@tonic-gate 	char *owner;		/* same size as ut_user */
2927c478bd9Sstevel@tonic-gate 	char *old_owner;	/* same size as ut_user */
2937c478bd9Sstevel@tonic-gate 	char group[16];		/* whatever */
2947c478bd9Sstevel@tonic-gate 	int  modelen;		/* number of mode bytes (3 or 4); */
2957c478bd9Sstevel@tonic-gate 	int mode;		/* the complete file mode */
2967c478bd9Sstevel@tonic-gate 	char path[MAXPATHLEN];	/* NUL terminated pathname */
2977c478bd9Sstevel@tonic-gate 	char type;		/* */
2987c478bd9Sstevel@tonic-gate 	char realtype;		/* */
2997c478bd9Sstevel@tonic-gate };
3007c478bd9Sstevel@tonic-gate 
3017c478bd9Sstevel@tonic-gate #define	LINE_OK		0
3027c478bd9Sstevel@tonic-gate #define	LINE_IGNORE	1
3037c478bd9Sstevel@tonic-gate #define	LINE_ERROR	2
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate static void
put_line(FILE * f,struct parsed_line * line)3067c478bd9Sstevel@tonic-gate put_line(FILE *f, struct parsed_line *line)
3077c478bd9Sstevel@tonic-gate {
3087c478bd9Sstevel@tonic-gate 	if (f != NULL)
3097c478bd9Sstevel@tonic-gate 		if (line->rest)
3107c478bd9Sstevel@tonic-gate 			(void) fprintf(f, "%s%.*o %s %s", line->start,
3117c478bd9Sstevel@tonic-gate 			    line->modelen, line->mode, line->owner, line->rest);
3127c478bd9Sstevel@tonic-gate 		else
3137c478bd9Sstevel@tonic-gate 			(void) fputs(line->start, f);
3147c478bd9Sstevel@tonic-gate }
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate /*
3177c478bd9Sstevel@tonic-gate  * the first field is the path, the second the type, the
3187c478bd9Sstevel@tonic-gate  * third the class, the fourth the mode, when appropriate.
3197c478bd9Sstevel@tonic-gate  * We're interested in
3207c478bd9Sstevel@tonic-gate  *		f (file)
3217c478bd9Sstevel@tonic-gate  *		e (edited file)
3227c478bd9Sstevel@tonic-gate  *		v (volatile file)
3237c478bd9Sstevel@tonic-gate  *		d (directory)
3247c478bd9Sstevel@tonic-gate  *		c (character devices)
3257c478bd9Sstevel@tonic-gate  *		b (block devices)
3267c478bd9Sstevel@tonic-gate  */
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate static int
parse_line(struct parsed_line * parse,char * buf,const char * name,int lineno)3297c478bd9Sstevel@tonic-gate parse_line(struct parsed_line *parse, char *buf, const char *name, int lineno)
3307c478bd9Sstevel@tonic-gate {
3317c478bd9Sstevel@tonic-gate 	char *tmp;
3327c478bd9Sstevel@tonic-gate 	char *p = buf;
3337c478bd9Sstevel@tonic-gate 	char *end, *q;
3347c478bd9Sstevel@tonic-gate 
3357c478bd9Sstevel@tonic-gate 	parse->start = buf;
3367c478bd9Sstevel@tonic-gate 	parse->rest = 0;		/* makes put_line work */
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 	/* Trim trailing spaces */
3397c478bd9Sstevel@tonic-gate 	end = buf + strlen(buf);
3407c478bd9Sstevel@tonic-gate 	while (end > buf+1 && isspace(end[-2])) {
3417c478bd9Sstevel@tonic-gate 		end -= 1;
3427c478bd9Sstevel@tonic-gate 		end[-1] = end[0];
3437c478bd9Sstevel@tonic-gate 		end[0] = '\0';
3447c478bd9Sstevel@tonic-gate 	}
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate 	while (*p && isspace(*p))
3477c478bd9Sstevel@tonic-gate 		p++;
3487c478bd9Sstevel@tonic-gate 
3497c478bd9Sstevel@tonic-gate 	if (*p == '#' || *p == ':' || *p == '\0')
3507c478bd9Sstevel@tonic-gate 		return (LINE_IGNORE);
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate 	/*
3537c478bd9Sstevel@tonic-gate 	 * Special directives; we really should follow the include
3547c478bd9Sstevel@tonic-gate 	 * directives but we certainly need to look at default
3557c478bd9Sstevel@tonic-gate 	 */
3567c478bd9Sstevel@tonic-gate 	if (*p == '!') {
3577c478bd9Sstevel@tonic-gate 		p++;
3587c478bd9Sstevel@tonic-gate 		while (*p && isspace(*p))
3597c478bd9Sstevel@tonic-gate 			p++;
3607c478bd9Sstevel@tonic-gate 
3617c478bd9Sstevel@tonic-gate 		if (!*p || *p == '\n')
3627c478bd9Sstevel@tonic-gate 			return (LINE_IGNORE);
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 		if (strncmp(p, "default", 7) == 0) {
3657c478bd9Sstevel@tonic-gate 			NEXTWORD(p, end, 1);
3667c478bd9Sstevel@tonic-gate 			parse->type = 'f';
3677c478bd9Sstevel@tonic-gate 			parse->realtype = 'D';
3687c478bd9Sstevel@tonic-gate 			strcpy(parse->path, "(default)");
3697c478bd9Sstevel@tonic-gate 			tmp = p;
3707c478bd9Sstevel@tonic-gate 			NEXTWORD(p, end, 1);
3717c478bd9Sstevel@tonic-gate 			goto domode;
3727c478bd9Sstevel@tonic-gate 		} else if (strncmp(p, "include", 7) == 0) {
3737c478bd9Sstevel@tonic-gate 			NEXTWORD(p, end, 1);
3747c478bd9Sstevel@tonic-gate 			if (strstr(p, PROTO) == NULL)
3757c478bd9Sstevel@tonic-gate 				fprintf(stderr, "including file %s", p);
3767c478bd9Sstevel@tonic-gate 		}
3777c478bd9Sstevel@tonic-gate 		return (LINE_IGNORE);
3787c478bd9Sstevel@tonic-gate 	}
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 	/*
3817c478bd9Sstevel@tonic-gate 	 * Parse the pkgmap line:
3827c478bd9Sstevel@tonic-gate 	 * [<number>] <type> <class> <path> [<major> <minor>]
3837c478bd9Sstevel@tonic-gate 	 * [ <mode> <owner> <group> .... ]
3847c478bd9Sstevel@tonic-gate 	 */
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 	/* Skip first column for non-prototype (i.e., pkgmap) files */
3877c478bd9Sstevel@tonic-gate 	if (isdigit(*p))
3887c478bd9Sstevel@tonic-gate 		NEXTWORD(p, end, 1);
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 	parse->realtype = parse->type = *p;
3917c478bd9Sstevel@tonic-gate 
3927c478bd9Sstevel@tonic-gate 	switch (parse->type) {
3937c478bd9Sstevel@tonic-gate 	case 'i': case 's': case 'l':
3947c478bd9Sstevel@tonic-gate 		return (LINE_IGNORE);
3957c478bd9Sstevel@tonic-gate 	}
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate 	NEXTWORD(p, end, 1);
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 	/* skip class */
4007c478bd9Sstevel@tonic-gate 	NEXTWORD(p, end, 1);
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 	/*
4037c478bd9Sstevel@tonic-gate 	 * p now points to pathname
4047c478bd9Sstevel@tonic-gate 	 * At this point, we could have no mode because we are
4057c478bd9Sstevel@tonic-gate 	 * using a default.
4067c478bd9Sstevel@tonic-gate 	 */
4077c478bd9Sstevel@tonic-gate 	tmp = p;
4087c478bd9Sstevel@tonic-gate 	NEXTWORD(p, end, 0);
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate 	/* end points to space after name */
4117c478bd9Sstevel@tonic-gate 	(void) strncpy(parse->path, tmp, end - tmp);
4127c478bd9Sstevel@tonic-gate 	parse->path[end - tmp] = '\0';
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 	switch (parse->type) {
4157c478bd9Sstevel@tonic-gate 	case 'e':
4167c478bd9Sstevel@tonic-gate 	case 'v':
4177c478bd9Sstevel@tonic-gate 		/* type 'e' and 'v' are files, just like 'f', use 'f' in out */
4187c478bd9Sstevel@tonic-gate 		parse->type = 'f';
4197c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
4207c478bd9Sstevel@tonic-gate 	case 'f':
4217c478bd9Sstevel@tonic-gate 	case 'd':
4227c478bd9Sstevel@tonic-gate 	case 'p': /* FIFO - assume mode is sensible, don't treat as file */
4237c478bd9Sstevel@tonic-gate 		break;
4247c478bd9Sstevel@tonic-gate 
4257c478bd9Sstevel@tonic-gate 	case 'x': /* Exclusive directory */
4267c478bd9Sstevel@tonic-gate 		parse->type = 'd';
4277c478bd9Sstevel@tonic-gate 		break;
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate 	/* device files have class major minor, skip */
4307c478bd9Sstevel@tonic-gate 	case 'c':
4317c478bd9Sstevel@tonic-gate 	case 'b':
4327c478bd9Sstevel@tonic-gate 		NEXTWORD(p, end, 1); NEXTWORD(p, end, 1);
4337c478bd9Sstevel@tonic-gate 		break;
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 	default:
4367c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "Unknown type '%c', %s:%d\n",
4377c478bd9Sstevel@tonic-gate 			    parse->type, name, lineno);
4387c478bd9Sstevel@tonic-gate 		return (LINE_ERROR);
4397c478bd9Sstevel@tonic-gate 	}
4407c478bd9Sstevel@tonic-gate 	tmp = p;
4417c478bd9Sstevel@tonic-gate 	NEXTWORD(p, end, 1);
4427c478bd9Sstevel@tonic-gate 
4437c478bd9Sstevel@tonic-gate domode:
4447c478bd9Sstevel@tonic-gate 	/*
4457c478bd9Sstevel@tonic-gate 	 * the mode is either a 4 digit number (file is sticky/set-uid or
4467c478bd9Sstevel@tonic-gate 	 * set-gid or the mode has a leading 0) or a three digit number
4477c478bd9Sstevel@tonic-gate 	 * mode has all the mode bits, mode points to the three least
4487c478bd9Sstevel@tonic-gate 	 * significant bit so fthe mode
4497c478bd9Sstevel@tonic-gate 	 */
4507c478bd9Sstevel@tonic-gate 	parse->mode = 0;
4517c478bd9Sstevel@tonic-gate 	for (q = tmp; q < end; q++) {
4527c478bd9Sstevel@tonic-gate 		if (!isdigit(*q) || *q > '7') {
4537c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
4547c478bd9Sstevel@tonic-gate 			    "Warning: Unparseble mode \"%.*s\" at %s:%d\n",
4557c478bd9Sstevel@tonic-gate 			    end-tmp, tmp, name, lineno);
4567c478bd9Sstevel@tonic-gate 			return (LINE_IGNORE);
4577c478bd9Sstevel@tonic-gate 		}
4587c478bd9Sstevel@tonic-gate 		parse->mode <<= 3;
4597c478bd9Sstevel@tonic-gate 		parse->mode += *q - '0';
4607c478bd9Sstevel@tonic-gate 	}
4617c478bd9Sstevel@tonic-gate 	parse->modelen = end - tmp;
4627c478bd9Sstevel@tonic-gate 	tmp[0] = '\0';
4637c478bd9Sstevel@tonic-gate 
4647c478bd9Sstevel@tonic-gate 	parse->old_owner = parse->owner = p;
4657c478bd9Sstevel@tonic-gate 
4667c478bd9Sstevel@tonic-gate 	NEXTWORD(p, end, 1);
4677c478bd9Sstevel@tonic-gate 
4687c478bd9Sstevel@tonic-gate 	parse->rest = end+1;
4697c478bd9Sstevel@tonic-gate 	*end = '\0';
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate 	(void) memset(parse->group, 0, sizeof (parse->group));
4727c478bd9Sstevel@tonic-gate 	(void) strncpy(parse->group, end+1, strcspn(end+1, " \t\n"));
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate 	return (LINE_OK);
4757c478bd9Sstevel@tonic-gate }
4767c478bd9Sstevel@tonic-gate 
4777c478bd9Sstevel@tonic-gate static void
update_map(char * name,char * basedir,int basedir_len)4787c478bd9Sstevel@tonic-gate update_map(char *name, char *basedir, int basedir_len)
4797c478bd9Sstevel@tonic-gate {
4807c478bd9Sstevel@tonic-gate 	char buf[8192];
4817c478bd9Sstevel@tonic-gate 	int i;
4827c478bd9Sstevel@tonic-gate 	FILE *map, *newmap;
4837c478bd9Sstevel@tonic-gate 	char newname[MAXPATHLEN];
4847c478bd9Sstevel@tonic-gate 	int nchanges = 0;
4857c478bd9Sstevel@tonic-gate 	unsigned int lineno = 0;
4867c478bd9Sstevel@tonic-gate 	struct parsed_line line;
4877c478bd9Sstevel@tonic-gate 	char *fname;
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate 	map = fopen(name, "r");
4907c478bd9Sstevel@tonic-gate 	if (map == 0) {
4917c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "Can't open \"%s\"\n", name);
4927c478bd9Sstevel@tonic-gate 		return;
4937c478bd9Sstevel@tonic-gate 	}
4947c478bd9Sstevel@tonic-gate 	(void) strcpy(newname, name);
4957c478bd9Sstevel@tonic-gate 	(void) strcat(newname, ".new");
4967c478bd9Sstevel@tonic-gate 	if (makenew) {
4977c478bd9Sstevel@tonic-gate 		newmap = fopen(newname, "w");
4987c478bd9Sstevel@tonic-gate 		if (newmap == 0)
4997c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "Can't open %s for writing\n",
5007c478bd9Sstevel@tonic-gate 			    name);
5017c478bd9Sstevel@tonic-gate 	} else
5027c478bd9Sstevel@tonic-gate 		newmap = 0;
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 	/* Get last one or two components non-trivial of pathname */
5057c478bd9Sstevel@tonic-gate 	if (verbose) {
5067c478bd9Sstevel@tonic-gate 		char *tmp = name + strlen(name);
5077c478bd9Sstevel@tonic-gate 		int cnt = 0, first = 0;
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate 		while (--tmp > name && cnt < 2) {
5107c478bd9Sstevel@tonic-gate 			if (*tmp == '/') {
5117c478bd9Sstevel@tonic-gate 				if (++cnt == 1)
5127c478bd9Sstevel@tonic-gate 					first = tmp - name;
5137c478bd9Sstevel@tonic-gate 				else  {
5147c478bd9Sstevel@tonic-gate 					fname = tmp + 1;
5157c478bd9Sstevel@tonic-gate 					/* Triviality check */
5167c478bd9Sstevel@tonic-gate 					if (tmp - name > first - 4)
5177c478bd9Sstevel@tonic-gate 						cnt--;
5187c478bd9Sstevel@tonic-gate 				}
5197c478bd9Sstevel@tonic-gate 			}
5207c478bd9Sstevel@tonic-gate 		}
5217c478bd9Sstevel@tonic-gate 		if (cnt < 2)
5227c478bd9Sstevel@tonic-gate 			fname = name;
5237c478bd9Sstevel@tonic-gate 	}
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate 	nchanges = 0;
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 	for (; fgets(buf, sizeof (buf), map) != 0; put_line(newmap, &line)) {
5287c478bd9Sstevel@tonic-gate 
5297c478bd9Sstevel@tonic-gate 		int root_owner, mode_diff = 0;
5307c478bd9Sstevel@tonic-gate 		int changed = 0;
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate 		lineno ++;
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate 		switch (parse_line(&line, buf, name, lineno)) {
5357c478bd9Sstevel@tonic-gate 		case LINE_IGNORE:
5367c478bd9Sstevel@tonic-gate 			continue;
5377c478bd9Sstevel@tonic-gate 		case LINE_ERROR:
5387c478bd9Sstevel@tonic-gate 			errors++;
5397c478bd9Sstevel@tonic-gate 			continue;
5407c478bd9Sstevel@tonic-gate 		}
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate 		if (restrictto) {
5437c478bd9Sstevel@tonic-gate 			char nbuf[MAXPATHLEN];
5447c478bd9Sstevel@tonic-gate 			snprintf(nbuf, sizeof (nbuf), "%.*s%s", basedir_len,
5457c478bd9Sstevel@tonic-gate 			    basedir, line.path);
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate 			if (item_search(restrictto, nbuf) == -1)
5487c478bd9Sstevel@tonic-gate 				continue;
5497c478bd9Sstevel@tonic-gate 		}
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate 		if (dirsonly && line.type != 'd')
5527c478bd9Sstevel@tonic-gate 			continue;
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate 		root_owner = strcmp(line.owner, "root") == 0;
5557c478bd9Sstevel@tonic-gate 		if (dosu && line.type == 'f' && (line.mode & (S_ISUID|S_ISGID)))
5567c478bd9Sstevel@tonic-gate 			mode_diff = line.mode & (S_IRGRP|S_IROTH);
5577c478bd9Sstevel@tonic-gate 
5587c478bd9Sstevel@tonic-gate 		/*
5597c478bd9Sstevel@tonic-gate 		 * The following heuristics are used to determine whether a file
5607c478bd9Sstevel@tonic-gate 		 * can be safely chown'ed to root:
5617c478bd9Sstevel@tonic-gate 		 *	- it's not set-uid.
5627c478bd9Sstevel@tonic-gate 		 *	and one of the following applies:
5637c478bd9Sstevel@tonic-gate 		 *	    - it's not writable by the current owner and is
5647c478bd9Sstevel@tonic-gate 		 *	    group/world readable
5657c478bd9Sstevel@tonic-gate 		 *	    - it's world executable and a file
5667c478bd9Sstevel@tonic-gate 		 *	    - owner, group and world permissions are identical
5677c478bd9Sstevel@tonic-gate 		 *	    - it's a bin owned directory or a "non-volatile"
5687c478bd9Sstevel@tonic-gate 		 *	    file (any owner) for which group and other r-x
5697c478bd9Sstevel@tonic-gate 		 *	    permissions are identical, or it's a bin owned
5707c478bd9Sstevel@tonic-gate 		 *	    executable or it's a /etc/security/dev/ device
5717c478bd9Sstevel@tonic-gate 		 */
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate 		if (doowner && !(line.mode & S_ISUID) &&
5747c478bd9Sstevel@tonic-gate 		    !root_owner &&
5757c478bd9Sstevel@tonic-gate 		    ((!(line.mode & S_IWUSR) &&
5767c478bd9Sstevel@tonic-gate 			(line.mode&(S_IRGRP|S_IROTH)) == (S_IRGRP|S_IROTH)) ||
5777c478bd9Sstevel@tonic-gate 			(line.type == 'f' && (line.mode & S_IXOTH)) ||
5787c478bd9Sstevel@tonic-gate 			((line.mode & 07) == ((line.mode>>3) & 07) &&
5797c478bd9Sstevel@tonic-gate 			    (line.mode & 07) == ((line.mode>>6) & 07) &&
5807c478bd9Sstevel@tonic-gate 			    strcmp(line.owner, "uucp") != 0) ||
5817c478bd9Sstevel@tonic-gate 			((line.type == 'd' && strcmp(line.owner, "bin") == 0 ||
5827c478bd9Sstevel@tonic-gate 			    (editable && strcmp(line.owner, "bin") == 0  ?
5837c478bd9Sstevel@tonic-gate 				    line.type : line.realtype)  == 'f') &&
5847c478bd9Sstevel@tonic-gate 			    ((line.mode & 05) == ((line.mode>>3) & 05) ||
5857c478bd9Sstevel@tonic-gate 				(line.mode & 0100) &&
5867c478bd9Sstevel@tonic-gate 				strcmp(line.owner, "bin") == 0) &&
5877c478bd9Sstevel@tonic-gate 			    ((line.mode & 0105) != 0 ||
5887c478bd9Sstevel@tonic-gate 				basedir_len < 18 &&
5897c478bd9Sstevel@tonic-gate 				strncmp(basedir, "/etc/security/dev/",
5907c478bd9Sstevel@tonic-gate 				    basedir_len) == 0 &&
5917c478bd9Sstevel@tonic-gate 				strncmp(line.path, "/etc/security/dev/"
5927c478bd9Sstevel@tonic-gate 				    + basedir_len, 18 - basedir_len) == 0)))) {
5937c478bd9Sstevel@tonic-gate 			if (!diffout) {
5947c478bd9Sstevel@tonic-gate 				if (!changed && verbose && !nchanges)
5957c478bd9Sstevel@tonic-gate 					(void) printf("%s:\n", fname);
5967c478bd9Sstevel@tonic-gate 				(void) printf("%c o %s -> root %s%s [%.*o]\n",
5977c478bd9Sstevel@tonic-gate 					line.realtype, line.owner, basedir,
5987c478bd9Sstevel@tonic-gate 					line.path, line.modelen, line.mode);
5997c478bd9Sstevel@tonic-gate 			}
6007c478bd9Sstevel@tonic-gate 			line.owner = "root";
6017c478bd9Sstevel@tonic-gate 			root_owner = 1;
6027c478bd9Sstevel@tonic-gate 			changed = 1;
6037c478bd9Sstevel@tonic-gate 		}
6047c478bd9Sstevel@tonic-gate 		/*
6057c478bd9Sstevel@tonic-gate 		 * Strip user write bit if owner != root and executable by user.
6067c478bd9Sstevel@tonic-gate 		 * root can write even if no write bits set
6077c478bd9Sstevel@tonic-gate 		 * Could prevent  executables from being overwritten.
6087c478bd9Sstevel@tonic-gate 		 */
6097c478bd9Sstevel@tonic-gate 		if (douserwrite && line.type == 'f' && !root_owner &&
6107c478bd9Sstevel@tonic-gate 		    (line.mode & (S_IWUSR|S_IXUSR)) == (S_IWUSR|S_IXUSR))
6117c478bd9Sstevel@tonic-gate 			mode_diff |= S_IWUSR;
6127c478bd9Sstevel@tonic-gate 
6137c478bd9Sstevel@tonic-gate 
6147c478bd9Sstevel@tonic-gate 		if (domodes && (line.mode & (S_IWGRP|S_IWOTH)) != 0 &&
6157c478bd9Sstevel@tonic-gate 		    (line.mode & S_ISVTX) == 0) {
6167c478bd9Sstevel@tonic-gate 			if (basedir_len <= 1) { /* root dir */
6177c478bd9Sstevel@tonic-gate 				for (i = 0; i < nexceptions; i++) {
6187c478bd9Sstevel@tonic-gate 					if (strcmp(line.path,
6197c478bd9Sstevel@tonic-gate 					    exceptions[i]+basedir_len) == 0)
6207c478bd9Sstevel@tonic-gate 						break;
6217c478bd9Sstevel@tonic-gate 				}
6227c478bd9Sstevel@tonic-gate 			} else {
6237c478bd9Sstevel@tonic-gate 				for (i = 0; i < nexceptions; i++) {
6247c478bd9Sstevel@tonic-gate 					if (strncmp(basedir, exceptions[i],
6257c478bd9Sstevel@tonic-gate 						basedir_len) == 0 &&
6267c478bd9Sstevel@tonic-gate 					    strcmp(line.path,
6277c478bd9Sstevel@tonic-gate 						exceptions[i]+basedir_len) == 0)
6287c478bd9Sstevel@tonic-gate 						break;
6297c478bd9Sstevel@tonic-gate 				}
6307c478bd9Sstevel@tonic-gate 			}
6317c478bd9Sstevel@tonic-gate 			if (i == nexceptions)
6327c478bd9Sstevel@tonic-gate 				mode_diff |= line.mode & (S_IWGRP|S_IWOTH);
6337c478bd9Sstevel@tonic-gate 		}
6347c478bd9Sstevel@tonic-gate 
6357c478bd9Sstevel@tonic-gate 		if (mode_diff) {
6367c478bd9Sstevel@tonic-gate 			int oldmode = line.mode;
6377c478bd9Sstevel@tonic-gate 
6387c478bd9Sstevel@tonic-gate 			line.mode &= ~mode_diff;
6397c478bd9Sstevel@tonic-gate 
6407c478bd9Sstevel@tonic-gate 			if (line.mode != oldmode) {
6417c478bd9Sstevel@tonic-gate 				if (!diffout) {
6427c478bd9Sstevel@tonic-gate 					if (!changed && verbose && !nchanges)
6437c478bd9Sstevel@tonic-gate 						(void) printf("%s:\n", fname);
6447c478bd9Sstevel@tonic-gate 					printf("%c %c %04o -> %04o %s%s\n",
6457c478bd9Sstevel@tonic-gate 					    line.realtype,
6467c478bd9Sstevel@tonic-gate 					    (mode_diff & (S_IRGRP|S_IROTH)) ?
6477c478bd9Sstevel@tonic-gate 						's' : 'm',
6487c478bd9Sstevel@tonic-gate 						oldmode, line.mode, basedir,
6497c478bd9Sstevel@tonic-gate 						line.path);
6507c478bd9Sstevel@tonic-gate 				}
6517c478bd9Sstevel@tonic-gate 				changed = 1;
6527c478bd9Sstevel@tonic-gate 			}
6537c478bd9Sstevel@tonic-gate 		}
6547c478bd9Sstevel@tonic-gate 		nchanges += changed;
6557c478bd9Sstevel@tonic-gate 		if (diffout && changed) {
6567c478bd9Sstevel@tonic-gate 			if (nchanges == 1 && verbose)
6577c478bd9Sstevel@tonic-gate 				(void) printf("%s:\n", fname);
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate 			(void) printf("< %c %04o %s %s %s%s\n", line.realtype,
6607c478bd9Sstevel@tonic-gate 			    line.mode | mode_diff, line.old_owner, line.group,
6617c478bd9Sstevel@tonic-gate 			    basedir, line.path);
6627c478bd9Sstevel@tonic-gate 			(void) printf("> %c %04o %s %s %s%s\n", line.realtype,
6637c478bd9Sstevel@tonic-gate 			    line.mode, line.owner, line.group, basedir,
6647c478bd9Sstevel@tonic-gate 			    line.path);
6657c478bd9Sstevel@tonic-gate 		}
6667c478bd9Sstevel@tonic-gate 	}
6677c478bd9Sstevel@tonic-gate 	(void) fclose(map);
6687c478bd9Sstevel@tonic-gate 
6697c478bd9Sstevel@tonic-gate 	if (newmap != NULL) {
6707c478bd9Sstevel@tonic-gate 		(void) fflush(newmap);
6717c478bd9Sstevel@tonic-gate 		if (ferror(newmap)) {
6727c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "Error writing %s\n", name);
6737c478bd9Sstevel@tonic-gate 			return;
6747c478bd9Sstevel@tonic-gate 		}
6757c478bd9Sstevel@tonic-gate 		(void) fclose(newmap);
6767c478bd9Sstevel@tonic-gate 		if (nchanges == 0)
6777c478bd9Sstevel@tonic-gate 			(void) unlink(newname);
6787c478bd9Sstevel@tonic-gate 		else if (installnew) {
6797c478bd9Sstevel@tonic-gate 			char oldname[MAXPATHLEN];
6807c478bd9Sstevel@tonic-gate 
6817c478bd9Sstevel@tonic-gate 			(void) strcpy(oldname, name);
6827c478bd9Sstevel@tonic-gate 			(void) strcat(oldname, ".old");
6837c478bd9Sstevel@tonic-gate 			if (rename(name, oldname) == -1 ||
6847c478bd9Sstevel@tonic-gate 			    rename(newname, name) == -1)
6857c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
6867c478bd9Sstevel@tonic-gate 				    "Couldn't install %s: %s\n",
6877c478bd9Sstevel@tonic-gate 				    newname, strerror(errno));
6887c478bd9Sstevel@tonic-gate 		}
6897c478bd9Sstevel@tonic-gate 	}
6907c478bd9Sstevel@tonic-gate }
691