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