summaryrefslogtreecommitdiff
path: root/usr/src/cmd/chmod/chmod.c
diff options
context:
space:
mode:
authorahrens <none@none>2005-10-31 11:33:35 -0800
committerahrens <none@none>2005-10-31 11:33:35 -0800
commitfa9e4066f08beec538e775443c5be79dd423fcab (patch)
tree576d99665e57bb7cb70584431adb08c14d47e3ce /usr/src/cmd/chmod/chmod.c
parentf1b64740276f67fc6914c1d855f2af601efe99ac (diff)
downloadillumos-joyent-fa9e4066f08beec538e775443c5be79dd423fcab.tar.gz
PSARC 2002/240 ZFS
6338653 Integrate ZFS PSARC 2004/652 - DKIOCFLUSH 5096886 Write caching disks need mechanism to flush cache to physical media
Diffstat (limited to 'usr/src/cmd/chmod/chmod.c')
-rw-r--r--usr/src/cmd/chmod/chmod.c304
1 files changed, 281 insertions, 23 deletions
diff --git a/usr/src/cmd/chmod/chmod.c b/usr/src/cmd/chmod/chmod.c
index 37f07e0b2e..71dc1de1df 100644
--- a/usr/src/cmd/chmod/chmod.c
+++ b/usr/src/cmd/chmod/chmod.c
@@ -20,7 +20,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -44,6 +44,7 @@
* chmod option mode files
* where
* mode is [ugoa][+-=][rwxXlstugo] or an octal number
+ * mode is [<+|->A[# <number] ]<aclspec>
* option is -R and -f
*/
@@ -63,8 +64,10 @@
#include <string.h> /* strerror() */
#include <stdarg.h>
#include <limits.h>
+#include <ctype.h>
#include <errno.h>
#include <sys/acl.h>
+#include <aclutils.h>
static int rflag;
static int fflag;
@@ -77,25 +80,38 @@ 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
+
+typedef struct acl_args {
+ acl_t *acl_aclp;
+ int acl_slot;
+ 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);
static int
-dochmod(char *name, char *path, mode_t umsk),
-chmodr(char *dir, char *path, mode_t mode, mode_t umsk);
+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);
static void handle_acl(char *name, o_mode_t group_clear_bits,
- o_mode_t group_set_bits);
+ o_mode_t group_set_bits);
-static void
-usage(void);
+static void usage(void);
-void
-errmsg(int severity, int code, char *format, ...);
+void errmsg(int severity, int code, char *format, ...);
-static void
-parseargs(int ac, char *av[]);
+static void parseargs(int ac, char *av[]);
+
+int
+parse_acl_args(char *arg, acl_args_t **acl_args);
int
main(int argc, char *argv[])
@@ -103,6 +119,7 @@ main(int argc, char *argv[])
int i, c;
int status = 0;
mode_t umsk;
+ acl_args_t *acl_args = NULL;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
@@ -134,9 +151,16 @@ main(int argc, char *argv[])
mac -= optind;
mav += optind;
- if (mac < 2) {
- usage();
- exit(2);
+ if (mac >= 2 && (mav[0][0] == 'A')) {
+ if (parse_acl_args(*mav, &acl_args)) {
+ usage();
+ exit(2);
+ }
+ } else {
+ if (mac < 2) {
+ usage();
+ exit(2);
+ }
}
ms = mav[0];
@@ -144,14 +168,15 @@ main(int argc, char *argv[])
umsk = umask(0);
(void) umask(umsk);
- for (i = 1; i < mac; i++)
- status += dochmod(mav[i], mav[i], umsk);
+ for (i = 1; i < mac; i++) {
+ status += dochmod(mav[i], mav[i], umsk, acl_args);
+ }
return (fflag ? 0 : status);
}
static int
-dochmod(char *name, char *path, mode_t umsk)
+dochmod(char *name, char *path, mode_t umsk, acl_args_t *aclp)
{
static struct stat st;
int linkflg = 0;
@@ -172,9 +197,11 @@ dochmod(char *name, char *path, mode_t umsk)
/* 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));
+ return (chmodr(name, path, st.st_mode, umsk, aclp));
- if (chmod(name, newmode_common(ms, st.st_mode, umsk, name, path,
+ 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);
@@ -195,7 +222,7 @@ dochmod(char *name, char *path, mode_t umsk)
static int
-chmodr(char *dir, char *path, mode_t mode, mode_t umsk)
+chmodr(char *dir, char *path, mode_t mode, mode_t umsk, acl_args_t *aclp)
{
DIR *dirp;
@@ -204,6 +231,7 @@ chmodr(char *dir, char *path, mode_t mode, mode_t umsk)
char currdir[PATH_MAX+1]; /* current dir name + '/' */
char parentdir[PATH_MAX+1]; /* parent dir name + '/' */
int ecode;
+ struct stat st;
o_mode_t group_clear_bits, group_set_bits;
if (getcwd(savedir, PATH_MAX) == 0)
@@ -213,7 +241,14 @@ chmodr(char *dir, char *path, mode_t mode, mode_t umsk)
/*
* Change what we are given before doing it's contents
*/
- if (chmod(dir, newmode_common(ms, mode, umsk, dir, path,
+ if (aclp) {
+ if (lstat(dir, &st) < 0) {
+ errmsg(2, 0, gettext("can't access %s\n"), path);
+ return (1);
+ }
+ if (doacl(dir, &st, aclp) != 0)
+ 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);
return (1);
@@ -226,8 +261,11 @@ chmodr(char *dir, char *path, mode_t mode, mode_t umsk)
* permissions changes to both the acl mask and the
* general group permissions.
*/
- if (group_clear_bits || group_set_bits)
- handle_acl(dir, group_clear_bits, group_set_bits);
+
+ if (aclp == NULL) { /* only necessary when not setting ACL */
+ if (group_clear_bits || group_set_bits)
+ handle_acl(dir, group_clear_bits, group_set_bits);
+ }
if (chdir(dir) < 0) {
errmsg(2, 0, "%s/%s: %s\n", savedir, dir, strerror(errno));
@@ -255,7 +293,7 @@ chmodr(char *dir, char *path, mode_t mode, mode_t umsk)
for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
(void) strcpy(currdir, parentdir);
(void) strcat(currdir, dp->d_name);
- ecode += dochmod(dp->d_name, currdir, umsk);
+ ecode += dochmod(dp->d_name, currdir, umsk, aclp);
}
(void) closedir(dirp);
if (chdir(savedir) < 0) {
@@ -301,13 +339,26 @@ usage(void)
"usage:\tchmod [-fR] <absolute-mode> file ...\n"));
(void) fprintf(stderr, gettext(
+ "\tchmod [-fR] <ACL-operation> file ...\n"));
+
+ (void) fprintf(stderr, gettext(
"\tchmod [-fR] <symbolic-mode-list> file ...\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"));
+
+ (void) fprintf(stderr, gettext(
+ "where \t<ACL-operation> is one of the following\n"));
+ (void) fprintf(stderr, gettext("\tA-<acl_specification>\n"));
+ (void) fprintf(stderr, gettext("\tA[number]-\n"));
+ (void) fprintf(stderr, gettext(
+ "\tA[number]{+|=}<acl_specification>\n"));
+ (void) fprintf(stderr, gettext(
+ "where \t<acl-specification> is a comma-separated list of ACEs\n"));
}
/*
@@ -373,6 +424,74 @@ parseargs(int ac, char *av[])
mav[mac] = (char *)NULL;
}
+int
+parse_acl_args(char *arg, acl_args_t **acl_args)
+{
+ acl_t *new_acl = NULL;
+ int slot;
+ int error;
+ int len;
+ int action;
+ acl_args_t *new_acl_args;
+ char *acl_spec = NULL;
+ char *end;
+
+ if (arg[0] != 'A')
+ return (1);
+
+ slot = strtol(&arg[1], &end, 10);
+
+ len = strlen(arg);
+ switch (*end) {
+ case '+':
+ action = ACL_ADD;
+ acl_spec = ++end;
+ break;
+ case '-':
+ if (len == 2 && arg[0] == 'A' && arg[1] == '-')
+ action = ACL_STRIP;
+ else
+ action = ACL_DELETE;
+ if (action != ACL_STRIP) {
+ acl_spec = ++end;
+ if (acl_spec[0] == '\0') {
+ action = ACL_SLOT_DELETE;
+ acl_spec = NULL;
+ } else if (arg[1] != '-')
+ return (1);
+ }
+ break;
+ case '=':
+ action = ACL_REPLACE;
+ acl_spec = ++end;
+ break;
+ default:
+ return (1);
+ }
+
+ if ((action == ACL_REPLACE || action == ACL_ADD) && acl_spec[0] == '\0')
+ return (1);
+
+ if (acl_spec) {
+ if (error = acl_fromtext(acl_spec, &new_acl)) {
+ errmsg(1, 1, "%s\n", acl_strerror(error));
+ return (1);
+ }
+ }
+
+ new_acl_args = malloc(sizeof (acl_args_t));
+ if (new_acl_args == NULL)
+ return (1);
+
+ new_acl_args->acl_aclp = new_acl;
+ new_acl_args->acl_slot = slot;
+ new_acl_args->acl_action = action;
+
+ *acl_args = new_acl_args;
+
+ return (0);
+}
+
/*
* This function is called whenever the group permissions of a file
* is being modified. According to the chmod(1) manpage, any
@@ -388,6 +507,14 @@ handle_acl(char *name, o_mode_t group_clear_bits, o_mode_t group_set_bits)
aclent_t *aclp, *tp;
o_mode_t newperm;
+ /*
+ * if this file system support ace_t acl's
+ * then simply return since we don't have an
+ * acl mask to deal with
+ */
+ if (pathconf(name, _PC_ACL_ENABLED) == _ACL_ACE_ENABLED)
+ return;
+
if ((aclcnt = acl(name, GETACLCNT, 0, NULL)) <= MIN_ACL_ENTRIES)
return; /* it's just a trivial acl; no need to change it */
@@ -424,3 +551,134 @@ handle_acl(char *name, o_mode_t group_clear_bits, o_mode_t group_set_bits)
}
free(aclp);
}
+
+static int
+doacl(char *file, struct stat *st, acl_args_t *acl_args)
+{
+ acl_t *aclp;
+ acl_t *set_aclp;
+ int error = 0;
+ void *to, *from;
+ int len;
+ int isdir;
+
+ isdir = S_ISDIR(st->st_mode);
+
+ error = acl_get(file, 0, &aclp);
+
+ if (error != 0) {
+ errmsg(1, 1, "%s\n", acl_strerror(error));
+ return (1);
+ }
+
+ 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);
+ }
+ set_aclp = aclp;
+ break;
+ case ACL_SLOT_DELETE:
+
+ if (acl_args->acl_slot + 1 > aclp->acl_cnt) {
+ errmsg(1, 1,
+ gettext("Invalid slot specified for removal\n"));
+ acl_free(aclp);
+ return (1);
+ }
+
+ if (acl_args->acl_slot == 0 && aclp->acl_cnt == 1) {
+ errmsg(1, 1,
+ gettext("Can't remove all ACL "
+ "entries from a file\n"));
+ acl_free(aclp);
+ return (1);
+ }
+
+ /*
+ * remove a single entry
+ *
+ * if last entry just adjust acl_cnt
+ */
+
+ if ((acl_args->acl_slot + 1) == aclp->acl_cnt)
+ aclp->acl_cnt--;
+ else {
+ to = (char *)aclp->acl_aclp +
+ (acl_args->acl_slot * aclp->acl_entry_size);
+ from = (char *)to + aclp->acl_entry_size;
+ len = (aclp->acl_cnt - acl_args->acl_slot - 1) *
+ aclp->acl_entry_size;
+ (void) memmove(to, from, len);
+ aclp->acl_cnt--;
+ }
+ set_aclp = aclp;
+ break;
+
+ case ACL_DELETE:
+ if ((error = acl_removeentries(aclp, acl_args->acl_aclp,
+ acl_args->acl_slot, ACL_REMOVE_ALL)) != 0) {
+ errmsg(1, 1, "%s\n", acl_strerror(error));
+ acl_free(aclp);
+ return (1);
+ }
+
+ if (aclp->acl_cnt == 0) {
+ errmsg(1, 1,
+ gettext("Can't remove all ACL "
+ "entries from a file\n"));
+ acl_free(aclp);
+ return (1);
+ }
+
+ set_aclp = aclp;
+ break;
+ case ACL_REPLACE:
+ if (acl_args->acl_slot >= 0) {
+ error = acl_modifyentries(aclp, acl_args->acl_aclp,
+ acl_args->acl_slot);
+ if (error) {
+ errmsg(1, 1, "%s\n", acl_strerror(error));
+ acl_free(aclp);
+ return (1);
+ }
+ set_aclp = aclp;
+ } else {
+ set_aclp = acl_args->acl_aclp;
+ }
+ break;
+ case ACL_STRIP:
+ error = acl_strip(file, st->st_uid, st->st_gid, st->st_mode);
+ if (error) {
+ errmsg(1, 1, "%s\n", acl_strerror(error));
+ return (1);
+ }
+ acl_free(aclp);
+ return (0);
+ /*NOTREACHED*/
+ default:
+ errmsg(1, 0, gettext("Unknown ACL action requested\n"));
+ return (1);
+ break;
+ }
+
+ error = acl_check(set_aclp, isdir);
+
+ if (error) {
+ errmsg(1, 0, "%s\n%s", acl_strerror(error),
+ gettext("See chmod(1) for more information on "
+ "valid ACL syntax\n"));
+ return (1);
+ }
+ if ((error = acl_set(file, set_aclp)) != 0) {
+ errmsg(1, 0, gettext("Failed to set ACL: %s\n"),
+ acl_strerror(error));
+ acl_free(aclp);
+ return (1);
+ }
+ acl_free(aclp);
+ return (0);
+}