4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
41 * chmod option mode files
43 * mode is [ugoa][+-=][rwxXlstugo] or an octal number
44 * mode is [<+|->A[# <number] ]<aclspec>
46 * option is -R, -f, and -@
50 * Note that many convolutions are necessary
51 * due to the re-use of bits between locking
58 #include <sys/types.h>
63 #include <string.h> /* strerror() */
70 #include <libnvpair.h>
71 #include <libcmdutils.h>
81 static int mac
; /* Alternate to argc (for parseargs) */
82 static char **mav
; /* Alternate to argv (for parseargs) */
84 static char *ms
; /* Points to the mode argument */
88 #define ACL_SLOT_DELETE 3
93 #define RIGHTBRACE '}'
97 #define A_COMPACT_TYPE 'c'
98 #define A_VERBOSE_TYPE 'v'
99 #define A_ALLATTRS_TYPE 'a'
102 #define A_INVERSE_OP '-'
103 #define A_REPLACE_OP '='
104 #define A_UNDEF_OP '\0'
106 #define A_SET_TEXT "set"
107 #define A_INVERSE_TEXT "clear"
109 #define A_SET_VAL B_TRUE
110 #define A_CLEAR_VAL B_FALSE
115 #define sec_acls secptr.acls
116 #define sec_attrs secptr.attrs
118 typedef struct acl_args
{
130 chmod_sec_t sec_type
;
137 typedef struct attr_name
{
139 struct attr_name
*next
;
143 extern mode_t
newmode_common(char *ms
, mode_t new_mode
, mode_t umsk
,
144 char *file
, char *path
, o_mode_t
*group_clear_bits
,
145 o_mode_t
*group_set_bits
);
147 static int chmodr(char *dir
, char *path
, mode_t mode
, mode_t umsk
,
148 sec_args_t
*secp
, attr_name_t
*attrname
);
149 static int doacl(char *file
, struct stat
*st
, acl_args_t
*aclp
);
150 static int dochmod(char *name
, char *path
, mode_t umsk
, sec_args_t
*secp
,
151 attr_name_t
*attrnames
);
152 static void handle_acl(char *name
, o_mode_t group_clear_bits
,
153 o_mode_t group_set_bits
);
154 void errmsg(int severity
, int code
, char *format
, ...);
155 static void free_attr_names(attr_name_t
*attrnames
);
156 static void parseargs(int ac
, char *av
[]);
157 static int parse_acl_args(char *arg
, sec_args_t
**sec_args
);
158 static int parse_attr_args(char *arg
, sec_args_t
**sec_args
);
159 static void print_attrs(int flag
);
160 static int set_attrs(char *file
, attr_name_t
*attrnames
, nvlist_t
*attr_nvlist
);
161 static void usage(void);
164 main(int argc
, char *argv
[])
169 sec_args_t
*sec_args
= NULL
;
170 attr_name_t
*attrnames
= NULL
;
171 attr_name_t
*attrend
= NULL
;
174 (void) setlocale(LC_ALL
, "");
175 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
176 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
178 (void) textdomain(TEXT_DOMAIN
);
180 parseargs(argc
, argv
);
182 while ((c
= getopt(mac
, mav
, "Rf@:")) != EOF
) {
191 if (((tattr
= malloc(sizeof (attr_name_t
))) == NULL
) ||
192 ((tattr
->name
= strdup(optarg
)) == NULL
)) {
196 if (attrnames
== NULL
) {
198 attrnames
->next
= NULL
;
200 attrend
->next
= tattr
;
211 * Check for sufficient arguments
217 if ((mac
>= 2) && (mav
[0][0] == 'A')) {
218 if (attrnames
!= NULL
) {
219 free_attr_names(attrnames
);
222 if (parse_acl_args(*mav
, &sec_args
)) {
226 } else if ((mac
>= 2) && (mav
[0][0] == 'S')) {
227 if (parse_attr_args(*mav
, &sec_args
)) {
231 /* A no-op attribute operation was specified. */
232 } else if (sec_args
->sec_attrs
== NULL
) {
240 if (attrnames
!= NULL
) {
241 free_attr_names(attrnames
);
251 for (i
= 1; i
< mac
; i
++) {
252 status
+= dochmod(mav
[i
], mav
[i
], umsk
, sec_args
, attrnames
);
255 return (fflag
? 0 : status
);
259 free_attr_names(attr_name_t
*attrnames
)
261 attr_name_t
*attrnamesptr
= attrnames
;
264 while (attrnamesptr
!= NULL
) {
265 tptr
= attrnamesptr
->next
;
266 if (attrnamesptr
->name
!= NULL
) {
267 free(attrnamesptr
->name
);
274 dochmod(char *name
, char *path
, mode_t umsk
, sec_args_t
*secp
,
275 attr_name_t
*attrnames
)
277 static struct stat st
;
279 o_mode_t group_clear_bits
, group_set_bits
;
281 if (lstat(name
, &st
) < 0) {
282 errmsg(2, 0, gettext("can't access %s\n"), path
);
286 if ((st
.st_mode
& S_IFMT
) == S_IFLNK
) {
288 if (stat(name
, &st
) < 0) {
289 errmsg(2, 0, gettext("can't access %s\n"), path
);
294 /* Do not recurse if directory is object of symbolic link */
295 if (rflag
&& ((st
.st_mode
& S_IFMT
) == S_IFDIR
) && !linkflg
) {
296 return (chmodr(name
, path
, st
.st_mode
, umsk
, secp
, attrnames
));
300 if (secp
->sec_type
== SEC_ACL
) {
301 return (doacl(name
, &st
, secp
->sec_acls
));
302 } else if (secp
->sec_type
== SEC_ATTR
) {
303 return (set_attrs(name
, attrnames
, secp
->sec_attrs
));
308 if (chmod(name
, newmode_common(ms
, st
.st_mode
, umsk
, name
, path
,
309 &group_clear_bits
, &group_set_bits
)) == -1) {
310 errmsg(2, 0, gettext("can't change %s\n"), path
);
316 * If the group permissions of the file are being modified,
317 * make sure that the file's ACL (if it has one) is
318 * modified also, since chmod is supposed to apply group
319 * permissions changes to both the acl mask and the
320 * general group permissions.
322 if (group_clear_bits
|| group_set_bits
)
323 handle_acl(name
, group_clear_bits
, group_set_bits
);
329 chmodr(char *dir
, char *path
, mode_t mode
, mode_t umsk
, sec_args_t
*secp
,
330 attr_name_t
*attrnames
)
335 char savedir
[PATH_MAX
]; /* dir name to restore */
336 char currdir
[PATH_MAX
+1]; /* current dir name + '/' */
337 char parentdir
[PATH_MAX
+1]; /* parent dir name + '/' */
340 o_mode_t group_clear_bits
, group_set_bits
;
342 if (getcwd(savedir
, PATH_MAX
) == 0)
343 errmsg(2, 255, gettext("chmod: could not getcwd %s\n"),
347 * Change what we are given before doing it's contents
350 if (lstat(dir
, &st
) < 0) {
351 errmsg(2, 0, gettext("can't access %s\n"), path
);
354 if (secp
->sec_type
== SEC_ACL
) {
355 (void) doacl(dir
, &st
, secp
->sec_acls
);
356 } else if (secp
->sec_type
== SEC_ATTR
) {
357 (void) set_attrs(dir
, attrnames
, secp
->sec_attrs
);
361 } else if (chmod(dir
, newmode_common(ms
, mode
, umsk
, dir
, path
,
362 &group_clear_bits
, &group_set_bits
)) < 0) {
363 errmsg(2, 0, gettext("can't change %s\n"), path
);
367 * If the group permissions of the file are being modified,
368 * make sure that the file's ACL (if it has one) is
369 * modified also, since chmod is supposed to apply group
370 * permissions changes to both the acl mask and the
371 * general group permissions.
375 /* only necessary when not setting ACL or system attributes */
376 if (group_clear_bits
|| group_set_bits
)
377 handle_acl(dir
, group_clear_bits
, group_set_bits
);
380 if (chdir(dir
) < 0) {
381 errmsg(2, 0, "%s/%s: %s\n", savedir
, dir
, strerror(errno
));
384 if ((dirp
= opendir(".")) == NULL
) {
385 errmsg(2, 0, "%s\n", strerror(errno
));
391 * Save parent directory path before recursive chmod.
392 * We'll need this for error printing purposes. Add
393 * a trailing '/' to the path except in the case where
394 * the path is just '/'
397 if (strlcpy(parentdir
, path
, PATH_MAX
+ 1) >= PATH_MAX
+ 1) {
398 errmsg(2, 0, gettext("directory path name too long: %s\n"),
402 if (strcmp(path
, "/") != 0)
403 if (strlcat(parentdir
, "/", PATH_MAX
+ 1) >= PATH_MAX
+ 1) {
405 gettext("directory path name too long: %s/\n"),
411 for (dp
= readdir(dirp
); dp
!= NULL
; dp
= readdir(dirp
)) {
413 if (strcmp(dp
->d_name
, ".") == 0 || /* skip . and .. */
414 strcmp(dp
->d_name
, "..") == 0) {
417 if (strlcpy(currdir
, parentdir
, PATH_MAX
+ 1) >= PATH_MAX
+ 1) {
419 gettext("directory path name too long: %s\n"),
423 if (strlcat(currdir
, dp
->d_name
, PATH_MAX
+ 1)
426 gettext("directory path name too long: %s%s\n"),
427 currdir
, dp
->d_name
);
430 ecode
+= dochmod(dp
->d_name
, currdir
, umsk
, secp
, attrnames
);
432 (void) closedir(dirp
);
433 if (chdir(savedir
) < 0) {
434 errmsg(2, 255, gettext("can't change back to %s\n"), savedir
);
436 return (ecode
? 1 : 0);
441 errmsg(int severity
, int code
, char *format
, ...)
444 static char *msg
[] = {
451 va_start(ap
, format
);
454 * Always print error message if this is a fatal error (code != 0);
455 * otherwise, print message if fflag == 0 (no -f option specified)
457 if (!fflag
|| (code
!= 0)) {
458 (void) fprintf(stderr
,
459 "chmod: %s: ", gettext(msg
[severity
]));
460 (void) vfprintf(stderr
, format
, ap
);
466 exit(fflag
? 0 : code
);
472 (void) fprintf(stderr
, gettext(
473 "usage:\tchmod [-fR] <absolute-mode> file ...\n"));
475 (void) fprintf(stderr
, gettext(
476 "\tchmod [-fR] [-@ attribute] ... "
477 "S<attribute-operation> file ...\n"));
479 (void) fprintf(stderr
, gettext(
480 "\tchmod [-fR] <ACL-operation> file ...\n"));
482 (void) fprintf(stderr
, gettext(
483 "\tchmod [-fR] <symbolic-mode-list> file ...\n\n"));
485 (void) fprintf(stderr
, gettext(
486 "where \t<symbolic-mode-list> is a comma-separated list of\n"));
487 (void) fprintf(stderr
, gettext(
488 "\t[ugoa]{+|-|=}[rwxXlstugo]\n\n"));
490 (void) fprintf(stderr
, gettext(
491 "where \t<attribute-operation> is a comma-separated list of\n"
492 "\tone or more of the following\n"));
493 (void) fprintf(stderr
, gettext(
494 "\t[+|-|=]c[<compact-attribute-list>|{<compact-attribute-list>}]\n"
495 "\t[+|-|=]v[<verbose-attribute-setting>|"
496 "\'{\'<verbose-attribute-setting-list>\'}\']\n"
498 (void) fprintf(stderr
, gettext(
499 "where \t<compact-attribute-list> is a list of zero or more of\n"));
500 print_attrs(ATTR_OPTS
);
501 (void) fprintf(stderr
, gettext(
502 "where \t<verbose-attribute-setting> is one of\n"));
503 print_attrs(ATTR_NAMES
);
504 (void) fprintf(stderr
, gettext(
505 "\tand can be, optionally, immediately preceded by \"no\"\n\n"));
507 (void) fprintf(stderr
, gettext(
508 "where \t<ACL-operation> is one of the following\n"));
509 (void) fprintf(stderr
, gettext("\tA-<acl_specification>\n"));
510 (void) fprintf(stderr
, gettext("\tA[number]-\n"));
511 (void) fprintf(stderr
, gettext(
512 "\tA[number]{+|=}<acl_specification>\n"));
513 (void) fprintf(stderr
, gettext(
514 "where \t<acl-specification> is a comma-separated list of ACEs\n"));
518 * parseargs - generate getopt-friendly argument list for backwards
519 * compatibility with earlier Solaris usage (eg, chmod -w
522 * assumes the existence of a static set of alternates to argc and argv,
523 * (namely, mac, and mav[]).
528 parseargs(int ac
, char *av
[])
530 int i
; /* current argument */
531 int fflag
; /* arg list contains "--" */
532 size_t mav_num
; /* number of entries in mav[] */
535 * We add an extra argument slot, in case we need to jam a "--"
536 * argument into the list.
539 mav_num
= (size_t)ac
+2;
541 if ((mav
= calloc(mav_num
, sizeof (char *))) == NULL
) {
546 /* scan for the use of "--" in the argument list */
548 for (fflag
= i
= 0; i
< ac
; i
++) {
549 if (strcmp(av
[i
], "--") == 0)
553 /* process the arguments */
556 (av
[i
] != (char *)NULL
) && (av
[i
][0] != (char)NULL
);
558 if (!fflag
&& av
[i
][0] == '-') {
560 * If there is not already a "--" argument specified,
561 * and the argument starts with '-' but does not
562 * contain any of the official option letters, then it
563 * is probably a mode argument beginning with '-'.
564 * Force a "--" into the argument stream in front of
568 if ((strchr(av
[i
], 'R') == NULL
&&
569 strchr(av
[i
], 'f') == NULL
) &&
570 strchr(av
[i
], '@') == NULL
) {
571 if ((mav
[mac
++] = strdup("--")) == NULL
) {
578 if ((mav
[mac
++] = strdup(av
[i
])) == NULL
) {
584 mav
[mac
] = (char *)NULL
;
588 parse_acl_args(char *arg
, sec_args_t
**sec_args
)
590 acl_t
*new_acl
= NULL
;
594 acl_args_t
*new_acl_args
;
595 char *acl_spec
= NULL
;
601 slot
= strtol(&arg
[1], &end
, 10);
610 if (len
== 2 && arg
[0] == 'A' && arg
[1] == '-')
614 if (action
!= ACL_STRIP
) {
616 if (acl_spec
[0] == '\0') {
617 action
= ACL_SLOT_DELETE
;
619 } else if (arg
[1] != '-')
625 * Was slot specified?
629 action
= ACL_REPLACE
;
636 if ((action
== ACL_REPLACE
|| action
== ACL_ADD
) && acl_spec
[0] == '\0')
640 if (acl_parse(acl_spec
, &new_acl
)) {
645 new_acl_args
= malloc(sizeof (acl_args_t
));
646 if (new_acl_args
== NULL
)
649 new_acl_args
->acl_aclp
= new_acl
;
650 new_acl_args
->acl_slot
= slot
;
651 new_acl_args
->acl_action
= action
;
653 if ((*sec_args
= malloc(sizeof (sec_args_t
))) == NULL
) {
657 (*sec_args
)->sec_type
= SEC_ACL
;
658 (*sec_args
)->sec_acls
= new_acl_args
;
664 * This function is called whenever the group permissions of a file
665 * is being modified. According to the chmod(1) manpage, any
666 * change made to the group permissions must be applied to both
667 * the acl mask and the acl's GROUP_OBJ. The chmod(2) already
668 * set the mask, so this routine needs to make the same change
672 handle_acl(char *name
, o_mode_t group_clear_bits
, o_mode_t group_set_bits
)
678 * if this file system support ace_t acl's
679 * then simply return since we don't have an
680 * acl mask to deal with
682 if (pathconf(name
, _PC_ACL_ENABLED
) == _ACL_ACE_ENABLED
)
684 if ((aclcnt
= acl(name
, GETACLCNT
, 0, NULL
)) <= MIN_ACL_ENTRIES
)
685 return; /* it's just a trivial acl; no need to change it */
686 if ((aclp
= (aclent_t
*)malloc((sizeof (aclent_t
)) * aclcnt
))
692 if (acl(name
, GETACL
, aclcnt
, aclp
) < 0) {
694 (void) fprintf(stderr
, "chmod: ");
698 for (tp
= aclp
, n
= aclcnt
; n
--; tp
++) {
699 if (tp
->a_type
== GROUP_OBJ
) {
700 newperm
= tp
->a_perm
;
701 if (group_clear_bits
!= 0)
702 newperm
&= ~group_clear_bits
;
703 if (group_set_bits
!= 0)
704 newperm
|= group_set_bits
;
705 if (newperm
!= tp
->a_perm
) {
706 tp
->a_perm
= newperm
;
707 if (acl(name
, SETACL
, aclcnt
, aclp
)
709 (void) fprintf(stderr
, "chmod: ");
720 doacl(char *file
, struct stat
*st
, acl_args_t
*acl_args
)
728 isdir
= S_ISDIR(st
->st_mode
);
730 error
= acl_get(file
, 0, &aclp
);
733 errmsg(1, 0, "%s\n", acl_strerror(error
));
736 switch (acl_args
->acl_action
) {
738 if ((error
= acl_addentries(aclp
,
739 acl_args
->acl_aclp
, acl_args
->acl_slot
)) != 0) {
740 errmsg(1, 0, "%s\n", acl_strerror(error
));
746 case ACL_SLOT_DELETE
:
747 if (acl_args
->acl_slot
+ 1 > aclp
->acl_cnt
) {
749 gettext("Invalid slot specified for removal\n"));
754 if (acl_args
->acl_slot
== 0 && aclp
->acl_cnt
== 1) {
756 gettext("Can't remove all ACL "
757 "entries from a file\n"));
763 * remove a single entry
765 * if last entry just adjust acl_cnt
768 if ((acl_args
->acl_slot
+ 1) == aclp
->acl_cnt
)
771 to
= (char *)aclp
->acl_aclp
+
772 (acl_args
->acl_slot
* aclp
->acl_entry_size
);
773 from
= (char *)to
+ aclp
->acl_entry_size
;
774 len
= (aclp
->acl_cnt
- acl_args
->acl_slot
- 1) *
775 aclp
->acl_entry_size
;
776 (void) memmove(to
, from
, len
);
783 if ((error
= acl_removeentries(aclp
, acl_args
->acl_aclp
,
784 acl_args
->acl_slot
, ACL_REMOVE_ALL
)) != 0) {
785 errmsg(1, 0, "%s\n", acl_strerror(error
));
790 if (aclp
->acl_cnt
== 0) {
792 gettext("Can't remove all ACL "
793 "entries from a file\n"));
801 if (acl_args
->acl_slot
>= 0) {
802 error
= acl_modifyentries(aclp
, acl_args
->acl_aclp
,
805 errmsg(1, 0, "%s\n", acl_strerror(error
));
811 set_aclp
= acl_args
->acl_aclp
;
815 error
= acl_strip(file
, st
->st_uid
, st
->st_gid
, st
->st_mode
);
817 errmsg(1, 0, "%s\n", acl_strerror(error
));
825 errmsg(1, 2, gettext("Unknown ACL action requested\n"));
828 error
= acl_check(set_aclp
, isdir
);
831 errmsg(1, 2, "%s\n%s", acl_strerror(error
),
832 gettext("See chmod(1) for more information on "
833 "valid ACL syntax\n"));
835 if ((error
= acl_set(file
, set_aclp
)) != 0) {
836 errmsg(1, 0, gettext("Failed to set ACL: %s\n"),
837 acl_strerror(error
));
846 * Prints out the attributes in their verbose form:
847 * '{'[["no"]<attribute-name>][,["no"]<attribute-name>]...'}'
848 * similar to output of ls -/v.
851 print_nvlist(nvlist_t
*attr_nvlist
)
855 nvlist_t
*lptr
= attr_nvlist
;
856 nvpair_t
*pair
= NULL
;
858 (void) fprintf(stderr
, "\t%c", LEFTBRACE
);
859 while (pair
= nvlist_next_nvpair(lptr
, pair
)) {
860 if (nvpair_value_boolean_value(pair
, &value
) == 0) {
861 (void) fprintf(stderr
, "%s%s%s",
862 firsttime
? "" : A_SEP_TOK
,
863 (value
== A_SET_VAL
) ? "" : "no",
867 (void) fprintf(stderr
, gettext(
868 "<error retrieving attributes: %s>"),
873 (void) fprintf(stderr
, "%c\n", RIGHTBRACE
);
877 * Add an attribute name and boolean value to an nvlist if an action is to be
878 * performed for that attribute. The nvlist will be used later to set all the
879 * attributes in the nvlist in one operation through a call to setattrat().
881 * If a set operation ('+') was specified, then a boolean representation of the
882 * attribute's value will be added to the nvlist for that attribute name. If an
883 * inverse operation ('-') was specified, then a boolean representation of the
884 * inverse of the attribute's value will be added to the nvlist for that
887 * Returns an nvlist of attribute name and boolean value pairs if there are
888 * attribute actions to be performed, otherwise returns NULL.
891 set_attrs_nvlist(char *attractptr
, int numofattrs
)
893 int attribute_set
= 0;
895 nvlist_t
*attr_nvlist
;
897 if (nvlist_alloc(&attr_nvlist
, NV_UNIQUE_NAME
, 0) != 0) {
902 for (i
= 0; i
< numofattrs
; i
++) {
903 if (attractptr
[i
] != '\0') {
904 if ((nvlist_add_boolean_value(attr_nvlist
,
906 (attractptr
[i
] == A_SET_OP
))) != 0) {
907 errmsg(1, 2, gettext(
908 "unable to propagate attribute names and"
909 "values: %s\n"), strerror(errno
));
915 return (attribute_set
? attr_nvlist
: NULL
);
919 * Set the attributes of file, or if specified, of the named attribute file,
920 * attrname. Build an nvlist of attribute names and values and call setattrat()
921 * to set the attributes in one operation.
923 * Returns 0 if successful, otherwise returns 1.
926 set_file_attrs(char *file
, char *attrname
, nvlist_t
*attr_nvlist
)
931 if (attrname
!= NULL
) {
934 filename
= basename(file
);
937 if ((rc
= setattrat(AT_FDCWD
, XATTR_VIEW_READWRITE
, filename
,
938 attr_nvlist
)) != 0) {
942 emsg
= gettext("not supported");
945 emsg
= gettext("not privileged");
950 errmsg(1, 0, gettext(
951 "cannot set the following attributes on "
953 (attrname
== NULL
) ? "" : gettext("attribute "),
954 (attrname
== NULL
) ? "" : attrname
,
955 (attrname
== NULL
) ? "" : gettext(" of "),
957 print_nvlist(attr_nvlist
);
966 return (open(".", O_RDONLY
));
973 if (fchdir(cwd
) != 0) {
974 errmsg(1, 1, gettext(
975 "can't change to current working directory\n"));
982 * Returns 1 if filename is a system attribute file, otherwise
986 is_sattr(char *filename
)
988 return (sysattr_type(filename
) != _NOT_SATTR
);
992 * Perform the action on the specified named attribute file for the file
993 * associated with the input file descriptor. If the named attribute file
994 * is "*", then the action is to be performed on all the named attribute files
995 * of the file associated with the input file descriptor.
998 set_named_attrs(char *file
, int parentfd
, char *attrname
, nvlist_t
*attr_nvlist
)
1006 if ((attrname
== NULL
) || (strcmp(attrname
, "*") != 0)) {
1008 * Make sure the named attribute exists and extended system
1009 * attributes are supported on the underlying file system.
1011 if (attrname
!= NULL
) {
1012 if (fstatat(parentfd
, attrname
, &st
,
1013 AT_SYMLINK_NOFOLLOW
) < 0) {
1014 errmsg(2, 0, gettext(
1015 "can't access attribute %s of %s\n"),
1019 if (sysattr_support(attrname
, _PC_SATTR_ENABLED
) != 1) {
1020 errmsg(1, 0, gettext(
1021 "extended system attributes not supported "
1022 "for attribute %s of %s\n"),
1028 error
= set_file_attrs(file
, attrname
, attr_nvlist
);
1031 if (((dirfd
= dup(parentfd
)) == -1) ||
1032 ((dirp
= fdopendir(dirfd
)) == NULL
)) {
1033 errmsg(1, 0, gettext(
1034 "cannot open dir pointer of file %s\n"), file
);
1036 (void) close(dirfd
);
1041 while (dp
= readdir(dirp
)) {
1043 * Process all extended attribute files except
1044 * ".", "..", and extended system attribute files.
1046 if ((strcmp(dp
->d_name
, ".") == 0) ||
1047 (strcmp(dp
->d_name
, "..") == 0) ||
1048 is_sattr(dp
->d_name
)) {
1052 if (set_named_attrs(file
, parentfd
, dp
->d_name
,
1053 attr_nvlist
) != 0) {
1058 (void) closedir(dirp
);
1062 return ((error
== 0) ? 0 : 1);
1066 * Set the attributes of the specified file, or if specified with -@ on the
1067 * command line, the specified named attributes of the specified file.
1069 * Returns 0 if successful, otherwise returns 1.
1072 set_attrs(char *file
, attr_name_t
*attrnames
, nvlist_t
*attr_nvlist
)
1079 attr_name_t
*tattr
= attrnames
;
1081 if (attr_nvlist
== NULL
) {
1085 if (sysattr_support(file
, _PC_SATTR_ENABLED
) != 1) {
1086 errmsg(1, 0, gettext(
1087 "extended system attributes not supported for %s\n"), file
);
1092 * Open the parent directory and change into it before attempting
1093 * to set the attributes of the file.
1095 if (attrnames
== NULL
) {
1096 tpath
= strdup(file
);
1097 parentd
= dirname(tpath
);
1098 parentfd
= open(parentd
, O_RDONLY
);
1100 parentfd
= attropen(file
, ".", O_RDONLY
);
1102 if (parentfd
== -1) {
1103 errmsg(1, 0, gettext(
1104 "cannot open attribute directory of %s\n"), file
);
1105 if (tpath
!= NULL
) {
1111 if ((cwd
= save_cwd()) < 0) {
1112 errmsg(1, 1, gettext(
1113 "can't get current working directory\n"));
1115 if (fchdir(parentfd
) != 0) {
1116 errmsg(1, 0, gettext(
1117 "can't change to parent %sdirectory of %s\n"),
1118 (attrnames
== NULL
) ? "" : gettext("attribute "), file
);
1120 (void) close(parentfd
);
1121 if (tpath
!= NULL
) {
1128 * If no named attribute file names were provided on the command line
1129 * then set the attributes of the base file, otherwise, set the
1130 * attributes for each of the named attribute files specified.
1132 if (attrnames
== NULL
) {
1133 error
= set_named_attrs(file
, parentfd
, NULL
, attr_nvlist
);
1136 while (tattr
!= NULL
) {
1137 if (set_named_attrs(file
, parentfd
, tattr
->name
,
1138 attr_nvlist
) != 0) {
1141 tattr
= tattr
->next
;
1144 (void) close(parentfd
);
1147 return ((error
== 0) ? 0 : 1);
1151 * Prints the attributes in either the compact or verbose form indicated
1155 print_attrs(int flag
)
1158 static int numofattrs
;
1161 numofattrs
= attr_count();
1163 (void) fprintf(stderr
, gettext("\t["));
1164 for (i
= 0; i
< numofattrs
; i
++) {
1165 if ((attr_to_xattr_view(i
) != XATTR_VIEW_READWRITE
) ||
1166 (attr_to_data_type(i
) != DATA_TYPE_BOOLEAN_VALUE
)) {
1169 (void) fprintf(stderr
, "%s%s",
1170 (firsttime
== 1) ? "" : gettext("|"),
1171 (flag
== ATTR_OPTS
) ? attr_to_option(i
) : attr_to_name(i
));
1174 (void) fprintf(stderr
, gettext("]\n"));
1178 * Record what action should be taken on the specified attribute. Only boolean
1179 * read-write attributes can be manipulated.
1181 * Returns 0 if successful, otherwise returns 1.
1184 set_attr_args(f_attr_t attr
, char action
, char *attractptr
)
1186 if ((attr_to_xattr_view(attr
) == XATTR_VIEW_READWRITE
) &&
1187 (attr_to_data_type(attr
) == DATA_TYPE_BOOLEAN_VALUE
)) {
1188 attractptr
[attr
] = action
;
1195 * Parses the entry and assigns the appropriate action (either '+' or '-' in
1196 * attribute's position in the character array pointed to by attractptr, where
1197 * upon exit, attractptr is positional and the value of each character specifies
1198 * whether to set (a '+'), clear (a '-'), or leave untouched (a '\0') the
1201 * If the entry is an attribute name, then the A_SET_OP action is to be
1202 * performed for this attribute. If the entry is an attribute name proceeded
1203 * with "no", then the A_INVERSE_OP action is to be performed for this
1204 * attribute. If the entry is one or more attribute option letters, then step
1205 * through each of the option letters marking the action to be performed for
1206 * each of the attributes associated with the letter as A_SET_OP.
1208 * Returns 0 if the entry was a valid attribute(s) and the action to be
1209 * performed on that attribute(s) has been recorded, otherwise returns 1.
1212 parse_entry(char *entry
, char action
, char atype
, int len
, char *attractptr
)
1214 char aopt
[2] = {'\0', '\0'};
1218 if (atype
== A_VERBOSE_TYPE
) {
1219 if ((attr
= name_to_attr(entry
)) != F_ATTR_INVAL
) {
1220 return (set_attr_args(attr
,
1221 (action
== A_REPLACE_OP
) ? A_SET_OP
: action
,
1223 } else if ((len
> 2) && (strncmp(entry
, "no", 2) == 0) &&
1224 ((attr
= name_to_attr(entry
+ 2)) != F_ATTR_INVAL
)) {
1225 return (set_attr_args(attr
, ((action
== A_REPLACE_OP
) ||
1226 (action
== A_SET_OP
)) ? A_INVERSE_OP
: A_SET_OP
,
1231 } else if (atype
== A_COMPACT_TYPE
) {
1232 for (aptr
= entry
; *aptr
!= '\0'; aptr
++) {
1235 * The output of 'ls' can be used as the attribute mode
1236 * specification for chmod. This output can contain a
1237 * hypen ('-') for each attribute that is not set. If
1238 * so, ignore them. If a replace action is being
1239 * performed, then all attributes that don't have an
1240 * action set here, will be cleared down the line.
1245 if (set_attr_args(option_to_attr(aopt
),
1246 (action
== A_REPLACE_OP
) ? A_SET_OP
: action
,
1257 * Parse the attribute specification, aoptsstr. Upon completion, attr_nvlist
1258 * will point to an nvlist which contains pairs of attribute names and values
1259 * to be set; attr_nvlist will be NULL if it is a no-op.
1261 * The attribute specification format is
1262 * S[oper]attr_type[attribute_list]
1264 * + set operation of specified attributes in attribute list.
1265 * This is the default operation.
1266 * - inverse operation of specified attributes in attribute list
1267 * = replace operation of all attributes. All attribute operations
1268 * depend on those specified in the attribute list. Attributes
1269 * not specified in the attribute list will be cleared.
1270 * where attr_type is
1271 * c compact type. Each entry in the attribute list is a character
1272 * option representing an associated attribute name.
1273 * v verbose type. Each entry in the attribute list is an
1274 * an attribute name which can optionally be preceeded with "no"
1275 * (to imply the attribute should be cleared).
1276 * a all attributes type. The oper should be applied to all
1277 * read-write boolean system attributes. No attribute list should
1278 * be specified after an 'a' attribute type.
1280 * Returns 0 if aoptsstr contained a valid attribute specification,
1281 * otherwise, returns 1.
1284 parse_attr_args(char *aoptsstr
, sec_args_t
**sec_args
)
1295 char tok
[] = {'\0', '\0'};
1300 if ((*aoptsstr
!= 'S') || (*(aoptsstr
+ 1) == '\0')) {
1304 if ((eptr
= strdup(aoptsstr
+ 1)) == NULL
) {
1311 * Create a positional character array to determine a single attribute
1312 * operation to be performed, where each index represents the system
1313 * attribute affected, and it's value in the array represents the action
1314 * to be performed, i.e., a value of '+' means to set the attribute, a
1315 * value of '-' means to clear the attribute, and a value of '\0' means
1316 * to leave the attribute untouched. Initially, this positional
1317 * character array is all '\0's, representing a no-op.
1319 if ((numofattrs
= attr_count()) < 1) {
1320 errmsg(1, 1, gettext("system attributes not supported\n"));
1323 if ((attractptr
= calloc(numofattrs
, sizeof (char))) == NULL
) {
1328 if ((*sec_args
= malloc(sizeof (sec_args_t
))) == NULL
) {
1332 (*sec_args
)->sec_type
= SEC_ATTR
;
1333 (*sec_args
)->sec_attrs
= NULL
;
1335 /* Parse each attribute operation within the attribute specification. */
1336 while ((entry
!= NULL
) && (*entry
!= '\0')) {
1340 /* Get the operator. */
1347 case A_COMPACT_TYPE
:
1348 case A_VERBOSE_TYPE
:
1349 case A_ALLATTRS_TYPE
:
1357 /* An attribute type must be specified. */
1358 if (atype
== '\0') {
1359 if ((*entry
== A_COMPACT_TYPE
) ||
1360 (*entry
== A_VERBOSE_TYPE
) ||
1361 (*entry
== A_ALLATTRS_TYPE
)) {
1368 /* Get the attribute specification separator. */
1369 if (*entry
== LEFTBRACE
) {
1376 /* Get the attribute operation */
1377 if ((nextentry
= strpbrk(entry
, tok
)) != NULL
) {
1382 /* Check for a no-op */
1383 if ((*entry
== '\0') && (atype
!= A_ALLATTRS_TYPE
) &&
1384 (action
!= A_REPLACE_OP
)) {
1390 * Step through the attribute operation, setting the
1391 * appropriate values for the specified attributes in the
1392 * character array, attractptr. A value of '+' will mean the
1393 * attribute is to be set, and a value of '-' will mean the
1394 * attribute is to be cleared. If the value of an attribute
1395 * remains '\0', then no action is to be taken on that
1396 * attribute. As multiple operations specified are
1397 * accumulated, a single attribute setting operation is
1398 * represented in attractptr.
1400 len
= strlen(entry
);
1401 if ((*tok
== RIGHTBRACE
) || (action
== A_REPLACE_OP
) ||
1402 (atype
== A_ALLATTRS_TYPE
)) {
1404 if ((action
== A_REPLACE_OP
) ||
1405 (atype
== A_ALLATTRS_TYPE
)) {
1406 (void) memset(attractptr
, '\0', numofattrs
);
1410 if ((teptr
= strdup(entry
)) == NULL
) {
1415 while (subentry
!= NULL
) {
1416 if ((nextattr
= strpbrk(subentry
,
1417 A_SEP_TOK
)) != NULL
) {
1421 if (parse_entry(subentry
, action
,
1422 atype
, len
, attractptr
) != 0) {
1425 subentry
= nextattr
;
1431 * If performing the replace action, record the
1432 * attributes and values for the rest of the
1433 * attributes that have not already been recorded,
1434 * otherwise record the specified action for all
1435 * attributes. Note: set_attr_args() will only record
1436 * the attribute and action if it is a boolean
1437 * read-write attribute so we don't need to worry
1438 * about checking it here.
1440 if ((action
== A_REPLACE_OP
) ||
1441 (atype
== A_ALLATTRS_TYPE
)) {
1442 for (i
= 0; i
< numofattrs
; i
++) {
1443 if (attractptr
[i
] == A_UNDEF_OP
) {
1444 (void) set_attr_args(i
,
1445 (action
== A_SET_OP
) ?
1446 A_SET_OP
: A_INVERSE_OP
,
1453 if (parse_entry(entry
, action
, atype
, len
,
1462 * Populate an nvlist with attribute name and boolean value pairs
1463 * using the single attribute operation.
1465 (*sec_args
)->sec_attrs
= set_attrs_nvlist(attractptr
, numofattrs
);