xref: /onnv-gate/usr/src/cmd/chmod/chmod.c (revision 12871:d5c0ca4a2e95)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51591Scasper  * Common Development and Distribution License (the "License").
61591Scasper  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*12871SAmrita.Sadhukhan@Sun.COM  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
230Sstevel@tonic-gate  */
240Sstevel@tonic-gate 
250Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T			*/
260Sstevel@tonic-gate /*	  All Rights Reserved						*/
270Sstevel@tonic-gate /*									*/
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
310Sstevel@tonic-gate  * The Regents of the University of California
320Sstevel@tonic-gate  * All Rights Reserved
330Sstevel@tonic-gate  *
340Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
350Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
360Sstevel@tonic-gate  * contributors.
370Sstevel@tonic-gate  */
380Sstevel@tonic-gate 
390Sstevel@tonic-gate /*
400Sstevel@tonic-gate  * chmod option mode files
410Sstevel@tonic-gate  * where
420Sstevel@tonic-gate  *	mode is [ugoa][+-=][rwxXlstugo] or an octal number
43789Sahrens  *	mode is [<+|->A[# <number] ]<aclspec>
445331Samw  *	mode is S<attrspec>
455331Samw  *	option is -R, -f, and -@
460Sstevel@tonic-gate  */
470Sstevel@tonic-gate 
480Sstevel@tonic-gate /*
490Sstevel@tonic-gate  *  Note that many convolutions are necessary
500Sstevel@tonic-gate  *  due to the re-use of bits between locking
510Sstevel@tonic-gate  *  and setgid
520Sstevel@tonic-gate  */
530Sstevel@tonic-gate 
540Sstevel@tonic-gate #include <unistd.h>
550Sstevel@tonic-gate #include <stdlib.h>
560Sstevel@tonic-gate #include <stdio.h>
570Sstevel@tonic-gate #include <sys/types.h>
580Sstevel@tonic-gate #include <sys/stat.h>
595331Samw #include <fcntl.h>
600Sstevel@tonic-gate #include <dirent.h>
610Sstevel@tonic-gate #include <locale.h>
620Sstevel@tonic-gate #include <string.h>	/* strerror() */
630Sstevel@tonic-gate #include <stdarg.h>
640Sstevel@tonic-gate #include <limits.h>
65789Sahrens #include <ctype.h>
660Sstevel@tonic-gate #include <errno.h>
670Sstevel@tonic-gate #include <sys/acl.h>
68789Sahrens #include <aclutils.h>
695331Samw #include <libnvpair.h>
705331Samw #include <libcmdutils.h>
715331Samw #include <libgen.h>
725331Samw #include <attr.h>
730Sstevel@tonic-gate 
740Sstevel@tonic-gate static int	rflag;
750Sstevel@tonic-gate static int	fflag;
760Sstevel@tonic-gate 
770Sstevel@tonic-gate extern int	optind;
780Sstevel@tonic-gate extern int	errno;
790Sstevel@tonic-gate 
800Sstevel@tonic-gate static int	mac;		/* Alternate to argc (for parseargs) */
810Sstevel@tonic-gate static char	**mav;		/* Alternate to argv (for parseargs) */
820Sstevel@tonic-gate 
830Sstevel@tonic-gate static char	*ms;		/* Points to the mode argument */
840Sstevel@tonic-gate 
855331Samw #define	ACL_ADD			1
865331Samw #define	ACL_DELETE		2
875331Samw #define	ACL_SLOT_DELETE		3
885331Samw #define	ACL_REPLACE		4
895331Samw #define	ACL_STRIP		5
905331Samw 
915331Samw #define	LEFTBRACE	'{'
925331Samw #define	RIGHTBRACE	'}'
935331Samw #define	A_SEP		','
945331Samw #define	A_SEP_TOK	","
955331Samw 
965331Samw #define	A_COMPACT_TYPE	'c'
975331Samw #define	A_VERBOSE_TYPE	'v'
985331Samw #define	A_ALLATTRS_TYPE	'a'
995331Samw 
1005331Samw #define	A_SET_OP	'+'
1015331Samw #define	A_INVERSE_OP	'-'
1025331Samw #define	A_REPLACE_OP	'='
1035331Samw #define	A_UNDEF_OP	'\0'
1045331Samw 
1055331Samw #define	A_SET_TEXT	"set"
1065331Samw #define	A_INVERSE_TEXT	"clear"
1075331Samw 
1085331Samw #define	A_SET_VAL	B_TRUE
1095331Samw #define	A_CLEAR_VAL	B_FALSE
1105331Samw 
1115331Samw #define	ATTR_OPTS	0
1125331Samw #define	ATTR_NAMES	1
1135331Samw 
1145331Samw #define	sec_acls	secptr.acls
1155331Samw #define	sec_attrs	secptr.attrs
116789Sahrens 
117789Sahrens typedef struct acl_args {
118789Sahrens 	acl_t	*acl_aclp;
119789Sahrens 	int	acl_slot;
120789Sahrens 	int	acl_action;
121789Sahrens } acl_args_t;
122789Sahrens 
1235331Samw typedef enum {
1245331Samw 	SEC_ACL,
1255331Samw 	SEC_ATTR
1265331Samw } chmod_sec_t;
1275331Samw 
1285331Samw typedef struct {
1295331Samw 	chmod_sec_t		sec_type;
1305331Samw 	union {
1315331Samw 		acl_args_t	*acls;
1325331Samw 		nvlist_t	*attrs;
1335331Samw 	} secptr;
1345331Samw } sec_args_t;
1350Sstevel@tonic-gate 
1365331Samw typedef struct attr_name {
1375331Samw 	char			*name;
1385331Samw 	struct attr_name	*next;
1395331Samw } attr_name_t;
1405331Samw 
1415331Samw 
1425331Samw extern mode_t newmode_common(char *ms, mode_t new_mode, mode_t umsk,
1435331Samw     char *file, char *path, o_mode_t *group_clear_bits,
1445331Samw     o_mode_t *group_set_bits);
1455331Samw 
1465331Samw static int chmodr(char *dir, char *path, mode_t mode, mode_t umsk,
1475331Samw     sec_args_t *secp, attr_name_t *attrname);
148789Sahrens static int doacl(char *file, struct stat *st, acl_args_t *aclp);
1495331Samw static int dochmod(char *name, char *path, mode_t umsk, sec_args_t *secp,
1505331Samw     attr_name_t *attrnames);
1510Sstevel@tonic-gate static void handle_acl(char *name, o_mode_t group_clear_bits,
152789Sahrens     o_mode_t group_set_bits);
1535331Samw void errmsg(int severity, int code, char *format, ...);
1545331Samw static void free_attr_names(attr_name_t *attrnames);
1555331Samw static void parseargs(int ac, char *av[]);
1565331Samw static int parse_acl_args(char *arg, sec_args_t **sec_args);
1575331Samw static int parse_attr_args(char *arg, sec_args_t **sec_args);
1585331Samw static void print_attrs(int flag);
1595331Samw static int set_attrs(char *file, attr_name_t *attrnames, nvlist_t *attr_nvlist);
160789Sahrens static void usage(void);
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate int
main(int argc,char * argv[])1630Sstevel@tonic-gate main(int argc, char *argv[])
1640Sstevel@tonic-gate {
1655331Samw 	int		i, c;
1665331Samw 	int		status = 0;
1675331Samw 	mode_t		umsk;
1685331Samw 	sec_args_t	*sec_args = NULL;
1695331Samw 	attr_name_t	*attrnames = NULL;
1705331Samw 	attr_name_t	*attrend = NULL;
1715331Samw 	attr_name_t	*tattr;
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
1740Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
1750Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
1760Sstevel@tonic-gate #endif
1770Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate 	parseargs(argc, argv);
1800Sstevel@tonic-gate 
1815331Samw 	while ((c = getopt(mac, mav, "Rf@:")) != EOF) {
1820Sstevel@tonic-gate 		switch (c) {
1830Sstevel@tonic-gate 		case 'R':
1840Sstevel@tonic-gate 			rflag++;
1850Sstevel@tonic-gate 			break;
1860Sstevel@tonic-gate 		case 'f':
1870Sstevel@tonic-gate 			fflag++;
1880Sstevel@tonic-gate 			break;
1895331Samw 		case '@':
1905331Samw 			if (((tattr = malloc(sizeof (attr_name_t))) == NULL) ||
1915331Samw 			    ((tattr->name = strdup(optarg)) == NULL)) {
1925331Samw 				perror("chmod");
1935331Samw 				exit(2);
1945331Samw 			}
1955331Samw 			if (attrnames == NULL) {
1965331Samw 				attrnames = tattr;
1975331Samw 				attrnames->next = NULL;
1985331Samw 			} else {
1995331Samw 				attrend->next = tattr;
2005331Samw 			}
2015331Samw 			attrend = tattr;
2025331Samw 			break;
2030Sstevel@tonic-gate 		case '?':
2040Sstevel@tonic-gate 			usage();
2050Sstevel@tonic-gate 			exit(2);
2060Sstevel@tonic-gate 		}
2070Sstevel@tonic-gate 	}
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate 	/*
2100Sstevel@tonic-gate 	 * Check for sufficient arguments
2110Sstevel@tonic-gate 	 * or a usage error.
2120Sstevel@tonic-gate 	 */
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 	mac -= optind;
2150Sstevel@tonic-gate 	mav += optind;
2165331Samw 	if ((mac >= 2) && (mav[0][0] == 'A')) {
2175331Samw 		if (attrnames != NULL) {
2185331Samw 			free_attr_names(attrnames);
2195331Samw 			attrnames = NULL;
2205331Samw 		}
2215331Samw 		if (parse_acl_args(*mav, &sec_args)) {
222789Sahrens 			usage();
223789Sahrens 			exit(2);
224789Sahrens 		}
2255331Samw 	} else if ((mac >= 2) && (mav[0][0] == 'S')) {
2265331Samw 		if (parse_attr_args(*mav, &sec_args)) {
2275331Samw 			usage();
2285331Samw 			exit(2);
2295331Samw 
2305331Samw 		/* A no-op attribute operation was specified. */
2315331Samw 		} else if (sec_args->sec_attrs == NULL) {
2325331Samw 			exit(0);
2335331Samw 		}
234789Sahrens 	} else {
235789Sahrens 		if (mac < 2) {
236789Sahrens 			usage();
237789Sahrens 			exit(2);
238789Sahrens 		}
2395331Samw 		if (attrnames != NULL) {
2405331Samw 			free_attr_names(attrnames);
2415331Samw 			attrnames = NULL;
2425331Samw 		}
2430Sstevel@tonic-gate 	}
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 	ms = mav[0];
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate 	umsk = umask(0);
2480Sstevel@tonic-gate 	(void) umask(umsk);
2490Sstevel@tonic-gate 
250789Sahrens 	for (i = 1; i < mac; i++) {
2515331Samw 		status += dochmod(mav[i], mav[i], umsk, sec_args, attrnames);
252789Sahrens 	}
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate 	return (fflag ? 0 : status);
2550Sstevel@tonic-gate }
2560Sstevel@tonic-gate 
2575331Samw static void
free_attr_names(attr_name_t * attrnames)2585331Samw free_attr_names(attr_name_t *attrnames)
2595331Samw {
2605331Samw 	attr_name_t	*attrnamesptr = attrnames;
2615331Samw 	attr_name_t	*tptr;
2625331Samw 
2635331Samw 	while (attrnamesptr != NULL) {
2645331Samw 		tptr = attrnamesptr->next;
2655331Samw 		if (attrnamesptr->name != NULL) {
2665331Samw 			free(attrnamesptr->name);
2675331Samw 		}
2685331Samw 		attrnamesptr = tptr;
2695331Samw 	}
2705331Samw }
2715331Samw 
2720Sstevel@tonic-gate static int
dochmod(char * name,char * path,mode_t umsk,sec_args_t * secp,attr_name_t * attrnames)2735331Samw dochmod(char *name, char *path, mode_t umsk, sec_args_t *secp,
2745331Samw     attr_name_t *attrnames)
2750Sstevel@tonic-gate {
2760Sstevel@tonic-gate 	static struct stat st;
2770Sstevel@tonic-gate 	int linkflg = 0;
2780Sstevel@tonic-gate 	o_mode_t	group_clear_bits, group_set_bits;
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate 	if (lstat(name, &st) < 0) {
2810Sstevel@tonic-gate 		errmsg(2, 0, gettext("can't access %s\n"), path);
2820Sstevel@tonic-gate 		return (1);
2830Sstevel@tonic-gate 	}
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 	if ((st.st_mode & S_IFMT) == S_IFLNK) {
2860Sstevel@tonic-gate 		linkflg = 1;
2870Sstevel@tonic-gate 		if (stat(name, &st) < 0) {
2880Sstevel@tonic-gate 			errmsg(2, 0, gettext("can't access %s\n"), path);
2890Sstevel@tonic-gate 			return (1);
2900Sstevel@tonic-gate 		}
2910Sstevel@tonic-gate 	}
2920Sstevel@tonic-gate 
2930Sstevel@tonic-gate 	/* Do not recurse if directory is object of symbolic link */
2945331Samw 	if (rflag && ((st.st_mode & S_IFMT) == S_IFDIR) && !linkflg) {
2955331Samw 		return (chmodr(name, path, st.st_mode, umsk, secp, attrnames));
2965331Samw 	}
2970Sstevel@tonic-gate 
2985331Samw 	if (secp != NULL) {
2995331Samw 		if (secp->sec_type == SEC_ACL) {
3005331Samw 			return (doacl(name, &st, secp->sec_acls));
3015331Samw 		} else if (secp->sec_type == SEC_ATTR) {
3025331Samw 			return (set_attrs(name, attrnames, secp->sec_attrs));
3035331Samw 		} else {
3045331Samw 			return (1);
3055331Samw 		}
3065331Samw 	} else {
3075331Samw 		if (chmod(name, newmode_common(ms, st.st_mode, umsk, name, path,
3085331Samw 		    &group_clear_bits, &group_set_bits)) == -1) {
3095331Samw 			errmsg(2, 0, gettext("can't change %s\n"), path);
3105331Samw 			return (1);
3115331Samw 		}
3120Sstevel@tonic-gate 	}
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate 	/*
3150Sstevel@tonic-gate 	 * If the group permissions of the file are being modified,
3160Sstevel@tonic-gate 	 * make sure that the file's ACL (if it has one) is
3170Sstevel@tonic-gate 	 * modified also, since chmod is supposed to apply group
3180Sstevel@tonic-gate 	 * permissions changes to both the acl mask and the
3190Sstevel@tonic-gate 	 * general group permissions.
3200Sstevel@tonic-gate 	 */
3210Sstevel@tonic-gate 	if (group_clear_bits || group_set_bits)
3220Sstevel@tonic-gate 		handle_acl(name, group_clear_bits, group_set_bits);
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate 	return (0);
3250Sstevel@tonic-gate }
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate static int
chmodr(char * dir,char * path,mode_t mode,mode_t umsk,sec_args_t * secp,attr_name_t * attrnames)3285331Samw chmodr(char *dir, char *path,  mode_t mode, mode_t umsk, sec_args_t *secp,
3295331Samw     attr_name_t *attrnames)
3300Sstevel@tonic-gate {
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate 	DIR *dirp;
3330Sstevel@tonic-gate 	struct dirent *dp;
3340Sstevel@tonic-gate 	char savedir[PATH_MAX];			/* dir name to restore */
3350Sstevel@tonic-gate 	char currdir[PATH_MAX+1];		/* current dir name + '/' */
3360Sstevel@tonic-gate 	char parentdir[PATH_MAX+1];		/* parent dir name  + '/' */
3370Sstevel@tonic-gate 	int ecode;
338789Sahrens 	struct stat st;
3390Sstevel@tonic-gate 	o_mode_t	group_clear_bits, group_set_bits;
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 	if (getcwd(savedir, PATH_MAX) == 0)
3420Sstevel@tonic-gate 		errmsg(2, 255, gettext("chmod: could not getcwd %s\n"),
3430Sstevel@tonic-gate 		    savedir);
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate 	/*
3460Sstevel@tonic-gate 	 * Change what we are given before doing it's contents
3470Sstevel@tonic-gate 	 */
3485331Samw 	if (secp != NULL) {
349789Sahrens 		if (lstat(dir, &st) < 0) {
350789Sahrens 			errmsg(2, 0, gettext("can't access %s\n"), path);
351789Sahrens 			return (1);
352789Sahrens 		}
3535331Samw 		if (secp->sec_type == SEC_ACL) {
354*12871SAmrita.Sadhukhan@Sun.COM 			(void) doacl(dir, &st, secp->sec_acls);
3555331Samw 		} else if (secp->sec_type == SEC_ATTR) {
356*12871SAmrita.Sadhukhan@Sun.COM 			(void) set_attrs(dir, attrnames, secp->sec_attrs);
3575331Samw 		} else {
358789Sahrens 			return (1);
3595331Samw 		}
360789Sahrens 	} else if (chmod(dir, newmode_common(ms, mode, umsk, dir, path,
3610Sstevel@tonic-gate 	    &group_clear_bits, &group_set_bits)) < 0) {
3620Sstevel@tonic-gate 		errmsg(2, 0, gettext("can't change %s\n"), path);
3630Sstevel@tonic-gate 	}
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate 	/*
3660Sstevel@tonic-gate 	 * If the group permissions of the file are being modified,
3670Sstevel@tonic-gate 	 * make sure that the file's ACL (if it has one) is
3680Sstevel@tonic-gate 	 * modified also, since chmod is supposed to apply group
3690Sstevel@tonic-gate 	 * permissions changes to both the acl mask and the
3700Sstevel@tonic-gate 	 * general group permissions.
3710Sstevel@tonic-gate 	 */
372789Sahrens 
3735331Samw 	if (secp != NULL) {
3745331Samw 		/* only necessary when not setting ACL or system attributes */
375789Sahrens 		if (group_clear_bits || group_set_bits)
376789Sahrens 			handle_acl(dir, group_clear_bits, group_set_bits);
377789Sahrens 	}
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate 	if (chdir(dir) < 0) {
3800Sstevel@tonic-gate 		errmsg(2, 0, "%s/%s: %s\n", savedir, dir, strerror(errno));
3810Sstevel@tonic-gate 		return (1);
3820Sstevel@tonic-gate 	}
3830Sstevel@tonic-gate 	if ((dirp = opendir(".")) == NULL) {
3840Sstevel@tonic-gate 		errmsg(2, 0, "%s\n", strerror(errno));
3850Sstevel@tonic-gate 		return (1);
3860Sstevel@tonic-gate 	}
3870Sstevel@tonic-gate 	ecode = 0;
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate 	/*
3900Sstevel@tonic-gate 	 * Save parent directory path before recursive chmod.
3910Sstevel@tonic-gate 	 * We'll need this for error printing purposes. Add
3920Sstevel@tonic-gate 	 * a trailing '/' to the path except in the case where
3930Sstevel@tonic-gate 	 * the path is just '/'
3940Sstevel@tonic-gate 	 */
3950Sstevel@tonic-gate 
3963520Sas145665 	if (strlcpy(parentdir, path, PATH_MAX + 1) >= PATH_MAX + 1) {
3973520Sas145665 		errmsg(2, 0, gettext("directory path name too long: %s\n"),
3983520Sas145665 		    path);
3993520Sas145665 		return (1);
4003520Sas145665 	}
4010Sstevel@tonic-gate 	if (strcmp(path, "/") != 0)
4023520Sas145665 		if (strlcat(parentdir, "/", PATH_MAX + 1) >= PATH_MAX + 1) {
4033520Sas145665 			errmsg(2, 0,
4043520Sas145665 			    gettext("directory path name too long: %s/\n"),
4053520Sas145665 			    parentdir);
4063520Sas145665 			return (1);
4073520Sas145665 		}
4083520Sas145665 
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 	for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))  {
4113520Sas145665 
4121591Scasper 		if (strcmp(dp->d_name, ".") == 0 ||	/* skip . and .. */
4131591Scasper 		    strcmp(dp->d_name, "..") == 0) {
4141591Scasper 			continue;
4151591Scasper 		}
4163520Sas145665 		if (strlcpy(currdir, parentdir, PATH_MAX + 1) >= PATH_MAX + 1) {
4173520Sas145665 			errmsg(2, 0,
4183520Sas145665 			    gettext("directory path name too long: %s\n"),
4193520Sas145665 			    parentdir);
4203520Sas145665 			return (1);
4213520Sas145665 		}
4223520Sas145665 		if (strlcat(currdir, dp->d_name, PATH_MAX + 1)
4233520Sas145665 		    >= PATH_MAX + 1) {
4243520Sas145665 			errmsg(2, 0,
4253520Sas145665 			    gettext("directory path name too long: %s%s\n"),
4263520Sas145665 			    currdir, dp->d_name);
4273520Sas145665 			return (1);
4283520Sas145665 		}
4295331Samw 		ecode += dochmod(dp->d_name, currdir, umsk, secp, attrnames);
4300Sstevel@tonic-gate 	}
4310Sstevel@tonic-gate 	(void) closedir(dirp);
4320Sstevel@tonic-gate 	if (chdir(savedir) < 0) {
4330Sstevel@tonic-gate 		errmsg(2, 255, gettext("can't change back to %s\n"), savedir);
4340Sstevel@tonic-gate 	}
4350Sstevel@tonic-gate 	return (ecode ? 1 : 0);
4360Sstevel@tonic-gate }
4370Sstevel@tonic-gate 
4380Sstevel@tonic-gate /* PRINTFLIKE3 */
4390Sstevel@tonic-gate void
errmsg(int severity,int code,char * format,...)4400Sstevel@tonic-gate errmsg(int severity, int code, char *format, ...)
4410Sstevel@tonic-gate {
4420Sstevel@tonic-gate 	va_list ap;
4430Sstevel@tonic-gate 	static char *msg[] = {
4440Sstevel@tonic-gate 	"",
4450Sstevel@tonic-gate 	"ERROR",
4460Sstevel@tonic-gate 	"WARNING",
4470Sstevel@tonic-gate 	""
4480Sstevel@tonic-gate 	};
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate 	va_start(ap, format);
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate 	/*
4530Sstevel@tonic-gate 	 * Always print error message if this is a fatal error (code == 0);
4540Sstevel@tonic-gate 	 * otherwise, print message if fflag == 0 (no -f option specified)
4550Sstevel@tonic-gate 	 */
4560Sstevel@tonic-gate 	if (!fflag || (code != 0)) {
4570Sstevel@tonic-gate 		(void) fprintf(stderr,
4585331Samw 		    "chmod: %s: ", gettext(msg[severity]));
4590Sstevel@tonic-gate 		(void) vfprintf(stderr, format, ap);
4600Sstevel@tonic-gate 	}
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate 	va_end(ap);
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 	if (code != 0)
4650Sstevel@tonic-gate 		exit(fflag ? 0 : code);
4660Sstevel@tonic-gate }
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate static void
usage(void)4690Sstevel@tonic-gate usage(void)
4700Sstevel@tonic-gate {
4710Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(
4720Sstevel@tonic-gate 	    "usage:\tchmod [-fR] <absolute-mode> file ...\n"));
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(
4755331Samw 	    "\tchmod [-fR] [-@ attribute] ... "
4765331Samw 	    "S<attribute-operation> file ...\n"));
4775331Samw 
4785331Samw 	(void) fprintf(stderr, gettext(
479789Sahrens 	    "\tchmod [-fR] <ACL-operation> file ...\n"));
480789Sahrens 
481789Sahrens 	(void) fprintf(stderr, gettext(
4825331Samw 	    "\tchmod [-fR] <symbolic-mode-list> file ...\n\n"));
483789Sahrens 
4840Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(
4850Sstevel@tonic-gate 	    "where \t<symbolic-mode-list> is a comma-separated list of\n"));
4865331Samw 	(void) fprintf(stderr, gettext(
4875331Samw 	    "\t[ugoa]{+|-|=}[rwxXlstugo]\n\n"));
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(
4905331Samw 	    "where \t<attribute-operation> is a comma-separated list of\n"
4915331Samw 	    "\tone or more of the following\n"));
4925331Samw 	(void) fprintf(stderr, gettext(
4935331Samw 	    "\t[+|-|=]c[<compact-attribute-list>|{<compact-attribute-list>}]\n"
4945331Samw 	    "\t[+|-|=]v[<verbose-attribute-setting>|"
4955331Samw 	    "\'{\'<verbose-attribute-setting-list>\'}\']\n"
4965331Samw 	    "\t[+|-|=]a\n"));
4975331Samw 	(void) fprintf(stderr, gettext(
4985331Samw 	    "where \t<compact-attribute-list> is a list of zero or more of\n"));
4995331Samw 	print_attrs(ATTR_OPTS);
5005331Samw 	(void) fprintf(stderr, gettext(
5015331Samw 	    "where \t<verbose-attribute-setting> is one of\n"));
5025331Samw 	print_attrs(ATTR_NAMES);
5035331Samw 	(void) fprintf(stderr, gettext(
5045331Samw 	    "\tand can be, optionally, immediately preceded by \"no\"\n\n"));
505789Sahrens 
506789Sahrens 	(void) fprintf(stderr, gettext(
507789Sahrens 	    "where \t<ACL-operation> is one of the following\n"));
508789Sahrens 	(void) fprintf(stderr, gettext("\tA-<acl_specification>\n"));
509789Sahrens 	(void) fprintf(stderr, gettext("\tA[number]-\n"));
510789Sahrens 	(void) fprintf(stderr, gettext(
511789Sahrens 	    "\tA[number]{+|=}<acl_specification>\n"));
512789Sahrens 	(void) fprintf(stderr, gettext(
513789Sahrens 	    "where \t<acl-specification> is a comma-separated list of ACEs\n"));
5140Sstevel@tonic-gate }
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate /*
5170Sstevel@tonic-gate  *  parseargs - generate getopt-friendly argument list for backwards
5180Sstevel@tonic-gate  *		compatibility with earlier Solaris usage (eg, chmod -w
5190Sstevel@tonic-gate  *		foo).
5200Sstevel@tonic-gate  *
5210Sstevel@tonic-gate  *  assumes the existence of a static set of alternates to argc and argv,
5220Sstevel@tonic-gate  *  (namely, mac, and mav[]).
5230Sstevel@tonic-gate  *
5240Sstevel@tonic-gate  */
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate static void
parseargs(int ac,char * av[])5270Sstevel@tonic-gate parseargs(int ac, char *av[])
5280Sstevel@tonic-gate {
5290Sstevel@tonic-gate 	int i;			/* current argument			*/
5300Sstevel@tonic-gate 	int fflag;		/* arg list contains "--"		*/
5310Sstevel@tonic-gate 	size_t mav_num;		/* number of entries in mav[]		*/
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate 	/*
5340Sstevel@tonic-gate 	 * We add an extra argument slot, in case we need to jam a "--"
5350Sstevel@tonic-gate 	 * argument into the list.
5360Sstevel@tonic-gate 	 */
5370Sstevel@tonic-gate 
5380Sstevel@tonic-gate 	mav_num = (size_t)ac+2;
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 	if ((mav = calloc(mav_num, sizeof (char *))) == NULL) {
5410Sstevel@tonic-gate 		perror("chmod");
5420Sstevel@tonic-gate 		exit(2);
5430Sstevel@tonic-gate 	}
5440Sstevel@tonic-gate 
5450Sstevel@tonic-gate 	/* scan for the use of "--" in the argument list */
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate 	for (fflag = i = 0; i < ac; i ++) {
5480Sstevel@tonic-gate 		if (strcmp(av[i], "--") == 0)
5495331Samw 			fflag = 1;
5500Sstevel@tonic-gate 	}
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 	/* process the arguments */
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 	for (i = mac = 0;
5550Sstevel@tonic-gate 	    (av[i] != (char *)NULL) && (av[i][0] != (char)NULL);
5560Sstevel@tonic-gate 	    i++) {
5570Sstevel@tonic-gate 		if (!fflag && av[i][0] == '-') {
5580Sstevel@tonic-gate 			/*
5590Sstevel@tonic-gate 			 *  If there is not already a "--" argument specified,
5600Sstevel@tonic-gate 			 *  and the argument starts with '-' but does not
5610Sstevel@tonic-gate 			 *  contain any of the official option letters, then it
5620Sstevel@tonic-gate 			 *  is probably a mode argument beginning with '-'.
5630Sstevel@tonic-gate 			 *  Force a "--" into the argument stream in front of
5640Sstevel@tonic-gate 			 *  it.
5650Sstevel@tonic-gate 			 */
5660Sstevel@tonic-gate 
5670Sstevel@tonic-gate 			if ((strchr(av[i], 'R') == NULL &&
5685331Samw 			    strchr(av[i], 'f') == NULL) &&
5695331Samw 			    strchr(av[i], '@') == NULL) {
5705331Samw 				if ((mav[mac++] = strdup("--")) == NULL) {
5715331Samw 					perror("chmod");
5725331Samw 					exit(2);
5735331Samw 				}
5740Sstevel@tonic-gate 			}
5750Sstevel@tonic-gate 		}
5760Sstevel@tonic-gate 
5775331Samw 		if ((mav[mac++] = strdup(av[i])) == NULL) {
5785331Samw 			perror("chmod");
5795331Samw 			exit(2);
5805331Samw 		}
5810Sstevel@tonic-gate 	}
5820Sstevel@tonic-gate 
5830Sstevel@tonic-gate 	mav[mac] = (char *)NULL;
5840Sstevel@tonic-gate }
5850Sstevel@tonic-gate 
5865331Samw static int
parse_acl_args(char * arg,sec_args_t ** sec_args)5875331Samw parse_acl_args(char *arg, sec_args_t **sec_args)
588789Sahrens {
589789Sahrens 	acl_t *new_acl = NULL;
590789Sahrens 	int slot;
591789Sahrens 	int len;
592789Sahrens 	int action;
593789Sahrens 	acl_args_t *new_acl_args;
594789Sahrens 	char *acl_spec = NULL;
595789Sahrens 	char *end;
596789Sahrens 
597789Sahrens 	if (arg[0] != 'A')
598789Sahrens 		return (1);
599789Sahrens 
600789Sahrens 	slot = strtol(&arg[1], &end, 10);
601789Sahrens 
602789Sahrens 	len = strlen(arg);
603789Sahrens 	switch (*end) {
604789Sahrens 	case '+':
605789Sahrens 		action = ACL_ADD;
606789Sahrens 		acl_spec = ++end;
607789Sahrens 		break;
608789Sahrens 	case '-':
609789Sahrens 		if (len == 2 && arg[0] == 'A' && arg[1] == '-')
610789Sahrens 			action = ACL_STRIP;
611789Sahrens 		else
612789Sahrens 			action = ACL_DELETE;
613789Sahrens 		if (action != ACL_STRIP) {
614789Sahrens 			acl_spec = ++end;
615789Sahrens 			if (acl_spec[0] == '\0') {
616789Sahrens 				action = ACL_SLOT_DELETE;
617789Sahrens 				acl_spec = NULL;
618789Sahrens 			} else if (arg[1] != '-')
619789Sahrens 				return (1);
620789Sahrens 		}
621789Sahrens 		break;
622789Sahrens 	case '=':
623865Smarks 		/*
624865Smarks 		 * Was slot specified?
625865Smarks 		 */
626865Smarks 		if (arg[1] == '=')
627865Smarks 			slot = -1;
628789Sahrens 		action = ACL_REPLACE;
629789Sahrens 		acl_spec = ++end;
630789Sahrens 		break;
631789Sahrens 	default:
632789Sahrens 		return (1);
633789Sahrens 	}
634789Sahrens 
635789Sahrens 	if ((action == ACL_REPLACE || action == ACL_ADD) && acl_spec[0] == '\0')
636789Sahrens 		return (1);
637789Sahrens 
638789Sahrens 	if (acl_spec) {
6391420Smarks 		if (acl_parse(acl_spec, &new_acl)) {
6401420Smarks 			exit(1);
641789Sahrens 		}
642789Sahrens 	}
643789Sahrens 
644789Sahrens 	new_acl_args = malloc(sizeof (acl_args_t));
645789Sahrens 	if (new_acl_args == NULL)
646789Sahrens 		return (1);
647789Sahrens 
648789Sahrens 	new_acl_args->acl_aclp = new_acl;
649789Sahrens 	new_acl_args->acl_slot = slot;
650789Sahrens 	new_acl_args->acl_action = action;
651789Sahrens 
6525331Samw 	if ((*sec_args = malloc(sizeof (sec_args_t))) == NULL) {
6535331Samw 		perror("chmod");
6545331Samw 		exit(2);
6555331Samw 	}
6565331Samw 	(*sec_args)->sec_type = SEC_ACL;
6575331Samw 	(*sec_args)->sec_acls = new_acl_args;
658789Sahrens 
659789Sahrens 	return (0);
660789Sahrens }
661789Sahrens 
6620Sstevel@tonic-gate /*
6630Sstevel@tonic-gate  * This function is called whenever the group permissions of a file
6640Sstevel@tonic-gate  * is being modified.  According to the chmod(1) manpage, any
6650Sstevel@tonic-gate  * change made to the group permissions must be applied to both
6660Sstevel@tonic-gate  * the acl mask and the acl's GROUP_OBJ.  The chmod(2) already
6670Sstevel@tonic-gate  * set the mask, so this routine needs to make the same change
6680Sstevel@tonic-gate  * to the GROUP_OBJ.
6690Sstevel@tonic-gate  */
6700Sstevel@tonic-gate static void
handle_acl(char * name,o_mode_t group_clear_bits,o_mode_t group_set_bits)6710Sstevel@tonic-gate handle_acl(char *name, o_mode_t group_clear_bits, o_mode_t group_set_bits)
6720Sstevel@tonic-gate {
6730Sstevel@tonic-gate 	int aclcnt, n;
6740Sstevel@tonic-gate 	aclent_t *aclp, *tp;
6750Sstevel@tonic-gate 	o_mode_t newperm;
676789Sahrens 	/*
677789Sahrens 	 * if this file system support ace_t acl's
678789Sahrens 	 * then simply return since we don't have an
679789Sahrens 	 * acl mask to deal with
680789Sahrens 	 */
681789Sahrens 	if (pathconf(name, _PC_ACL_ENABLED) == _ACL_ACE_ENABLED)
682789Sahrens 		return;
6830Sstevel@tonic-gate 	if ((aclcnt = acl(name, GETACLCNT, 0, NULL)) <= MIN_ACL_ENTRIES)
6840Sstevel@tonic-gate 		return;	/* it's just a trivial acl; no need to change it */
6850Sstevel@tonic-gate 	if ((aclp = (aclent_t *)malloc((sizeof (aclent_t)) * aclcnt))
6860Sstevel@tonic-gate 	    == NULL) {
6870Sstevel@tonic-gate 		perror("chmod");
6880Sstevel@tonic-gate 		exit(2);
6890Sstevel@tonic-gate 	}
6900Sstevel@tonic-gate 
6910Sstevel@tonic-gate 	if (acl(name, GETACL, aclcnt, aclp) < 0) {
6920Sstevel@tonic-gate 		free(aclp);
6930Sstevel@tonic-gate 		(void) fprintf(stderr, "chmod: ");
6940Sstevel@tonic-gate 		perror(name);
6950Sstevel@tonic-gate 		return;
6960Sstevel@tonic-gate 	}
6970Sstevel@tonic-gate 	for (tp = aclp, n = aclcnt; n--; tp++) {
6980Sstevel@tonic-gate 		if (tp->a_type == GROUP_OBJ) {
6990Sstevel@tonic-gate 			newperm = tp->a_perm;
7000Sstevel@tonic-gate 			if (group_clear_bits != 0)
7010Sstevel@tonic-gate 				newperm &= ~group_clear_bits;
7020Sstevel@tonic-gate 			if (group_set_bits != 0)
7030Sstevel@tonic-gate 				newperm |= group_set_bits;
7040Sstevel@tonic-gate 			if (newperm != tp->a_perm) {
7050Sstevel@tonic-gate 				tp->a_perm = newperm;
7060Sstevel@tonic-gate 				if (acl(name, SETACL, aclcnt, aclp)
7070Sstevel@tonic-gate 				    < 0) {
7080Sstevel@tonic-gate 					(void) fprintf(stderr, "chmod: ");
7090Sstevel@tonic-gate 					perror(name);
7100Sstevel@tonic-gate 				}
7110Sstevel@tonic-gate 			}
7120Sstevel@tonic-gate 			break;
7130Sstevel@tonic-gate 		}
7140Sstevel@tonic-gate 	}
7150Sstevel@tonic-gate 	free(aclp);
7160Sstevel@tonic-gate }
717789Sahrens 
718789Sahrens static int
doacl(char * file,struct stat * st,acl_args_t * acl_args)719789Sahrens doacl(char *file, struct stat *st, acl_args_t *acl_args)
720789Sahrens {
721789Sahrens 	acl_t *aclp;
722789Sahrens 	acl_t *set_aclp;
723789Sahrens 	int error = 0;
724789Sahrens 	void *to, *from;
725789Sahrens 	int len;
726789Sahrens 	int isdir;
727789Sahrens 	isdir = S_ISDIR(st->st_mode);
728789Sahrens 
729789Sahrens 	error = acl_get(file, 0, &aclp);
730789Sahrens 
731789Sahrens 	if (error != 0) {
732789Sahrens 		errmsg(1, 1, "%s\n", acl_strerror(error));
733789Sahrens 		return (1);
734789Sahrens 	}
735789Sahrens 	switch (acl_args->acl_action) {
736789Sahrens 	case ACL_ADD:
737789Sahrens 		if ((error = acl_addentries(aclp,
7385331Samw 		    acl_args->acl_aclp, acl_args->acl_slot)) != 0) {
7395331Samw 			errmsg(1, 1, "%s\n", acl_strerror(error));
7405331Samw 			acl_free(aclp);
7415331Samw 			return (1);
742789Sahrens 		}
743789Sahrens 		set_aclp = aclp;
744789Sahrens 		break;
745789Sahrens 	case ACL_SLOT_DELETE:
746789Sahrens 		if (acl_args->acl_slot + 1 > aclp->acl_cnt) {
747789Sahrens 			errmsg(1, 1,
748789Sahrens 			    gettext("Invalid slot specified for removal\n"));
749789Sahrens 			acl_free(aclp);
750789Sahrens 			return (1);
751789Sahrens 		}
752789Sahrens 
753789Sahrens 		if (acl_args->acl_slot == 0 && aclp->acl_cnt == 1) {
754789Sahrens 			errmsg(1, 1,
755789Sahrens 			    gettext("Can't remove all ACL "
756789Sahrens 			    "entries from a file\n"));
757789Sahrens 			acl_free(aclp);
758789Sahrens 			return (1);
759789Sahrens 		}
760789Sahrens 
761789Sahrens 		/*
762789Sahrens 		 * remove a single entry
763789Sahrens 		 *
764789Sahrens 		 * if last entry just adjust acl_cnt
765789Sahrens 		 */
766789Sahrens 
767789Sahrens 		if ((acl_args->acl_slot + 1) == aclp->acl_cnt)
768789Sahrens 			aclp->acl_cnt--;
769789Sahrens 		else {
770789Sahrens 			to = (char *)aclp->acl_aclp +
771789Sahrens 			    (acl_args->acl_slot * aclp->acl_entry_size);
772789Sahrens 			from = (char *)to + aclp->acl_entry_size;
773789Sahrens 			len = (aclp->acl_cnt - acl_args->acl_slot - 1) *
774789Sahrens 			    aclp->acl_entry_size;
775789Sahrens 			(void) memmove(to, from, len);
776789Sahrens 			aclp->acl_cnt--;
777789Sahrens 		}
778789Sahrens 		set_aclp = aclp;
779789Sahrens 		break;
780789Sahrens 
781789Sahrens 	case ACL_DELETE:
782789Sahrens 		if ((error = acl_removeentries(aclp, acl_args->acl_aclp,
783789Sahrens 		    acl_args->acl_slot, ACL_REMOVE_ALL)) != 0) {
784789Sahrens 			errmsg(1, 1, "%s\n", acl_strerror(error));
785789Sahrens 			acl_free(aclp);
786789Sahrens 			return (1);
787789Sahrens 		}
788789Sahrens 
789789Sahrens 		if (aclp->acl_cnt == 0) {
790789Sahrens 			errmsg(1, 1,
791789Sahrens 			    gettext("Can't remove all ACL "
792789Sahrens 			    "entries from a file\n"));
793789Sahrens 			acl_free(aclp);
794789Sahrens 			return (1);
795789Sahrens 		}
796789Sahrens 
797789Sahrens 		set_aclp = aclp;
798789Sahrens 		break;
799789Sahrens 	case ACL_REPLACE:
800789Sahrens 		if (acl_args->acl_slot >= 0)  {
801789Sahrens 			error = acl_modifyentries(aclp, acl_args->acl_aclp,
802789Sahrens 			    acl_args->acl_slot);
803789Sahrens 			if (error) {
804789Sahrens 				errmsg(1, 1, "%s\n", acl_strerror(error));
805789Sahrens 				acl_free(aclp);
806789Sahrens 				return (1);
807789Sahrens 			}
808789Sahrens 			set_aclp = aclp;
809789Sahrens 		} else {
810789Sahrens 			set_aclp = acl_args->acl_aclp;
811789Sahrens 		}
812789Sahrens 		break;
813789Sahrens 	case ACL_STRIP:
814789Sahrens 		error = acl_strip(file, st->st_uid, st->st_gid, st->st_mode);
815789Sahrens 		if (error) {
816789Sahrens 			errmsg(1, 1, "%s\n", acl_strerror(error));
817789Sahrens 			return (1);
818789Sahrens 		}
819789Sahrens 		acl_free(aclp);
820789Sahrens 		return (0);
821789Sahrens 		/*NOTREACHED*/
822789Sahrens 	default:
823789Sahrens 		errmsg(1, 0, gettext("Unknown ACL action requested\n"));
824789Sahrens 		return (1);
825789Sahrens 		break;
826789Sahrens 	}
827789Sahrens 	error = acl_check(set_aclp, isdir);
828789Sahrens 
829789Sahrens 	if (error) {
830789Sahrens 		errmsg(1, 0, "%s\n%s", acl_strerror(error),
831789Sahrens 		    gettext("See chmod(1) for more information on "
832789Sahrens 		    "valid ACL syntax\n"));
833789Sahrens 		return (1);
834789Sahrens 	}
835789Sahrens 	if ((error = acl_set(file, set_aclp)) != 0) {
836789Sahrens 			errmsg(1, 0, gettext("Failed to set ACL: %s\n"),
837789Sahrens 			    acl_strerror(error));
838789Sahrens 			acl_free(aclp);
839789Sahrens 			return (1);
840789Sahrens 	}
841789Sahrens 	acl_free(aclp);
842789Sahrens 	return (0);
843789Sahrens }
8445331Samw 
8455331Samw /*
8465331Samw  * Prints out the attributes in their verbose form:
8475331Samw  *	'{'[["no"]<attribute-name>][,["no"]<attribute-name>]...'}'
8485331Samw  * similar to output of ls -/v.
8495331Samw  */
8505331Samw static void
print_nvlist(nvlist_t * attr_nvlist)8515331Samw print_nvlist(nvlist_t *attr_nvlist)
8525331Samw {
8535331Samw 	int		firsttime = 1;
8545331Samw 	boolean_t	value;
8555331Samw 	nvlist_t	*lptr = attr_nvlist;
8565331Samw 	nvpair_t	*pair = NULL;
8575331Samw 
8585331Samw 	(void) fprintf(stderr, "\t%c", LEFTBRACE);
8595331Samw 	while (pair = nvlist_next_nvpair(lptr, pair)) {
8605331Samw 		if (nvpair_value_boolean_value(pair, &value) == 0) {
8615331Samw 			(void) fprintf(stderr, "%s%s%s",
8625331Samw 			    firsttime ? "" : A_SEP_TOK,
8635331Samw 			    (value == A_SET_VAL) ? "" : "no",
8645331Samw 			    nvpair_name(pair));
8655331Samw 			firsttime = 0;
8665331Samw 		} else {
8675331Samw 			(void) fprintf(stderr, gettext(
8685331Samw 			    "<error retrieving attributes: %s>"),
8695331Samw 			    strerror(errno));
8705331Samw 			break;
8715331Samw 		}
8725331Samw 	}
8735331Samw 	(void) fprintf(stderr, "%c\n", RIGHTBRACE);
8745331Samw }
8755331Samw 
8765331Samw /*
8775331Samw  * Add an attribute name and boolean value to an nvlist if an action is to be
8785331Samw  * performed for that attribute.  The nvlist will be used later to set all the
8795331Samw  * attributes in the nvlist in one operation through a call to setattrat().
8805331Samw  *
8815331Samw  * If a set operation ('+') was specified, then a boolean representation of the
8825331Samw  * attribute's value will be added to the nvlist for that attribute name.  If an
8835331Samw  * inverse operation ('-') was specified, then a boolean representation of the
8845331Samw  * inverse of the attribute's value will be added to the nvlist for that
8855331Samw  * attribute name.
8865331Samw  *
8875331Samw  * Returns an nvlist of attribute name and boolean value pairs if there are
8885331Samw  * attribute actions to be performed, otherwise returns NULL.
8895331Samw  */
8905331Samw static nvlist_t *
set_attrs_nvlist(char * attractptr,int numofattrs)8915331Samw set_attrs_nvlist(char *attractptr, int numofattrs)
8925331Samw {
8935331Samw 	int		attribute_set = 0;
8945331Samw 	f_attr_t	i;
8955331Samw 	nvlist_t	*attr_nvlist;
8965331Samw 
8975331Samw 	if (nvlist_alloc(&attr_nvlist, NV_UNIQUE_NAME, 0) != 0) {
8985331Samw 		perror("chmod");
8995331Samw 		exit(2);
9005331Samw 	}
9015331Samw 
9025331Samw 	for (i = 0; i < numofattrs; i++) {
9035331Samw 		if (attractptr[i] != '\0') {
9045331Samw 			if ((nvlist_add_boolean_value(attr_nvlist,
9055331Samw 			    attr_to_name(i),
9065331Samw 			    (attractptr[i] == A_SET_OP))) != 0) {
9075331Samw 				errmsg(1, 2, gettext(
9085331Samw 				    "unable to propagate attribute names and"
9095331Samw 				    "values: %s\n"), strerror(errno));
9105331Samw 			} else {
9115331Samw 				attribute_set = 1;
9125331Samw 			}
9135331Samw 		}
9145331Samw 	}
9155331Samw 	return (attribute_set ? attr_nvlist : NULL);
9165331Samw }
9175331Samw 
9185331Samw /*
9195331Samw  * Set the attributes of file, or if specified, of the named attribute file,
9205331Samw  * attrname.  Build an nvlist of attribute names and values and call setattrat()
9215331Samw  * to set the attributes in one operation.
9225331Samw  *
9235331Samw  * Returns 0 if successful, otherwise returns 1.
9245331Samw  */
9255331Samw static int
set_file_attrs(char * file,char * attrname,nvlist_t * attr_nvlist)9265331Samw set_file_attrs(char *file, char *attrname, nvlist_t *attr_nvlist)
9275331Samw {
9285331Samw 	int	rc;
9295331Samw 	char	*filename;
9305331Samw 
9315331Samw 	if (attrname != NULL) {
9325331Samw 		filename = attrname;
9335331Samw 	} else {
9345331Samw 		filename = basename(file);
9355331Samw 	}
9365331Samw 
9375331Samw 	if ((rc = setattrat(AT_FDCWD, XATTR_VIEW_READWRITE, filename,
9385331Samw 	    attr_nvlist)) != 0) {
9395331Samw 		char *emsg;
9405331Samw 		switch (errno) {
9415331Samw 		case EINVAL:
9425331Samw 			emsg = gettext("not supported");
9435331Samw 			break;
9445331Samw 		case EPERM:
9455331Samw 			emsg = gettext("not privileged");
9465331Samw 			break;
9475331Samw 		default:
9485331Samw 			emsg = strerror(rc);
9495331Samw 		}
9505331Samw 		errmsg(1, 0, gettext(
9515331Samw 		    "cannot set the following attributes on "
9525331Samw 		    "%s%s%s%s: %s\n"),
9535331Samw 		    (attrname == NULL) ? "" : gettext("attribute "),
9545331Samw 		    (attrname == NULL) ? "" : attrname,
9555331Samw 		    (attrname == NULL) ? "" : gettext(" of "),
9565331Samw 		    file, emsg);
9575331Samw 		print_nvlist(attr_nvlist);
9585331Samw 	}
9595331Samw 
9605331Samw 	return (rc);
9615331Samw }
9625331Samw 
9635331Samw static int
save_cwd(void)9645331Samw save_cwd(void)
9655331Samw {
9665331Samw 	return (open(".", O_RDONLY));
9675331Samw }
9685331Samw 
9695331Samw static void
rest_cwd(int cwd)9705331Samw rest_cwd(int cwd)
9715331Samw {
9725331Samw 	if (cwd != -1) {
9735331Samw 		if (fchdir(cwd) != 0) {
9745331Samw 			errmsg(1, 1, gettext(
9755331Samw 			    "can't change to current working directory\n"));
9765331Samw 		}
9775331Samw 		(void) close(cwd);
9785331Samw 	}
9795331Samw }
9805331Samw 
9815331Samw /*
9825331Samw  * Returns 1 if filename is a system attribute file, otherwise
9835331Samw  * returns 0.
9845331Samw  */
9855331Samw static int
is_sattr(char * filename)9865331Samw is_sattr(char *filename)
9875331Samw {
9885331Samw 	return (sysattr_type(filename) != _NOT_SATTR);
9895331Samw }
9905331Samw 
9915331Samw /*
9925331Samw  * Perform the action on the specified named attribute file for the file
9935331Samw  * associated with the input file descriptor.  If the named attribute file
9945331Samw  * is "*", then the action is to be performed on all the named attribute files
9955331Samw  * of the file associated with the input file descriptor.
9965331Samw  */
9975331Samw static int
set_named_attrs(char * file,int parentfd,char * attrname,nvlist_t * attr_nvlist)9985331Samw set_named_attrs(char *file, int parentfd, char *attrname, nvlist_t *attr_nvlist)
9995331Samw {
10005331Samw 	int		dirfd;
10015331Samw 	int		error = 0;
10025331Samw 	DIR		*dirp = NULL;
10035331Samw 	struct dirent	*dp;
10045331Samw 	struct stat	st;
10055331Samw 
10065331Samw 	if ((attrname == NULL) || (strcmp(attrname, "*") != 0)) {
10075331Samw 		/*
10085331Samw 		 * Make sure the named attribute exists and extended system
10095331Samw 		 * attributes are supported on the underlying file system.
10105331Samw 		 */
10115331Samw 		if (attrname != NULL) {
10125331Samw 			if (fstatat(parentfd, attrname, &st,
10135331Samw 			    AT_SYMLINK_NOFOLLOW) < 0) {
10145331Samw 				errmsg(2, 0, gettext(
10155331Samw 				    "can't access attribute %s of %s\n"),
10165331Samw 				    attrname, file);
10175331Samw 				return (1);
10185331Samw 			}
10195331Samw 			if (sysattr_support(attrname, _PC_SATTR_ENABLED) != 1) {
10205331Samw 				errmsg(1, 0, gettext(
10215331Samw 				    "extended system attributes not supported "
10225331Samw 				    "for attribute %s of %s\n"),
10235331Samw 				    attrname, file);
10245331Samw 				return (1);
10255331Samw 			}
10265331Samw 		}
10275331Samw 
10285331Samw 		error = set_file_attrs(file, attrname, attr_nvlist);
10295331Samw 
10305331Samw 	} else {
10315331Samw 		if (((dirfd = dup(parentfd)) == -1) ||
10325331Samw 		    ((dirp = fdopendir(dirfd)) == NULL)) {
10335331Samw 			errmsg(1, 0, gettext(
10345331Samw 			    "cannot open dir pointer of file %s\n"), file);
10355331Samw 			if (dirfd > 0) {
10365331Samw 				(void) close(dirfd);
10375331Samw 			}
10385331Samw 			return (1);
10395331Samw 		}
10405331Samw 
10415331Samw 		while (dp = readdir(dirp)) {
10425331Samw 			/*
10435331Samw 			 * Process all extended attribute files except
10445331Samw 			 * ".", "..", and extended system attribute files.
10455331Samw 			 */
10465331Samw 			if ((strcmp(dp->d_name, ".") == 0) ||
10475331Samw 			    (strcmp(dp->d_name, "..") == 0) ||
10485331Samw 			    is_sattr(dp->d_name)) {
10495331Samw 				continue;
10505331Samw 			}
10515331Samw 
10525331Samw 			if (set_named_attrs(file, parentfd, dp->d_name,
10535331Samw 			    attr_nvlist) != 0) {
10545331Samw 				error++;
10555331Samw 			}
10565331Samw 		}
10575331Samw 		if (dirp != NULL) {
10585331Samw 			(void) closedir(dirp);
10595331Samw 		}
10605331Samw 	}
10615331Samw 
10625331Samw 	return ((error == 0) ? 0 : 1);
10635331Samw }
10645331Samw 
10655331Samw /*
10665331Samw  * Set the attributes of the specified file, or if specified with -@ on the
10675331Samw  * command line, the specified named attributes of the specified file.
10685331Samw  *
10695331Samw  * Returns 0 if successful, otherwise returns 1.
10705331Samw  */
10715331Samw static int
set_attrs(char * file,attr_name_t * attrnames,nvlist_t * attr_nvlist)10725331Samw set_attrs(char *file, attr_name_t *attrnames, nvlist_t *attr_nvlist)
10735331Samw {
10745331Samw 	char		*parentd;
10755331Samw 	char		*tpath = NULL;
10765331Samw 	int		cwd;
10775331Samw 	int		error = 0;
10785331Samw 	int		parentfd;
10795331Samw 	attr_name_t	*tattr = attrnames;
10805331Samw 
10815331Samw 	if (attr_nvlist == NULL) {
10825331Samw 		return (0);
10835331Samw 	}
10845331Samw 
10855331Samw 	if (sysattr_support(file, _PC_SATTR_ENABLED) != 1) {
10865331Samw 		errmsg(1, 0, gettext(
10875331Samw 		    "extended system attributes not supported for %s\n"), file);
10885331Samw 		return (1);
10895331Samw 	}
10905331Samw 
10915331Samw 	/*
10925331Samw 	 * Open the parent directory and change into it before attempting
10935331Samw 	 * to set the attributes of the file.
10945331Samw 	 */
10955331Samw 	if (attrnames == NULL) {
10965331Samw 		tpath = strdup(file);
10975331Samw 		parentd = dirname(tpath);
10985331Samw 		parentfd = open(parentd, O_RDONLY);
10995331Samw 	} else {
11005331Samw 		parentfd = attropen(file, ".", O_RDONLY);
11015331Samw 	}
11025331Samw 	if (parentfd == -1) {
11035331Samw 		errmsg(1, 0, gettext(
11045331Samw 		    "cannot open attribute directory of %s\n"), file);
11055331Samw 		if (tpath != NULL) {
11065331Samw 			free(tpath);
11075331Samw 		}
11085331Samw 		return (1);
11095331Samw 	}
11105331Samw 
11115331Samw 	if ((cwd = save_cwd()) < 0) {
11125331Samw 		errmsg(1, 1, gettext(
11135331Samw 		    "can't get current working directory\n"));
11145331Samw 	}
11155331Samw 	if (fchdir(parentfd) != 0) {
11165331Samw 		errmsg(1, 0, gettext(
11175331Samw 		    "can't change to parent %sdirectory of %s\n"),
11185331Samw 		    (attrnames == NULL) ? "" : gettext("attribute "), file);
11195331Samw 		(void) close(cwd);
11205331Samw 		(void) close(parentfd);
11215331Samw 		if (tpath != NULL) {
11225331Samw 			free(tpath);
11235331Samw 		}
11245331Samw 		return (1);
11255331Samw 	}
11265331Samw 
11275331Samw 	/*
11285331Samw 	 * If no named attribute file names were provided on the command line
11295331Samw 	 * then set the attributes of the base file, otherwise, set the
11305331Samw 	 * attributes for each of the named attribute files specified.
11315331Samw 	 */
11325331Samw 	if (attrnames == NULL) {
11335331Samw 		error = set_named_attrs(file, parentfd, NULL, attr_nvlist);
11345331Samw 		free(tpath);
11355331Samw 	} else {
11365331Samw 		while (tattr != NULL) {
11375331Samw 			if (set_named_attrs(file, parentfd, tattr->name,
11385331Samw 			    attr_nvlist) != 0) {
11395331Samw 				error++;
11405331Samw 			}
11415331Samw 			tattr = tattr->next;
11425331Samw 		}
11435331Samw 	}
11445331Samw 	(void) close(parentfd);
11455331Samw 	rest_cwd(cwd);
11465331Samw 
11475331Samw 	return ((error == 0) ? 0 : 1);
11485331Samw }
11495331Samw 
11505331Samw /*
11515331Samw  * Prints the attributes in either the compact or verbose form indicated
11525331Samw  * by flag.
11535331Samw  */
11545331Samw static void
print_attrs(int flag)11555331Samw print_attrs(int flag)
11565331Samw {
11575331Samw 	f_attr_t	i;
11585331Samw 	static int	numofattrs;
11595331Samw 	int		firsttime = 1;
11605331Samw 
11615331Samw 	numofattrs = attr_count();
11625331Samw 
11635331Samw 	(void) fprintf(stderr, gettext("\t["));
11645331Samw 	for (i = 0; i < numofattrs; i++) {
11655331Samw 		if ((attr_to_xattr_view(i) != XATTR_VIEW_READWRITE) ||
11665331Samw 		    (attr_to_data_type(i) != DATA_TYPE_BOOLEAN_VALUE)) {
11675331Samw 			continue;
11685331Samw 		}
11695331Samw 		(void) fprintf(stderr, "%s%s",
11705331Samw 		    (firsttime == 1) ? "" : gettext("|"),
11715331Samw 		    (flag == ATTR_OPTS) ? attr_to_option(i) : attr_to_name(i));
11725331Samw 		firsttime = 0;
11735331Samw 	}
11745331Samw 	(void) fprintf(stderr, gettext("]\n"));
11755331Samw }
11765331Samw 
11775331Samw /*
11785331Samw  * Record what action should be taken on the specified attribute. Only boolean
11795331Samw  * read-write attributes can be manipulated.
11805331Samw  *
11815331Samw  * Returns 0 if successful, otherwise returns 1.
11825331Samw  */
11835331Samw static int
set_attr_args(f_attr_t attr,char action,char * attractptr)11845331Samw set_attr_args(f_attr_t attr, char action, char *attractptr)
11855331Samw {
11865331Samw 	if ((attr_to_xattr_view(attr) == XATTR_VIEW_READWRITE) &&
11875331Samw 	    (attr_to_data_type(attr) == DATA_TYPE_BOOLEAN_VALUE)) {
11885331Samw 		attractptr[attr] = action;
11895331Samw 		return (0);
11905331Samw 	}
11915331Samw 	return (1);
11925331Samw }
11935331Samw 
11945331Samw /*
11955331Samw  * Parses the entry and assigns the appropriate action (either '+' or '-' in
11965331Samw  * attribute's position in the character array pointed to by attractptr, where
11975331Samw  * upon exit, attractptr is positional and the value of each character specifies
11985331Samw  * whether to set (a '+'), clear (a '-'), or leave untouched (a '\0') the
11995331Samw  * attribute value.
12005331Samw  *
12015331Samw  * If the entry is an attribute name, then the A_SET_OP action is to be
12025331Samw  * performed for this attribute.  If the entry is an attribute name proceeded
12035331Samw  * with "no", then the A_INVERSE_OP action is to be performed for this
12045331Samw  * attribute.  If the entry is one or more attribute option letters, then step
12055331Samw  * through each of the option letters marking the action to be performed for
12065331Samw  * each of the attributes associated with the letter as A_SET_OP.
12075331Samw  *
12085331Samw  * Returns 0 if the entry was a valid attribute(s) and the action to be
12095331Samw  * performed on that attribute(s) has been recorded, otherwise returns 1.
12105331Samw  */
12115331Samw static int
parse_entry(char * entry,char action,char atype,int len,char * attractptr)12125331Samw parse_entry(char *entry, char action, char atype, int len, char *attractptr)
12135331Samw {
12145331Samw 	char		aopt[2] = {'\0', '\0'};
12155331Samw 	char		*aptr;
12165331Samw 	f_attr_t	attr;
12175331Samw 
12185331Samw 	if (atype == A_VERBOSE_TYPE) {
12195331Samw 		if ((attr = name_to_attr(entry)) != F_ATTR_INVAL) {
12205331Samw 			return (set_attr_args(attr,
12215331Samw 			    (action == A_REPLACE_OP) ? A_SET_OP : action,
12225331Samw 			    attractptr));
12235331Samw 		} else if ((len > 2) && (strncmp(entry, "no", 2) == 0) &&
12245331Samw 		    ((attr = name_to_attr(entry + 2)) != F_ATTR_INVAL)) {
12255331Samw 			return (set_attr_args(attr, ((action == A_REPLACE_OP) ||
12265331Samw 			    (action == A_SET_OP)) ? A_INVERSE_OP : A_SET_OP,
12275331Samw 			    attractptr));
12285331Samw 		} else {
12295331Samw 			return (1);
12305331Samw 		}
12315331Samw 	} else if (atype == A_COMPACT_TYPE) {
12325331Samw 		for (aptr = entry; *aptr != '\0'; aptr++) {
12335331Samw 			*aopt = *aptr;
12345331Samw 			/*
12355331Samw 			 * The output of 'ls' can be used as the attribute mode
12365331Samw 			 * specification for chmod.  This output can contain a
12375331Samw 			 * hypen ('-') for each attribute that is not set.  If
12385331Samw 			 * so, ignore them.  If a replace action is being
12395331Samw 			 * performed, then all attributes that don't have an
12405331Samw 			 * action set here, will be cleared down the line.
12415331Samw 			 */
12425331Samw 			if (*aptr == '-') {
12435331Samw 				continue;
12445331Samw 			}
12455331Samw 			if (set_attr_args(option_to_attr(aopt),
12465331Samw 			    (action == A_REPLACE_OP) ? A_SET_OP : action,
12475331Samw 			    attractptr) != 0) {
12485331Samw 				return (1);
12495331Samw 			}
12505331Samw 		}
12515331Samw 		return (0);
12525331Samw 	}
12535331Samw 	return (1);
12545331Samw }
12555331Samw 
12565331Samw /*
12575331Samw  * Parse the attribute specification, aoptsstr.  Upon completion, attr_nvlist
12585331Samw  * will point to an nvlist which contains pairs of attribute names and values
12595331Samw  * to be set; attr_nvlist will be NULL if it is a no-op.
12605331Samw  *
12615331Samw  * The attribute specification format is
12625331Samw  *	S[oper]attr_type[attribute_list]
12635331Samw  * where oper is
12645331Samw  *	+	set operation of specified attributes in attribute list.
12655331Samw  *		This is the default operation.
12665331Samw  *	-	inverse operation of specified attributes in attribute list
12675331Samw  *	=	replace operation of all attributes.  All attribute operations
12685331Samw  *		depend on those specified in the attribute list.  Attributes
12695331Samw  *		not specified in the attribute list will be cleared.
12705331Samw  * where attr_type is
12715331Samw  *	c	compact type.  Each entry in the attribute list is a character
12725331Samw  *		option representing an associated attribute name.
12735331Samw  *	v	verbose type.  Each entry in the attribute list is an
12745331Samw  *		an attribute name which can optionally be preceeded with "no"
12755331Samw  *		(to imply the attribute should be cleared).
12765331Samw  *	a	all attributes type.  The oper should be applied to all
12775331Samw  *		read-write boolean system attributes.  No attribute list should
12785331Samw  *		be specified after an 'a' attribute type.
12795331Samw  *
12805331Samw  * Returns 0 if aoptsstr contained a valid attribute specification,
12815331Samw  * otherwise, returns 1.
12825331Samw  */
12835331Samw static int
parse_attr_args(char * aoptsstr,sec_args_t ** sec_args)12845331Samw parse_attr_args(char *aoptsstr, sec_args_t **sec_args)
12855331Samw {
12865331Samw 	char		action;
12875331Samw 	char		*attractptr;
12885331Samw 	char		atype;
12895331Samw 	char		*entry;
12905331Samw 	char		*eptr;
12915331Samw 	char		*nextattr;
12925331Samw 	char		*nextentry;
12935331Samw 	char		*subentry;
12945331Samw 	char		*teptr;
12955331Samw 	char		tok[] = {'\0', '\0'};
12965331Samw 	int		len;
12975331Samw 	f_attr_t	i;
12985331Samw 	int		numofattrs;
12995331Samw 
13005331Samw 	if ((*aoptsstr != 'S') || (*(aoptsstr + 1) == '\0')) {
13015331Samw 		return (1);
13025331Samw 	}
13035331Samw 
13045331Samw 	if ((eptr = strdup(aoptsstr + 1)) == NULL) {
13055331Samw 		perror("chmod");
13065331Samw 		exit(2);
13075331Samw 	}
13085331Samw 	entry = eptr;
13095331Samw 
13105331Samw 	/*
13115331Samw 	 * Create a positional character array to determine a single attribute
13125331Samw 	 * operation to be performed, where each index represents the system
13135331Samw 	 * attribute affected, and it's value in the array represents the action
13145331Samw 	 * to be performed, i.e., a value of '+' means to set the attribute, a
13155331Samw 	 * value of '-' means to clear the attribute, and a value of '\0' means
13165331Samw 	 * to leave the attribute untouched.  Initially, this positional
13175331Samw 	 * character array is all '\0's, representing a no-op.
13185331Samw 	 */
13195331Samw 	if ((numofattrs = attr_count()) < 1) {
13205331Samw 		errmsg(1, 1, gettext("system attributes not supported\n"));
13215331Samw 	}
13225331Samw 
13235331Samw 	if ((attractptr = calloc(numofattrs, sizeof (char))) == NULL) {
13245331Samw 		perror("chmod");
13255331Samw 		exit(2);
13265331Samw 	}
13275331Samw 
13285331Samw 	if ((*sec_args = malloc(sizeof (sec_args_t))) == NULL) {
13295331Samw 		perror("chmod");
13305331Samw 		exit(2);
13315331Samw 	}
13325331Samw 	(*sec_args)->sec_type = SEC_ATTR;
13335331Samw 	(*sec_args)->sec_attrs = NULL;
13345331Samw 
13355331Samw 	/* Parse each attribute operation within the attribute specification. */
13365331Samw 	while ((entry != NULL) && (*entry != '\0')) {
13375331Samw 		action = A_SET_OP;
13385331Samw 		atype = '\0';
13395331Samw 
13405331Samw 		/* Get the operator. */
13415331Samw 		switch (*entry) {
13425331Samw 		case A_SET_OP:
13435331Samw 		case A_INVERSE_OP:
13445331Samw 		case A_REPLACE_OP:
13455331Samw 			action = *entry++;
13465331Samw 			break;
13475331Samw 		case A_COMPACT_TYPE:
13485331Samw 		case A_VERBOSE_TYPE:
13495331Samw 		case A_ALLATTRS_TYPE:
13505331Samw 			atype = *entry++;
13515331Samw 			action = A_SET_OP;
13525331Samw 			break;
13535331Samw 		default:
13545331Samw 			break;
13555331Samw 		}
13565331Samw 
13575331Samw 		/* An attribute type must be specified. */
13585331Samw 		if (atype == '\0') {
13595331Samw 			if ((*entry == A_COMPACT_TYPE) ||
13605331Samw 			    (*entry == A_VERBOSE_TYPE) ||
13615331Samw 			    (*entry == A_ALLATTRS_TYPE)) {
13625331Samw 				atype = *entry++;
13635331Samw 			} else {
13645331Samw 				return (1);
13655331Samw 			}
13665331Samw 		}
13675331Samw 
13685331Samw 		/* Get the attribute specification separator. */
13695331Samw 		if (*entry == LEFTBRACE) {
13705331Samw 			*tok = RIGHTBRACE;
13715331Samw 			entry++;
13725331Samw 		} else {
13735331Samw 			*tok = A_SEP;
13745331Samw 		}
13755331Samw 
13765331Samw 		/* Get the attribute operation */
13775331Samw 		if ((nextentry = strpbrk(entry, tok)) != NULL) {
13785331Samw 			*nextentry = '\0';
13795331Samw 			nextentry++;
13805331Samw 		}
13815331Samw 
13825331Samw 		/* Check for a no-op */
13835331Samw 		if ((*entry == '\0') && (atype != A_ALLATTRS_TYPE) &&
13845331Samw 		    (action != A_REPLACE_OP)) {
13855331Samw 			entry = nextentry;
13865331Samw 			continue;
13875331Samw 		}
13885331Samw 
13895331Samw 		/*
13905331Samw 		 * Step through the attribute operation, setting the
13915331Samw 		 * appropriate values for the specified attributes in the
13925331Samw 		 * character array, attractptr. A value of '+' will mean the
13935331Samw 		 * attribute is to be set, and a value of '-' will mean the
13945331Samw 		 * attribute is to be cleared.  If the value of an attribute
13955331Samw 		 * remains '\0', then no action is to be taken on that
13965331Samw 		 * attribute.  As multiple operations specified are
13975331Samw 		 * accumulated, a single attribute setting operation is
13985331Samw 		 * represented in attractptr.
13995331Samw 		 */
14005331Samw 		len = strlen(entry);
14015331Samw 		if ((*tok == RIGHTBRACE) || (action == A_REPLACE_OP) ||
14025331Samw 		    (atype == A_ALLATTRS_TYPE)) {
14035331Samw 
14045331Samw 			if ((action == A_REPLACE_OP) ||
14055331Samw 			    (atype == A_ALLATTRS_TYPE)) {
14065331Samw 				(void) memset(attractptr, '\0', numofattrs);
14075331Samw 			}
14085331Samw 
14095331Samw 			if (len > 0) {
14105331Samw 				if ((teptr = strdup(entry)) == NULL) {
14115331Samw 					perror("chmod");
14125331Samw 					exit(2);
14135331Samw 				}
14145331Samw 				subentry = teptr;
14155331Samw 				while (subentry != NULL) {
14165331Samw 					if ((nextattr = strpbrk(subentry,
14175331Samw 					    A_SEP_TOK)) != NULL) {
14185331Samw 						*nextattr = '\0';
14195331Samw 						nextattr++;
14205331Samw 					}
14215331Samw 					if (parse_entry(subentry, action,
14225331Samw 					    atype, len, attractptr) != 0) {
14235331Samw 						return (1);
14245331Samw 					}
14255331Samw 					subentry = nextattr;
14265331Samw 				}
14275331Samw 				free(teptr);
14285331Samw 			}
14295331Samw 
14305331Samw 			/*
14315331Samw 			 * If performing the replace action, record the
14325331Samw 			 * attributes and values for the rest of the
14335331Samw 			 * attributes that have not already been recorded,
14345331Samw 			 * otherwise record the specified action for all
14355331Samw 			 * attributes.  Note: set_attr_args() will only record
14365331Samw 			 * the attribute and action if it is a boolean
14375331Samw 			 * read-write attribute so we don't need to worry
14385331Samw 			 * about checking it here.
14395331Samw 			 */
14405331Samw 			if ((action == A_REPLACE_OP) ||
14415331Samw 			    (atype == A_ALLATTRS_TYPE)) {
14425331Samw 				for (i = 0; i < numofattrs; i++) {
14435331Samw 					if (attractptr[i] == A_UNDEF_OP) {
14445331Samw 						(void) set_attr_args(i,
14455331Samw 						    (action == A_SET_OP) ?
14465331Samw 						    A_SET_OP : A_INVERSE_OP,
14475331Samw 						    attractptr);
14485331Samw 					}
14495331Samw 				}
14505331Samw 			}
14515331Samw 
14525331Samw 		} else {
14535331Samw 			if (parse_entry(entry, action, atype, len,
14545331Samw 			    attractptr) != 0) {
14555331Samw 				return (1);
14565331Samw 			}
14575331Samw 		}
14585331Samw 		entry = nextentry;
14595331Samw 	}
14605331Samw 
14615331Samw 	/*
14625331Samw 	 * Populate an nvlist with attribute name and boolean value pairs
14635331Samw 	 * using the single attribute operation.
14645331Samw 	 */
14655331Samw 	(*sec_args)->sec_attrs = set_attrs_nvlist(attractptr, numofattrs);
14665331Samw 	free(attractptr);
14675331Samw 	free(eptr);
14685331Samw 
14695331Samw 	return (0);
14705331Samw }
1471