xref: /titanic_52/usr/src/cmd/chmod/chmod.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 
27*7c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T			*/
28*7c478bd9Sstevel@tonic-gate /*	  All Rights Reserved						*/
29*7c478bd9Sstevel@tonic-gate /*									*/
30*7c478bd9Sstevel@tonic-gate 
31*7c478bd9Sstevel@tonic-gate /*
32*7c478bd9Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
33*7c478bd9Sstevel@tonic-gate  * The Regents of the University of California
34*7c478bd9Sstevel@tonic-gate  * All Rights Reserved
35*7c478bd9Sstevel@tonic-gate  *
36*7c478bd9Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
37*7c478bd9Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
38*7c478bd9Sstevel@tonic-gate  * contributors.
39*7c478bd9Sstevel@tonic-gate  */
40*7c478bd9Sstevel@tonic-gate 
41*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
42*7c478bd9Sstevel@tonic-gate 
43*7c478bd9Sstevel@tonic-gate /*
44*7c478bd9Sstevel@tonic-gate  * chmod option mode files
45*7c478bd9Sstevel@tonic-gate  * where
46*7c478bd9Sstevel@tonic-gate  *	mode is [ugoa][+-=][rwxXlstugo] or an octal number
47*7c478bd9Sstevel@tonic-gate  *	option is -R and -f
48*7c478bd9Sstevel@tonic-gate  */
49*7c478bd9Sstevel@tonic-gate 
50*7c478bd9Sstevel@tonic-gate /*
51*7c478bd9Sstevel@tonic-gate  *  Note that many convolutions are necessary
52*7c478bd9Sstevel@tonic-gate  *  due to the re-use of bits between locking
53*7c478bd9Sstevel@tonic-gate  *  and setgid
54*7c478bd9Sstevel@tonic-gate  */
55*7c478bd9Sstevel@tonic-gate 
56*7c478bd9Sstevel@tonic-gate #include <unistd.h>
57*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
58*7c478bd9Sstevel@tonic-gate #include <stdio.h>
59*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
60*7c478bd9Sstevel@tonic-gate #include <sys/stat.h>
61*7c478bd9Sstevel@tonic-gate #include <dirent.h>
62*7c478bd9Sstevel@tonic-gate #include <locale.h>
63*7c478bd9Sstevel@tonic-gate #include <string.h>	/* strerror() */
64*7c478bd9Sstevel@tonic-gate #include <stdarg.h>
65*7c478bd9Sstevel@tonic-gate #include <limits.h>
66*7c478bd9Sstevel@tonic-gate #include <errno.h>
67*7c478bd9Sstevel@tonic-gate #include <sys/acl.h>
68*7c478bd9Sstevel@tonic-gate 
69*7c478bd9Sstevel@tonic-gate static int	rflag;
70*7c478bd9Sstevel@tonic-gate static int	fflag;
71*7c478bd9Sstevel@tonic-gate 
72*7c478bd9Sstevel@tonic-gate extern int	optind;
73*7c478bd9Sstevel@tonic-gate extern int	errno;
74*7c478bd9Sstevel@tonic-gate 
75*7c478bd9Sstevel@tonic-gate static int	mac;		/* Alternate to argc (for parseargs) */
76*7c478bd9Sstevel@tonic-gate static char	**mav;		/* Alternate to argv (for parseargs) */
77*7c478bd9Sstevel@tonic-gate 
78*7c478bd9Sstevel@tonic-gate static char	*ms;		/* Points to the mode argument */
79*7c478bd9Sstevel@tonic-gate 
80*7c478bd9Sstevel@tonic-gate extern mode_t
81*7c478bd9Sstevel@tonic-gate newmode_common(char *ms, mode_t new_mode, mode_t umsk, char *file, char *path,
82*7c478bd9Sstevel@tonic-gate 	o_mode_t *group_clear_bits, o_mode_t *group_set_bits);
83*7c478bd9Sstevel@tonic-gate 
84*7c478bd9Sstevel@tonic-gate static int
85*7c478bd9Sstevel@tonic-gate dochmod(char *name, char *path, mode_t umsk),
86*7c478bd9Sstevel@tonic-gate chmodr(char *dir, char *path, mode_t mode, mode_t umsk);
87*7c478bd9Sstevel@tonic-gate 
88*7c478bd9Sstevel@tonic-gate static void handle_acl(char *name, o_mode_t group_clear_bits,
89*7c478bd9Sstevel@tonic-gate 	o_mode_t group_set_bits);
90*7c478bd9Sstevel@tonic-gate 
91*7c478bd9Sstevel@tonic-gate static void
92*7c478bd9Sstevel@tonic-gate usage(void);
93*7c478bd9Sstevel@tonic-gate 
94*7c478bd9Sstevel@tonic-gate void
95*7c478bd9Sstevel@tonic-gate errmsg(int severity, int code, char *format, ...);
96*7c478bd9Sstevel@tonic-gate 
97*7c478bd9Sstevel@tonic-gate static void
98*7c478bd9Sstevel@tonic-gate parseargs(int ac, char *av[]);
99*7c478bd9Sstevel@tonic-gate 
100*7c478bd9Sstevel@tonic-gate int
101*7c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
102*7c478bd9Sstevel@tonic-gate {
103*7c478bd9Sstevel@tonic-gate 	int i, c;
104*7c478bd9Sstevel@tonic-gate 	int status = 0;
105*7c478bd9Sstevel@tonic-gate 	mode_t umsk;
106*7c478bd9Sstevel@tonic-gate 
107*7c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
108*7c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
109*7c478bd9Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
110*7c478bd9Sstevel@tonic-gate #endif
111*7c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
112*7c478bd9Sstevel@tonic-gate 
113*7c478bd9Sstevel@tonic-gate 	parseargs(argc, argv);
114*7c478bd9Sstevel@tonic-gate 
115*7c478bd9Sstevel@tonic-gate 	while ((c = getopt(mac, mav, "Rf")) != EOF) {
116*7c478bd9Sstevel@tonic-gate 		switch (c) {
117*7c478bd9Sstevel@tonic-gate 		case 'R':
118*7c478bd9Sstevel@tonic-gate 			rflag++;
119*7c478bd9Sstevel@tonic-gate 			break;
120*7c478bd9Sstevel@tonic-gate 		case 'f':
121*7c478bd9Sstevel@tonic-gate 			fflag++;
122*7c478bd9Sstevel@tonic-gate 			break;
123*7c478bd9Sstevel@tonic-gate 		case '?':
124*7c478bd9Sstevel@tonic-gate 			usage();
125*7c478bd9Sstevel@tonic-gate 			exit(2);
126*7c478bd9Sstevel@tonic-gate 		}
127*7c478bd9Sstevel@tonic-gate 	}
128*7c478bd9Sstevel@tonic-gate 
129*7c478bd9Sstevel@tonic-gate 	/*
130*7c478bd9Sstevel@tonic-gate 	 * Check for sufficient arguments
131*7c478bd9Sstevel@tonic-gate 	 * or a usage error.
132*7c478bd9Sstevel@tonic-gate 	 */
133*7c478bd9Sstevel@tonic-gate 
134*7c478bd9Sstevel@tonic-gate 	mac -= optind;
135*7c478bd9Sstevel@tonic-gate 	mav += optind;
136*7c478bd9Sstevel@tonic-gate 
137*7c478bd9Sstevel@tonic-gate 	if (mac < 2) {
138*7c478bd9Sstevel@tonic-gate 		usage();
139*7c478bd9Sstevel@tonic-gate 		exit(2);
140*7c478bd9Sstevel@tonic-gate 	}
141*7c478bd9Sstevel@tonic-gate 
142*7c478bd9Sstevel@tonic-gate 	ms = mav[0];
143*7c478bd9Sstevel@tonic-gate 
144*7c478bd9Sstevel@tonic-gate 	umsk = umask(0);
145*7c478bd9Sstevel@tonic-gate 	(void) umask(umsk);
146*7c478bd9Sstevel@tonic-gate 
147*7c478bd9Sstevel@tonic-gate 	for (i = 1; i < mac; i++)
148*7c478bd9Sstevel@tonic-gate 		status += dochmod(mav[i], mav[i], umsk);
149*7c478bd9Sstevel@tonic-gate 
150*7c478bd9Sstevel@tonic-gate 	return (fflag ? 0 : status);
151*7c478bd9Sstevel@tonic-gate }
152*7c478bd9Sstevel@tonic-gate 
153*7c478bd9Sstevel@tonic-gate static int
154*7c478bd9Sstevel@tonic-gate dochmod(char *name, char *path, mode_t umsk)
155*7c478bd9Sstevel@tonic-gate {
156*7c478bd9Sstevel@tonic-gate 	static struct stat st;
157*7c478bd9Sstevel@tonic-gate 	int linkflg = 0;
158*7c478bd9Sstevel@tonic-gate 	o_mode_t	group_clear_bits, group_set_bits;
159*7c478bd9Sstevel@tonic-gate 
160*7c478bd9Sstevel@tonic-gate 	if (lstat(name, &st) < 0) {
161*7c478bd9Sstevel@tonic-gate 		errmsg(2, 0, gettext("can't access %s\n"), path);
162*7c478bd9Sstevel@tonic-gate 		return (1);
163*7c478bd9Sstevel@tonic-gate 	}
164*7c478bd9Sstevel@tonic-gate 
165*7c478bd9Sstevel@tonic-gate 	if ((st.st_mode & S_IFMT) == S_IFLNK) {
166*7c478bd9Sstevel@tonic-gate 		linkflg = 1;
167*7c478bd9Sstevel@tonic-gate 		if (stat(name, &st) < 0) {
168*7c478bd9Sstevel@tonic-gate 			errmsg(2, 0, gettext("can't access %s\n"), path);
169*7c478bd9Sstevel@tonic-gate 			return (1);
170*7c478bd9Sstevel@tonic-gate 		}
171*7c478bd9Sstevel@tonic-gate 	}
172*7c478bd9Sstevel@tonic-gate 
173*7c478bd9Sstevel@tonic-gate 	/* Do not recurse if directory is object of symbolic link */
174*7c478bd9Sstevel@tonic-gate 	if (rflag && ((st.st_mode & S_IFMT) == S_IFDIR) && !linkflg)
175*7c478bd9Sstevel@tonic-gate 		return (chmodr(name, path, st.st_mode, umsk));
176*7c478bd9Sstevel@tonic-gate 
177*7c478bd9Sstevel@tonic-gate 	if (chmod(name, newmode_common(ms, st.st_mode, umsk, name, path,
178*7c478bd9Sstevel@tonic-gate 	    &group_clear_bits, &group_set_bits)) == -1) {
179*7c478bd9Sstevel@tonic-gate 		errmsg(2, 0, gettext("can't change %s\n"), path);
180*7c478bd9Sstevel@tonic-gate 		return (1);
181*7c478bd9Sstevel@tonic-gate 	}
182*7c478bd9Sstevel@tonic-gate 
183*7c478bd9Sstevel@tonic-gate 	/*
184*7c478bd9Sstevel@tonic-gate 	 * If the group permissions of the file are being modified,
185*7c478bd9Sstevel@tonic-gate 	 * make sure that the file's ACL (if it has one) is
186*7c478bd9Sstevel@tonic-gate 	 * modified also, since chmod is supposed to apply group
187*7c478bd9Sstevel@tonic-gate 	 * permissions changes to both the acl mask and the
188*7c478bd9Sstevel@tonic-gate 	 * general group permissions.
189*7c478bd9Sstevel@tonic-gate 	 */
190*7c478bd9Sstevel@tonic-gate 	if (group_clear_bits || group_set_bits)
191*7c478bd9Sstevel@tonic-gate 		handle_acl(name, group_clear_bits, group_set_bits);
192*7c478bd9Sstevel@tonic-gate 
193*7c478bd9Sstevel@tonic-gate 	return (0);
194*7c478bd9Sstevel@tonic-gate }
195*7c478bd9Sstevel@tonic-gate 
196*7c478bd9Sstevel@tonic-gate 
197*7c478bd9Sstevel@tonic-gate static int
198*7c478bd9Sstevel@tonic-gate chmodr(char *dir, char *path,  mode_t mode, mode_t umsk)
199*7c478bd9Sstevel@tonic-gate {
200*7c478bd9Sstevel@tonic-gate 
201*7c478bd9Sstevel@tonic-gate 	DIR *dirp;
202*7c478bd9Sstevel@tonic-gate 	struct dirent *dp;
203*7c478bd9Sstevel@tonic-gate 	char savedir[PATH_MAX];			/* dir name to restore */
204*7c478bd9Sstevel@tonic-gate 	char currdir[PATH_MAX+1];		/* current dir name + '/' */
205*7c478bd9Sstevel@tonic-gate 	char parentdir[PATH_MAX+1];		/* parent dir name  + '/' */
206*7c478bd9Sstevel@tonic-gate 	int ecode;
207*7c478bd9Sstevel@tonic-gate 	o_mode_t	group_clear_bits, group_set_bits;
208*7c478bd9Sstevel@tonic-gate 
209*7c478bd9Sstevel@tonic-gate 	if (getcwd(savedir, PATH_MAX) == 0)
210*7c478bd9Sstevel@tonic-gate 		errmsg(2, 255, gettext("chmod: could not getcwd %s\n"),
211*7c478bd9Sstevel@tonic-gate 		    savedir);
212*7c478bd9Sstevel@tonic-gate 
213*7c478bd9Sstevel@tonic-gate 	/*
214*7c478bd9Sstevel@tonic-gate 	 * Change what we are given before doing it's contents
215*7c478bd9Sstevel@tonic-gate 	 */
216*7c478bd9Sstevel@tonic-gate 	if (chmod(dir, newmode_common(ms, mode, umsk, dir, path,
217*7c478bd9Sstevel@tonic-gate 	    &group_clear_bits, &group_set_bits)) < 0) {
218*7c478bd9Sstevel@tonic-gate 		errmsg(2, 0, gettext("can't change %s\n"), path);
219*7c478bd9Sstevel@tonic-gate 		return (1);
220*7c478bd9Sstevel@tonic-gate 	}
221*7c478bd9Sstevel@tonic-gate 
222*7c478bd9Sstevel@tonic-gate 	/*
223*7c478bd9Sstevel@tonic-gate 	 * If the group permissions of the file are being modified,
224*7c478bd9Sstevel@tonic-gate 	 * make sure that the file's ACL (if it has one) is
225*7c478bd9Sstevel@tonic-gate 	 * modified also, since chmod is supposed to apply group
226*7c478bd9Sstevel@tonic-gate 	 * permissions changes to both the acl mask and the
227*7c478bd9Sstevel@tonic-gate 	 * general group permissions.
228*7c478bd9Sstevel@tonic-gate 	 */
229*7c478bd9Sstevel@tonic-gate 	if (group_clear_bits || group_set_bits)
230*7c478bd9Sstevel@tonic-gate 		handle_acl(dir, group_clear_bits, group_set_bits);
231*7c478bd9Sstevel@tonic-gate 
232*7c478bd9Sstevel@tonic-gate 	if (chdir(dir) < 0) {
233*7c478bd9Sstevel@tonic-gate 		errmsg(2, 0, "%s/%s: %s\n", savedir, dir, strerror(errno));
234*7c478bd9Sstevel@tonic-gate 		return (1);
235*7c478bd9Sstevel@tonic-gate 	}
236*7c478bd9Sstevel@tonic-gate 	if ((dirp = opendir(".")) == NULL) {
237*7c478bd9Sstevel@tonic-gate 		errmsg(2, 0, "%s\n", strerror(errno));
238*7c478bd9Sstevel@tonic-gate 		return (1);
239*7c478bd9Sstevel@tonic-gate 	}
240*7c478bd9Sstevel@tonic-gate 	dp = readdir(dirp);
241*7c478bd9Sstevel@tonic-gate 	dp = readdir(dirp); /* read "." and ".." */
242*7c478bd9Sstevel@tonic-gate 	ecode = 0;
243*7c478bd9Sstevel@tonic-gate 
244*7c478bd9Sstevel@tonic-gate 	/*
245*7c478bd9Sstevel@tonic-gate 	 * Save parent directory path before recursive chmod.
246*7c478bd9Sstevel@tonic-gate 	 * We'll need this for error printing purposes. Add
247*7c478bd9Sstevel@tonic-gate 	 * a trailing '/' to the path except in the case where
248*7c478bd9Sstevel@tonic-gate 	 * the path is just '/'
249*7c478bd9Sstevel@tonic-gate 	 */
250*7c478bd9Sstevel@tonic-gate 
251*7c478bd9Sstevel@tonic-gate 	(void) strcpy(parentdir, path);
252*7c478bd9Sstevel@tonic-gate 	if (strcmp(path, "/") != 0)
253*7c478bd9Sstevel@tonic-gate 		(void) strcat(parentdir, "/");
254*7c478bd9Sstevel@tonic-gate 
255*7c478bd9Sstevel@tonic-gate 	for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))  {
256*7c478bd9Sstevel@tonic-gate 		(void) strcpy(currdir, parentdir);
257*7c478bd9Sstevel@tonic-gate 		(void) strcat(currdir, dp->d_name);
258*7c478bd9Sstevel@tonic-gate 		ecode += dochmod(dp->d_name, currdir, umsk);
259*7c478bd9Sstevel@tonic-gate 	}
260*7c478bd9Sstevel@tonic-gate 	(void) closedir(dirp);
261*7c478bd9Sstevel@tonic-gate 	if (chdir(savedir) < 0) {
262*7c478bd9Sstevel@tonic-gate 		errmsg(2, 255, gettext("can't change back to %s\n"), savedir);
263*7c478bd9Sstevel@tonic-gate 	}
264*7c478bd9Sstevel@tonic-gate 	return (ecode ? 1 : 0);
265*7c478bd9Sstevel@tonic-gate }
266*7c478bd9Sstevel@tonic-gate 
267*7c478bd9Sstevel@tonic-gate /* PRINTFLIKE3 */
268*7c478bd9Sstevel@tonic-gate void
269*7c478bd9Sstevel@tonic-gate errmsg(int severity, int code, char *format, ...)
270*7c478bd9Sstevel@tonic-gate {
271*7c478bd9Sstevel@tonic-gate 	va_list ap;
272*7c478bd9Sstevel@tonic-gate 	static char *msg[] = {
273*7c478bd9Sstevel@tonic-gate 	"",
274*7c478bd9Sstevel@tonic-gate 	"ERROR",
275*7c478bd9Sstevel@tonic-gate 	"WARNING",
276*7c478bd9Sstevel@tonic-gate 	""
277*7c478bd9Sstevel@tonic-gate 	};
278*7c478bd9Sstevel@tonic-gate 
279*7c478bd9Sstevel@tonic-gate 	va_start(ap, format);
280*7c478bd9Sstevel@tonic-gate 
281*7c478bd9Sstevel@tonic-gate 	/*
282*7c478bd9Sstevel@tonic-gate 	 * Always print error message if this is a fatal error (code == 0);
283*7c478bd9Sstevel@tonic-gate 	 * otherwise, print message if fflag == 0 (no -f option specified)
284*7c478bd9Sstevel@tonic-gate 	 */
285*7c478bd9Sstevel@tonic-gate 	if (!fflag || (code != 0)) {
286*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
287*7c478bd9Sstevel@tonic-gate 			"chmod: %s: ", gettext(msg[severity]));
288*7c478bd9Sstevel@tonic-gate 		(void) vfprintf(stderr, format, ap);
289*7c478bd9Sstevel@tonic-gate 	}
290*7c478bd9Sstevel@tonic-gate 
291*7c478bd9Sstevel@tonic-gate 	va_end(ap);
292*7c478bd9Sstevel@tonic-gate 
293*7c478bd9Sstevel@tonic-gate 	if (code != 0)
294*7c478bd9Sstevel@tonic-gate 		exit(fflag ? 0 : code);
295*7c478bd9Sstevel@tonic-gate }
296*7c478bd9Sstevel@tonic-gate 
297*7c478bd9Sstevel@tonic-gate static void
298*7c478bd9Sstevel@tonic-gate usage(void)
299*7c478bd9Sstevel@tonic-gate {
300*7c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(
301*7c478bd9Sstevel@tonic-gate 	    "usage:\tchmod [-fR] <absolute-mode> file ...\n"));
302*7c478bd9Sstevel@tonic-gate 
303*7c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(
304*7c478bd9Sstevel@tonic-gate 	    "\tchmod [-fR] <symbolic-mode-list> file ...\n"));
305*7c478bd9Sstevel@tonic-gate 
306*7c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(
307*7c478bd9Sstevel@tonic-gate 	    "where \t<symbolic-mode-list> is a comma-separated list of\n"));
308*7c478bd9Sstevel@tonic-gate 
309*7c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(
310*7c478bd9Sstevel@tonic-gate 	    "\t[ugoa]{+|-|=}[rwxXlstugo]\n"));
311*7c478bd9Sstevel@tonic-gate }
312*7c478bd9Sstevel@tonic-gate 
313*7c478bd9Sstevel@tonic-gate /*
314*7c478bd9Sstevel@tonic-gate  *  parseargs - generate getopt-friendly argument list for backwards
315*7c478bd9Sstevel@tonic-gate  *		compatibility with earlier Solaris usage (eg, chmod -w
316*7c478bd9Sstevel@tonic-gate  *		foo).
317*7c478bd9Sstevel@tonic-gate  *
318*7c478bd9Sstevel@tonic-gate  *  assumes the existence of a static set of alternates to argc and argv,
319*7c478bd9Sstevel@tonic-gate  *  (namely, mac, and mav[]).
320*7c478bd9Sstevel@tonic-gate  *
321*7c478bd9Sstevel@tonic-gate  */
322*7c478bd9Sstevel@tonic-gate 
323*7c478bd9Sstevel@tonic-gate static void
324*7c478bd9Sstevel@tonic-gate parseargs(int ac, char *av[])
325*7c478bd9Sstevel@tonic-gate {
326*7c478bd9Sstevel@tonic-gate 	int i;			/* current argument			*/
327*7c478bd9Sstevel@tonic-gate 	int fflag;		/* arg list contains "--"		*/
328*7c478bd9Sstevel@tonic-gate 	size_t mav_num;		/* number of entries in mav[]		*/
329*7c478bd9Sstevel@tonic-gate 
330*7c478bd9Sstevel@tonic-gate 	/*
331*7c478bd9Sstevel@tonic-gate 	 * We add an extra argument slot, in case we need to jam a "--"
332*7c478bd9Sstevel@tonic-gate 	 * argument into the list.
333*7c478bd9Sstevel@tonic-gate 	 */
334*7c478bd9Sstevel@tonic-gate 
335*7c478bd9Sstevel@tonic-gate 	mav_num = (size_t)ac+2;
336*7c478bd9Sstevel@tonic-gate 
337*7c478bd9Sstevel@tonic-gate 	if ((mav = calloc(mav_num, sizeof (char *))) == NULL) {
338*7c478bd9Sstevel@tonic-gate 		perror("chmod");
339*7c478bd9Sstevel@tonic-gate 		exit(2);
340*7c478bd9Sstevel@tonic-gate 	}
341*7c478bd9Sstevel@tonic-gate 
342*7c478bd9Sstevel@tonic-gate 	/* scan for the use of "--" in the argument list */
343*7c478bd9Sstevel@tonic-gate 
344*7c478bd9Sstevel@tonic-gate 	for (fflag = i = 0; i < ac; i ++) {
345*7c478bd9Sstevel@tonic-gate 		if (strcmp(av[i], "--") == 0)
346*7c478bd9Sstevel@tonic-gate 		    fflag = 1;
347*7c478bd9Sstevel@tonic-gate 	}
348*7c478bd9Sstevel@tonic-gate 
349*7c478bd9Sstevel@tonic-gate 	/* process the arguments */
350*7c478bd9Sstevel@tonic-gate 
351*7c478bd9Sstevel@tonic-gate 	for (i = mac = 0;
352*7c478bd9Sstevel@tonic-gate 	    (av[i] != (char *)NULL) && (av[i][0] != (char)NULL);
353*7c478bd9Sstevel@tonic-gate 	    i++) {
354*7c478bd9Sstevel@tonic-gate 		if (!fflag && av[i][0] == '-') {
355*7c478bd9Sstevel@tonic-gate 			/*
356*7c478bd9Sstevel@tonic-gate 			 *  If there is not already a "--" argument specified,
357*7c478bd9Sstevel@tonic-gate 			 *  and the argument starts with '-' but does not
358*7c478bd9Sstevel@tonic-gate 			 *  contain any of the official option letters, then it
359*7c478bd9Sstevel@tonic-gate 			 *  is probably a mode argument beginning with '-'.
360*7c478bd9Sstevel@tonic-gate 			 *  Force a "--" into the argument stream in front of
361*7c478bd9Sstevel@tonic-gate 			 *  it.
362*7c478bd9Sstevel@tonic-gate 			 */
363*7c478bd9Sstevel@tonic-gate 
364*7c478bd9Sstevel@tonic-gate 			if ((strchr(av[i], 'R') == NULL &&
365*7c478bd9Sstevel@tonic-gate 			    strchr(av[i], 'f') == NULL)) {
366*7c478bd9Sstevel@tonic-gate 				mav[mac++] = strdup("--");
367*7c478bd9Sstevel@tonic-gate 			}
368*7c478bd9Sstevel@tonic-gate 		}
369*7c478bd9Sstevel@tonic-gate 
370*7c478bd9Sstevel@tonic-gate 		mav[mac++] = strdup(av[i]);
371*7c478bd9Sstevel@tonic-gate 	}
372*7c478bd9Sstevel@tonic-gate 
373*7c478bd9Sstevel@tonic-gate 	mav[mac] = (char *)NULL;
374*7c478bd9Sstevel@tonic-gate }
375*7c478bd9Sstevel@tonic-gate 
376*7c478bd9Sstevel@tonic-gate /*
377*7c478bd9Sstevel@tonic-gate  * This function is called whenever the group permissions of a file
378*7c478bd9Sstevel@tonic-gate  * is being modified.  According to the chmod(1) manpage, any
379*7c478bd9Sstevel@tonic-gate  * change made to the group permissions must be applied to both
380*7c478bd9Sstevel@tonic-gate  * the acl mask and the acl's GROUP_OBJ.  The chmod(2) already
381*7c478bd9Sstevel@tonic-gate  * set the mask, so this routine needs to make the same change
382*7c478bd9Sstevel@tonic-gate  * to the GROUP_OBJ.
383*7c478bd9Sstevel@tonic-gate  */
384*7c478bd9Sstevel@tonic-gate static void
385*7c478bd9Sstevel@tonic-gate handle_acl(char *name, o_mode_t group_clear_bits, o_mode_t group_set_bits)
386*7c478bd9Sstevel@tonic-gate {
387*7c478bd9Sstevel@tonic-gate 	int aclcnt, n;
388*7c478bd9Sstevel@tonic-gate 	aclent_t *aclp, *tp;
389*7c478bd9Sstevel@tonic-gate 	o_mode_t newperm;
390*7c478bd9Sstevel@tonic-gate 
391*7c478bd9Sstevel@tonic-gate 	if ((aclcnt = acl(name, GETACLCNT, 0, NULL)) <= MIN_ACL_ENTRIES)
392*7c478bd9Sstevel@tonic-gate 		return;	/* it's just a trivial acl; no need to change it */
393*7c478bd9Sstevel@tonic-gate 
394*7c478bd9Sstevel@tonic-gate 	if ((aclp = (aclent_t *)malloc((sizeof (aclent_t)) * aclcnt))
395*7c478bd9Sstevel@tonic-gate 	    == NULL) {
396*7c478bd9Sstevel@tonic-gate 		perror("chmod");
397*7c478bd9Sstevel@tonic-gate 		exit(2);
398*7c478bd9Sstevel@tonic-gate 	}
399*7c478bd9Sstevel@tonic-gate 
400*7c478bd9Sstevel@tonic-gate 	if (acl(name, GETACL, aclcnt, aclp) < 0) {
401*7c478bd9Sstevel@tonic-gate 		free(aclp);
402*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "chmod: ");
403*7c478bd9Sstevel@tonic-gate 		perror(name);
404*7c478bd9Sstevel@tonic-gate 		return;
405*7c478bd9Sstevel@tonic-gate 	}
406*7c478bd9Sstevel@tonic-gate 
407*7c478bd9Sstevel@tonic-gate 	for (tp = aclp, n = aclcnt; n--; tp++) {
408*7c478bd9Sstevel@tonic-gate 		if (tp->a_type == GROUP_OBJ) {
409*7c478bd9Sstevel@tonic-gate 			newperm = tp->a_perm;
410*7c478bd9Sstevel@tonic-gate 			if (group_clear_bits != 0)
411*7c478bd9Sstevel@tonic-gate 				newperm &= ~group_clear_bits;
412*7c478bd9Sstevel@tonic-gate 			if (group_set_bits != 0)
413*7c478bd9Sstevel@tonic-gate 				newperm |= group_set_bits;
414*7c478bd9Sstevel@tonic-gate 			if (newperm != tp->a_perm) {
415*7c478bd9Sstevel@tonic-gate 				tp->a_perm = newperm;
416*7c478bd9Sstevel@tonic-gate 				if (acl(name, SETACL, aclcnt, aclp)
417*7c478bd9Sstevel@tonic-gate 				    < 0) {
418*7c478bd9Sstevel@tonic-gate 					(void) fprintf(stderr, "chmod: ");
419*7c478bd9Sstevel@tonic-gate 					perror(name);
420*7c478bd9Sstevel@tonic-gate 				}
421*7c478bd9Sstevel@tonic-gate 			}
422*7c478bd9Sstevel@tonic-gate 			break;
423*7c478bd9Sstevel@tonic-gate 		}
424*7c478bd9Sstevel@tonic-gate 	}
425*7c478bd9Sstevel@tonic-gate 	free(aclp);
426*7c478bd9Sstevel@tonic-gate }
427