xref: /titanic_50/usr/src/cmd/chmod/chmod.c (revision da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0)
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
5c4518760Scasper  * Common Development and Distribution License (the "License").
6c4518760Scasper  * 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  */
217c478bd9Sstevel@tonic-gate /*
22401f278dSas145665  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T			*/
277c478bd9Sstevel@tonic-gate /*	  All Rights Reserved						*/
287c478bd9Sstevel@tonic-gate /*									*/
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate /*
317c478bd9Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
327c478bd9Sstevel@tonic-gate  * The Regents of the University of California
337c478bd9Sstevel@tonic-gate  * All Rights Reserved
347c478bd9Sstevel@tonic-gate  *
357c478bd9Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
367c478bd9Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
377c478bd9Sstevel@tonic-gate  * contributors.
387c478bd9Sstevel@tonic-gate  */
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate /*
437c478bd9Sstevel@tonic-gate  * chmod option mode files
447c478bd9Sstevel@tonic-gate  * where
457c478bd9Sstevel@tonic-gate  *	mode is [ugoa][+-=][rwxXlstugo] or an octal number
46fa9e4066Sahrens  *	mode is [<+|->A[# <number] ]<aclspec>
47*da6c28aaSamw  *	mode is S<attrspec>
48*da6c28aaSamw  *	option is -R, -f, and -@
497c478bd9Sstevel@tonic-gate  */
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate /*
527c478bd9Sstevel@tonic-gate  *  Note that many convolutions are necessary
537c478bd9Sstevel@tonic-gate  *  due to the re-use of bits between locking
547c478bd9Sstevel@tonic-gate  *  and setgid
557c478bd9Sstevel@tonic-gate  */
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate #include <unistd.h>
587c478bd9Sstevel@tonic-gate #include <stdlib.h>
597c478bd9Sstevel@tonic-gate #include <stdio.h>
607c478bd9Sstevel@tonic-gate #include <sys/types.h>
617c478bd9Sstevel@tonic-gate #include <sys/stat.h>
62*da6c28aaSamw #include <fcntl.h>
637c478bd9Sstevel@tonic-gate #include <dirent.h>
647c478bd9Sstevel@tonic-gate #include <locale.h>
657c478bd9Sstevel@tonic-gate #include <string.h>	/* strerror() */
667c478bd9Sstevel@tonic-gate #include <stdarg.h>
677c478bd9Sstevel@tonic-gate #include <limits.h>
68fa9e4066Sahrens #include <ctype.h>
697c478bd9Sstevel@tonic-gate #include <errno.h>
707c478bd9Sstevel@tonic-gate #include <sys/acl.h>
71fa9e4066Sahrens #include <aclutils.h>
72*da6c28aaSamw #include <libnvpair.h>
73*da6c28aaSamw #include <libcmdutils.h>
74*da6c28aaSamw #include <libgen.h>
75*da6c28aaSamw #include <attr.h>
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate static int	rflag;
787c478bd9Sstevel@tonic-gate static int	fflag;
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate extern int	optind;
817c478bd9Sstevel@tonic-gate extern int	errno;
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate static int	mac;		/* Alternate to argc (for parseargs) */
847c478bd9Sstevel@tonic-gate static char	**mav;		/* Alternate to argv (for parseargs) */
857c478bd9Sstevel@tonic-gate 
867c478bd9Sstevel@tonic-gate static char	*ms;		/* Points to the mode argument */
877c478bd9Sstevel@tonic-gate 
88fa9e4066Sahrens #define	ACL_ADD			1
89fa9e4066Sahrens #define	ACL_DELETE		2
90fa9e4066Sahrens #define	ACL_SLOT_DELETE		3
91fa9e4066Sahrens #define	ACL_REPLACE		4
92fa9e4066Sahrens #define	ACL_STRIP		5
93fa9e4066Sahrens 
94*da6c28aaSamw #define	LEFTBRACE	'{'
95*da6c28aaSamw #define	RIGHTBRACE	'}'
96*da6c28aaSamw #define	A_SEP		','
97*da6c28aaSamw #define	A_SEP_TOK	","
98*da6c28aaSamw 
99*da6c28aaSamw #define	A_COMPACT_TYPE	'c'
100*da6c28aaSamw #define	A_VERBOSE_TYPE	'v'
101*da6c28aaSamw #define	A_ALLATTRS_TYPE	'a'
102*da6c28aaSamw 
103*da6c28aaSamw #define	A_SET_OP	'+'
104*da6c28aaSamw #define	A_INVERSE_OP	'-'
105*da6c28aaSamw #define	A_REPLACE_OP	'='
106*da6c28aaSamw #define	A_UNDEF_OP	'\0'
107*da6c28aaSamw 
108*da6c28aaSamw #define	A_SET_TEXT	"set"
109*da6c28aaSamw #define	A_INVERSE_TEXT	"clear"
110*da6c28aaSamw 
111*da6c28aaSamw #define	A_SET_VAL	B_TRUE
112*da6c28aaSamw #define	A_CLEAR_VAL	B_FALSE
113*da6c28aaSamw 
114*da6c28aaSamw #define	ATTR_OPTS	0
115*da6c28aaSamw #define	ATTR_NAMES	1
116*da6c28aaSamw 
117*da6c28aaSamw #define	sec_acls	secptr.acls
118*da6c28aaSamw #define	sec_attrs	secptr.attrs
119*da6c28aaSamw 
120fa9e4066Sahrens typedef struct acl_args {
121fa9e4066Sahrens 	acl_t	*acl_aclp;
122fa9e4066Sahrens 	int	acl_slot;
123fa9e4066Sahrens 	int	acl_action;
124fa9e4066Sahrens } acl_args_t;
125fa9e4066Sahrens 
126*da6c28aaSamw typedef enum {
127*da6c28aaSamw 	SEC_ACL,
128*da6c28aaSamw 	SEC_ATTR
129*da6c28aaSamw } chmod_sec_t;
1307c478bd9Sstevel@tonic-gate 
131*da6c28aaSamw typedef struct {
132*da6c28aaSamw 	chmod_sec_t		sec_type;
133*da6c28aaSamw 	union {
134*da6c28aaSamw 		acl_args_t	*acls;
135*da6c28aaSamw 		nvlist_t	*attrs;
136*da6c28aaSamw 	} secptr;
137*da6c28aaSamw } sec_args_t;
138*da6c28aaSamw 
139*da6c28aaSamw typedef struct attr_name {
140*da6c28aaSamw 	char			*name;
141*da6c28aaSamw 	struct attr_name	*next;
142*da6c28aaSamw } attr_name_t;
143*da6c28aaSamw 
144*da6c28aaSamw 
145*da6c28aaSamw extern mode_t newmode_common(char *ms, mode_t new_mode, mode_t umsk,
146*da6c28aaSamw     char *file, char *path, o_mode_t *group_clear_bits,
147*da6c28aaSamw     o_mode_t *group_set_bits);
148*da6c28aaSamw 
149*da6c28aaSamw static int chmodr(char *dir, char *path, mode_t mode, mode_t umsk,
150*da6c28aaSamw     sec_args_t *secp, attr_name_t *attrname);
151fa9e4066Sahrens static int doacl(char *file, struct stat *st, acl_args_t *aclp);
152*da6c28aaSamw static int dochmod(char *name, char *path, mode_t umsk, sec_args_t *secp,
153*da6c28aaSamw     attr_name_t *attrnames);
1547c478bd9Sstevel@tonic-gate static void handle_acl(char *name, o_mode_t group_clear_bits,
1557c478bd9Sstevel@tonic-gate     o_mode_t group_set_bits);
156fa9e4066Sahrens void errmsg(int severity, int code, char *format, ...);
157*da6c28aaSamw static void free_attr_names(attr_name_t *attrnames);
158fa9e4066Sahrens static void parseargs(int ac, char *av[]);
159*da6c28aaSamw static int parse_acl_args(char *arg, sec_args_t **sec_args);
160*da6c28aaSamw static int parse_attr_args(char *arg, sec_args_t **sec_args);
161*da6c28aaSamw static void print_attrs(int flag);
162*da6c28aaSamw static int set_attrs(char *file, attr_name_t *attrnames, nvlist_t *attr_nvlist);
163*da6c28aaSamw static void usage(void);
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate int
1667c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
1677c478bd9Sstevel@tonic-gate {
1687c478bd9Sstevel@tonic-gate 	int		i, c;
1697c478bd9Sstevel@tonic-gate 	int		status = 0;
1707c478bd9Sstevel@tonic-gate 	mode_t		umsk;
171*da6c28aaSamw 	sec_args_t	*sec_args = NULL;
172*da6c28aaSamw 	attr_name_t	*attrnames = NULL;
173*da6c28aaSamw 	attr_name_t	*attrend = NULL;
174*da6c28aaSamw 	attr_name_t	*tattr;
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
1777c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
1787c478bd9Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
1797c478bd9Sstevel@tonic-gate #endif
1807c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate 	parseargs(argc, argv);
1837c478bd9Sstevel@tonic-gate 
184*da6c28aaSamw 	while ((c = getopt(mac, mav, "Rf@:")) != EOF) {
1857c478bd9Sstevel@tonic-gate 		switch (c) {
1867c478bd9Sstevel@tonic-gate 		case 'R':
1877c478bd9Sstevel@tonic-gate 			rflag++;
1887c478bd9Sstevel@tonic-gate 			break;
1897c478bd9Sstevel@tonic-gate 		case 'f':
1907c478bd9Sstevel@tonic-gate 			fflag++;
1917c478bd9Sstevel@tonic-gate 			break;
192*da6c28aaSamw 		case '@':
193*da6c28aaSamw 			if (((tattr = malloc(sizeof (attr_name_t))) == NULL) ||
194*da6c28aaSamw 			    ((tattr->name = strdup(optarg)) == NULL)) {
195*da6c28aaSamw 				perror("chmod");
196*da6c28aaSamw 				exit(2);
197*da6c28aaSamw 			}
198*da6c28aaSamw 			if (attrnames == NULL) {
199*da6c28aaSamw 				attrnames = tattr;
200*da6c28aaSamw 				attrnames->next = NULL;
201*da6c28aaSamw 			} else {
202*da6c28aaSamw 				attrend->next = tattr;
203*da6c28aaSamw 			}
204*da6c28aaSamw 			attrend = tattr;
205*da6c28aaSamw 			break;
2067c478bd9Sstevel@tonic-gate 		case '?':
2077c478bd9Sstevel@tonic-gate 			usage();
2087c478bd9Sstevel@tonic-gate 			exit(2);
2097c478bd9Sstevel@tonic-gate 		}
2107c478bd9Sstevel@tonic-gate 	}
2117c478bd9Sstevel@tonic-gate 
2127c478bd9Sstevel@tonic-gate 	/*
2137c478bd9Sstevel@tonic-gate 	 * Check for sufficient arguments
2147c478bd9Sstevel@tonic-gate 	 * or a usage error.
2157c478bd9Sstevel@tonic-gate 	 */
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate 	mac -= optind;
2187c478bd9Sstevel@tonic-gate 	mav += optind;
219*da6c28aaSamw 	if ((mac >= 2) && (mav[0][0] == 'A')) {
220*da6c28aaSamw 		if (attrnames != NULL) {
221*da6c28aaSamw 			free_attr_names(attrnames);
222*da6c28aaSamw 			attrnames = NULL;
223*da6c28aaSamw 		}
224*da6c28aaSamw 		if (parse_acl_args(*mav, &sec_args)) {
225fa9e4066Sahrens 			usage();
226fa9e4066Sahrens 			exit(2);
227fa9e4066Sahrens 		}
228*da6c28aaSamw 	} else if ((mac >= 2) && (mav[0][0] == 'S')) {
229*da6c28aaSamw 		if (parse_attr_args(*mav, &sec_args)) {
230*da6c28aaSamw 			usage();
231*da6c28aaSamw 			exit(2);
232*da6c28aaSamw 
233*da6c28aaSamw 		/* A no-op attribute operation was specified. */
234*da6c28aaSamw 		} else if (sec_args->sec_attrs == NULL) {
235*da6c28aaSamw 			exit(0);
236*da6c28aaSamw 		}
237fa9e4066Sahrens 	} else {
2387c478bd9Sstevel@tonic-gate 		if (mac < 2) {
2397c478bd9Sstevel@tonic-gate 			usage();
2407c478bd9Sstevel@tonic-gate 			exit(2);
2417c478bd9Sstevel@tonic-gate 		}
242*da6c28aaSamw 		if (attrnames != NULL) {
243*da6c28aaSamw 			free_attr_names(attrnames);
244*da6c28aaSamw 			attrnames = NULL;
245*da6c28aaSamw 		}
246fa9e4066Sahrens 	}
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 	ms = mav[0];
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate 	umsk = umask(0);
2517c478bd9Sstevel@tonic-gate 	(void) umask(umsk);
2527c478bd9Sstevel@tonic-gate 
253fa9e4066Sahrens 	for (i = 1; i < mac; i++) {
254*da6c28aaSamw 		status += dochmod(mav[i], mav[i], umsk, sec_args, attrnames);
255fa9e4066Sahrens 	}
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate 	return (fflag ? 0 : status);
2587c478bd9Sstevel@tonic-gate }
2597c478bd9Sstevel@tonic-gate 
260*da6c28aaSamw static void
261*da6c28aaSamw free_attr_names(attr_name_t *attrnames)
262*da6c28aaSamw {
263*da6c28aaSamw 	attr_name_t	*attrnamesptr = attrnames;
264*da6c28aaSamw 	attr_name_t	*tptr;
265*da6c28aaSamw 
266*da6c28aaSamw 	while (attrnamesptr != NULL) {
267*da6c28aaSamw 		tptr = attrnamesptr->next;
268*da6c28aaSamw 		if (attrnamesptr->name != NULL) {
269*da6c28aaSamw 			free(attrnamesptr->name);
270*da6c28aaSamw 		}
271*da6c28aaSamw 		attrnamesptr = tptr;
272*da6c28aaSamw 	}
273*da6c28aaSamw }
274*da6c28aaSamw 
2757c478bd9Sstevel@tonic-gate static int
276*da6c28aaSamw dochmod(char *name, char *path, mode_t umsk, sec_args_t *secp,
277*da6c28aaSamw     attr_name_t *attrnames)
2787c478bd9Sstevel@tonic-gate {
2797c478bd9Sstevel@tonic-gate 	static struct stat st;
2807c478bd9Sstevel@tonic-gate 	int linkflg = 0;
2817c478bd9Sstevel@tonic-gate 	o_mode_t	group_clear_bits, group_set_bits;
2827c478bd9Sstevel@tonic-gate 
2837c478bd9Sstevel@tonic-gate 	if (lstat(name, &st) < 0) {
2847c478bd9Sstevel@tonic-gate 		errmsg(2, 0, gettext("can't access %s\n"), path);
2857c478bd9Sstevel@tonic-gate 		return (1);
2867c478bd9Sstevel@tonic-gate 	}
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate 	if ((st.st_mode & S_IFMT) == S_IFLNK) {
2897c478bd9Sstevel@tonic-gate 		linkflg = 1;
2907c478bd9Sstevel@tonic-gate 		if (stat(name, &st) < 0) {
2917c478bd9Sstevel@tonic-gate 			errmsg(2, 0, gettext("can't access %s\n"), path);
2927c478bd9Sstevel@tonic-gate 			return (1);
2937c478bd9Sstevel@tonic-gate 		}
2947c478bd9Sstevel@tonic-gate 	}
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 	/* Do not recurse if directory is object of symbolic link */
297*da6c28aaSamw 	if (rflag && ((st.st_mode & S_IFMT) == S_IFDIR) && !linkflg) {
298*da6c28aaSamw 		return (chmodr(name, path, st.st_mode, umsk, secp, attrnames));
299*da6c28aaSamw 	}
3007c478bd9Sstevel@tonic-gate 
301*da6c28aaSamw 	if (secp != NULL) {
302*da6c28aaSamw 		if (secp->sec_type == SEC_ACL) {
303*da6c28aaSamw 			return (doacl(name, &st, secp->sec_acls));
304*da6c28aaSamw 		} else if (secp->sec_type == SEC_ATTR) {
305*da6c28aaSamw 			return (set_attrs(name, attrnames, secp->sec_attrs));
306*da6c28aaSamw 		} else {
307*da6c28aaSamw 			return (1);
308*da6c28aaSamw 		}
309*da6c28aaSamw 	} else {
310*da6c28aaSamw 		if (chmod(name, newmode_common(ms, st.st_mode, umsk, name, path,
3117c478bd9Sstevel@tonic-gate 		    &group_clear_bits, &group_set_bits)) == -1) {
3127c478bd9Sstevel@tonic-gate 			errmsg(2, 0, gettext("can't change %s\n"), path);
3137c478bd9Sstevel@tonic-gate 			return (1);
3147c478bd9Sstevel@tonic-gate 		}
315*da6c28aaSamw 	}
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 	/*
3187c478bd9Sstevel@tonic-gate 	 * If the group permissions of the file are being modified,
3197c478bd9Sstevel@tonic-gate 	 * make sure that the file's ACL (if it has one) is
3207c478bd9Sstevel@tonic-gate 	 * modified also, since chmod is supposed to apply group
3217c478bd9Sstevel@tonic-gate 	 * permissions changes to both the acl mask and the
3227c478bd9Sstevel@tonic-gate 	 * general group permissions.
3237c478bd9Sstevel@tonic-gate 	 */
3247c478bd9Sstevel@tonic-gate 	if (group_clear_bits || group_set_bits)
3257c478bd9Sstevel@tonic-gate 		handle_acl(name, group_clear_bits, group_set_bits);
3267c478bd9Sstevel@tonic-gate 
3277c478bd9Sstevel@tonic-gate 	return (0);
3287c478bd9Sstevel@tonic-gate }
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate static int
331*da6c28aaSamw chmodr(char *dir, char *path,  mode_t mode, mode_t umsk, sec_args_t *secp,
332*da6c28aaSamw     attr_name_t *attrnames)
3337c478bd9Sstevel@tonic-gate {
3347c478bd9Sstevel@tonic-gate 
3357c478bd9Sstevel@tonic-gate 	DIR *dirp;
3367c478bd9Sstevel@tonic-gate 	struct dirent *dp;
3377c478bd9Sstevel@tonic-gate 	char savedir[PATH_MAX];			/* dir name to restore */
3387c478bd9Sstevel@tonic-gate 	char currdir[PATH_MAX+1];		/* current dir name + '/' */
3397c478bd9Sstevel@tonic-gate 	char parentdir[PATH_MAX+1];		/* parent dir name  + '/' */
3407c478bd9Sstevel@tonic-gate 	int ecode;
341fa9e4066Sahrens 	struct stat st;
3427c478bd9Sstevel@tonic-gate 	o_mode_t	group_clear_bits, group_set_bits;
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 	if (getcwd(savedir, PATH_MAX) == 0)
3457c478bd9Sstevel@tonic-gate 		errmsg(2, 255, gettext("chmod: could not getcwd %s\n"),
3467c478bd9Sstevel@tonic-gate 		    savedir);
3477c478bd9Sstevel@tonic-gate 
3487c478bd9Sstevel@tonic-gate 	/*
3497c478bd9Sstevel@tonic-gate 	 * Change what we are given before doing it's contents
3507c478bd9Sstevel@tonic-gate 	 */
351*da6c28aaSamw 	if (secp != NULL) {
352fa9e4066Sahrens 		if (lstat(dir, &st) < 0) {
353fa9e4066Sahrens 			errmsg(2, 0, gettext("can't access %s\n"), path);
354fa9e4066Sahrens 			return (1);
355fa9e4066Sahrens 		}
356*da6c28aaSamw 		if (secp->sec_type == SEC_ACL) {
357*da6c28aaSamw 			if (doacl(dir, &st, secp->sec_acls) != 0)
358fa9e4066Sahrens 				return (1);
359*da6c28aaSamw 		} else if (secp->sec_type == SEC_ATTR) {
360*da6c28aaSamw 			if (set_attrs(dir, attrnames, secp->sec_attrs) != 0) {
361*da6c28aaSamw 				return (1);
362*da6c28aaSamw 			}
363*da6c28aaSamw 		} else {
364*da6c28aaSamw 			return (1);
365*da6c28aaSamw 		}
366fa9e4066Sahrens 	} else if (chmod(dir, newmode_common(ms, mode, umsk, dir, path,
3677c478bd9Sstevel@tonic-gate 	    &group_clear_bits, &group_set_bits)) < 0) {
3687c478bd9Sstevel@tonic-gate 		errmsg(2, 0, gettext("can't change %s\n"), path);
3697c478bd9Sstevel@tonic-gate 		return (1);
3707c478bd9Sstevel@tonic-gate 	}
3717c478bd9Sstevel@tonic-gate 
3727c478bd9Sstevel@tonic-gate 	/*
3737c478bd9Sstevel@tonic-gate 	 * If the group permissions of the file are being modified,
3747c478bd9Sstevel@tonic-gate 	 * make sure that the file's ACL (if it has one) is
3757c478bd9Sstevel@tonic-gate 	 * modified also, since chmod is supposed to apply group
3767c478bd9Sstevel@tonic-gate 	 * permissions changes to both the acl mask and the
3777c478bd9Sstevel@tonic-gate 	 * general group permissions.
3787c478bd9Sstevel@tonic-gate 	 */
379fa9e4066Sahrens 
380*da6c28aaSamw 	if (secp != NULL) {
381*da6c28aaSamw 		/* only necessary when not setting ACL or system attributes */
3827c478bd9Sstevel@tonic-gate 		if (group_clear_bits || group_set_bits)
3837c478bd9Sstevel@tonic-gate 			handle_acl(dir, group_clear_bits, group_set_bits);
384fa9e4066Sahrens 	}
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 	if (chdir(dir) < 0) {
3877c478bd9Sstevel@tonic-gate 		errmsg(2, 0, "%s/%s: %s\n", savedir, dir, strerror(errno));
3887c478bd9Sstevel@tonic-gate 		return (1);
3897c478bd9Sstevel@tonic-gate 	}
3907c478bd9Sstevel@tonic-gate 	if ((dirp = opendir(".")) == NULL) {
3917c478bd9Sstevel@tonic-gate 		errmsg(2, 0, "%s\n", strerror(errno));
3927c478bd9Sstevel@tonic-gate 		return (1);
3937c478bd9Sstevel@tonic-gate 	}
3947c478bd9Sstevel@tonic-gate 	ecode = 0;
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate 	/*
3977c478bd9Sstevel@tonic-gate 	 * Save parent directory path before recursive chmod.
3987c478bd9Sstevel@tonic-gate 	 * We'll need this for error printing purposes. Add
3997c478bd9Sstevel@tonic-gate 	 * a trailing '/' to the path except in the case where
4007c478bd9Sstevel@tonic-gate 	 * the path is just '/'
4017c478bd9Sstevel@tonic-gate 	 */
4027c478bd9Sstevel@tonic-gate 
403401f278dSas145665 	if (strlcpy(parentdir, path, PATH_MAX + 1) >= PATH_MAX + 1) {
404401f278dSas145665 		errmsg(2, 0, gettext("directory path name too long: %s\n"),
405401f278dSas145665 		    path);
406401f278dSas145665 		return (1);
407401f278dSas145665 	}
4087c478bd9Sstevel@tonic-gate 	if (strcmp(path, "/") != 0)
409401f278dSas145665 		if (strlcat(parentdir, "/", PATH_MAX + 1) >= PATH_MAX + 1) {
410401f278dSas145665 			errmsg(2, 0,
411401f278dSas145665 			    gettext("directory path name too long: %s/\n"),
412401f278dSas145665 			    parentdir);
413401f278dSas145665 			return (1);
414401f278dSas145665 		}
415401f278dSas145665 
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate 	for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))  {
418401f278dSas145665 
419c4518760Scasper 		if (strcmp(dp->d_name, ".") == 0 ||	/* skip . and .. */
420c4518760Scasper 		    strcmp(dp->d_name, "..") == 0) {
421c4518760Scasper 			continue;
422c4518760Scasper 		}
423401f278dSas145665 		if (strlcpy(currdir, parentdir, PATH_MAX + 1) >= PATH_MAX + 1) {
424401f278dSas145665 			errmsg(2, 0,
425401f278dSas145665 			    gettext("directory path name too long: %s\n"),
426401f278dSas145665 			    parentdir);
427401f278dSas145665 			return (1);
428401f278dSas145665 		}
429401f278dSas145665 		if (strlcat(currdir, dp->d_name, PATH_MAX + 1)
430401f278dSas145665 		    >= PATH_MAX + 1) {
431401f278dSas145665 			errmsg(2, 0,
432401f278dSas145665 			    gettext("directory path name too long: %s%s\n"),
433401f278dSas145665 			    currdir, dp->d_name);
434401f278dSas145665 			return (1);
435401f278dSas145665 		}
436*da6c28aaSamw 		ecode += dochmod(dp->d_name, currdir, umsk, secp, attrnames);
4377c478bd9Sstevel@tonic-gate 	}
4387c478bd9Sstevel@tonic-gate 	(void) closedir(dirp);
4397c478bd9Sstevel@tonic-gate 	if (chdir(savedir) < 0) {
4407c478bd9Sstevel@tonic-gate 		errmsg(2, 255, gettext("can't change back to %s\n"), savedir);
4417c478bd9Sstevel@tonic-gate 	}
4427c478bd9Sstevel@tonic-gate 	return (ecode ? 1 : 0);
4437c478bd9Sstevel@tonic-gate }
4447c478bd9Sstevel@tonic-gate 
4457c478bd9Sstevel@tonic-gate /* PRINTFLIKE3 */
4467c478bd9Sstevel@tonic-gate void
4477c478bd9Sstevel@tonic-gate errmsg(int severity, int code, char *format, ...)
4487c478bd9Sstevel@tonic-gate {
4497c478bd9Sstevel@tonic-gate 	va_list ap;
4507c478bd9Sstevel@tonic-gate 	static char *msg[] = {
4517c478bd9Sstevel@tonic-gate 	"",
4527c478bd9Sstevel@tonic-gate 	"ERROR",
4537c478bd9Sstevel@tonic-gate 	"WARNING",
4547c478bd9Sstevel@tonic-gate 	""
4557c478bd9Sstevel@tonic-gate 	};
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate 	va_start(ap, format);
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 	/*
4607c478bd9Sstevel@tonic-gate 	 * Always print error message if this is a fatal error (code == 0);
4617c478bd9Sstevel@tonic-gate 	 * otherwise, print message if fflag == 0 (no -f option specified)
4627c478bd9Sstevel@tonic-gate 	 */
4637c478bd9Sstevel@tonic-gate 	if (!fflag || (code != 0)) {
4647c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
4657c478bd9Sstevel@tonic-gate 		    "chmod: %s: ", gettext(msg[severity]));
4667c478bd9Sstevel@tonic-gate 		(void) vfprintf(stderr, format, ap);
4677c478bd9Sstevel@tonic-gate 	}
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate 	va_end(ap);
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate 	if (code != 0)
4727c478bd9Sstevel@tonic-gate 		exit(fflag ? 0 : code);
4737c478bd9Sstevel@tonic-gate }
4747c478bd9Sstevel@tonic-gate 
4757c478bd9Sstevel@tonic-gate static void
4767c478bd9Sstevel@tonic-gate usage(void)
4777c478bd9Sstevel@tonic-gate {
4787c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(
4797c478bd9Sstevel@tonic-gate 	    "usage:\tchmod [-fR] <absolute-mode> file ...\n"));
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(
482*da6c28aaSamw 	    "\tchmod [-fR] [-@ attribute] ... "
483*da6c28aaSamw 	    "S<attribute-operation> file ...\n"));
484*da6c28aaSamw 
485*da6c28aaSamw 	(void) fprintf(stderr, gettext(
486fa9e4066Sahrens 	    "\tchmod [-fR] <ACL-operation> file ...\n"));
487fa9e4066Sahrens 
488fa9e4066Sahrens 	(void) fprintf(stderr, gettext(
489*da6c28aaSamw 	    "\tchmod [-fR] <symbolic-mode-list> file ...\n\n"));
490fa9e4066Sahrens 
4917c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(
4927c478bd9Sstevel@tonic-gate 	    "where \t<symbolic-mode-list> is a comma-separated list of\n"));
493*da6c28aaSamw 	(void) fprintf(stderr, gettext(
494*da6c28aaSamw 	    "\t[ugoa]{+|-|=}[rwxXlstugo]\n\n"));
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(
497*da6c28aaSamw 	    "where \t<attribute-operation> is a comma-separated list of\n"
498*da6c28aaSamw 	    "\tone or more of the following\n"));
499*da6c28aaSamw 	(void) fprintf(stderr, gettext(
500*da6c28aaSamw 	    "\t[+|-|=]c[<compact-attribute-list>|{<compact-attribute-list>}]\n"
501*da6c28aaSamw 	    "\t[+|-|=]v[<verbose-attribute-setting>|"
502*da6c28aaSamw 	    "\'{\'<verbose-attribute-setting-list>\'}\']\n"
503*da6c28aaSamw 	    "\t[+|-|=]a\n"));
504*da6c28aaSamw 	(void) fprintf(stderr, gettext(
505*da6c28aaSamw 	    "where \t<compact-attribute-list> is a list of zero or more of\n"));
506*da6c28aaSamw 	print_attrs(ATTR_OPTS);
507*da6c28aaSamw 	(void) fprintf(stderr, gettext(
508*da6c28aaSamw 	    "where \t<verbose-attribute-setting> is one of\n"));
509*da6c28aaSamw 	print_attrs(ATTR_NAMES);
510*da6c28aaSamw 	(void) fprintf(stderr, gettext(
511*da6c28aaSamw 	    "\tand can be, optionally, immediately preceded by \"no\"\n\n"));
512fa9e4066Sahrens 
513fa9e4066Sahrens 	(void) fprintf(stderr, gettext(
514fa9e4066Sahrens 	    "where \t<ACL-operation> is one of the following\n"));
515fa9e4066Sahrens 	(void) fprintf(stderr, gettext("\tA-<acl_specification>\n"));
516fa9e4066Sahrens 	(void) fprintf(stderr, gettext("\tA[number]-\n"));
517fa9e4066Sahrens 	(void) fprintf(stderr, gettext(
518fa9e4066Sahrens 	    "\tA[number]{+|=}<acl_specification>\n"));
519fa9e4066Sahrens 	(void) fprintf(stderr, gettext(
520fa9e4066Sahrens 	    "where \t<acl-specification> is a comma-separated list of ACEs\n"));
5217c478bd9Sstevel@tonic-gate }
5227c478bd9Sstevel@tonic-gate 
5237c478bd9Sstevel@tonic-gate /*
5247c478bd9Sstevel@tonic-gate  *  parseargs - generate getopt-friendly argument list for backwards
5257c478bd9Sstevel@tonic-gate  *		compatibility with earlier Solaris usage (eg, chmod -w
5267c478bd9Sstevel@tonic-gate  *		foo).
5277c478bd9Sstevel@tonic-gate  *
5287c478bd9Sstevel@tonic-gate  *  assumes the existence of a static set of alternates to argc and argv,
5297c478bd9Sstevel@tonic-gate  *  (namely, mac, and mav[]).
5307c478bd9Sstevel@tonic-gate  *
5317c478bd9Sstevel@tonic-gate  */
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate static void
5347c478bd9Sstevel@tonic-gate parseargs(int ac, char *av[])
5357c478bd9Sstevel@tonic-gate {
5367c478bd9Sstevel@tonic-gate 	int i;			/* current argument			*/
5377c478bd9Sstevel@tonic-gate 	int fflag;		/* arg list contains "--"		*/
5387c478bd9Sstevel@tonic-gate 	size_t mav_num;		/* number of entries in mav[]		*/
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate 	/*
5417c478bd9Sstevel@tonic-gate 	 * We add an extra argument slot, in case we need to jam a "--"
5427c478bd9Sstevel@tonic-gate 	 * argument into the list.
5437c478bd9Sstevel@tonic-gate 	 */
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate 	mav_num = (size_t)ac+2;
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate 	if ((mav = calloc(mav_num, sizeof (char *))) == NULL) {
5487c478bd9Sstevel@tonic-gate 		perror("chmod");
5497c478bd9Sstevel@tonic-gate 		exit(2);
5507c478bd9Sstevel@tonic-gate 	}
5517c478bd9Sstevel@tonic-gate 
5527c478bd9Sstevel@tonic-gate 	/* scan for the use of "--" in the argument list */
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate 	for (fflag = i = 0; i < ac; i ++) {
5557c478bd9Sstevel@tonic-gate 		if (strcmp(av[i], "--") == 0)
5567c478bd9Sstevel@tonic-gate 			fflag = 1;
5577c478bd9Sstevel@tonic-gate 	}
5587c478bd9Sstevel@tonic-gate 
5597c478bd9Sstevel@tonic-gate 	/* process the arguments */
5607c478bd9Sstevel@tonic-gate 
5617c478bd9Sstevel@tonic-gate 	for (i = mac = 0;
5627c478bd9Sstevel@tonic-gate 	    (av[i] != (char *)NULL) && (av[i][0] != (char)NULL);
5637c478bd9Sstevel@tonic-gate 	    i++) {
5647c478bd9Sstevel@tonic-gate 		if (!fflag && av[i][0] == '-') {
5657c478bd9Sstevel@tonic-gate 			/*
5667c478bd9Sstevel@tonic-gate 			 *  If there is not already a "--" argument specified,
5677c478bd9Sstevel@tonic-gate 			 *  and the argument starts with '-' but does not
5687c478bd9Sstevel@tonic-gate 			 *  contain any of the official option letters, then it
5697c478bd9Sstevel@tonic-gate 			 *  is probably a mode argument beginning with '-'.
5707c478bd9Sstevel@tonic-gate 			 *  Force a "--" into the argument stream in front of
5717c478bd9Sstevel@tonic-gate 			 *  it.
5727c478bd9Sstevel@tonic-gate 			 */
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate 			if ((strchr(av[i], 'R') == NULL &&
575*da6c28aaSamw 			    strchr(av[i], 'f') == NULL) &&
576*da6c28aaSamw 			    strchr(av[i], '@') == NULL) {
577*da6c28aaSamw 				if ((mav[mac++] = strdup("--")) == NULL) {
578*da6c28aaSamw 					perror("chmod");
579*da6c28aaSamw 					exit(2);
580*da6c28aaSamw 				}
5817c478bd9Sstevel@tonic-gate 			}
5827c478bd9Sstevel@tonic-gate 		}
5837c478bd9Sstevel@tonic-gate 
584*da6c28aaSamw 		if ((mav[mac++] = strdup(av[i])) == NULL) {
585*da6c28aaSamw 			perror("chmod");
586*da6c28aaSamw 			exit(2);
587*da6c28aaSamw 		}
5887c478bd9Sstevel@tonic-gate 	}
5897c478bd9Sstevel@tonic-gate 
5907c478bd9Sstevel@tonic-gate 	mav[mac] = (char *)NULL;
5917c478bd9Sstevel@tonic-gate }
5927c478bd9Sstevel@tonic-gate 
593*da6c28aaSamw static int
594*da6c28aaSamw parse_acl_args(char *arg, sec_args_t **sec_args)
595fa9e4066Sahrens {
596fa9e4066Sahrens 	acl_t *new_acl = NULL;
597fa9e4066Sahrens 	int slot;
598fa9e4066Sahrens 	int len;
599fa9e4066Sahrens 	int action;
600fa9e4066Sahrens 	acl_args_t *new_acl_args;
601fa9e4066Sahrens 	char *acl_spec = NULL;
602fa9e4066Sahrens 	char *end;
603fa9e4066Sahrens 
604fa9e4066Sahrens 	if (arg[0] != 'A')
605fa9e4066Sahrens 		return (1);
606fa9e4066Sahrens 
607fa9e4066Sahrens 	slot = strtol(&arg[1], &end, 10);
608fa9e4066Sahrens 
609fa9e4066Sahrens 	len = strlen(arg);
610fa9e4066Sahrens 	switch (*end) {
611fa9e4066Sahrens 	case '+':
612fa9e4066Sahrens 		action = ACL_ADD;
613fa9e4066Sahrens 		acl_spec = ++end;
614fa9e4066Sahrens 		break;
615fa9e4066Sahrens 	case '-':
616fa9e4066Sahrens 		if (len == 2 && arg[0] == 'A' && arg[1] == '-')
617fa9e4066Sahrens 			action = ACL_STRIP;
618fa9e4066Sahrens 		else
619fa9e4066Sahrens 			action = ACL_DELETE;
620fa9e4066Sahrens 		if (action != ACL_STRIP) {
621fa9e4066Sahrens 			acl_spec = ++end;
622fa9e4066Sahrens 			if (acl_spec[0] == '\0') {
623fa9e4066Sahrens 				action = ACL_SLOT_DELETE;
624fa9e4066Sahrens 				acl_spec = NULL;
625fa9e4066Sahrens 			} else if (arg[1] != '-')
626fa9e4066Sahrens 				return (1);
627fa9e4066Sahrens 		}
628fa9e4066Sahrens 		break;
629fa9e4066Sahrens 	case '=':
63055601ddbSmarks 		/*
63155601ddbSmarks 		 * Was slot specified?
63255601ddbSmarks 		 */
63355601ddbSmarks 		if (arg[1] == '=')
63455601ddbSmarks 			slot = -1;
635fa9e4066Sahrens 		action = ACL_REPLACE;
636fa9e4066Sahrens 		acl_spec = ++end;
637fa9e4066Sahrens 		break;
638fa9e4066Sahrens 	default:
639fa9e4066Sahrens 		return (1);
640fa9e4066Sahrens 	}
641fa9e4066Sahrens 
642fa9e4066Sahrens 	if ((action == ACL_REPLACE || action == ACL_ADD) && acl_spec[0] == '\0')
643fa9e4066Sahrens 		return (1);
644fa9e4066Sahrens 
645fa9e4066Sahrens 	if (acl_spec) {
6465a5eeccaSmarks 		if (acl_parse(acl_spec, &new_acl)) {
6475a5eeccaSmarks 			exit(1);
648fa9e4066Sahrens 		}
649fa9e4066Sahrens 	}
650fa9e4066Sahrens 
651fa9e4066Sahrens 	new_acl_args = malloc(sizeof (acl_args_t));
652fa9e4066Sahrens 	if (new_acl_args == NULL)
653fa9e4066Sahrens 		return (1);
654fa9e4066Sahrens 
655fa9e4066Sahrens 	new_acl_args->acl_aclp = new_acl;
656fa9e4066Sahrens 	new_acl_args->acl_slot = slot;
657fa9e4066Sahrens 	new_acl_args->acl_action = action;
658fa9e4066Sahrens 
659*da6c28aaSamw 	if ((*sec_args = malloc(sizeof (sec_args_t))) == NULL) {
660*da6c28aaSamw 		perror("chmod");
661*da6c28aaSamw 		exit(2);
662*da6c28aaSamw 	}
663*da6c28aaSamw 	(*sec_args)->sec_type = SEC_ACL;
664*da6c28aaSamw 	(*sec_args)->sec_acls = new_acl_args;
665fa9e4066Sahrens 
666fa9e4066Sahrens 	return (0);
667fa9e4066Sahrens }
668fa9e4066Sahrens 
6697c478bd9Sstevel@tonic-gate /*
6707c478bd9Sstevel@tonic-gate  * This function is called whenever the group permissions of a file
6717c478bd9Sstevel@tonic-gate  * is being modified.  According to the chmod(1) manpage, any
6727c478bd9Sstevel@tonic-gate  * change made to the group permissions must be applied to both
6737c478bd9Sstevel@tonic-gate  * the acl mask and the acl's GROUP_OBJ.  The chmod(2) already
6747c478bd9Sstevel@tonic-gate  * set the mask, so this routine needs to make the same change
6757c478bd9Sstevel@tonic-gate  * to the GROUP_OBJ.
6767c478bd9Sstevel@tonic-gate  */
6777c478bd9Sstevel@tonic-gate static void
6787c478bd9Sstevel@tonic-gate handle_acl(char *name, o_mode_t group_clear_bits, o_mode_t group_set_bits)
6797c478bd9Sstevel@tonic-gate {
6807c478bd9Sstevel@tonic-gate 	int aclcnt, n;
6817c478bd9Sstevel@tonic-gate 	aclent_t *aclp, *tp;
6827c478bd9Sstevel@tonic-gate 	o_mode_t newperm;
683fa9e4066Sahrens 	/*
684fa9e4066Sahrens 	 * if this file system support ace_t acl's
685fa9e4066Sahrens 	 * then simply return since we don't have an
686fa9e4066Sahrens 	 * acl mask to deal with
687fa9e4066Sahrens 	 */
688fa9e4066Sahrens 	if (pathconf(name, _PC_ACL_ENABLED) == _ACL_ACE_ENABLED)
689fa9e4066Sahrens 		return;
6907c478bd9Sstevel@tonic-gate 	if ((aclcnt = acl(name, GETACLCNT, 0, NULL)) <= MIN_ACL_ENTRIES)
6917c478bd9Sstevel@tonic-gate 		return;	/* it's just a trivial acl; no need to change it */
6927c478bd9Sstevel@tonic-gate 	if ((aclp = (aclent_t *)malloc((sizeof (aclent_t)) * aclcnt))
6937c478bd9Sstevel@tonic-gate 	    == NULL) {
6947c478bd9Sstevel@tonic-gate 		perror("chmod");
6957c478bd9Sstevel@tonic-gate 		exit(2);
6967c478bd9Sstevel@tonic-gate 	}
6977c478bd9Sstevel@tonic-gate 
6987c478bd9Sstevel@tonic-gate 	if (acl(name, GETACL, aclcnt, aclp) < 0) {
6997c478bd9Sstevel@tonic-gate 		free(aclp);
7007c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "chmod: ");
7017c478bd9Sstevel@tonic-gate 		perror(name);
7027c478bd9Sstevel@tonic-gate 		return;
7037c478bd9Sstevel@tonic-gate 	}
7047c478bd9Sstevel@tonic-gate 	for (tp = aclp, n = aclcnt; n--; tp++) {
7057c478bd9Sstevel@tonic-gate 		if (tp->a_type == GROUP_OBJ) {
7067c478bd9Sstevel@tonic-gate 			newperm = tp->a_perm;
7077c478bd9Sstevel@tonic-gate 			if (group_clear_bits != 0)
7087c478bd9Sstevel@tonic-gate 				newperm &= ~group_clear_bits;
7097c478bd9Sstevel@tonic-gate 			if (group_set_bits != 0)
7107c478bd9Sstevel@tonic-gate 				newperm |= group_set_bits;
7117c478bd9Sstevel@tonic-gate 			if (newperm != tp->a_perm) {
7127c478bd9Sstevel@tonic-gate 				tp->a_perm = newperm;
7137c478bd9Sstevel@tonic-gate 				if (acl(name, SETACL, aclcnt, aclp)
7147c478bd9Sstevel@tonic-gate 				    < 0) {
7157c478bd9Sstevel@tonic-gate 					(void) fprintf(stderr, "chmod: ");
7167c478bd9Sstevel@tonic-gate 					perror(name);
7177c478bd9Sstevel@tonic-gate 				}
7187c478bd9Sstevel@tonic-gate 			}
7197c478bd9Sstevel@tonic-gate 			break;
7207c478bd9Sstevel@tonic-gate 		}
7217c478bd9Sstevel@tonic-gate 	}
7227c478bd9Sstevel@tonic-gate 	free(aclp);
7237c478bd9Sstevel@tonic-gate }
724fa9e4066Sahrens 
725fa9e4066Sahrens static int
726fa9e4066Sahrens doacl(char *file, struct stat *st, acl_args_t *acl_args)
727fa9e4066Sahrens {
728fa9e4066Sahrens 	acl_t *aclp;
729fa9e4066Sahrens 	acl_t *set_aclp;
730fa9e4066Sahrens 	int error = 0;
731fa9e4066Sahrens 	void *to, *from;
732fa9e4066Sahrens 	int len;
733fa9e4066Sahrens 	int isdir;
734fa9e4066Sahrens 	isdir = S_ISDIR(st->st_mode);
735fa9e4066Sahrens 
736fa9e4066Sahrens 	error = acl_get(file, 0, &aclp);
737fa9e4066Sahrens 
738fa9e4066Sahrens 	if (error != 0) {
739fa9e4066Sahrens 		errmsg(1, 1, "%s\n", acl_strerror(error));
740fa9e4066Sahrens 		return (1);
741fa9e4066Sahrens 	}
742fa9e4066Sahrens 	switch (acl_args->acl_action) {
743fa9e4066Sahrens 	case ACL_ADD:
744fa9e4066Sahrens 		if ((error = acl_addentries(aclp,
745fa9e4066Sahrens 		    acl_args->acl_aclp, acl_args->acl_slot)) != 0) {
746fa9e4066Sahrens 			errmsg(1, 1, "%s\n", acl_strerror(error));
747fa9e4066Sahrens 			acl_free(aclp);
748fa9e4066Sahrens 			return (1);
749fa9e4066Sahrens 		}
750fa9e4066Sahrens 		set_aclp = aclp;
751fa9e4066Sahrens 		break;
752fa9e4066Sahrens 	case ACL_SLOT_DELETE:
753fa9e4066Sahrens 		if (acl_args->acl_slot + 1 > aclp->acl_cnt) {
754fa9e4066Sahrens 			errmsg(1, 1,
755fa9e4066Sahrens 			    gettext("Invalid slot specified for removal\n"));
756fa9e4066Sahrens 			acl_free(aclp);
757fa9e4066Sahrens 			return (1);
758fa9e4066Sahrens 		}
759fa9e4066Sahrens 
760fa9e4066Sahrens 		if (acl_args->acl_slot == 0 && aclp->acl_cnt == 1) {
761fa9e4066Sahrens 			errmsg(1, 1,
762fa9e4066Sahrens 			    gettext("Can't remove all ACL "
763fa9e4066Sahrens 			    "entries from a file\n"));
764fa9e4066Sahrens 			acl_free(aclp);
765fa9e4066Sahrens 			return (1);
766fa9e4066Sahrens 		}
767fa9e4066Sahrens 
768fa9e4066Sahrens 		/*
769fa9e4066Sahrens 		 * remove a single entry
770fa9e4066Sahrens 		 *
771fa9e4066Sahrens 		 * if last entry just adjust acl_cnt
772fa9e4066Sahrens 		 */
773fa9e4066Sahrens 
774fa9e4066Sahrens 		if ((acl_args->acl_slot + 1) == aclp->acl_cnt)
775fa9e4066Sahrens 			aclp->acl_cnt--;
776fa9e4066Sahrens 		else {
777fa9e4066Sahrens 			to = (char *)aclp->acl_aclp +
778fa9e4066Sahrens 			    (acl_args->acl_slot * aclp->acl_entry_size);
779fa9e4066Sahrens 			from = (char *)to + aclp->acl_entry_size;
780fa9e4066Sahrens 			len = (aclp->acl_cnt - acl_args->acl_slot - 1) *
781fa9e4066Sahrens 			    aclp->acl_entry_size;
782fa9e4066Sahrens 			(void) memmove(to, from, len);
783fa9e4066Sahrens 			aclp->acl_cnt--;
784fa9e4066Sahrens 		}
785fa9e4066Sahrens 		set_aclp = aclp;
786fa9e4066Sahrens 		break;
787fa9e4066Sahrens 
788fa9e4066Sahrens 	case ACL_DELETE:
789fa9e4066Sahrens 		if ((error = acl_removeentries(aclp, acl_args->acl_aclp,
790fa9e4066Sahrens 		    acl_args->acl_slot, ACL_REMOVE_ALL)) != 0) {
791fa9e4066Sahrens 			errmsg(1, 1, "%s\n", acl_strerror(error));
792fa9e4066Sahrens 			acl_free(aclp);
793fa9e4066Sahrens 			return (1);
794fa9e4066Sahrens 		}
795fa9e4066Sahrens 
796fa9e4066Sahrens 		if (aclp->acl_cnt == 0) {
797fa9e4066Sahrens 			errmsg(1, 1,
798fa9e4066Sahrens 			    gettext("Can't remove all ACL "
799fa9e4066Sahrens 			    "entries from a file\n"));
800fa9e4066Sahrens 			acl_free(aclp);
801fa9e4066Sahrens 			return (1);
802fa9e4066Sahrens 		}
803fa9e4066Sahrens 
804fa9e4066Sahrens 		set_aclp = aclp;
805fa9e4066Sahrens 		break;
806fa9e4066Sahrens 	case ACL_REPLACE:
807fa9e4066Sahrens 		if (acl_args->acl_slot >= 0)  {
808fa9e4066Sahrens 			error = acl_modifyentries(aclp, acl_args->acl_aclp,
809fa9e4066Sahrens 			    acl_args->acl_slot);
810fa9e4066Sahrens 			if (error) {
811fa9e4066Sahrens 				errmsg(1, 1, "%s\n", acl_strerror(error));
812fa9e4066Sahrens 				acl_free(aclp);
813fa9e4066Sahrens 				return (1);
814fa9e4066Sahrens 			}
815fa9e4066Sahrens 			set_aclp = aclp;
816fa9e4066Sahrens 		} else {
817fa9e4066Sahrens 			set_aclp = acl_args->acl_aclp;
818fa9e4066Sahrens 		}
819fa9e4066Sahrens 		break;
820fa9e4066Sahrens 	case ACL_STRIP:
821fa9e4066Sahrens 		error = acl_strip(file, st->st_uid, st->st_gid, st->st_mode);
822fa9e4066Sahrens 		if (error) {
823fa9e4066Sahrens 			errmsg(1, 1, "%s\n", acl_strerror(error));
824fa9e4066Sahrens 			return (1);
825fa9e4066Sahrens 		}
826fa9e4066Sahrens 		acl_free(aclp);
827fa9e4066Sahrens 		return (0);
828fa9e4066Sahrens 		/*NOTREACHED*/
829fa9e4066Sahrens 	default:
830fa9e4066Sahrens 		errmsg(1, 0, gettext("Unknown ACL action requested\n"));
831fa9e4066Sahrens 		return (1);
832fa9e4066Sahrens 		break;
833fa9e4066Sahrens 	}
834fa9e4066Sahrens 	error = acl_check(set_aclp, isdir);
835fa9e4066Sahrens 
836fa9e4066Sahrens 	if (error) {
837fa9e4066Sahrens 		errmsg(1, 0, "%s\n%s", acl_strerror(error),
838fa9e4066Sahrens 		    gettext("See chmod(1) for more information on "
839fa9e4066Sahrens 		    "valid ACL syntax\n"));
840fa9e4066Sahrens 		return (1);
841fa9e4066Sahrens 	}
842fa9e4066Sahrens 	if ((error = acl_set(file, set_aclp)) != 0) {
843fa9e4066Sahrens 			errmsg(1, 0, gettext("Failed to set ACL: %s\n"),
844fa9e4066Sahrens 			    acl_strerror(error));
845fa9e4066Sahrens 			acl_free(aclp);
846fa9e4066Sahrens 			return (1);
847fa9e4066Sahrens 	}
848fa9e4066Sahrens 	acl_free(aclp);
849fa9e4066Sahrens 	return (0);
850fa9e4066Sahrens }
851*da6c28aaSamw 
852*da6c28aaSamw /*
853*da6c28aaSamw  * Prints out the attributes in their verbose form:
854*da6c28aaSamw  *	'{'[["no"]<attribute-name>][,["no"]<attribute-name>]...'}'
855*da6c28aaSamw  * similar to output of ls -/v.
856*da6c28aaSamw  */
857*da6c28aaSamw static void
858*da6c28aaSamw print_nvlist(nvlist_t *attr_nvlist)
859*da6c28aaSamw {
860*da6c28aaSamw 	int		firsttime = 1;
861*da6c28aaSamw 	boolean_t	value;
862*da6c28aaSamw 	nvlist_t	*lptr = attr_nvlist;
863*da6c28aaSamw 	nvpair_t	*pair = NULL;
864*da6c28aaSamw 
865*da6c28aaSamw 	(void) fprintf(stderr, "\t%c", LEFTBRACE);
866*da6c28aaSamw 	while (pair = nvlist_next_nvpair(lptr, pair)) {
867*da6c28aaSamw 		if (nvpair_value_boolean_value(pair, &value) == 0) {
868*da6c28aaSamw 			(void) fprintf(stderr, "%s%s%s",
869*da6c28aaSamw 			    firsttime ? "" : A_SEP_TOK,
870*da6c28aaSamw 			    (value == A_SET_VAL) ? "" : "no",
871*da6c28aaSamw 			    nvpair_name(pair));
872*da6c28aaSamw 			firsttime = 0;
873*da6c28aaSamw 		} else {
874*da6c28aaSamw 			(void) fprintf(stderr, gettext(
875*da6c28aaSamw 			    "<error retrieving attributes: %s>"),
876*da6c28aaSamw 			    strerror(errno));
877*da6c28aaSamw 			break;
878*da6c28aaSamw 		}
879*da6c28aaSamw 	}
880*da6c28aaSamw 	(void) fprintf(stderr, "%c\n", RIGHTBRACE);
881*da6c28aaSamw }
882*da6c28aaSamw 
883*da6c28aaSamw /*
884*da6c28aaSamw  * Add an attribute name and boolean value to an nvlist if an action is to be
885*da6c28aaSamw  * performed for that attribute.  The nvlist will be used later to set all the
886*da6c28aaSamw  * attributes in the nvlist in one operation through a call to setattrat().
887*da6c28aaSamw  *
888*da6c28aaSamw  * If a set operation ('+') was specified, then a boolean representation of the
889*da6c28aaSamw  * attribute's value will be added to the nvlist for that attribute name.  If an
890*da6c28aaSamw  * inverse operation ('-') was specified, then a boolean representation of the
891*da6c28aaSamw  * inverse of the attribute's value will be added to the nvlist for that
892*da6c28aaSamw  * attribute name.
893*da6c28aaSamw  *
894*da6c28aaSamw  * Returns an nvlist of attribute name and boolean value pairs if there are
895*da6c28aaSamw  * attribute actions to be performed, otherwise returns NULL.
896*da6c28aaSamw  */
897*da6c28aaSamw static nvlist_t *
898*da6c28aaSamw set_attrs_nvlist(char *attractptr, int numofattrs)
899*da6c28aaSamw {
900*da6c28aaSamw 	int		attribute_set = 0;
901*da6c28aaSamw 	f_attr_t	i;
902*da6c28aaSamw 	nvlist_t	*attr_nvlist;
903*da6c28aaSamw 
904*da6c28aaSamw 	if (nvlist_alloc(&attr_nvlist, NV_UNIQUE_NAME, 0) != 0) {
905*da6c28aaSamw 		perror("chmod");
906*da6c28aaSamw 		exit(2);
907*da6c28aaSamw 	}
908*da6c28aaSamw 
909*da6c28aaSamw 	for (i = 0; i < numofattrs; i++) {
910*da6c28aaSamw 		if (attractptr[i] != '\0') {
911*da6c28aaSamw 			if ((nvlist_add_boolean_value(attr_nvlist,
912*da6c28aaSamw 			    attr_to_name(i),
913*da6c28aaSamw 			    (attractptr[i] == A_SET_OP))) != 0) {
914*da6c28aaSamw 				errmsg(1, 2, gettext(
915*da6c28aaSamw 				    "unable to propagate attribute names and"
916*da6c28aaSamw 				    "values: %s\n"), strerror(errno));
917*da6c28aaSamw 			} else {
918*da6c28aaSamw 				attribute_set = 1;
919*da6c28aaSamw 			}
920*da6c28aaSamw 		}
921*da6c28aaSamw 	}
922*da6c28aaSamw 	return (attribute_set ? attr_nvlist : NULL);
923*da6c28aaSamw }
924*da6c28aaSamw 
925*da6c28aaSamw /*
926*da6c28aaSamw  * Set the attributes of file, or if specified, of the named attribute file,
927*da6c28aaSamw  * attrname.  Build an nvlist of attribute names and values and call setattrat()
928*da6c28aaSamw  * to set the attributes in one operation.
929*da6c28aaSamw  *
930*da6c28aaSamw  * Returns 0 if successful, otherwise returns 1.
931*da6c28aaSamw  */
932*da6c28aaSamw static int
933*da6c28aaSamw set_file_attrs(char *file, char *attrname, nvlist_t *attr_nvlist)
934*da6c28aaSamw {
935*da6c28aaSamw 	int	rc;
936*da6c28aaSamw 	char	*filename;
937*da6c28aaSamw 
938*da6c28aaSamw 	if (attrname != NULL) {
939*da6c28aaSamw 		filename = attrname;
940*da6c28aaSamw 	} else {
941*da6c28aaSamw 		filename = basename(file);
942*da6c28aaSamw 	}
943*da6c28aaSamw 
944*da6c28aaSamw 	if ((rc = setattrat(AT_FDCWD, XATTR_VIEW_READWRITE, filename,
945*da6c28aaSamw 	    attr_nvlist)) != 0) {
946*da6c28aaSamw 		char *emsg;
947*da6c28aaSamw 		switch (errno) {
948*da6c28aaSamw 		case EINVAL:
949*da6c28aaSamw 			emsg = gettext("not supported");
950*da6c28aaSamw 			break;
951*da6c28aaSamw 		case EPERM:
952*da6c28aaSamw 			emsg = gettext("not privileged");
953*da6c28aaSamw 			break;
954*da6c28aaSamw 		default:
955*da6c28aaSamw 			emsg = strerror(rc);
956*da6c28aaSamw 		}
957*da6c28aaSamw 		errmsg(1, 0, gettext(
958*da6c28aaSamw 		    "cannot set the following attributes on "
959*da6c28aaSamw 		    "%s%s%s%s: %s\n"),
960*da6c28aaSamw 		    (attrname == NULL) ? "" : gettext("attribute "),
961*da6c28aaSamw 		    (attrname == NULL) ? "" : attrname,
962*da6c28aaSamw 		    (attrname == NULL) ? "" : gettext(" of "),
963*da6c28aaSamw 		    file, emsg);
964*da6c28aaSamw 		print_nvlist(attr_nvlist);
965*da6c28aaSamw 	}
966*da6c28aaSamw 
967*da6c28aaSamw 	return (rc);
968*da6c28aaSamw }
969*da6c28aaSamw 
970*da6c28aaSamw static int
971*da6c28aaSamw save_cwd(void)
972*da6c28aaSamw {
973*da6c28aaSamw 	return (open(".", O_RDONLY));
974*da6c28aaSamw }
975*da6c28aaSamw 
976*da6c28aaSamw static void
977*da6c28aaSamw rest_cwd(int cwd)
978*da6c28aaSamw {
979*da6c28aaSamw 	if (cwd != -1) {
980*da6c28aaSamw 		if (fchdir(cwd) != 0) {
981*da6c28aaSamw 			errmsg(1, 1, gettext(
982*da6c28aaSamw 			    "can't change to current working directory\n"));
983*da6c28aaSamw 		}
984*da6c28aaSamw 		(void) close(cwd);
985*da6c28aaSamw 	}
986*da6c28aaSamw }
987*da6c28aaSamw 
988*da6c28aaSamw /*
989*da6c28aaSamw  * Returns 1 if filename is a system attribute file, otherwise
990*da6c28aaSamw  * returns 0.
991*da6c28aaSamw  */
992*da6c28aaSamw static int
993*da6c28aaSamw is_sattr(char *filename)
994*da6c28aaSamw {
995*da6c28aaSamw 	return (sysattr_type(filename) != _NOT_SATTR);
996*da6c28aaSamw }
997*da6c28aaSamw 
998*da6c28aaSamw /*
999*da6c28aaSamw  * Perform the action on the specified named attribute file for the file
1000*da6c28aaSamw  * associated with the input file descriptor.  If the named attribute file
1001*da6c28aaSamw  * is "*", then the action is to be performed on all the named attribute files
1002*da6c28aaSamw  * of the file associated with the input file descriptor.
1003*da6c28aaSamw  */
1004*da6c28aaSamw static int
1005*da6c28aaSamw set_named_attrs(char *file, int parentfd, char *attrname, nvlist_t *attr_nvlist)
1006*da6c28aaSamw {
1007*da6c28aaSamw 	int		dirfd;
1008*da6c28aaSamw 	int		error = 0;
1009*da6c28aaSamw 	DIR		*dirp = NULL;
1010*da6c28aaSamw 	struct dirent	*dp;
1011*da6c28aaSamw 	struct stat	st;
1012*da6c28aaSamw 
1013*da6c28aaSamw 	if ((attrname == NULL) || (strcmp(attrname, "*") != 0)) {
1014*da6c28aaSamw 		/*
1015*da6c28aaSamw 		 * Make sure the named attribute exists and extended system
1016*da6c28aaSamw 		 * attributes are supported on the underlying file system.
1017*da6c28aaSamw 		 */
1018*da6c28aaSamw 		if (attrname != NULL) {
1019*da6c28aaSamw 			if (fstatat(parentfd, attrname, &st,
1020*da6c28aaSamw 			    AT_SYMLINK_NOFOLLOW) < 0) {
1021*da6c28aaSamw 				errmsg(2, 0, gettext(
1022*da6c28aaSamw 				    "can't access attribute %s of %s\n"),
1023*da6c28aaSamw 				    attrname, file);
1024*da6c28aaSamw 				return (1);
1025*da6c28aaSamw 			}
1026*da6c28aaSamw 			if (sysattr_support(attrname, _PC_SATTR_ENABLED) != 1) {
1027*da6c28aaSamw 				errmsg(1, 0, gettext(
1028*da6c28aaSamw 				    "extended system attributes not supported "
1029*da6c28aaSamw 				    "for attribute %s of %s\n"),
1030*da6c28aaSamw 				    attrname, file);
1031*da6c28aaSamw 				return (1);
1032*da6c28aaSamw 			}
1033*da6c28aaSamw 		}
1034*da6c28aaSamw 
1035*da6c28aaSamw 		error = set_file_attrs(file, attrname, attr_nvlist);
1036*da6c28aaSamw 
1037*da6c28aaSamw 	} else {
1038*da6c28aaSamw 		if (((dirfd = dup(parentfd)) == -1) ||
1039*da6c28aaSamw 		    ((dirp = fdopendir(dirfd)) == NULL)) {
1040*da6c28aaSamw 			errmsg(1, 0, gettext(
1041*da6c28aaSamw 			    "cannot open dir pointer of file %s\n"), file);
1042*da6c28aaSamw 			if (dirfd > 0) {
1043*da6c28aaSamw 				(void) close(dirfd);
1044*da6c28aaSamw 			}
1045*da6c28aaSamw 			return (1);
1046*da6c28aaSamw 		}
1047*da6c28aaSamw 
1048*da6c28aaSamw 		while (dp = readdir(dirp)) {
1049*da6c28aaSamw 			/*
1050*da6c28aaSamw 			 * Process all extended attribute files except
1051*da6c28aaSamw 			 * ".", "..", and extended system attribute files.
1052*da6c28aaSamw 			 */
1053*da6c28aaSamw 			if ((strcmp(dp->d_name, ".") == 0) ||
1054*da6c28aaSamw 			    (strcmp(dp->d_name, "..") == 0) ||
1055*da6c28aaSamw 			    is_sattr(dp->d_name)) {
1056*da6c28aaSamw 				continue;
1057*da6c28aaSamw 			}
1058*da6c28aaSamw 
1059*da6c28aaSamw 			if (set_named_attrs(file, parentfd, dp->d_name,
1060*da6c28aaSamw 			    attr_nvlist) != 0) {
1061*da6c28aaSamw 				error++;
1062*da6c28aaSamw 			}
1063*da6c28aaSamw 		}
1064*da6c28aaSamw 		if (dirp != NULL) {
1065*da6c28aaSamw 			(void) closedir(dirp);
1066*da6c28aaSamw 		}
1067*da6c28aaSamw 	}
1068*da6c28aaSamw 
1069*da6c28aaSamw 	return ((error == 0) ? 0 : 1);
1070*da6c28aaSamw }
1071*da6c28aaSamw 
1072*da6c28aaSamw /*
1073*da6c28aaSamw  * Set the attributes of the specified file, or if specified with -@ on the
1074*da6c28aaSamw  * command line, the specified named attributes of the specified file.
1075*da6c28aaSamw  *
1076*da6c28aaSamw  * Returns 0 if successful, otherwise returns 1.
1077*da6c28aaSamw  */
1078*da6c28aaSamw static int
1079*da6c28aaSamw set_attrs(char *file, attr_name_t *attrnames, nvlist_t *attr_nvlist)
1080*da6c28aaSamw {
1081*da6c28aaSamw 	char		*parentd;
1082*da6c28aaSamw 	char		*tpath = NULL;
1083*da6c28aaSamw 	int		cwd;
1084*da6c28aaSamw 	int		error = 0;
1085*da6c28aaSamw 	int		parentfd;
1086*da6c28aaSamw 	attr_name_t	*tattr = attrnames;
1087*da6c28aaSamw 
1088*da6c28aaSamw 	if (attr_nvlist == NULL) {
1089*da6c28aaSamw 		return (0);
1090*da6c28aaSamw 	}
1091*da6c28aaSamw 
1092*da6c28aaSamw 	if (sysattr_support(file, _PC_SATTR_ENABLED) != 1) {
1093*da6c28aaSamw 		errmsg(1, 0, gettext(
1094*da6c28aaSamw 		    "extended system attributes not supported for %s\n"), file);
1095*da6c28aaSamw 		return (1);
1096*da6c28aaSamw 	}
1097*da6c28aaSamw 
1098*da6c28aaSamw 	/*
1099*da6c28aaSamw 	 * Open the parent directory and change into it before attempting
1100*da6c28aaSamw 	 * to set the attributes of the file.
1101*da6c28aaSamw 	 */
1102*da6c28aaSamw 	if (attrnames == NULL) {
1103*da6c28aaSamw 		tpath = strdup(file);
1104*da6c28aaSamw 		parentd = dirname(tpath);
1105*da6c28aaSamw 		parentfd = open(parentd, O_RDONLY);
1106*da6c28aaSamw 	} else {
1107*da6c28aaSamw 		parentfd = attropen(file, ".", O_RDONLY);
1108*da6c28aaSamw 	}
1109*da6c28aaSamw 	if (parentfd == -1) {
1110*da6c28aaSamw 		errmsg(1, 0, gettext(
1111*da6c28aaSamw 		    "cannot open attribute directory of %s\n"), file);
1112*da6c28aaSamw 		if (tpath != NULL) {
1113*da6c28aaSamw 			free(tpath);
1114*da6c28aaSamw 		}
1115*da6c28aaSamw 		return (1);
1116*da6c28aaSamw 	}
1117*da6c28aaSamw 
1118*da6c28aaSamw 	if ((cwd = save_cwd()) < 0) {
1119*da6c28aaSamw 		errmsg(1, 1, gettext(
1120*da6c28aaSamw 		    "can't get current working directory\n"));
1121*da6c28aaSamw 	}
1122*da6c28aaSamw 	if (fchdir(parentfd) != 0) {
1123*da6c28aaSamw 		errmsg(1, 0, gettext(
1124*da6c28aaSamw 		    "can't change to parent %sdirectory of %s\n"),
1125*da6c28aaSamw 		    (attrnames == NULL) ? "" : gettext("attribute "), file);
1126*da6c28aaSamw 		(void) close(cwd);
1127*da6c28aaSamw 		(void) close(parentfd);
1128*da6c28aaSamw 		if (tpath != NULL) {
1129*da6c28aaSamw 			free(tpath);
1130*da6c28aaSamw 		}
1131*da6c28aaSamw 		return (1);
1132*da6c28aaSamw 	}
1133*da6c28aaSamw 
1134*da6c28aaSamw 	/*
1135*da6c28aaSamw 	 * If no named attribute file names were provided on the command line
1136*da6c28aaSamw 	 * then set the attributes of the base file, otherwise, set the
1137*da6c28aaSamw 	 * attributes for each of the named attribute files specified.
1138*da6c28aaSamw 	 */
1139*da6c28aaSamw 	if (attrnames == NULL) {
1140*da6c28aaSamw 		error = set_named_attrs(file, parentfd, NULL, attr_nvlist);
1141*da6c28aaSamw 		free(tpath);
1142*da6c28aaSamw 	} else {
1143*da6c28aaSamw 		while (tattr != NULL) {
1144*da6c28aaSamw 			if (set_named_attrs(file, parentfd, tattr->name,
1145*da6c28aaSamw 			    attr_nvlist) != 0) {
1146*da6c28aaSamw 				error++;
1147*da6c28aaSamw 			}
1148*da6c28aaSamw 			tattr = tattr->next;
1149*da6c28aaSamw 		}
1150*da6c28aaSamw 	}
1151*da6c28aaSamw 	(void) close(parentfd);
1152*da6c28aaSamw 	rest_cwd(cwd);
1153*da6c28aaSamw 
1154*da6c28aaSamw 	return ((error == 0) ? 0 : 1);
1155*da6c28aaSamw }
1156*da6c28aaSamw 
1157*da6c28aaSamw /*
1158*da6c28aaSamw  * Prints the attributes in either the compact or verbose form indicated
1159*da6c28aaSamw  * by flag.
1160*da6c28aaSamw  */
1161*da6c28aaSamw static void
1162*da6c28aaSamw print_attrs(int flag)
1163*da6c28aaSamw {
1164*da6c28aaSamw 	f_attr_t	i;
1165*da6c28aaSamw 	static int	numofattrs;
1166*da6c28aaSamw 	int		firsttime = 1;
1167*da6c28aaSamw 
1168*da6c28aaSamw 	numofattrs = attr_count();
1169*da6c28aaSamw 
1170*da6c28aaSamw 	(void) fprintf(stderr, gettext("\t["));
1171*da6c28aaSamw 	for (i = 0; i < numofattrs; i++) {
1172*da6c28aaSamw 		if ((attr_to_xattr_view(i) != XATTR_VIEW_READWRITE) ||
1173*da6c28aaSamw 		    (attr_to_data_type(i) != DATA_TYPE_BOOLEAN_VALUE)) {
1174*da6c28aaSamw 			continue;
1175*da6c28aaSamw 		}
1176*da6c28aaSamw 		(void) fprintf(stderr, "%s%s",
1177*da6c28aaSamw 		    (firsttime == 1) ? "" : gettext("|"),
1178*da6c28aaSamw 		    (flag == ATTR_OPTS) ? attr_to_option(i) : attr_to_name(i));
1179*da6c28aaSamw 		firsttime = 0;
1180*da6c28aaSamw 	}
1181*da6c28aaSamw 	(void) fprintf(stderr, gettext("]\n"));
1182*da6c28aaSamw }
1183*da6c28aaSamw 
1184*da6c28aaSamw /*
1185*da6c28aaSamw  * Record what action should be taken on the specified attribute. Only boolean
1186*da6c28aaSamw  * read-write attributes can be manipulated.
1187*da6c28aaSamw  *
1188*da6c28aaSamw  * Returns 0 if successful, otherwise returns 1.
1189*da6c28aaSamw  */
1190*da6c28aaSamw static int
1191*da6c28aaSamw set_attr_args(f_attr_t attr, char action, char *attractptr)
1192*da6c28aaSamw {
1193*da6c28aaSamw 	if ((attr_to_xattr_view(attr) == XATTR_VIEW_READWRITE) &&
1194*da6c28aaSamw 	    (attr_to_data_type(attr) == DATA_TYPE_BOOLEAN_VALUE)) {
1195*da6c28aaSamw 		attractptr[attr] = action;
1196*da6c28aaSamw 		return (0);
1197*da6c28aaSamw 	}
1198*da6c28aaSamw 	return (1);
1199*da6c28aaSamw }
1200*da6c28aaSamw 
1201*da6c28aaSamw /*
1202*da6c28aaSamw  * Parses the entry and assigns the appropriate action (either '+' or '-' in
1203*da6c28aaSamw  * attribute's position in the character array pointed to by attractptr, where
1204*da6c28aaSamw  * upon exit, attractptr is positional and the value of each character specifies
1205*da6c28aaSamw  * whether to set (a '+'), clear (a '-'), or leave untouched (a '\0') the
1206*da6c28aaSamw  * attribute value.
1207*da6c28aaSamw  *
1208*da6c28aaSamw  * If the entry is an attribute name, then the A_SET_OP action is to be
1209*da6c28aaSamw  * performed for this attribute.  If the entry is an attribute name proceeded
1210*da6c28aaSamw  * with "no", then the A_INVERSE_OP action is to be performed for this
1211*da6c28aaSamw  * attribute.  If the entry is one or more attribute option letters, then step
1212*da6c28aaSamw  * through each of the option letters marking the action to be performed for
1213*da6c28aaSamw  * each of the attributes associated with the letter as A_SET_OP.
1214*da6c28aaSamw  *
1215*da6c28aaSamw  * Returns 0 if the entry was a valid attribute(s) and the action to be
1216*da6c28aaSamw  * performed on that attribute(s) has been recorded, otherwise returns 1.
1217*da6c28aaSamw  */
1218*da6c28aaSamw static int
1219*da6c28aaSamw parse_entry(char *entry, char action, char atype, int len, char *attractptr)
1220*da6c28aaSamw {
1221*da6c28aaSamw 	char		aopt[2] = {'\0', '\0'};
1222*da6c28aaSamw 	char		*aptr;
1223*da6c28aaSamw 	f_attr_t	attr;
1224*da6c28aaSamw 
1225*da6c28aaSamw 	if (atype == A_VERBOSE_TYPE) {
1226*da6c28aaSamw 		if ((attr = name_to_attr(entry)) != F_ATTR_INVAL) {
1227*da6c28aaSamw 			return (set_attr_args(attr,
1228*da6c28aaSamw 			    (action == A_REPLACE_OP) ? A_SET_OP : action,
1229*da6c28aaSamw 			    attractptr));
1230*da6c28aaSamw 		} else if ((len > 2) && (strncmp(entry, "no", 2) == 0) &&
1231*da6c28aaSamw 		    ((attr = name_to_attr(entry + 2)) != F_ATTR_INVAL)) {
1232*da6c28aaSamw 			return (set_attr_args(attr, ((action == A_REPLACE_OP) ||
1233*da6c28aaSamw 			    (action == A_SET_OP)) ? A_INVERSE_OP : A_SET_OP,
1234*da6c28aaSamw 			    attractptr));
1235*da6c28aaSamw 		} else {
1236*da6c28aaSamw 			return (1);
1237*da6c28aaSamw 		}
1238*da6c28aaSamw 	} else if (atype == A_COMPACT_TYPE) {
1239*da6c28aaSamw 		for (aptr = entry; *aptr != '\0'; aptr++) {
1240*da6c28aaSamw 			*aopt = *aptr;
1241*da6c28aaSamw 			/*
1242*da6c28aaSamw 			 * The output of 'ls' can be used as the attribute mode
1243*da6c28aaSamw 			 * specification for chmod.  This output can contain a
1244*da6c28aaSamw 			 * hypen ('-') for each attribute that is not set.  If
1245*da6c28aaSamw 			 * so, ignore them.  If a replace action is being
1246*da6c28aaSamw 			 * performed, then all attributes that don't have an
1247*da6c28aaSamw 			 * action set here, will be cleared down the line.
1248*da6c28aaSamw 			 */
1249*da6c28aaSamw 			if (*aptr == '-') {
1250*da6c28aaSamw 				continue;
1251*da6c28aaSamw 			}
1252*da6c28aaSamw 			if (set_attr_args(option_to_attr(aopt),
1253*da6c28aaSamw 			    (action == A_REPLACE_OP) ? A_SET_OP : action,
1254*da6c28aaSamw 			    attractptr) != 0) {
1255*da6c28aaSamw 				return (1);
1256*da6c28aaSamw 			}
1257*da6c28aaSamw 		}
1258*da6c28aaSamw 		return (0);
1259*da6c28aaSamw 	}
1260*da6c28aaSamw 	return (1);
1261*da6c28aaSamw }
1262*da6c28aaSamw 
1263*da6c28aaSamw /*
1264*da6c28aaSamw  * Parse the attribute specification, aoptsstr.  Upon completion, attr_nvlist
1265*da6c28aaSamw  * will point to an nvlist which contains pairs of attribute names and values
1266*da6c28aaSamw  * to be set; attr_nvlist will be NULL if it is a no-op.
1267*da6c28aaSamw  *
1268*da6c28aaSamw  * The attribute specification format is
1269*da6c28aaSamw  *	S[oper]attr_type[attribute_list]
1270*da6c28aaSamw  * where oper is
1271*da6c28aaSamw  *	+	set operation of specified attributes in attribute list.
1272*da6c28aaSamw  *		This is the default operation.
1273*da6c28aaSamw  *	-	inverse operation of specified attributes in attribute list
1274*da6c28aaSamw  *	=	replace operation of all attributes.  All attribute operations
1275*da6c28aaSamw  *		depend on those specified in the attribute list.  Attributes
1276*da6c28aaSamw  *		not specified in the attribute list will be cleared.
1277*da6c28aaSamw  * where attr_type is
1278*da6c28aaSamw  *	c	compact type.  Each entry in the attribute list is a character
1279*da6c28aaSamw  *		option representing an associated attribute name.
1280*da6c28aaSamw  *	v	verbose type.  Each entry in the attribute list is an
1281*da6c28aaSamw  *		an attribute name which can optionally be preceeded with "no"
1282*da6c28aaSamw  *		(to imply the attribute should be cleared).
1283*da6c28aaSamw  *	a	all attributes type.  The oper should be applied to all
1284*da6c28aaSamw  *		read-write boolean system attributes.  No attribute list should
1285*da6c28aaSamw  *		be specified after an 'a' attribute type.
1286*da6c28aaSamw  *
1287*da6c28aaSamw  * Returns 0 if aoptsstr contained a valid attribute specification,
1288*da6c28aaSamw  * otherwise, returns 1.
1289*da6c28aaSamw  */
1290*da6c28aaSamw static int
1291*da6c28aaSamw parse_attr_args(char *aoptsstr, sec_args_t **sec_args)
1292*da6c28aaSamw {
1293*da6c28aaSamw 	char		action;
1294*da6c28aaSamw 	char		*attractptr;
1295*da6c28aaSamw 	char		atype;
1296*da6c28aaSamw 	char		*entry;
1297*da6c28aaSamw 	char		*eptr;
1298*da6c28aaSamw 	char		*nextattr;
1299*da6c28aaSamw 	char		*nextentry;
1300*da6c28aaSamw 	char		*subentry;
1301*da6c28aaSamw 	char		*teptr;
1302*da6c28aaSamw 	char		tok[] = {'\0', '\0'};
1303*da6c28aaSamw 	int		len;
1304*da6c28aaSamw 	f_attr_t	i;
1305*da6c28aaSamw 	int		numofattrs;
1306*da6c28aaSamw 
1307*da6c28aaSamw 	if ((*aoptsstr != 'S') || (*(aoptsstr + 1) == '\0')) {
1308*da6c28aaSamw 		return (1);
1309*da6c28aaSamw 	}
1310*da6c28aaSamw 
1311*da6c28aaSamw 	if ((eptr = strdup(aoptsstr + 1)) == NULL) {
1312*da6c28aaSamw 		perror("chmod");
1313*da6c28aaSamw 		exit(2);
1314*da6c28aaSamw 	}
1315*da6c28aaSamw 	entry = eptr;
1316*da6c28aaSamw 
1317*da6c28aaSamw 	/*
1318*da6c28aaSamw 	 * Create a positional character array to determine a single attribute
1319*da6c28aaSamw 	 * operation to be performed, where each index represents the system
1320*da6c28aaSamw 	 * attribute affected, and it's value in the array represents the action
1321*da6c28aaSamw 	 * to be performed, i.e., a value of '+' means to set the attribute, a
1322*da6c28aaSamw 	 * value of '-' means to clear the attribute, and a value of '\0' means
1323*da6c28aaSamw 	 * to leave the attribute untouched.  Initially, this positional
1324*da6c28aaSamw 	 * character array is all '\0's, representing a no-op.
1325*da6c28aaSamw 	 */
1326*da6c28aaSamw 	if ((numofattrs = attr_count()) < 1) {
1327*da6c28aaSamw 		errmsg(1, 1, gettext("system attributes not supported\n"));
1328*da6c28aaSamw 	}
1329*da6c28aaSamw 
1330*da6c28aaSamw 	if ((attractptr = calloc(numofattrs, sizeof (char))) == NULL) {
1331*da6c28aaSamw 		perror("chmod");
1332*da6c28aaSamw 		exit(2);
1333*da6c28aaSamw 	}
1334*da6c28aaSamw 
1335*da6c28aaSamw 	if ((*sec_args = malloc(sizeof (sec_args_t))) == NULL) {
1336*da6c28aaSamw 		perror("chmod");
1337*da6c28aaSamw 		exit(2);
1338*da6c28aaSamw 	}
1339*da6c28aaSamw 	(*sec_args)->sec_type = SEC_ATTR;
1340*da6c28aaSamw 	(*sec_args)->sec_attrs = NULL;
1341*da6c28aaSamw 
1342*da6c28aaSamw 	/* Parse each attribute operation within the attribute specification. */
1343*da6c28aaSamw 	while ((entry != NULL) && (*entry != '\0')) {
1344*da6c28aaSamw 		action = A_SET_OP;
1345*da6c28aaSamw 		atype = '\0';
1346*da6c28aaSamw 
1347*da6c28aaSamw 		/* Get the operator. */
1348*da6c28aaSamw 		switch (*entry) {
1349*da6c28aaSamw 		case A_SET_OP:
1350*da6c28aaSamw 		case A_INVERSE_OP:
1351*da6c28aaSamw 		case A_REPLACE_OP:
1352*da6c28aaSamw 			action = *entry++;
1353*da6c28aaSamw 			break;
1354*da6c28aaSamw 		case A_COMPACT_TYPE:
1355*da6c28aaSamw 		case A_VERBOSE_TYPE:
1356*da6c28aaSamw 		case A_ALLATTRS_TYPE:
1357*da6c28aaSamw 			atype = *entry++;
1358*da6c28aaSamw 			action = A_SET_OP;
1359*da6c28aaSamw 			break;
1360*da6c28aaSamw 		default:
1361*da6c28aaSamw 			break;
1362*da6c28aaSamw 		}
1363*da6c28aaSamw 
1364*da6c28aaSamw 		/* An attribute type must be specified. */
1365*da6c28aaSamw 		if (atype == '\0') {
1366*da6c28aaSamw 			if ((*entry == A_COMPACT_TYPE) ||
1367*da6c28aaSamw 			    (*entry == A_VERBOSE_TYPE) ||
1368*da6c28aaSamw 			    (*entry == A_ALLATTRS_TYPE)) {
1369*da6c28aaSamw 				atype = *entry++;
1370*da6c28aaSamw 			} else {
1371*da6c28aaSamw 				return (1);
1372*da6c28aaSamw 			}
1373*da6c28aaSamw 		}
1374*da6c28aaSamw 
1375*da6c28aaSamw 		/* Get the attribute specification separator. */
1376*da6c28aaSamw 		if (*entry == LEFTBRACE) {
1377*da6c28aaSamw 			*tok = RIGHTBRACE;
1378*da6c28aaSamw 			entry++;
1379*da6c28aaSamw 		} else {
1380*da6c28aaSamw 			*tok = A_SEP;
1381*da6c28aaSamw 		}
1382*da6c28aaSamw 
1383*da6c28aaSamw 		/* Get the attribute operation */
1384*da6c28aaSamw 		if ((nextentry = strpbrk(entry, tok)) != NULL) {
1385*da6c28aaSamw 			*nextentry = '\0';
1386*da6c28aaSamw 			nextentry++;
1387*da6c28aaSamw 		}
1388*da6c28aaSamw 
1389*da6c28aaSamw 		/* Check for a no-op */
1390*da6c28aaSamw 		if ((*entry == '\0') && (atype != A_ALLATTRS_TYPE) &&
1391*da6c28aaSamw 		    (action != A_REPLACE_OP)) {
1392*da6c28aaSamw 			entry = nextentry;
1393*da6c28aaSamw 			continue;
1394*da6c28aaSamw 		}
1395*da6c28aaSamw 
1396*da6c28aaSamw 		/*
1397*da6c28aaSamw 		 * Step through the attribute operation, setting the
1398*da6c28aaSamw 		 * appropriate values for the specified attributes in the
1399*da6c28aaSamw 		 * character array, attractptr. A value of '+' will mean the
1400*da6c28aaSamw 		 * attribute is to be set, and a value of '-' will mean the
1401*da6c28aaSamw 		 * attribute is to be cleared.  If the value of an attribute
1402*da6c28aaSamw 		 * remains '\0', then no action is to be taken on that
1403*da6c28aaSamw 		 * attribute.  As multiple operations specified are
1404*da6c28aaSamw 		 * accumulated, a single attribute setting operation is
1405*da6c28aaSamw 		 * represented in attractptr.
1406*da6c28aaSamw 		 */
1407*da6c28aaSamw 		len = strlen(entry);
1408*da6c28aaSamw 		if ((*tok == RIGHTBRACE) || (action == A_REPLACE_OP) ||
1409*da6c28aaSamw 		    (atype == A_ALLATTRS_TYPE)) {
1410*da6c28aaSamw 
1411*da6c28aaSamw 			if ((action == A_REPLACE_OP) ||
1412*da6c28aaSamw 			    (atype == A_ALLATTRS_TYPE)) {
1413*da6c28aaSamw 				(void) memset(attractptr, '\0', numofattrs);
1414*da6c28aaSamw 			}
1415*da6c28aaSamw 
1416*da6c28aaSamw 			if (len > 0) {
1417*da6c28aaSamw 				if ((teptr = strdup(entry)) == NULL) {
1418*da6c28aaSamw 					perror("chmod");
1419*da6c28aaSamw 					exit(2);
1420*da6c28aaSamw 				}
1421*da6c28aaSamw 				subentry = teptr;
1422*da6c28aaSamw 				while (subentry != NULL) {
1423*da6c28aaSamw 					if ((nextattr = strpbrk(subentry,
1424*da6c28aaSamw 					    A_SEP_TOK)) != NULL) {
1425*da6c28aaSamw 						*nextattr = '\0';
1426*da6c28aaSamw 						nextattr++;
1427*da6c28aaSamw 					}
1428*da6c28aaSamw 					if (parse_entry(subentry, action,
1429*da6c28aaSamw 					    atype, len, attractptr) != 0) {
1430*da6c28aaSamw 						return (1);
1431*da6c28aaSamw 					}
1432*da6c28aaSamw 					subentry = nextattr;
1433*da6c28aaSamw 				}
1434*da6c28aaSamw 				free(teptr);
1435*da6c28aaSamw 			}
1436*da6c28aaSamw 
1437*da6c28aaSamw 			/*
1438*da6c28aaSamw 			 * If performing the replace action, record the
1439*da6c28aaSamw 			 * attributes and values for the rest of the
1440*da6c28aaSamw 			 * attributes that have not already been recorded,
1441*da6c28aaSamw 			 * otherwise record the specified action for all
1442*da6c28aaSamw 			 * attributes.  Note: set_attr_args() will only record
1443*da6c28aaSamw 			 * the attribute and action if it is a boolean
1444*da6c28aaSamw 			 * read-write attribute so we don't need to worry
1445*da6c28aaSamw 			 * about checking it here.
1446*da6c28aaSamw 			 */
1447*da6c28aaSamw 			if ((action == A_REPLACE_OP) ||
1448*da6c28aaSamw 			    (atype == A_ALLATTRS_TYPE)) {
1449*da6c28aaSamw 				for (i = 0; i < numofattrs; i++) {
1450*da6c28aaSamw 					if (attractptr[i] == A_UNDEF_OP) {
1451*da6c28aaSamw 						(void) set_attr_args(i,
1452*da6c28aaSamw 						    (action == A_SET_OP) ?
1453*da6c28aaSamw 						    A_SET_OP : A_INVERSE_OP,
1454*da6c28aaSamw 						    attractptr);
1455*da6c28aaSamw 					}
1456*da6c28aaSamw 				}
1457*da6c28aaSamw 			}
1458*da6c28aaSamw 
1459*da6c28aaSamw 		} else {
1460*da6c28aaSamw 			if (parse_entry(entry, action, atype, len,
1461*da6c28aaSamw 			    attractptr) != 0) {
1462*da6c28aaSamw 				return (1);
1463*da6c28aaSamw 			}
1464*da6c28aaSamw 		}
1465*da6c28aaSamw 		entry = nextentry;
1466*da6c28aaSamw 	}
1467*da6c28aaSamw 
1468*da6c28aaSamw 	/*
1469*da6c28aaSamw 	 * Populate an nvlist with attribute name and boolean value pairs
1470*da6c28aaSamw 	 * using the single attribute operation.
1471*da6c28aaSamw 	 */
1472*da6c28aaSamw 	(*sec_args)->sec_attrs = set_attrs_nvlist(attractptr, numofattrs);
1473*da6c28aaSamw 	free(attractptr);
1474*da6c28aaSamw 	free(eptr);
1475*da6c28aaSamw 
1476*da6c28aaSamw 	return (0);
1477*da6c28aaSamw }
1478