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