diff options
author | amw <none@none> | 2007-10-25 16:34:29 -0700 |
---|---|---|
committer | amw <none@none> | 2007-10-25 16:34:29 -0700 |
commit | da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0 (patch) | |
tree | 65be91fb78a6a66183197595333f2e8aafb4640a /usr/src/cmd/chmod/chmod.c | |
parent | e845e33dd0d1aea22db7edaa8c7d43955d24609b (diff) | |
download | illumos-joyent-da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0.tar.gz |
PSARC/2007/218 caller_context_t in all VOPs
PSARC/2007/227 VFS Feature Registration and ACL on Create
PSARC/2007/244 ZFS Case-insensitive support
PSARC/2007/315 Extensible Attribute Interfaces
PSARC/2007/394 ls(1) new command line options '-/' and '-%': CIFS system attributes support
PSARC/2007/403 Modified Access Checks for CIFS
PSARC/2007/410 Add system attribute support to chmod(1)
PSARC/2007/432 CIFS system attributes support for cp(1), pack(1), unpack(1), compress(1) and uncompress(1)
PSARC/2007/444 Rescind SETTABLE Attribute
PSARC/2007/459 CIFS system attributes support for cpio(1), pax(1), tar(1)
PSARC/2007/546 Update utilities to match CIFS system attributes changes.
PSARC/2007/560 ZFS sharesmb property
4890717 want append-only files
6417428 Case-insensitive file system name lookup to support CIFS
6417435 DOS attributes and additional timestamps to support for CIFS
6417442 File system quarantined and modified attributes to support an integrated Anti-Virus service
6417453 FS boolean property for rejecting/allowing invalid UTF-8 sequences in file names
6473733 RFE: Need support for open-deny modes
6473755 RFE: Need ability to reconcile oplock and delegation conflicts
6494624 sharemgr needs to support CIFS shares better
6546705 All vnode operations need to pass caller_context_t
6546706 Need VOP_SETATTR/VOP_GETATTR to support new, optional attributes
6546893 Solaris system attribute support
6550962 ZFS ACL inheritance needs to be enhanced to support Automatic Inheritance
6553589 RFE: VFS Feature Registration facility
6553770 RFE: ZFS support for ACL-on-CREATE (PSARC 2007/227)
6565581 ls(1) should support file system attributes proposed in PSARC/2007/315
6566784 NTFS streams are not copied along with the files.
6576205 cp(1), pack(1) and compress(1) should support file system attributes proposed in PSARC/2007/315
6578875 RFE: kernel interfaces for nbmand need improvement
6578883 RFE: VOP_SHRLOCK needs additional access types
6578885 chmod(1) should support file system attributes proposed in PSARC/2007/315
6578886 RFE: disallow nbmand state to change on remount
6583349 ACL parser needs to support audit/alarm ACE types
6590347 tar(1) should support filesystem attributes proposed in PSARC/2007/315
6597357 *tar* xv@ doesn't show the hidden directory even though it is restored
6597360 *tar* should re-init xattr info if openat() fails during extraction of and extended attribute
6597368 *tar* cannot restore hard linked extended attributes
6597374 *tar* doesn't display "x " when hard linked attributes are restored
6597375 *tar* extended attribute header off by one
6614861 *cpio* incorrectly archives extended system attributes with -@
6614896 *pax* incorrectly archives extended system attributes with -@
6615225 *tar* incorrectly archives extended system attributes with -@
6617183 CIFS Service - PSARC 2006/715
Diffstat (limited to 'usr/src/cmd/chmod/chmod.c')
-rw-r--r-- | usr/src/cmd/chmod/chmod.c | 892 |
1 files changed, 834 insertions, 58 deletions
diff --git a/usr/src/cmd/chmod/chmod.c b/usr/src/cmd/chmod/chmod.c index 29813f7d09..531c4bf3d2 100644 --- a/usr/src/cmd/chmod/chmod.c +++ b/usr/src/cmd/chmod/chmod.c @@ -44,7 +44,8 @@ * where * mode is [ugoa][+-=][rwxXlstugo] or an octal number * mode is [<+|->A[# <number] ]<aclspec> - * option is -R and -f + * mode is S<attrspec> + * option is -R, -f, and -@ */ /* @@ -58,6 +59,7 @@ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> +#include <fcntl.h> #include <dirent.h> #include <locale.h> #include <string.h> /* strerror() */ @@ -67,6 +69,10 @@ #include <errno.h> #include <sys/acl.h> #include <aclutils.h> +#include <libnvpair.h> +#include <libcmdutils.h> +#include <libgen.h> +#include <attr.h> static int rflag; static int fflag; @@ -79,11 +85,37 @@ static char **mav; /* Alternate to argv (for parseargs) */ static char *ms; /* Points to the mode argument */ -#define ACL_ADD 1 -#define ACL_DELETE 2 -#define ACL_SLOT_DELETE 3 -#define ACL_REPLACE 4 -#define ACL_STRIP 5 +#define ACL_ADD 1 +#define ACL_DELETE 2 +#define ACL_SLOT_DELETE 3 +#define ACL_REPLACE 4 +#define ACL_STRIP 5 + +#define LEFTBRACE '{' +#define RIGHTBRACE '}' +#define A_SEP ',' +#define A_SEP_TOK "," + +#define A_COMPACT_TYPE 'c' +#define A_VERBOSE_TYPE 'v' +#define A_ALLATTRS_TYPE 'a' + +#define A_SET_OP '+' +#define A_INVERSE_OP '-' +#define A_REPLACE_OP '=' +#define A_UNDEF_OP '\0' + +#define A_SET_TEXT "set" +#define A_INVERSE_TEXT "clear" + +#define A_SET_VAL B_TRUE +#define A_CLEAR_VAL B_FALSE + +#define ATTR_OPTS 0 +#define ATTR_NAMES 1 + +#define sec_acls secptr.acls +#define sec_attrs secptr.attrs typedef struct acl_args { acl_t *acl_aclp; @@ -91,34 +123,55 @@ typedef struct acl_args { int acl_action; } acl_args_t; -extern mode_t -newmode_common(char *ms, mode_t new_mode, mode_t umsk, char *file, char *path, - o_mode_t *group_clear_bits, o_mode_t *group_set_bits); +typedef enum { + SEC_ACL, + SEC_ATTR +} chmod_sec_t; -static int -dochmod(char *name, char *path, mode_t umsk, acl_args_t *aclp), -chmodr(char *dir, char *path, mode_t mode, mode_t umsk, acl_args_t *aclp); -static int doacl(char *file, struct stat *st, acl_args_t *aclp); +typedef struct { + chmod_sec_t sec_type; + union { + acl_args_t *acls; + nvlist_t *attrs; + } secptr; +} sec_args_t; -static void handle_acl(char *name, o_mode_t group_clear_bits, - o_mode_t group_set_bits); +typedef struct attr_name { + char *name; + struct attr_name *next; +} attr_name_t; -static void usage(void); -void errmsg(int severity, int code, char *format, ...); +extern mode_t newmode_common(char *ms, mode_t new_mode, mode_t umsk, + char *file, char *path, o_mode_t *group_clear_bits, + o_mode_t *group_set_bits); +static int chmodr(char *dir, char *path, mode_t mode, mode_t umsk, + sec_args_t *secp, attr_name_t *attrname); +static int doacl(char *file, struct stat *st, acl_args_t *aclp); +static int dochmod(char *name, char *path, mode_t umsk, sec_args_t *secp, + attr_name_t *attrnames); +static void handle_acl(char *name, o_mode_t group_clear_bits, + o_mode_t group_set_bits); +void errmsg(int severity, int code, char *format, ...); +static void free_attr_names(attr_name_t *attrnames); static void parseargs(int ac, char *av[]); - -int -parse_acl_args(char *arg, acl_args_t **acl_args); +static int parse_acl_args(char *arg, sec_args_t **sec_args); +static int parse_attr_args(char *arg, sec_args_t **sec_args); +static void print_attrs(int flag); +static int set_attrs(char *file, attr_name_t *attrnames, nvlist_t *attr_nvlist); +static void usage(void); int main(int argc, char *argv[]) { - int i, c; - int status = 0; - mode_t umsk; - acl_args_t *acl_args = NULL; + int i, c; + int status = 0; + mode_t umsk; + sec_args_t *sec_args = NULL; + attr_name_t *attrnames = NULL; + attr_name_t *attrend = NULL; + attr_name_t *tattr; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ @@ -128,7 +181,7 @@ main(int argc, char *argv[]) parseargs(argc, argv); - while ((c = getopt(mac, mav, "Rf")) != EOF) { + while ((c = getopt(mac, mav, "Rf@:")) != EOF) { switch (c) { case 'R': rflag++; @@ -136,6 +189,20 @@ main(int argc, char *argv[]) case 'f': fflag++; break; + case '@': + if (((tattr = malloc(sizeof (attr_name_t))) == NULL) || + ((tattr->name = strdup(optarg)) == NULL)) { + perror("chmod"); + exit(2); + } + if (attrnames == NULL) { + attrnames = tattr; + attrnames->next = NULL; + } else { + attrend->next = tattr; + } + attrend = tattr; + break; case '?': usage(); exit(2); @@ -149,16 +216,33 @@ main(int argc, char *argv[]) mac -= optind; mav += optind; - if (mac >= 2 && (mav[0][0] == 'A')) { - if (parse_acl_args(*mav, &acl_args)) { + if ((mac >= 2) && (mav[0][0] == 'A')) { + if (attrnames != NULL) { + free_attr_names(attrnames); + attrnames = NULL; + } + if (parse_acl_args(*mav, &sec_args)) { usage(); exit(2); } + } else if ((mac >= 2) && (mav[0][0] == 'S')) { + if (parse_attr_args(*mav, &sec_args)) { + usage(); + exit(2); + + /* A no-op attribute operation was specified. */ + } else if (sec_args->sec_attrs == NULL) { + exit(0); + } } else { if (mac < 2) { usage(); exit(2); } + if (attrnames != NULL) { + free_attr_names(attrnames); + attrnames = NULL; + } } ms = mav[0]; @@ -167,14 +251,30 @@ main(int argc, char *argv[]) (void) umask(umsk); for (i = 1; i < mac; i++) { - status += dochmod(mav[i], mav[i], umsk, acl_args); + status += dochmod(mav[i], mav[i], umsk, sec_args, attrnames); } return (fflag ? 0 : status); } +static void +free_attr_names(attr_name_t *attrnames) +{ + attr_name_t *attrnamesptr = attrnames; + attr_name_t *tptr; + + while (attrnamesptr != NULL) { + tptr = attrnamesptr->next; + if (attrnamesptr->name != NULL) { + free(attrnamesptr->name); + } + attrnamesptr = tptr; + } +} + static int -dochmod(char *name, char *path, mode_t umsk, acl_args_t *aclp) +dochmod(char *name, char *path, mode_t umsk, sec_args_t *secp, + attr_name_t *attrnames) { static struct stat st; int linkflg = 0; @@ -194,15 +294,24 @@ dochmod(char *name, char *path, mode_t umsk, acl_args_t *aclp) } /* Do not recurse if directory is object of symbolic link */ - if (rflag && ((st.st_mode & S_IFMT) == S_IFDIR) && !linkflg) - return (chmodr(name, path, st.st_mode, umsk, aclp)); + if (rflag && ((st.st_mode & S_IFMT) == S_IFDIR) && !linkflg) { + return (chmodr(name, path, st.st_mode, umsk, secp, attrnames)); + } - if (aclp) { - return (doacl(name, &st, aclp)); - } else if (chmod(name, newmode_common(ms, st.st_mode, umsk, name, path, - &group_clear_bits, &group_set_bits)) == -1) { - errmsg(2, 0, gettext("can't change %s\n"), path); - return (1); + if (secp != NULL) { + if (secp->sec_type == SEC_ACL) { + return (doacl(name, &st, secp->sec_acls)); + } else if (secp->sec_type == SEC_ATTR) { + return (set_attrs(name, attrnames, secp->sec_attrs)); + } else { + return (1); + } + } else { + if (chmod(name, newmode_common(ms, st.st_mode, umsk, name, path, + &group_clear_bits, &group_set_bits)) == -1) { + errmsg(2, 0, gettext("can't change %s\n"), path); + return (1); + } } /* @@ -218,9 +327,9 @@ dochmod(char *name, char *path, mode_t umsk, acl_args_t *aclp) return (0); } - static int -chmodr(char *dir, char *path, mode_t mode, mode_t umsk, acl_args_t *aclp) +chmodr(char *dir, char *path, mode_t mode, mode_t umsk, sec_args_t *secp, + attr_name_t *attrnames) { DIR *dirp; @@ -239,13 +348,21 @@ chmodr(char *dir, char *path, mode_t mode, mode_t umsk, acl_args_t *aclp) /* * Change what we are given before doing it's contents */ - if (aclp) { + if (secp != NULL) { if (lstat(dir, &st) < 0) { errmsg(2, 0, gettext("can't access %s\n"), path); return (1); } - if (doacl(dir, &st, aclp) != 0) + if (secp->sec_type == SEC_ACL) { + if (doacl(dir, &st, secp->sec_acls) != 0) + return (1); + } else if (secp->sec_type == SEC_ATTR) { + if (set_attrs(dir, attrnames, secp->sec_attrs) != 0) { + return (1); + } + } else { return (1); + } } else if (chmod(dir, newmode_common(ms, mode, umsk, dir, path, &group_clear_bits, &group_set_bits)) < 0) { errmsg(2, 0, gettext("can't change %s\n"), path); @@ -260,7 +377,8 @@ chmodr(char *dir, char *path, mode_t mode, mode_t umsk, acl_args_t *aclp) * general group permissions. */ - if (aclp == NULL) { /* only necessary when not setting ACL */ + if (secp != NULL) { + /* only necessary when not setting ACL or system attributes */ if (group_clear_bits || group_set_bits) handle_acl(dir, group_clear_bits, group_set_bits); } @@ -315,7 +433,7 @@ chmodr(char *dir, char *path, mode_t mode, mode_t umsk, acl_args_t *aclp) currdir, dp->d_name); return (1); } - ecode += dochmod(dp->d_name, currdir, umsk, aclp); + ecode += dochmod(dp->d_name, currdir, umsk, secp, attrnames); } (void) closedir(dirp); if (chdir(savedir) < 0) { @@ -344,7 +462,7 @@ errmsg(int severity, int code, char *format, ...) */ if (!fflag || (code != 0)) { (void) fprintf(stderr, - "chmod: %s: ", gettext(msg[severity])); + "chmod: %s: ", gettext(msg[severity])); (void) vfprintf(stderr, format, ap); } @@ -361,17 +479,36 @@ usage(void) "usage:\tchmod [-fR] <absolute-mode> file ...\n")); (void) fprintf(stderr, gettext( - "\tchmod [-fR] <ACL-operation> file ...\n")); + "\tchmod [-fR] [-@ attribute] ... " + "S<attribute-operation> file ...\n")); (void) fprintf(stderr, gettext( - "\tchmod [-fR] <symbolic-mode-list> file ...\n")); + "\tchmod [-fR] <ACL-operation> file ...\n")); + (void) fprintf(stderr, gettext( + "\tchmod [-fR] <symbolic-mode-list> file ...\n\n")); (void) fprintf(stderr, gettext( "where \t<symbolic-mode-list> is a comma-separated list of\n")); + (void) fprintf(stderr, gettext( + "\t[ugoa]{+|-|=}[rwxXlstugo]\n\n")); (void) fprintf(stderr, gettext( - "\t[ugoa]{+|-|=}[rwxXlstugo]\n")); + "where \t<attribute-operation> is a comma-separated list of\n" + "\tone or more of the following\n")); + (void) fprintf(stderr, gettext( + "\t[+|-|=]c[<compact-attribute-list>|{<compact-attribute-list>}]\n" + "\t[+|-|=]v[<verbose-attribute-setting>|" + "\'{\'<verbose-attribute-setting-list>\'}\']\n" + "\t[+|-|=]a\n")); + (void) fprintf(stderr, gettext( + "where \t<compact-attribute-list> is a list of zero or more of\n")); + print_attrs(ATTR_OPTS); + (void) fprintf(stderr, gettext( + "where \t<verbose-attribute-setting> is one of\n")); + print_attrs(ATTR_NAMES); + (void) fprintf(stderr, gettext( + "\tand can be, optionally, immediately preceded by \"no\"\n\n")); (void) fprintf(stderr, gettext( "where \t<ACL-operation> is one of the following\n")); @@ -416,7 +553,7 @@ parseargs(int ac, char *av[]) for (fflag = i = 0; i < ac; i ++) { if (strcmp(av[i], "--") == 0) - fflag = 1; + fflag = 1; } /* process the arguments */ @@ -435,19 +572,26 @@ parseargs(int ac, char *av[]) */ if ((strchr(av[i], 'R') == NULL && - strchr(av[i], 'f') == NULL)) { - mav[mac++] = strdup("--"); + strchr(av[i], 'f') == NULL) && + strchr(av[i], '@') == NULL) { + if ((mav[mac++] = strdup("--")) == NULL) { + perror("chmod"); + exit(2); + } } } - mav[mac++] = strdup(av[i]); + if ((mav[mac++] = strdup(av[i])) == NULL) { + perror("chmod"); + exit(2); + } } mav[mac] = (char *)NULL; } -int -parse_acl_args(char *arg, acl_args_t **acl_args) +static int +parse_acl_args(char *arg, sec_args_t **sec_args) { acl_t *new_acl = NULL; int slot; @@ -512,7 +656,12 @@ parse_acl_args(char *arg, acl_args_t **acl_args) new_acl_args->acl_slot = slot; new_acl_args->acl_action = action; - *acl_args = new_acl_args; + if ((*sec_args = malloc(sizeof (sec_args_t))) == NULL) { + perror("chmod"); + exit(2); + } + (*sec_args)->sec_type = SEC_ACL; + (*sec_args)->sec_acls = new_acl_args; return (0); } @@ -593,10 +742,10 @@ doacl(char *file, struct stat *st, acl_args_t *acl_args) switch (acl_args->acl_action) { case ACL_ADD: if ((error = acl_addentries(aclp, - acl_args->acl_aclp, acl_args->acl_slot)) != 0) { - errmsg(1, 1, "%s\n", acl_strerror(error)); - acl_free(aclp); - return (1); + acl_args->acl_aclp, acl_args->acl_slot)) != 0) { + errmsg(1, 1, "%s\n", acl_strerror(error)); + acl_free(aclp); + return (1); } set_aclp = aclp; break; @@ -699,3 +848,630 @@ doacl(char *file, struct stat *st, acl_args_t *acl_args) acl_free(aclp); return (0); } + +/* + * Prints out the attributes in their verbose form: + * '{'[["no"]<attribute-name>][,["no"]<attribute-name>]...'}' + * similar to output of ls -/v. + */ +static void +print_nvlist(nvlist_t *attr_nvlist) +{ + int firsttime = 1; + boolean_t value; + nvlist_t *lptr = attr_nvlist; + nvpair_t *pair = NULL; + + (void) fprintf(stderr, "\t%c", LEFTBRACE); + while (pair = nvlist_next_nvpair(lptr, pair)) { + if (nvpair_value_boolean_value(pair, &value) == 0) { + (void) fprintf(stderr, "%s%s%s", + firsttime ? "" : A_SEP_TOK, + (value == A_SET_VAL) ? "" : "no", + nvpair_name(pair)); + firsttime = 0; + } else { + (void) fprintf(stderr, gettext( + "<error retrieving attributes: %s>"), + strerror(errno)); + break; + } + } + (void) fprintf(stderr, "%c\n", RIGHTBRACE); +} + +/* + * Add an attribute name and boolean value to an nvlist if an action is to be + * performed for that attribute. The nvlist will be used later to set all the + * attributes in the nvlist in one operation through a call to setattrat(). + * + * If a set operation ('+') was specified, then a boolean representation of the + * attribute's value will be added to the nvlist for that attribute name. If an + * inverse operation ('-') was specified, then a boolean representation of the + * inverse of the attribute's value will be added to the nvlist for that + * attribute name. + * + * Returns an nvlist of attribute name and boolean value pairs if there are + * attribute actions to be performed, otherwise returns NULL. + */ +static nvlist_t * +set_attrs_nvlist(char *attractptr, int numofattrs) +{ + int attribute_set = 0; + f_attr_t i; + nvlist_t *attr_nvlist; + + if (nvlist_alloc(&attr_nvlist, NV_UNIQUE_NAME, 0) != 0) { + perror("chmod"); + exit(2); + } + + for (i = 0; i < numofattrs; i++) { + if (attractptr[i] != '\0') { + if ((nvlist_add_boolean_value(attr_nvlist, + attr_to_name(i), + (attractptr[i] == A_SET_OP))) != 0) { + errmsg(1, 2, gettext( + "unable to propagate attribute names and" + "values: %s\n"), strerror(errno)); + } else { + attribute_set = 1; + } + } + } + return (attribute_set ? attr_nvlist : NULL); +} + +/* + * Set the attributes of file, or if specified, of the named attribute file, + * attrname. Build an nvlist of attribute names and values and call setattrat() + * to set the attributes in one operation. + * + * Returns 0 if successful, otherwise returns 1. + */ +static int +set_file_attrs(char *file, char *attrname, nvlist_t *attr_nvlist) +{ + int rc; + char *filename; + + if (attrname != NULL) { + filename = attrname; + } else { + filename = basename(file); + } + + if ((rc = setattrat(AT_FDCWD, XATTR_VIEW_READWRITE, filename, + attr_nvlist)) != 0) { + char *emsg; + switch (errno) { + case EINVAL: + emsg = gettext("not supported"); + break; + case EPERM: + emsg = gettext("not privileged"); + break; + default: + emsg = strerror(rc); + } + errmsg(1, 0, gettext( + "cannot set the following attributes on " + "%s%s%s%s: %s\n"), + (attrname == NULL) ? "" : gettext("attribute "), + (attrname == NULL) ? "" : attrname, + (attrname == NULL) ? "" : gettext(" of "), + file, emsg); + print_nvlist(attr_nvlist); + } + + return (rc); +} + +static int +save_cwd(void) +{ + return (open(".", O_RDONLY)); +} + +static void +rest_cwd(int cwd) +{ + if (cwd != -1) { + if (fchdir(cwd) != 0) { + errmsg(1, 1, gettext( + "can't change to current working directory\n")); + } + (void) close(cwd); + } +} + +/* + * Returns 1 if filename is a system attribute file, otherwise + * returns 0. + */ +static int +is_sattr(char *filename) +{ + return (sysattr_type(filename) != _NOT_SATTR); +} + +/* + * Perform the action on the specified named attribute file for the file + * associated with the input file descriptor. If the named attribute file + * is "*", then the action is to be performed on all the named attribute files + * of the file associated with the input file descriptor. + */ +static int +set_named_attrs(char *file, int parentfd, char *attrname, nvlist_t *attr_nvlist) +{ + int dirfd; + int error = 0; + DIR *dirp = NULL; + struct dirent *dp; + struct stat st; + + if ((attrname == NULL) || (strcmp(attrname, "*") != 0)) { + /* + * Make sure the named attribute exists and extended system + * attributes are supported on the underlying file system. + */ + if (attrname != NULL) { + if (fstatat(parentfd, attrname, &st, + AT_SYMLINK_NOFOLLOW) < 0) { + errmsg(2, 0, gettext( + "can't access attribute %s of %s\n"), + attrname, file); + return (1); + } + if (sysattr_support(attrname, _PC_SATTR_ENABLED) != 1) { + errmsg(1, 0, gettext( + "extended system attributes not supported " + "for attribute %s of %s\n"), + attrname, file); + return (1); + } + } + + error = set_file_attrs(file, attrname, attr_nvlist); + + } else { + if (((dirfd = dup(parentfd)) == -1) || + ((dirp = fdopendir(dirfd)) == NULL)) { + errmsg(1, 0, gettext( + "cannot open dir pointer of file %s\n"), file); + if (dirfd > 0) { + (void) close(dirfd); + } + return (1); + } + + while (dp = readdir(dirp)) { + /* + * Process all extended attribute files except + * ".", "..", and extended system attribute files. + */ + if ((strcmp(dp->d_name, ".") == 0) || + (strcmp(dp->d_name, "..") == 0) || + is_sattr(dp->d_name)) { + continue; + } + + if (set_named_attrs(file, parentfd, dp->d_name, + attr_nvlist) != 0) { + error++; + } + } + if (dirp != NULL) { + (void) closedir(dirp); + } + } + + return ((error == 0) ? 0 : 1); +} + +/* + * Set the attributes of the specified file, or if specified with -@ on the + * command line, the specified named attributes of the specified file. + * + * Returns 0 if successful, otherwise returns 1. + */ +static int +set_attrs(char *file, attr_name_t *attrnames, nvlist_t *attr_nvlist) +{ + char *parentd; + char *tpath = NULL; + int cwd; + int error = 0; + int parentfd; + attr_name_t *tattr = attrnames; + + if (attr_nvlist == NULL) { + return (0); + } + + if (sysattr_support(file, _PC_SATTR_ENABLED) != 1) { + errmsg(1, 0, gettext( + "extended system attributes not supported for %s\n"), file); + return (1); + } + + /* + * Open the parent directory and change into it before attempting + * to set the attributes of the file. + */ + if (attrnames == NULL) { + tpath = strdup(file); + parentd = dirname(tpath); + parentfd = open(parentd, O_RDONLY); + } else { + parentfd = attropen(file, ".", O_RDONLY); + } + if (parentfd == -1) { + errmsg(1, 0, gettext( + "cannot open attribute directory of %s\n"), file); + if (tpath != NULL) { + free(tpath); + } + return (1); + } + + if ((cwd = save_cwd()) < 0) { + errmsg(1, 1, gettext( + "can't get current working directory\n")); + } + if (fchdir(parentfd) != 0) { + errmsg(1, 0, gettext( + "can't change to parent %sdirectory of %s\n"), + (attrnames == NULL) ? "" : gettext("attribute "), file); + (void) close(cwd); + (void) close(parentfd); + if (tpath != NULL) { + free(tpath); + } + return (1); + } + + /* + * If no named attribute file names were provided on the command line + * then set the attributes of the base file, otherwise, set the + * attributes for each of the named attribute files specified. + */ + if (attrnames == NULL) { + error = set_named_attrs(file, parentfd, NULL, attr_nvlist); + free(tpath); + } else { + while (tattr != NULL) { + if (set_named_attrs(file, parentfd, tattr->name, + attr_nvlist) != 0) { + error++; + } + tattr = tattr->next; + } + } + (void) close(parentfd); + rest_cwd(cwd); + + return ((error == 0) ? 0 : 1); +} + +/* + * Prints the attributes in either the compact or verbose form indicated + * by flag. + */ +static void +print_attrs(int flag) +{ + f_attr_t i; + static int numofattrs; + int firsttime = 1; + + numofattrs = attr_count(); + + (void) fprintf(stderr, gettext("\t[")); + for (i = 0; i < numofattrs; i++) { + if ((attr_to_xattr_view(i) != XATTR_VIEW_READWRITE) || + (attr_to_data_type(i) != DATA_TYPE_BOOLEAN_VALUE)) { + continue; + } + (void) fprintf(stderr, "%s%s", + (firsttime == 1) ? "" : gettext("|"), + (flag == ATTR_OPTS) ? attr_to_option(i) : attr_to_name(i)); + firsttime = 0; + } + (void) fprintf(stderr, gettext("]\n")); +} + +/* + * Record what action should be taken on the specified attribute. Only boolean + * read-write attributes can be manipulated. + * + * Returns 0 if successful, otherwise returns 1. + */ +static int +set_attr_args(f_attr_t attr, char action, char *attractptr) +{ + if ((attr_to_xattr_view(attr) == XATTR_VIEW_READWRITE) && + (attr_to_data_type(attr) == DATA_TYPE_BOOLEAN_VALUE)) { + attractptr[attr] = action; + return (0); + } + return (1); +} + +/* + * Parses the entry and assigns the appropriate action (either '+' or '-' in + * attribute's position in the character array pointed to by attractptr, where + * upon exit, attractptr is positional and the value of each character specifies + * whether to set (a '+'), clear (a '-'), or leave untouched (a '\0') the + * attribute value. + * + * If the entry is an attribute name, then the A_SET_OP action is to be + * performed for this attribute. If the entry is an attribute name proceeded + * with "no", then the A_INVERSE_OP action is to be performed for this + * attribute. If the entry is one or more attribute option letters, then step + * through each of the option letters marking the action to be performed for + * each of the attributes associated with the letter as A_SET_OP. + * + * Returns 0 if the entry was a valid attribute(s) and the action to be + * performed on that attribute(s) has been recorded, otherwise returns 1. + */ +static int +parse_entry(char *entry, char action, char atype, int len, char *attractptr) +{ + char aopt[2] = {'\0', '\0'}; + char *aptr; + f_attr_t attr; + + if (atype == A_VERBOSE_TYPE) { + if ((attr = name_to_attr(entry)) != F_ATTR_INVAL) { + return (set_attr_args(attr, + (action == A_REPLACE_OP) ? A_SET_OP : action, + attractptr)); + } else if ((len > 2) && (strncmp(entry, "no", 2) == 0) && + ((attr = name_to_attr(entry + 2)) != F_ATTR_INVAL)) { + return (set_attr_args(attr, ((action == A_REPLACE_OP) || + (action == A_SET_OP)) ? A_INVERSE_OP : A_SET_OP, + attractptr)); + } else { + return (1); + } + } else if (atype == A_COMPACT_TYPE) { + for (aptr = entry; *aptr != '\0'; aptr++) { + *aopt = *aptr; + /* + * The output of 'ls' can be used as the attribute mode + * specification for chmod. This output can contain a + * hypen ('-') for each attribute that is not set. If + * so, ignore them. If a replace action is being + * performed, then all attributes that don't have an + * action set here, will be cleared down the line. + */ + if (*aptr == '-') { + continue; + } + if (set_attr_args(option_to_attr(aopt), + (action == A_REPLACE_OP) ? A_SET_OP : action, + attractptr) != 0) { + return (1); + } + } + return (0); + } + return (1); +} + +/* + * Parse the attribute specification, aoptsstr. Upon completion, attr_nvlist + * will point to an nvlist which contains pairs of attribute names and values + * to be set; attr_nvlist will be NULL if it is a no-op. + * + * The attribute specification format is + * S[oper]attr_type[attribute_list] + * where oper is + * + set operation of specified attributes in attribute list. + * This is the default operation. + * - inverse operation of specified attributes in attribute list + * = replace operation of all attributes. All attribute operations + * depend on those specified in the attribute list. Attributes + * not specified in the attribute list will be cleared. + * where attr_type is + * c compact type. Each entry in the attribute list is a character + * option representing an associated attribute name. + * v verbose type. Each entry in the attribute list is an + * an attribute name which can optionally be preceeded with "no" + * (to imply the attribute should be cleared). + * a all attributes type. The oper should be applied to all + * read-write boolean system attributes. No attribute list should + * be specified after an 'a' attribute type. + * + * Returns 0 if aoptsstr contained a valid attribute specification, + * otherwise, returns 1. + */ +static int +parse_attr_args(char *aoptsstr, sec_args_t **sec_args) +{ + char action; + char *attractptr; + char atype; + char *entry; + char *eptr; + char *nextattr; + char *nextentry; + char *subentry; + char *teptr; + char tok[] = {'\0', '\0'}; + int len; + f_attr_t i; + int numofattrs; + + if ((*aoptsstr != 'S') || (*(aoptsstr + 1) == '\0')) { + return (1); + } + + if ((eptr = strdup(aoptsstr + 1)) == NULL) { + perror("chmod"); + exit(2); + } + entry = eptr; + + /* + * Create a positional character array to determine a single attribute + * operation to be performed, where each index represents the system + * attribute affected, and it's value in the array represents the action + * to be performed, i.e., a value of '+' means to set the attribute, a + * value of '-' means to clear the attribute, and a value of '\0' means + * to leave the attribute untouched. Initially, this positional + * character array is all '\0's, representing a no-op. + */ + if ((numofattrs = attr_count()) < 1) { + errmsg(1, 1, gettext("system attributes not supported\n")); + } + + if ((attractptr = calloc(numofattrs, sizeof (char))) == NULL) { + perror("chmod"); + exit(2); + } + + if ((*sec_args = malloc(sizeof (sec_args_t))) == NULL) { + perror("chmod"); + exit(2); + } + (*sec_args)->sec_type = SEC_ATTR; + (*sec_args)->sec_attrs = NULL; + + /* Parse each attribute operation within the attribute specification. */ + while ((entry != NULL) && (*entry != '\0')) { + action = A_SET_OP; + atype = '\0'; + + /* Get the operator. */ + switch (*entry) { + case A_SET_OP: + case A_INVERSE_OP: + case A_REPLACE_OP: + action = *entry++; + break; + case A_COMPACT_TYPE: + case A_VERBOSE_TYPE: + case A_ALLATTRS_TYPE: + atype = *entry++; + action = A_SET_OP; + break; + default: + break; + } + + /* An attribute type must be specified. */ + if (atype == '\0') { + if ((*entry == A_COMPACT_TYPE) || + (*entry == A_VERBOSE_TYPE) || + (*entry == A_ALLATTRS_TYPE)) { + atype = *entry++; + } else { + return (1); + } + } + + /* Get the attribute specification separator. */ + if (*entry == LEFTBRACE) { + *tok = RIGHTBRACE; + entry++; + } else { + *tok = A_SEP; + } + + /* Get the attribute operation */ + if ((nextentry = strpbrk(entry, tok)) != NULL) { + *nextentry = '\0'; + nextentry++; + } + + /* Check for a no-op */ + if ((*entry == '\0') && (atype != A_ALLATTRS_TYPE) && + (action != A_REPLACE_OP)) { + entry = nextentry; + continue; + } + + /* + * Step through the attribute operation, setting the + * appropriate values for the specified attributes in the + * character array, attractptr. A value of '+' will mean the + * attribute is to be set, and a value of '-' will mean the + * attribute is to be cleared. If the value of an attribute + * remains '\0', then no action is to be taken on that + * attribute. As multiple operations specified are + * accumulated, a single attribute setting operation is + * represented in attractptr. + */ + len = strlen(entry); + if ((*tok == RIGHTBRACE) || (action == A_REPLACE_OP) || + (atype == A_ALLATTRS_TYPE)) { + + if ((action == A_REPLACE_OP) || + (atype == A_ALLATTRS_TYPE)) { + (void) memset(attractptr, '\0', numofattrs); + } + + if (len > 0) { + if ((teptr = strdup(entry)) == NULL) { + perror("chmod"); + exit(2); + } + subentry = teptr; + while (subentry != NULL) { + if ((nextattr = strpbrk(subentry, + A_SEP_TOK)) != NULL) { + *nextattr = '\0'; + nextattr++; + } + if (parse_entry(subentry, action, + atype, len, attractptr) != 0) { + return (1); + } + subentry = nextattr; + } + free(teptr); + } + + /* + * If performing the replace action, record the + * attributes and values for the rest of the + * attributes that have not already been recorded, + * otherwise record the specified action for all + * attributes. Note: set_attr_args() will only record + * the attribute and action if it is a boolean + * read-write attribute so we don't need to worry + * about checking it here. + */ + if ((action == A_REPLACE_OP) || + (atype == A_ALLATTRS_TYPE)) { + for (i = 0; i < numofattrs; i++) { + if (attractptr[i] == A_UNDEF_OP) { + (void) set_attr_args(i, + (action == A_SET_OP) ? + A_SET_OP : A_INVERSE_OP, + attractptr); + } + } + } + + } else { + if (parse_entry(entry, action, atype, len, + attractptr) != 0) { + return (1); + } + } + entry = nextentry; + } + + /* + * Populate an nvlist with attribute name and boolean value pairs + * using the single attribute operation. + */ + (*sec_args)->sec_attrs = set_attrs_nvlist(attractptr, numofattrs); + free(attractptr); + free(eptr); + + return (0); +} |