summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/mgmt_create.c31
-rw-r--r--usr/src/cmd/iscsi/iscsitgtd/mgmt_remove.c31
-rw-r--r--usr/src/cmd/truss/codes.c10
-rw-r--r--usr/src/cmd/zdb/zdb.c3
-rw-r--r--usr/src/cmd/zfs/Makefile4
-rw-r--r--usr/src/cmd/zfs/zfs_main.c581
-rw-r--r--usr/src/cmd/zpool/zpool_main.c343
-rw-r--r--usr/src/cmd/ztest/ztest.c2
-rw-r--r--usr/src/common/zfs/zfs_deleg.c249
-rw-r--r--usr/src/common/zfs/zfs_deleg.h60
-rw-r--r--usr/src/common/zfs/zfs_namecheck.c29
-rw-r--r--usr/src/common/zfs/zfs_namecheck.h6
-rw-r--r--usr/src/common/zfs/zfs_prop.c103
-rw-r--r--usr/src/grub/grub-0.95/stage2/zfs-include/dsl_dir.h3
-rw-r--r--usr/src/grub/grub-0.95/stage2/zfs-include/zfs.h3
-rw-r--r--usr/src/lib/libiscsitgt/Makefile.com5
-rw-r--r--usr/src/lib/libiscsitgt/common/gen.c18
-rw-r--r--usr/src/lib/libiscsitgt/common/if_zfs.c42
-rw-r--r--usr/src/lib/libiscsitgt/common/libiscsitgt.h7
-rw-r--r--usr/src/lib/libiscsitgt/common/mapfile-vers3
-rw-r--r--usr/src/lib/libshare/common/libshare.c4
-rw-r--r--usr/src/lib/libshare/common/libshare.h2
-rw-r--r--usr/src/lib/libshare/common/libshare_impl.h11
-rw-r--r--usr/src/lib/libshare/common/libshare_zfs.c75
-rw-r--r--usr/src/lib/libshare/common/libsharecore.c16
-rw-r--r--usr/src/lib/libshare/common/mapfile-vers4
-rw-r--r--usr/src/lib/libshare/common/plugin.c4
-rw-r--r--usr/src/lib/libshare/nfs/libshare_nfs.c105
-rw-r--r--usr/src/lib/libzfs/Makefile.com4
-rw-r--r--usr/src/lib/libzfs/common/libzfs.h67
-rw-r--r--usr/src/lib/libzfs/common/libzfs_dataset.c946
-rw-r--r--usr/src/lib/libzfs/common/libzfs_impl.h3
-rw-r--r--usr/src/lib/libzfs/common/libzfs_mount.c44
-rw-r--r--usr/src/lib/libzfs/common/libzfs_pool.c79
-rw-r--r--usr/src/lib/libzfs/common/libzfs_util.c55
-rw-r--r--usr/src/lib/libzfs/common/mapfile-vers10
-rw-r--r--usr/src/lib/libzpool/Makefile.com1
-rw-r--r--usr/src/lib/libzpool/common/kernel.c42
-rw-r--r--usr/src/lib/libzpool/common/sys/zfs_context.h11
-rw-r--r--usr/src/uts/common/Makefile.files4
-rw-r--r--usr/src/uts/common/fs/nfs/nfs_sys.c25
-rw-r--r--usr/src/uts/common/fs/sharefs/sharetab.c34
-rw-r--r--usr/src/uts/common/fs/zfs/dmu.c3
-rw-r--r--usr/src/uts/common/fs/zfs/dmu_objset.c36
-rw-r--r--usr/src/uts/common/fs/zfs/dmu_send.c23
-rw-r--r--usr/src/uts/common/fs/zfs/dsl_dataset.c50
-rw-r--r--usr/src/uts/common/fs/zfs/dsl_deleg.c744
-rw-r--r--usr/src/uts/common/fs/zfs/dsl_dir.c34
-rw-r--r--usr/src/uts/common/fs/zfs/dsl_prop.c27
-rw-r--r--usr/src/uts/common/fs/zfs/dsl_synctask.c7
-rw-r--r--usr/src/uts/common/fs/zfs/spa.c25
-rw-r--r--usr/src/uts/common/fs/zfs/spa_history.c89
-rw-r--r--usr/src/uts/common/fs/zfs/sys/dmu.h5
-rw-r--r--usr/src/uts/common/fs/zfs/sys/dmu_objset.h2
-rw-r--r--usr/src/uts/common/fs/zfs/sys/dsl_deleg.h88
-rw-r--r--usr/src/uts/common/fs/zfs/sys/dsl_dir.h3
-rw-r--r--usr/src/uts/common/fs/zfs/sys/dsl_prop.h4
-rw-r--r--usr/src/uts/common/fs/zfs/sys/dsl_synctask.h5
-rw-r--r--usr/src/uts/common/fs/zfs/sys/spa.h19
-rw-r--r--usr/src/uts/common/fs/zfs/sys/spa_impl.h1
-rw-r--r--usr/src/uts/common/fs/zfs/sys/zfs_ctldir.h8
-rw-r--r--usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h26
-rw-r--r--usr/src/uts/common/fs/zfs/sys/zvol.h4
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_ctldir.c48
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_ioctl.c811
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_vfsops.c54
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_vnops.c7
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_znode.c8
-rw-r--r--usr/src/uts/common/fs/zfs/zvol.c13
-rw-r--r--usr/src/uts/common/os/policy.c69
-rw-r--r--usr/src/uts/common/sys/fs/zfs.h87
-rw-r--r--usr/src/uts/common/sys/policy.h1
72 files changed, 4703 insertions, 677 deletions
diff --git a/usr/src/cmd/iscsi/iscsitgtd/mgmt_create.c b/usr/src/cmd/iscsi/iscsitgtd/mgmt_create.c
index 3822265013..69e8c95725 100644
--- a/usr/src/cmd/iscsi/iscsitgtd/mgmt_create.c
+++ b/usr/src/cmd/iscsi/iscsitgtd/mgmt_create.c
@@ -450,18 +450,31 @@ create_zfs(tgt_node_t *x, ucred_t *cred)
xmlTextReaderPtr r;
const priv_set_t *eset;
- eset = ucred_getprivset(cred, PRIV_EFFECTIVE);
- if (eset != NULL ? !priv_ismember(eset, PRIV_SYS_CONFIG) :
- ucred_geteuid(cred) != 0) {
- xml_rtn_msg(&msg, ERR_NO_PERMISSION);
+ if (tgt_find_value_str(x, XML_ELEMENT_NAME, &dataset) == False) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_NAME);
goto error;
}
- if (tgt_find_value_str(x, XML_ELEMENT_NAME, &dataset) == False) {
- xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_NAME);
+ if (((zh = libzfs_init()) == NULL) ||
+ ((zfsh = zfs_open(zh, dataset, ZFS_TYPE_ANY)) == NULL)) {
+ xml_rtn_msg(&msg, ERR_INTERNAL_ERROR);
goto error;
}
+ eset = ucred_getprivset(cred, PRIV_EFFECTIVE);
+ if (eset != NULL ? !priv_ismember(eset, PRIV_SYS_CONFIG) :
+ ucred_geteuid(cred) != 0) {
+
+ /*
+ * See if user has ZFS dataset permissions to do operation
+ */
+ if (zfs_iscsi_perm_check(zh, dataset, cred) != 0) {
+ xml_rtn_msg(&msg, ERR_NO_PERMISSION);
+ goto error;
+ }
+ }
+
+
while ((dnode = tgt_node_next(targets_config, XML_ELEMENT_TARG,
dnode)) != NULL) {
if (strcmp(dnode->x_value, dataset) == 0) {
@@ -470,12 +483,6 @@ create_zfs(tgt_node_t *x, ucred_t *cred)
}
}
- if (((zh = libzfs_init()) == NULL) ||
- ((zfsh = zfs_open(zh, dataset, ZFS_TYPE_ANY)) == NULL)) {
- xml_rtn_msg(&msg, ERR_INTERNAL_ERROR);
- goto error;
- }
-
prop_len = 1024;
if ((prop = malloc(prop_len)) == NULL) {
xml_rtn_msg(&msg, ERR_INTERNAL_ERROR);
diff --git a/usr/src/cmd/iscsi/iscsitgtd/mgmt_remove.c b/usr/src/cmd/iscsi/iscsitgtd/mgmt_remove.c
index 0f907bb977..dd3c1f4434 100644
--- a/usr/src/cmd/iscsi/iscsitgtd/mgmt_remove.c
+++ b/usr/src/cmd/iscsi/iscsitgtd/mgmt_remove.c
@@ -37,6 +37,7 @@
#include <unistd.h>
#include <priv.h>
#include <syslog.h>
+#include <libzfs.h>
#include <iscsitgt_impl.h>
#include "utility.h"
@@ -94,21 +95,37 @@ remove_zfs(tgt_node_t *x, ucred_t *cred)
{
char *prop;
char *msg = NULL;
- tgt_node_t *targ = NULL;
+ tgt_node_t *targ = NULL;
const priv_set_t *eset;
+ libzfs_handle_t *zh = NULL;
- eset = ucred_getprivset(cred, PRIV_EFFECTIVE);
- if (eset != NULL ? !priv_ismember(eset, PRIV_SYS_CONFIG) :
- ucred_geteuid(cred) != 0) {
- xml_rtn_msg(&msg, ERR_NO_PERMISSION);
+ if (tgt_find_value_str(x, XML_ELEMENT_NAME, &prop) == False) {
+ xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_NAME);
return (msg);
}
- if (tgt_find_value_str(x, XML_ELEMENT_NAME, &prop) == False) {
- xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_NAME);
+ if ((zh = libzfs_init()) == NULL) {
+ xml_rtn_msg(&msg, ERR_INTERNAL_ERROR);
+ free(prop);
return (msg);
}
+ eset = ucred_getprivset(cred, PRIV_EFFECTIVE);
+ if (eset != NULL ? !priv_ismember(eset, PRIV_SYS_CONFIG) :
+ ucred_geteuid(cred) != 0) {
+ /*
+ * See if user has ZFS dataset permissions to do operation
+ */
+ if (zfs_iscsi_perm_check(zh, prop, cred) != 0) {
+ xml_rtn_msg(&msg, ERR_NO_PERMISSION);
+ free(prop);
+ libzfs_fini(zh);
+ return (msg);
+ }
+ }
+
+ libzfs_fini(zh);
+
while ((targ = tgt_node_next(targets_config, XML_ELEMENT_TARG, targ))
!= NULL) {
if (strcmp(targ->x_value, prop) == 0)
diff --git a/usr/src/cmd/truss/codes.c b/usr/src/cmd/truss/codes.c
index dbc2ccca9e..4e59786350 100644
--- a/usr/src/cmd/truss/codes.c
+++ b/usr/src/cmd/truss/codes.c
@@ -864,8 +864,6 @@ const struct ioc {
"zfs_cmd_t" },
{ (uint_t)ZFS_IOC_POOL_GET_HISTORY, "ZFS_IOC_POOL_GET_HISTORY",
"zfs_cmd_t" },
- { (uint_t)ZFS_IOC_POOL_LOG_HISTORY, "ZFS_IOC_POOL_LOG_HISTORY",
- "zfs_cmd_t" },
{ (uint_t)ZFS_IOC_VDEV_ADD, "ZFS_IOC_VDEV_ADD",
"zfs_cmd_t" },
{ (uint_t)ZFS_IOC_VDEV_REMOVE, "ZFS_IOC_VDEV_REMOVE",
@@ -926,6 +924,14 @@ const struct ioc {
"zfs_cmd_t" },
{ (uint_t)ZFS_IOC_POOL_GET_PROPS, "ZFS_IOC_POOL_GET_PROPS",
"zfs_cmd_t" },
+ { (uint_t)ZFS_IOC_SET_FSACL, "ZFS_IOC_SET_FSACL",
+ "zfs_cmd_t" },
+ { (uint_t)ZFS_IOC_GET_FSACL, "ZFS_IOC_GET_FSACL",
+ "zfs_cmd_t" },
+ { (uint_t)ZFS_IOC_ISCSI_PERM_CHECK, "ZFS_IOC_ISCSI_PERM_CHECK",
+ "zfs_cmd_t" },
+ { (uint_t)ZFS_IOC_SHARE, "ZFS_IOC_SHARE",
+ "zfs_cmd_t" },
/* kssl ioctls */
{ (uint_t)KSSL_ADD_ENTRY, "KSSL_ADD_ENTRY",
diff --git a/usr/src/cmd/zdb/zdb.c b/usr/src/cmd/zdb/zdb.c
index 41d61d2904..23cf656ebe 100644
--- a/usr/src/cmd/zdb/zdb.c
+++ b/usr/src/cmd/zdb/zdb.c
@@ -700,6 +700,8 @@ dump_dsl_dir(objset_t *os, uint64_t object, void *data, size_t size)
(void) printf("\t\treserved = %s\n", resv);
(void) printf("\t\tprops_zapobj = %llu\n",
(u_longlong_t)dd->dd_props_zapobj);
+ (void) printf("\t\tdeleg_zapobj = %llu\n",
+ (u_longlong_t)dd->dd_deleg_zapobj);
}
/*ARGSUSED*/
@@ -888,6 +890,7 @@ static object_viewer_t *object_viewer[DMU_OT_NUMTYPES] = {
dump_uint8, /* SPA history */
dump_uint64, /* SPA history offsets */
dump_zap, /* Pool properties */
+ dump_zap, /* DSL permissions */
};
static void
diff --git a/usr/src/cmd/zfs/Makefile b/usr/src/cmd/zfs/Makefile
index 9b80822041..4761bea33e 100644
--- a/usr/src/cmd/zfs/Makefile
+++ b/usr/src/cmd/zfs/Makefile
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -38,7 +38,7 @@ LINKPROGS= mount umount
ROOTETCFSTYPE= $(ROOTETC)/fs/$(FSTYPE)
USRLIBFSTYPE= $(ROOTLIB)/fs/$(FSTYPE)
-LDLIBS += -lzfs -luutil -lumem -lnvpair
+LDLIBS += -lzfs -luutil -lumem -lnvpair -lavl
C99MODE= -xc99=%all
C99LMODE= -Xc99=%all
diff --git a/usr/src/cmd/zfs/zfs_main.c b/usr/src/cmd/zfs/zfs_main.c
index 84dfaafedd..fd178add9e 100644
--- a/usr/src/cmd/zfs/zfs_main.c
+++ b/usr/src/cmd/zfs/zfs_main.c
@@ -32,6 +32,7 @@
#include <libgen.h>
#include <libintl.h>
#include <libuutil.h>
+#include <libnvpair.h>
#include <locale.h>
#include <stddef.h>
#include <stdio.h>
@@ -45,8 +46,10 @@
#include <sys/mnttab.h>
#include <sys/mount.h>
#include <sys/stat.h>
+#include <sys/avl.h>
#include <libzfs.h>
+#include <libuutil.h>
#include "zfs_iter.h"
#include "zfs_util.h"
@@ -72,6 +75,8 @@ static int zfs_do_unshare(int argc, char **argv);
static int zfs_do_send(int argc, char **argv);
static int zfs_do_receive(int argc, char **argv);
static int zfs_do_promote(int argc, char **argv);
+static int zfs_do_allow(int argc, char **argv);
+static int zfs_do_unallow(int argc, char **argv);
/*
* These libumem hooks provide a reasonable set of defaults for the allocator's
@@ -106,7 +111,9 @@ typedef enum {
HELP_SHARE,
HELP_SNAPSHOT,
HELP_UNMOUNT,
- HELP_UNSHARE
+ HELP_UNSHARE,
+ HELP_ALLOW,
+ HELP_UNALLOW
} zfs_help_t;
typedef struct zfs_command {
@@ -150,6 +157,10 @@ static zfs_command_t command_table[] = {
{ NULL },
{ "send", zfs_do_send, HELP_SEND },
{ "receive", zfs_do_receive, HELP_RECEIVE },
+ { NULL },
+ { "allow", zfs_do_allow, HELP_ALLOW },
+ { NULL },
+ { "unallow", zfs_do_unallow, HELP_UNALLOW },
};
#define NCOMMAND (sizeof (command_table) / sizeof (command_table[0]))
@@ -220,6 +231,47 @@ get_usage(zfs_help_t idx)
case HELP_UNSHARE:
return (gettext("\tunshare [-f] -a\n"
"\tunshare [-f] <filesystem|mountpoint>\n"));
+ case HELP_ALLOW:
+ return (gettext("\tallow [-l][-d] <everyone|user|group>[,"
+ "<everyone|user|group>...]\n\t "
+ "<perm>|@<setname>[,<perm>|@<setname>...]\n\t"
+ " <filesystem|volume\n"
+ "\tallow [-l] [-d] -u <user> "
+ "<perm>|@<setname>[,<perm>|@<setname>...]\n\t"
+ " <filesystem|volume>\n"
+ "\tallow [-l] [-d] -g <group> "
+ "<perm>|@<setname>[,<perm>|@<setname>...]\n\t"
+ " <filesystem|volume>\n"
+ "\tallow [-l] [-d] -e "
+ "<perm>|@<setname>[,<perm>|@<setname>...]\n\t"
+ " <filesystem|volume>\n"
+ "\tallow -c "
+ "<perm>|@<setname>[,<perm>|@<setname>...]\n\t"
+ " <filesystem|volume>\n"
+ "\tallow -s @setname "
+ "<perm>|@<setname>[,<perm>|@<setname>...]\n\t"
+ " <filesystem|volume>\n"));
+
+ case HELP_UNALLOW:
+ return (gettext("\tunallow [-r][-l][-d] <everyone|user|group>[,"
+ "<everyone|user|group>...] \n\t "
+ "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t"
+ " <filesystem|volume>\n"
+ "\tunallow [-r][-l][-d] -u user "
+ "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t"
+ " <filesystem|volume>\n"
+ "\tunallow [-r][-l][-d] -g group "
+ "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t"
+ " <filesystem|volume>\n"
+ "\tunallow [-r][-l][-d] -e "
+ "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t"
+ " <filesystem|volume>\n"
+ "\tunallow [-r] -c "
+ "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t"
+ " <filesystem|volume>\n"
+ "\tunallow [-r] -s @setname "
+ "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t"
+ " <filesystem|volume> \n\t"));
}
abort();
@@ -426,8 +478,6 @@ zfs_do_clone(int argc, char **argv)
ret = zfs_share(clone);
zfs_close(clone);
}
- zpool_log_history(g_zfs, argc + optind, argv - optind, argv[1],
- B_FALSE, B_FALSE);
}
zfs_close(zhp);
@@ -598,11 +648,6 @@ zfs_do_create(int argc, char **argv)
if (zfs_create(g_zfs, argv[0], type, props) != 0)
goto error;
- if (propval != NULL)
- *(propval - 1) = '=';
- zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0],
- B_FALSE, B_FALSE);
-
if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL)
goto error;
@@ -848,9 +893,6 @@ zfs_do_destroy(int argc, char **argv)
if (ret) {
(void) fprintf(stderr,
gettext("no snapshots destroyed\n"));
- } else {
- zpool_log_history(g_zfs, argc + optind, argv - optind,
- argv[0], B_FALSE, B_FALSE);
}
return (ret != 0);
}
@@ -890,7 +932,6 @@ zfs_do_destroy(int argc, char **argv)
return (1);
}
-
if (cb.cb_error ||
zfs_iter_dependents(zhp, B_FALSE, destroy_callback, &cb) != 0) {
zfs_close(zhp);
@@ -901,11 +942,10 @@ zfs_do_destroy(int argc, char **argv)
* Do the real thing. The callback will close the handle regardless of
* whether it succeeds or not.
*/
+
if (destroy_callback(zhp, &cb) != 0)
return (1);
- zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0],
- B_FALSE, B_FALSE);
return (0);
}
@@ -1181,20 +1221,14 @@ zfs_do_get(int argc, char **argv)
* useful for setting a property on a hierarchy-wide basis, regardless of any
* local modifications for each dataset.
*/
-typedef struct inherit_cbdata {
- char *cb_propname;
- boolean_t cb_any_successful;
-} inherit_cbdata_t;
static int
inherit_callback(zfs_handle_t *zhp, void *data)
{
- inherit_cbdata_t *cbp = data;
+ char *propname = data;
int ret;
- ret = zfs_prop_inherit(zhp, cbp->cb_propname);
- if (ret == 0)
- cbp->cb_any_successful = B_TRUE;
+ ret = zfs_prop_inherit(zhp, propname);
return (ret != 0);
}
@@ -1204,7 +1238,7 @@ zfs_do_inherit(int argc, char **argv)
boolean_t recurse = B_FALSE;
int c;
zfs_prop_t prop;
- inherit_cbdata_t cb;
+ char *propname;
int ret;
/* check options */
@@ -1234,43 +1268,35 @@ zfs_do_inherit(int argc, char **argv)
usage(B_FALSE);
}
- cb.cb_propname = argv[0];
+ propname = argv[0];
argc--;
argv++;
- if ((prop = zfs_name_to_prop(cb.cb_propname)) != ZFS_PROP_INVAL) {
+ if ((prop = zfs_name_to_prop(propname)) != ZFS_PROP_INVAL) {
if (zfs_prop_readonly(prop)) {
(void) fprintf(stderr, gettext(
"%s property is read-only\n"),
- cb.cb_propname);
+ propname);
return (1);
}
if (!zfs_prop_inheritable(prop)) {
(void) fprintf(stderr, gettext("'%s' property cannot "
- "be inherited\n"), cb.cb_propname);
+ "be inherited\n"), propname);
if (prop == ZFS_PROP_QUOTA ||
prop == ZFS_PROP_RESERVATION)
(void) fprintf(stderr, gettext("use 'zfs set "
- "%s=none' to clear\n"), cb.cb_propname);
+ "%s=none' to clear\n"), propname);
return (1);
}
- } else if (!zfs_prop_user(cb.cb_propname)) {
- (void) fprintf(stderr, gettext(
- "invalid property '%s'\n"),
- cb.cb_propname);
+ } else if (!zfs_prop_user(propname)) {
+ (void) fprintf(stderr, gettext("invalid property '%s'\n"),
+ propname);
usage(B_FALSE);
}
- cb.cb_any_successful = B_FALSE;
-
ret = zfs_for_each(argc, argv, recurse,
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, NULL,
- inherit_callback, &cb, B_FALSE);
-
- if (cb.cb_any_successful) {
- zpool_log_history(g_zfs, argc + optind + 1, argv - optind - 1,
- argv[0], B_FALSE, B_FALSE);
- }
+ inherit_callback, propname, B_FALSE);
return (ret);
}
@@ -1606,10 +1632,6 @@ zfs_do_rename(int argc, char **argv)
ret = (zfs_rename(zhp, argv[1], recurse) != 0);
- if (!ret)
- zpool_log_history(g_zfs, argc + optind, argv - optind, argv[1],
- B_FALSE, B_FALSE);
-
zfs_close(zhp);
return (ret);
}
@@ -1650,8 +1672,6 @@ zfs_do_promote(int argc, char **argv)
ret = (zfs_promote(zhp) != 0);
- if (!ret)
- zpool_log_history(g_zfs, argc, argv, argv[1], B_FALSE, B_FALSE);
zfs_close(zhp);
return (ret);
@@ -1820,11 +1840,6 @@ zfs_do_rollback(int argc, char **argv)
*/
ret = zfs_rollback(zhp, snap, force);
- if (!ret) {
- zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0],
- B_FALSE, B_FALSE);
- }
-
out:
zfs_close(snap);
zfs_close(zhp);
@@ -1843,7 +1858,6 @@ out:
typedef struct set_cbdata {
char *cb_propname;
char *cb_value;
- boolean_t cb_any_successful;
} set_cbdata_t;
static int
@@ -1864,7 +1878,6 @@ set_callback(zfs_handle_t *zhp, void *data)
}
return (1);
}
- cbp->cb_any_successful = B_TRUE;
return (0);
}
@@ -1902,7 +1915,6 @@ zfs_do_set(int argc, char **argv)
*cb.cb_value = '\0';
cb.cb_value++;
- cb.cb_any_successful = B_FALSE;
if (*cb.cb_propname == '\0') {
(void) fprintf(stderr,
@@ -1910,14 +1922,10 @@ zfs_do_set(int argc, char **argv)
usage(B_FALSE);
}
+
ret = zfs_for_each(argc - 2, argv + 2, B_FALSE,
ZFS_TYPE_ANY, NULL, NULL, set_callback, &cb, B_FALSE);
- if (cb.cb_any_successful) {
- *(cb.cb_value - 1) = '=';
- zpool_log_history(g_zfs, argc, argv, argv[2], B_FALSE, B_FALSE);
- }
-
return (ret);
}
@@ -1963,10 +1971,6 @@ zfs_do_snapshot(int argc, char **argv)
ret = zfs_snapshot(g_zfs, argv[0], recursive);
if (ret && recursive)
(void) fprintf(stderr, gettext("no snapshots were created\n"));
- if (!ret) {
- zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0],
- B_FALSE, B_FALSE);
- }
return (ret != 0);
}
@@ -2117,12 +2121,386 @@ zfs_do_receive(int argc, char **argv)
err = zfs_receive(g_zfs, argv[0], isprefix, verbose, dryrun, force,
STDIN_FILENO);
- if (!err) {
- zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0],
- B_FALSE, B_FALSE);
+ return (err != 0);
+}
+
+typedef struct allow_cb {
+ int a_permcnt;
+ size_t a_treeoffset;
+} allow_cb_t;
+
+static void
+zfs_print_perms(avl_tree_t *tree)
+{
+ zfs_perm_node_t *permnode;
+
+ permnode = avl_first(tree);
+ while (permnode != NULL) {
+ (void) printf("%s", permnode->z_pname);
+ permnode = AVL_NEXT(tree, permnode);
+ if (permnode)
+ (void) printf(",");
+ else
+ (void) printf("\n");
}
+}
- return (err != 0);
+/*
+ * Iterate over user/groups/everyone/... and the call perm_iter
+ * function to print actual permission when tree has >0 nodes.
+ */
+static void
+zfs_iter_perms(avl_tree_t *tree, const char *banner, allow_cb_t *cb)
+{
+ zfs_allow_node_t *item;
+ avl_tree_t *ptree;
+
+ item = avl_first(tree);
+ while (item) {
+ ptree = (void *)((char *)item + cb->a_treeoffset);
+ if (avl_numnodes(ptree)) {
+ if (cb->a_permcnt++ == 0)
+ (void) printf("%s\n", banner);
+ (void) printf("\t%s", item->z_key);
+ /*
+ * Avoid an extra space being printed
+ * for "everyone" which is keyed with a null
+ * string
+ */
+ if (item->z_key[0] != '\0')
+ (void) printf(" ");
+ zfs_print_perms(ptree);
+ }
+ item = AVL_NEXT(tree, item);
+ }
+}
+
+#define LINES "-------------------------------------------------------------\n"
+static int
+zfs_print_allows(char *ds)
+{
+ zfs_allow_t *curperms, *perms;
+ zfs_handle_t *zhp;
+ allow_cb_t allowcb = { 0 };
+ char banner[MAXPATHLEN];
+
+ if (ds[0] == '-')
+ usage(B_FALSE);
+
+ if (strrchr(ds, '@')) {
+ (void) fprintf(stderr, gettext("Snapshots don't have 'allow'"
+ " permissions\n"));
+ return (1);
+ }
+ if ((zhp = zfs_open(g_zfs, ds, ZFS_TYPE_ANY)) == NULL)
+ return (1);
+
+ if (zfs_perm_get(zhp, &perms)) {
+ (void) fprintf(stderr,
+ gettext("Failed to retrieve 'allows' on %s\n"), ds);
+ zfs_close(zhp);
+ return (1);
+ }
+
+ zfs_close(zhp);
+
+ if (perms != NULL)
+ (void) printf("%s", LINES);
+ for (curperms = perms; curperms; curperms = curperms->z_next) {
+
+ (void) snprintf(banner, sizeof (banner),
+ "Permission sets on (%s)", curperms->z_setpoint);
+ allowcb.a_treeoffset =
+ offsetof(zfs_allow_node_t, z_localdescend);
+ allowcb.a_permcnt = 0;
+ zfs_iter_perms(&curperms->z_sets, banner, &allowcb);
+
+ (void) snprintf(banner, sizeof (banner),
+ "Create time permissions on (%s)", curperms->z_setpoint);
+ allowcb.a_treeoffset =
+ offsetof(zfs_allow_node_t, z_localdescend);
+ allowcb.a_permcnt = 0;
+ zfs_iter_perms(&curperms->z_crperms, banner, &allowcb);
+
+
+ (void) snprintf(banner, sizeof (banner),
+ "Local permissions on (%s)", curperms->z_setpoint);
+ allowcb.a_treeoffset = offsetof(zfs_allow_node_t, z_local);
+ allowcb.a_permcnt = 0;
+ zfs_iter_perms(&curperms->z_user, banner, &allowcb);
+ zfs_iter_perms(&curperms->z_group, banner, &allowcb);
+ zfs_iter_perms(&curperms->z_everyone, banner, &allowcb);
+
+ (void) snprintf(banner, sizeof (banner),
+ "Descendent permissions on (%s)", curperms->z_setpoint);
+ allowcb.a_treeoffset = offsetof(zfs_allow_node_t, z_descend);
+ allowcb.a_permcnt = 0;
+ zfs_iter_perms(&curperms->z_user, banner, &allowcb);
+ zfs_iter_perms(&curperms->z_group, banner, &allowcb);
+ zfs_iter_perms(&curperms->z_everyone, banner, &allowcb);
+
+ (void) snprintf(banner, sizeof (banner),
+ "Local+Descendent permissions on (%s)",
+ curperms->z_setpoint);
+ allowcb.a_treeoffset =
+ offsetof(zfs_allow_node_t, z_localdescend);
+ allowcb.a_permcnt = 0;
+ zfs_iter_perms(&curperms->z_user, banner, &allowcb);
+ zfs_iter_perms(&curperms->z_group, banner, &allowcb);
+ zfs_iter_perms(&curperms->z_everyone, banner, &allowcb);
+
+ (void) printf("%s", LINES);
+ }
+ zfs_free_allows(perms);
+ return (0);
+}
+
+#define ALLOWOPTIONS "ldcsu:g:e"
+#define UNALLOWOPTIONS "ldcsu:g:er"
+
+/*
+ * Validate options, and build necessary datastructure to display/remove/add
+ * permissions.
+ * Returns 0 - If permissions should be added/removed
+ * Returns 1 - If permissions should be displayed.
+ * Returns -1 - on failure
+ */
+int
+parse_allow_args(int *argc, char **argv[], boolean_t unallow,
+ char **ds, int *recurse, nvlist_t **zperms)
+{
+ int c;
+ char *options = unallow ? UNALLOWOPTIONS : ALLOWOPTIONS;
+ zfs_deleg_inherit_t deleg_type = ZFS_DELEG_NONE;
+ zfs_deleg_who_type_t who_type = ZFS_DELEG_WHO_UNKNOWN;
+ char *who;
+ char *perms = NULL;
+ zfs_handle_t *zhp;
+
+ while ((c = getopt(*argc, *argv, options)) != -1) {
+ switch (c) {
+ case 'l':
+ if (who_type == ZFS_DELEG_CREATE ||
+ who_type == ZFS_DELEG_NAMED_SET)
+ usage(B_FALSE);
+
+ deleg_type |= ZFS_DELEG_PERM_LOCAL;
+ break;
+ case 'd':
+ if (who_type == ZFS_DELEG_CREATE ||
+ who_type == ZFS_DELEG_NAMED_SET)
+ usage(B_FALSE);
+
+ deleg_type |= ZFS_DELEG_PERM_DESCENDENT;
+ break;
+ case 'r':
+ *recurse = B_TRUE;
+ break;
+ case 'c':
+ if (who_type != ZFS_DELEG_WHO_UNKNOWN)
+ usage(B_FALSE);
+ if (deleg_type)
+ usage(B_FALSE);
+ who_type = ZFS_DELEG_CREATE;
+ break;
+ case 's':
+ if (who_type != ZFS_DELEG_WHO_UNKNOWN)
+ usage(B_FALSE);
+ if (deleg_type)
+ usage(B_FALSE);
+ who_type = ZFS_DELEG_NAMED_SET;
+ break;
+ case 'u':
+ if (who_type != ZFS_DELEG_WHO_UNKNOWN)
+ usage(B_FALSE);
+ who_type = ZFS_DELEG_USER;
+ who = optarg;
+ break;
+ case 'g':
+ if (who_type != ZFS_DELEG_WHO_UNKNOWN)
+ usage(B_FALSE);
+ who_type = ZFS_DELEG_GROUP;
+ who = optarg;
+ break;
+ case 'e':
+ if (who_type != ZFS_DELEG_WHO_UNKNOWN)
+ usage(B_FALSE);
+ who_type = ZFS_DELEG_EVERYONE;
+ break;
+ default:
+ usage(B_FALSE);
+ break;
+ }
+ }
+
+ if (deleg_type == 0)
+ deleg_type = ZFS_DELEG_PERM_LOCALDESCENDENT;
+
+ *argc -= optind;
+ *argv += optind;
+
+ if (unallow == B_FALSE && *argc == 1) {
+ /*
+ * Only print permissions if no options were processed
+ */
+ if (optind == 1)
+ return (1);
+ else
+ usage(B_FALSE);
+ }
+
+ /*
+ * initialize variables for zfs_build_perms based on number
+ * of arguments.
+ * 3 arguments ==> zfs [un]allow joe perm,perm,perm <dataset> or
+ * zfs [un]allow -s @set1 perm,perm <dataset>
+ * 2 arguments ==> zfs [un]allow -c perm,perm <dataset> or
+ * zfs [un]allow -u|-g <name> perm <dataset> or
+ * zfs [un]allow -e perm,perm <dataset>
+ * zfs unallow joe <dataset>
+ * zfs unallow -s @set1 <dataset>
+ * 1 argument ==> zfs [un]allow -e <dataset> or
+ * zfs [un]allow -c <dataset>
+ */
+
+ switch (*argc) {
+ case 3:
+ perms = (*argv)[1];
+ who = (*argv)[0];
+ *ds = (*argv)[2];
+
+ /*
+ * advance argc/argv for do_allow cases.
+ * for do_allow case make sure who have a know who type
+ * and its not a permission set.
+ */
+ if (unallow == B_TRUE) {
+ *argc -= 2;
+ *argv += 2;
+ } else if (who_type != ZFS_DELEG_WHO_UNKNOWN &&
+ who_type != ZFS_DELEG_NAMED_SET)
+ usage(B_FALSE);
+ break;
+
+ case 2:
+ if (unallow == B_TRUE && (who_type == ZFS_DELEG_EVERYONE ||
+ who_type == ZFS_DELEG_CREATE || who != NULL)) {
+ perms = (*argv)[0];
+ *ds = (*argv)[1];
+ } else {
+ if (unallow == B_FALSE &&
+ (who_type == ZFS_DELEG_WHO_UNKNOWN ||
+ who_type == ZFS_DELEG_NAMED_SET))
+ usage(B_FALSE);
+ else if (who_type == ZFS_DELEG_WHO_UNKNOWN ||
+ who_type == ZFS_DELEG_NAMED_SET)
+ who = (*argv)[0];
+ else if (who_type != ZFS_DELEG_NAMED_SET)
+ perms = (*argv)[0];
+ *ds = (*argv)[1];
+ }
+ if (unallow == B_TRUE) {
+ (*argc)--;
+ (*argv)++;
+ }
+ break;
+
+ case 1:
+ if (unallow == B_FALSE)
+ usage(B_FALSE);
+ if (who == NULL && who_type != ZFS_DELEG_CREATE &&
+ who_type != ZFS_DELEG_EVERYONE)
+ usage(B_FALSE);
+ *ds = (*argv)[0];
+ break;
+
+ default:
+ usage(B_FALSE);
+ }
+
+ if (strrchr(*ds, '@')) {
+ (void) fprintf(stderr,
+ gettext("Can't set or remove 'allow' permissions "
+ "on snapshots.\n"));
+ return (-1);
+ }
+
+ if ((zhp = zfs_open(g_zfs, *ds, ZFS_TYPE_ANY)) == NULL)
+ return (-1);
+
+ if ((zfs_build_perms(zhp, who, perms,
+ who_type, deleg_type, zperms)) != 0) {
+ zfs_close(zhp);
+ return (-1);
+ }
+ zfs_close(zhp);
+ return (0);
+}
+
+static int
+zfs_do_allow(int argc, char **argv)
+{
+ char *ds;
+ nvlist_t *zperms = NULL;
+ zfs_handle_t *zhp;
+ int unused;
+ int ret;
+
+ if ((ret = parse_allow_args(&argc, &argv, B_FALSE, &ds,
+ &unused, &zperms)) == -1)
+ return (1);
+
+ if (ret == 1)
+ return (zfs_print_allows(argv[0]));
+
+ if ((zhp = zfs_open(g_zfs, ds, ZFS_TYPE_ANY)) == NULL)
+ return (1);
+
+ if (zfs_perm_set(zhp, zperms)) {
+ zfs_close(zhp);
+ nvlist_free(zperms);
+ return (1);
+ }
+ nvlist_free(zperms);
+ zfs_close(zhp);
+
+ return (0);
+}
+
+static int
+unallow_callback(zfs_handle_t *zhp, void *data)
+{
+ nvlist_t *nvp = (nvlist_t *)data;
+ int error;
+
+ error = zfs_perm_remove(zhp, nvp);
+ if (error) {
+ (void) fprintf(stderr, gettext("Failed to remove permissions "
+ "on %s\n"), zfs_get_name(zhp));
+ }
+ return (error);
+}
+
+static int
+zfs_do_unallow(int argc, char **argv)
+{
+ int recurse = B_FALSE;
+ char *ds;
+ int error;
+ nvlist_t *zperms = NULL;
+
+ if (parse_allow_args(&argc, &argv, B_TRUE,
+ &ds, &recurse, &zperms) == -1)
+ return (1);
+
+ error = zfs_for_each(argc, argv, recurse,
+ ZFS_TYPE_FILESYSTEM|ZFS_TYPE_VOLUME, NULL,
+ NULL, unallow_callback, (void *)zperms, B_FALSE);
+
+ if (zperms)
+ nvlist_free(zperms);
+
+ return (error);
}
typedef struct get_all_cbdata {
@@ -3143,6 +3521,34 @@ do_volcheck(boolean_t isinit)
return (zpool_iter(g_zfs, volcheck, &isinit) ? 1 : 0);
}
+static int
+find_command_idx(char *command, int *idx)
+{
+ int i;
+
+ for (i = 0; i < NCOMMAND; i++) {
+ if (command_table[i].name == NULL)
+ continue;
+
+ if (strcmp(command, command_table[i].name) == 0) {
+ *idx = i;
+ return (0);
+ }
+ }
+ return (1);
+}
+
+zfs_prop_t
+propset_cb(zfs_prop_t prop, void *data)
+{
+ char *cmdname = (char *)data;
+
+ if (strcmp(cmdname, zfs_prop_to_name(prop)) == 0)
+ return (prop);
+
+ return (ZFS_PROP_CONT);
+}
+
int
main(int argc, char **argv)
{
@@ -3150,6 +3556,8 @@ main(int argc, char **argv)
int i;
char *progname;
char *cmdname;
+ char *str;
+ boolean_t found = B_FALSE;
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
@@ -3162,6 +3570,8 @@ main(int argc, char **argv)
return (1);
}
+ zpool_stage_history(g_zfs, argc, argv, B_TRUE, B_FALSE);
+
libzfs_print_on_error(g_zfs, B_TRUE);
if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) {
@@ -3220,18 +3630,41 @@ main(int argc, char **argv)
/*
* Run the appropriate command.
*/
- for (i = 0; i < NCOMMAND; i++) {
- if (command_table[i].name == NULL)
- continue;
+ if (find_command_idx(cmdname, &i) == 0) {
+ current_command = &command_table[i];
+ ret = command_table[i].func(argc - 1, argv + 1);
+ found = B_TRUE;
+ }
- if (strcmp(cmdname, command_table[i].name) == 0) {
+ /*
+ * Check and see if they are doing property=value
+ */
+ if (found == B_FALSE &&
+ ((str = strchr(cmdname, '=')) != NULL)) {
+ *str = '\0';
+ if (zfs_prop_iter(propset_cb, cmdname,
+ B_FALSE) != ZFS_PROP_INVAL)
+ found = B_TRUE;
+
+ if (found == B_FALSE && zfs_prop_user(cmdname))
+ found = B_TRUE;
+
+ if (found == B_TRUE &&
+ find_command_idx("set", &i) == 0) {
+ *str = '=';
current_command = &command_table[i];
- ret = command_table[i].func(argc - 1, argv + 1);
- break;
+ ret = command_table[i].func(argc, argv);
+ } else {
+ (void) fprintf(stderr,
+ gettext("invalid property '%s'\n"),
+ cmdname);
+ found = B_TRUE;
+ ret = 1;
}
+
}
- if (i == NCOMMAND) {
+ if (found == B_FALSE) {
(void) fprintf(stderr, gettext("unrecognized "
"command '%s'\n"), cmdname);
usage(B_FALSE);
diff --git a/usr/src/cmd/zpool/zpool_main.c b/usr/src/cmd/zpool/zpool_main.c
index 6dc540299f..0bd2146ec5 100644
--- a/usr/src/cmd/zpool/zpool_main.c
+++ b/usr/src/cmd/zpool/zpool_main.c
@@ -41,6 +41,8 @@
#include <strings.h>
#include <unistd.h>
#include <priv.h>
+#include <pwd.h>
+#include <zone.h>
#include <sys/fs/zfs.h>
#include <sys/stat.h>
@@ -188,12 +190,13 @@ get_usage(zpool_help_t idx) {
case HELP_EXPORT:
return (gettext("\texport [-f] <pool> ...\n"));
case HELP_HISTORY:
- return (gettext("\thistory [<pool>]\n"));
+ return (gettext("\thistory [-il] [<pool>] ...\n"));
case HELP_IMPORT:
return (gettext("\timport [-d dir] [-D]\n"
- "\timport [-d dir] [-D] [-f] [-o opts] [-R root] -a\n"
- "\timport [-d dir] [-D] [-f] [-o opts] [-R root ]"
- " <pool | id> [newpool]\n"));
+ "\timport [-p property=value] [-d dir] [-D] [-f] "
+ "[-o opts] [-R root] -a\n"
+ "\timport [-p property=value] [-d dir] [-D] [-f] \n"
+ "\t [-o opts] [-R root ] <pool | id> [newpool]\n"));
case HELP_IOSTAT:
return (gettext("\tiostat [-v] [pool] ... [interval "
"[count]]\n"));
@@ -524,10 +527,6 @@ zpool_do_add(int argc, char **argv)
ret = 0;
} else {
ret = (zpool_add(zhp, nvroot) != 0);
- if (!ret) {
- zpool_log_history(g_zfs, argc + 1 + optind,
- argv - 1 - optind, poolname, B_TRUE, B_FALSE);
- }
}
nvlist_free(nvroot);
@@ -569,10 +568,6 @@ zpool_do_remove(int argc, char **argv)
return (1);
ret = (zpool_vdev_remove(zhp, argv[1]) != 0);
- if (!ret) {
- zpool_log_history(g_zfs, ++argc, --argv, poolname, B_TRUE,
- B_FALSE);
- }
return (ret);
}
@@ -767,8 +762,6 @@ zpool_do_create(int argc, char **argv)
ret = zfs_share_nfs(pool);
zfs_close(pool);
}
- zpool_log_history(g_zfs, argc + optind, argv - optind,
- poolname, B_TRUE, B_TRUE);
} else if (libzfs_errno(g_zfs) == EZFS_INVALIDNAME) {
(void) fprintf(stderr, gettext("pool name may have "
"been omitted\n"));
@@ -841,9 +834,6 @@ zpool_do_destroy(int argc, char **argv)
return (1);
}
- zpool_log_history(g_zfs, argc + optind, argv - optind, pool, B_TRUE,
- B_FALSE);
-
ret = (zpool_destroy(zhp) != 0);
zpool_close(zhp);
@@ -904,9 +894,6 @@ zpool_do_export(int argc, char **argv)
continue;
}
- zpool_log_history(g_zfs, argc + optind, argv - optind, argv[i],
- B_TRUE, B_FALSE);
-
if (zpool_export(zhp) != 0)
ret = 1;
@@ -1219,12 +1206,13 @@ show_import(nvlist_t *config)
*/
static int
do_import(nvlist_t *config, const char *newname, const char *mntopts,
- const char *altroot, int force, int argc, char **argv)
+ const char *altroot, int force, nvlist_t *props)
{
zpool_handle_t *zhp;
char *name;
uint64_t state;
uint64_t version;
+ int error = 0;
verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
&name) == 0);
@@ -1277,17 +1265,33 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
if (newname != NULL)
name = (char *)newname;
- zpool_log_history(g_zfs, argc, argv, name, B_TRUE, B_FALSE);
-
verify((zhp = zpool_open(g_zfs, name)) != NULL);
+ if (props) {
+ nvpair_t *pair = nvlist_next_nvpair(props, NULL);
+ char *value;
+
+ if (pair != NULL) {
+ do {
+ verify((nvpair_value_string(pair,
+ &value)) == 0);
+
+ if ((error = zpool_set_prop(zhp,
+ nvpair_name(pair), value)) != 0)
+ break;
+
+ } while (pair = nvlist_next_nvpair(props, pair));
+ }
+ }
+
+
if (zpool_enable_datasets(zhp, mntopts, 0) != 0) {
zpool_close(zhp);
return (1);
}
zpool_close(zhp);
- return (0);
+ return (error);
}
/*
@@ -1309,6 +1313,10 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
*
* -a Import all pools found.
*
+ * -o temporary mount options.
+ *
+ * -p property=value
+ *
* The import command scans for pools to import, and import pools based on pool
* name and GUID. The pool can also be renamed as part of the import process.
*/
@@ -1329,12 +1337,15 @@ zpool_do_import(int argc, char **argv)
nvlist_t *config;
uint64_t searchguid;
char *searchname;
+ char *propname;
+ char *propval, *strval;
nvlist_t *found_config;
+ nvlist_t *props = NULL;
boolean_t first;
uint64_t pool_state;
/* check options */
- while ((c = getopt(argc, argv, ":Dfd:R:ao:")) != -1) {
+ while ((c = getopt(argc, argv, ":Dfd:R:ao:p:")) != -1) {
switch (c) {
case 'a':
do_all = B_TRUE;
@@ -1369,6 +1380,48 @@ zpool_do_import(int argc, char **argv)
"'%c' option\n"), optopt);
usage(B_FALSE);
break;
+ case 'p':
+ if (props == NULL &&
+ nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) {
+ (void) fprintf(stderr,
+ gettext("internal error: "
+ "out of memory\n"));
+ err = B_TRUE;
+ goto error;
+ }
+
+ propname = optarg;
+ if ((propval = strchr(propname, '=')) == NULL) {
+ (void) fprintf(stderr, gettext("missing "
+ "'=' for -o option\n"));
+ err = B_TRUE;
+ goto error;
+ }
+ *propval = '\0';
+ propval++;
+
+ if (zpool_name_to_prop(propname) == ZFS_PROP_INVAL) {
+ (void) fprintf(stderr,
+ gettext("property '%s' is "
+ "not a valid pool property\n"), propname);
+ err = B_TRUE;
+ goto error;
+ }
+
+ if (nvlist_lookup_string(props, propname,
+ &strval) == 0) {
+ (void) fprintf(stderr, gettext("property '%s' "
+ "specified multiple times\n"), propname);
+ err = B_TRUE;
+ goto error;
+ }
+ if (nvlist_add_string(props, propname, propval) != 0) {
+ (void) fprintf(stderr, gettext("internal "
+ "error: out of memory\n"));
+ err = B_TRUE;
+ goto error;
+ }
+ break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
@@ -1463,8 +1516,7 @@ zpool_do_import(int argc, char **argv)
if (do_all)
err |= do_import(config, NULL, mntopts,
- altroot, do_force, argc + optind,
- argv - optind);
+ altroot, do_force, props);
else
show_import(config);
} else if (searchname != NULL) {
@@ -1512,8 +1564,7 @@ zpool_do_import(int argc, char **argv)
err = B_TRUE;
} else {
err |= do_import(found_config, argc == 1 ? NULL :
- argv[1], mntopts, altroot, do_force, argc + optind,
- argv - optind);
+ argv[1], mntopts, altroot, do_force, props);
}
}
@@ -1525,6 +1576,9 @@ zpool_do_import(int argc, char **argv)
(void) fprintf(stderr,
gettext("no pools available to import\n"));
+error:
+ if (props)
+ nvlist_free(props);
nvlist_free(pools);
free(searchdirs);
@@ -2172,8 +2226,6 @@ zpool_do_attach_or_replace(int argc, char **argv, int replacing)
char *poolname, *old_disk, *new_disk;
zpool_handle_t *zhp;
int ret;
- int log_argc;
- char **log_argv;
/* check options */
while ((c = getopt(argc, argv, "f")) != -1) {
@@ -2188,8 +2240,6 @@ zpool_do_attach_or_replace(int argc, char **argv, int replacing)
}
}
- log_argc = argc;
- log_argv = argv;
argc -= optind;
argv += optind;
@@ -2247,11 +2297,6 @@ zpool_do_attach_or_replace(int argc, char **argv, int replacing)
ret = zpool_vdev_attach(zhp, old_disk, new_disk, nvroot, replacing);
- if (!ret) {
- zpool_log_history(g_zfs, log_argc, log_argv, poolname, B_TRUE,
- B_FALSE);
- }
-
nvlist_free(nvroot);
zpool_close(zhp);
@@ -2341,10 +2386,6 @@ zpool_do_detach(int argc, char **argv)
ret = zpool_vdev_detach(zhp, path);
- if (!ret) {
- zpool_log_history(g_zfs, argc + optind, argv - optind, poolname,
- B_TRUE, B_FALSE);
- }
zpool_close(zhp);
return (ret);
@@ -2411,10 +2452,6 @@ zpool_do_online(int argc, char **argv)
}
}
- if (!ret) {
- zpool_log_history(g_zfs, argc + optind, argv - optind, poolname,
- B_TRUE, B_FALSE);
- }
zpool_close(zhp);
return (ret);
@@ -2477,10 +2514,6 @@ zpool_do_offline(int argc, char **argv)
ret = 1;
}
- if (!ret) {
- zpool_log_history(g_zfs, argc + optind, argv - optind, poolname,
- B_TRUE, B_FALSE);
- }
zpool_close(zhp);
return (ret);
@@ -2517,8 +2550,6 @@ zpool_do_clear(int argc, char **argv)
if (zpool_clear(zhp, device) != 0)
ret = 1;
- if (!ret)
- zpool_log_history(g_zfs, argc, argv, pool, B_TRUE, B_FALSE);
zpool_close(zhp);
return (ret);
@@ -2547,11 +2578,6 @@ scrub_callback(zpool_handle_t *zhp, void *data)
err = zpool_scrub(zhp, cb->cb_type);
- if (!err) {
- zpool_log_history(g_zfs, cb->cb_argc, cb->cb_argv,
- zpool_get_name(zhp), B_TRUE, B_FALSE);
- }
-
return (err != 0);
}
@@ -3223,9 +3249,6 @@ upgrade_cb(zpool_handle_t *zhp, void *arg)
cbp->cb_first = B_FALSE;
ret = zpool_upgrade(zhp);
if (!ret) {
- zpool_log_history(g_zfs, cbp->cb_argc,
- cbp->cb_argv, zpool_get_name(zhp), B_TRUE,
- B_FALSE);
(void) printf(gettext("Successfully upgraded "
"'%s'\n"), zpool_get_name(zhp));
}
@@ -3257,7 +3280,6 @@ upgrade_one(zpool_handle_t *zhp, void *data)
nvlist_t *config;
uint64_t version;
int ret;
- upgrade_cbdata_t *cbp = data;
config = zpool_get_config(zhp, NULL);
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
@@ -3278,8 +3300,6 @@ upgrade_one(zpool_handle_t *zhp, void *data)
ret = zpool_upgrade(zhp);
if (!ret) {
- zpool_log_history(g_zfs, cbp->cb_argc, cbp->cb_argv,
- zpool_get_name(zhp), B_TRUE, B_FALSE);
(void) printf(gettext("Successfully upgraded '%s' "
"from version %llu to version %llu\n"), zpool_get_name(zhp),
(u_longlong_t)version, (u_longlong_t)ZFS_VERSION);
@@ -3357,8 +3377,9 @@ zpool_do_upgrade(int argc, char **argv)
(void) printf(gettext(" 4 zpool history\n"));
(void) printf(gettext(" 5 Compression using the gzip "
"algorithm\n"));
- (void) printf(gettext(" 6 pool properties "));
+ (void) printf(gettext(" 6 pool properties\n"));
(void) printf(gettext(" 7 Separate intent log devices\n"));
+ (void) printf(gettext(" 8 Delegated administration\n"));
(void) printf(gettext("For more information on a particular "
"version, including supported releases, see:\n\n"));
(void) printf("http://www.opensolaris.org/os/community/zfs/"
@@ -3399,6 +3420,49 @@ zpool_do_upgrade(int argc, char **argv)
return (ret);
}
+typedef struct hist_cbdata {
+ boolean_t first;
+ int longfmt;
+ int internal;
+} hist_cbdata_t;
+
+char *hist_event_table[LOG_END] = {
+ "invalid event",
+ "pool create",
+ "vdev add",
+ "pool remove",
+ "pool destroy",
+ "pool export",
+ "pool import",
+ "vdev attach",
+ "vdev replace",
+ "vdev detach",
+ "vdev online",
+ "vdev offline",
+ "vdev upgrade",
+ "pool clear",
+ "pool scrub",
+ "pool property set",
+ "create",
+ "clone",
+ "destroy",
+ "destroy_begin_sync",
+ "inherit",
+ "property set",
+ "quota set",
+ "permission update",
+ "permission remove",
+ "permission who remove",
+ "promote",
+ "receive",
+ "rename",
+ "reservation set",
+ "replay_inc_sync",
+ "replay_full_sync",
+ "rollback",
+ "snapshot",
+};
+
/*
* Print out the command history for a specific pool.
*/
@@ -3409,13 +3473,22 @@ get_history_one(zpool_handle_t *zhp, void *data)
nvlist_t **records;
uint_t numrecords;
char *cmdstr;
+ char *pathstr;
uint64_t dst_time;
time_t tsec;
struct tm t;
char tbuf[30];
int ret, i;
+ uint64_t who;
+ struct passwd *pwd;
+ char *hostname;
+ char *zonename;
+ char internalstr[MAXPATHLEN];
+ hist_cbdata_t *cb = (hist_cbdata_t *)data;
+ uint64_t txg;
+ uint64_t ievent;
- *(boolean_t *)data = B_FALSE;
+ cb->first = B_FALSE;
(void) printf(gettext("History for '%s':\n"), zpool_get_name(zhp));
@@ -3426,14 +3499,65 @@ get_history_one(zpool_handle_t *zhp, void *data)
&records, &numrecords) == 0);
for (i = 0; i < numrecords; i++) {
if (nvlist_lookup_uint64(records[i], ZPOOL_HIST_TIME,
- &dst_time) == 0) {
- verify(nvlist_lookup_string(records[i], ZPOOL_HIST_CMD,
- &cmdstr) == 0);
- tsec = dst_time;
- (void) localtime_r(&tsec, &t);
- (void) strftime(tbuf, sizeof (tbuf), "%F.%T", &t);
- (void) printf("%s %s\n", tbuf, cmdstr);
+ &dst_time) != 0)
+ continue;
+
+ /* is it an internal event or a standard event? */
+ if (nvlist_lookup_string(records[i], ZPOOL_HIST_CMD,
+ &cmdstr) != 0) {
+ if (cb->internal == 0)
+ continue;
+
+ if (nvlist_lookup_uint64(records[i],
+ ZPOOL_HIST_INT_EVENT, &ievent) != 0)
+ continue;
+ verify(nvlist_lookup_uint64(records[i],
+ ZPOOL_HIST_TXG, &txg) == 0);
+ verify(nvlist_lookup_string(records[i],
+ ZPOOL_HIST_INT_STR, &pathstr) == 0);
+ if (ievent > LOG_END)
+ continue;
+ (void) snprintf(internalstr,
+ sizeof (internalstr),
+ "[internal %s txg:%lld] %s",
+ hist_event_table[ievent], txg,
+ pathstr);
+ cmdstr = internalstr;
}
+ tsec = dst_time;
+ (void) localtime_r(&tsec, &t);
+ (void) strftime(tbuf, sizeof (tbuf), "%F.%T", &t);
+ (void) printf("%s %s", tbuf, cmdstr);
+
+ if (!cb->longfmt) {
+ (void) printf("\n");
+ continue;
+ }
+ (void) printf(" [");
+ if (nvlist_lookup_uint64(records[i],
+ ZPOOL_HIST_WHO, &who) == 0) {
+ pwd = getpwuid((uid_t)who);
+ if (pwd)
+ (void) printf("user %s on",
+ pwd->pw_name);
+ else
+ (void) printf("user %d on",
+ (int)who);
+ } else {
+ (void) printf(gettext("no info]\n"));
+ continue;
+ }
+ if (nvlist_lookup_string(records[i],
+ ZPOOL_HIST_HOST, &hostname) == 0) {
+ (void) printf(" %s", hostname);
+ }
+ if (nvlist_lookup_string(records[i],
+ ZPOOL_HIST_ZONE, &zonename) == 0) {
+ (void) printf(":%s", zonename);
+ }
+
+ (void) printf("]");
+ (void) printf("\n");
}
(void) printf("\n");
nvlist_free(nvhis);
@@ -3446,19 +3570,38 @@ get_history_one(zpool_handle_t *zhp, void *data)
*
* Displays the history of commands that modified pools.
*/
+
+
int
zpool_do_history(int argc, char **argv)
{
- boolean_t first = B_TRUE;
+ hist_cbdata_t cbdata = { 0 };
int ret;
+ int c;
+ cbdata.first = B_TRUE;
+ /* check options */
+ while ((c = getopt(argc, argv, "li")) != -1) {
+ switch (c) {
+ case 'l':
+ cbdata.longfmt = 1;
+ break;
+ case 'i':
+ cbdata.internal = 1;
+ break;
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
argc -= optind;
argv += optind;
ret = for_each_pool(argc, argv, B_FALSE, NULL, get_history_one,
- &first);
+ &cbdata);
- if (argc == 0 && first == B_TRUE) {
+ if (argc == 0 && cbdata.first == B_TRUE) {
(void) printf(gettext("no pools available\n"));
return (0);
}
@@ -3593,11 +3736,6 @@ zpool_do_set(int argc, char **argv)
error = for_each_pool(argc - 2, argv + 2, B_TRUE, NULL,
set_callback, &cb);
- if (cb.cb_any_successful) {
- *(cb.cb_value - 1) = '=';
- zpool_log_history(g_zfs, argc, argv, argv[2], B_FALSE, B_FALSE);
- }
-
return (error);
}
@@ -3618,13 +3756,25 @@ find_command_idx(char *command, int *idx)
return (1);
}
+zpool_prop_t
+propset_cb(zpool_prop_t prop, void *data)
+{
+ char *cmdname = (char *)data;
+
+ if (strcmp(cmdname, zpool_prop_to_name(prop)) == 0)
+ return (prop);
+
+ return (ZFS_PROP_CONT);
+}
+
int
main(int argc, char **argv)
{
int ret;
int i;
char *cmdname;
- int found = 0;
+ boolean_t found = B_FALSE;
+ char *str;
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
@@ -3649,6 +3799,12 @@ main(int argc, char **argv)
cmdname = argv[1];
+ /* Handle special case of pool create for staging history */
+ if (strcmp(cmdname, "create") != 0)
+ zpool_stage_history(g_zfs, argc, argv, B_FALSE, B_FALSE);
+ else
+ zpool_stage_history(g_zfs, argc, argv, B_FALSE, B_TRUE);
+
/*
* Special case '-?'
*/
@@ -3661,7 +3817,7 @@ main(int argc, char **argv)
if (find_command_idx(cmdname, &i) == 0) {
current_command = &command_table[i];
ret = command_table[i].func(argc - 1, argv + 1);
- found++;
+ found = B_TRUE;
}
/*
@@ -3674,7 +3830,28 @@ main(int argc, char **argv)
return (!!ioctl(fd, ZFS_IOC_POOL_FREEZE, buf));
}
- if (!found) {
+ /* is this a zpool property=value */
+ if (found == B_FALSE && ((str = strchr(cmdname, '=')) != NULL)) {
+ *str = '\0';
+ if (zpool_prop_iter(propset_cb, cmdname,
+ B_FALSE) != ZFS_PROP_INVAL) {
+ if (find_command_idx("set", &i) == 0) {
+ *str = '=';
+ current_command = &command_table[i];
+ ret = command_table[i].func(argc, argv);
+ found = B_TRUE;
+ }
+ }
+
+ if (found == B_FALSE) {
+ *str = '=';
+ (void) fprintf(stderr,
+ gettext("invalid property '%s'\n"), cmdname);
+ found = B_TRUE;
+ }
+ }
+
+ if (found == B_FALSE) {
(void) fprintf(stderr, gettext("unrecognized "
"command '%s'\n"), cmdname);
usage(B_FALSE);
diff --git a/usr/src/cmd/ztest/ztest.c b/usr/src/cmd/ztest/ztest.c
index 04f97821f9..289108f605 100644
--- a/usr/src/cmd/ztest/ztest.c
+++ b/usr/src/cmd/ztest/ztest.c
@@ -1089,7 +1089,7 @@ ztest_vdev_LUN_growth(ztest_args_t *za)
/* ARGSUSED */
static void
-ztest_create_cb(objset_t *os, void *arg, dmu_tx_t *tx)
+ztest_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
{
/*
* Create the directory object.
diff --git a/usr/src/common/zfs/zfs_deleg.c b/usr/src/common/zfs/zfs_deleg.c
new file mode 100644
index 0000000000..578d1c6680
--- /dev/null
+++ b/usr/src/common/zfs/zfs_deleg.c
@@ -0,0 +1,249 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#if defined(_KERNEL)
+#include <sys/systm.h>
+#include <sys/sunddi.h>
+#include <sys/ctype.h>
+#else
+#include <stdio.h>
+#include <unistd.h>
+#include <strings.h>
+#include <libnvpair.h>
+#include <ctype.h>
+#endif
+#include <sys/dsl_deleg.h>
+#include "zfs_deleg.h"
+#include "zfs_namecheck.h"
+
+/*
+ * permission table
+ */
+
+char *zfs_deleg_perm_tab[] = {
+ ZFS_DELEG_PERM_CREATE,
+ ZFS_DELEG_PERM_DESTROY,
+ ZFS_DELEG_PERM_SNAPSHOT,
+ ZFS_DELEG_PERM_ROLLBACK,
+ ZFS_DELEG_PERM_CLONE,
+ ZFS_DELEG_PERM_PROMOTE,
+ ZFS_DELEG_PERM_RENAME,
+ ZFS_DELEG_PERM_MOUNT,
+ ZFS_DELEG_PERM_SHARE,
+ ZFS_DELEG_PERM_SEND,
+ ZFS_DELEG_PERM_RECEIVE,
+ ZFS_DELEG_PERM_QUOTA,
+ ZFS_DELEG_PERM_RESERVATION,
+ ZFS_DELEG_PERM_VOLSIZE,
+ ZFS_DELEG_PERM_RECORDSIZE,
+ ZFS_DELEG_PERM_MOUNTPOINT,
+ ZFS_DELEG_PERM_SHARENFS,
+ ZFS_DELEG_PERM_CHECKSUM,
+ ZFS_DELEG_PERM_COMPRESSION,
+ ZFS_DELEG_PERM_ATIME,
+ ZFS_DELEG_PERM_DEVICES,
+ ZFS_DELEG_PERM_EXEC,
+ ZFS_DELEG_PERM_SETUID,
+ ZFS_DELEG_PERM_READONLY,
+ ZFS_DELEG_PERM_ZONED,
+ ZFS_DELEG_PERM_SNAPDIR,
+ ZFS_DELEG_PERM_ACLMODE,
+ ZFS_DELEG_PERM_ACLINHERIT,
+ ZFS_DELEG_PERM_ALLOW,
+ ZFS_DELEG_PERM_CANMOUNT,
+ ZFS_DELEG_PERM_USERPROP,
+ ZFS_DELEG_PERM_SHAREISCSI,
+ ZFS_DELEG_PERM_XATTR,
+ ZFS_DELEG_PERM_COPIES,
+ NULL
+};
+
+int
+zfs_deleg_type(char *name)
+{
+ return (name[0]);
+}
+
+static int
+zfs_valid_permission_name(char *perm)
+{
+ int i;
+ for (i = 0; zfs_deleg_perm_tab[i] != NULL; i++) {
+ if (strcmp(perm, zfs_deleg_perm_tab[i]) == 0)
+ return (0);
+ }
+
+ return (permset_namecheck(perm, NULL, NULL));
+}
+
+static int
+zfs_validate_who(char *who)
+{
+ int error = 0;
+ char *p;
+
+ switch (zfs_deleg_type(who)) {
+ case ZFS_DELEG_USER:
+ case ZFS_DELEG_GROUP:
+ case ZFS_DELEG_USER_SETS:
+ case ZFS_DELEG_GROUP_SETS:
+ if ((who[1] != 'l' || who[1] != 'd') &&
+ (who[2] != ZFS_DELEG_FIELD_SEP_CHR)) {
+ error = -1;
+ break;
+ }
+
+ for (p = &who[3]; p && *p; p++)
+ if (!isdigit(*p)) {
+ error = -1;
+ }
+ break;
+ case ZFS_DELEG_NAMED_SET:
+ case ZFS_DELEG_NAMED_SET_SETS:
+ error = permset_namecheck(&who[3], NULL, NULL);
+ break;
+
+ case ZFS_DELEG_CREATE:
+ case ZFS_DELEG_CREATE_SETS:
+ case ZFS_DELEG_EVERYONE:
+ case ZFS_DELEG_EVERYONE_SETS:
+ if (who[3] != '\0')
+ error = -1;
+ break;
+ default:
+ error = -1;
+ }
+
+ return (error);
+}
+
+static int
+zfs_validate_iflags(char *who)
+{
+ switch (zfs_deleg_type(who)) {
+ case ZFS_DELEG_NAMED_SET:
+ case ZFS_DELEG_NAMED_SET_SETS:
+ case ZFS_DELEG_CREATE:
+ case ZFS_DELEG_CREATE_SETS:
+ if (who[1] != '-')
+ return (-1);
+ break;
+ default:
+ if (who[1] != 'l' && who[1] != 'd')
+ return (-1);
+ break;
+ }
+ return (0);
+}
+
+int
+zfs_deleg_verify_nvlist(nvlist_t *nvp)
+{
+ nvpair_t *who, *perm_name;
+ nvlist_t *perms;
+ int error;
+
+ if (nvp == NULL)
+ return (-1);
+
+ who = nvlist_next_nvpair(nvp, NULL);
+ if (who == NULL)
+ return (-1);
+
+ do {
+ if (zfs_validate_who(nvpair_name(who)))
+ return (-1);
+
+ if (zfs_validate_iflags(nvpair_name(who)))
+ return (-1);
+
+ error = nvlist_lookup_nvlist(nvp, nvpair_name(who), &perms);
+
+ if (error && error != ENOENT)
+ return (-1);
+ if (error == ENOENT)
+ continue;
+
+ perm_name = nvlist_next_nvpair(perms, NULL);
+ if (perm_name == NULL) {
+ return (-1);
+ }
+ do {
+ error = zfs_valid_permission_name(
+ nvpair_name(perm_name));
+ if (error) {
+ return (-1);
+ }
+ } while (perm_name = nvlist_next_nvpair(perms, perm_name));
+ } while (who = nvlist_next_nvpair(nvp, who));
+ return (0);
+}
+
+/*
+ * Construct the base attribute name. The base attribute names
+ * are the "key" to locate the jump objects which contain the actual
+ * permissions. The base attribute names are encoded based on
+ * type of entry and whether it is a local or descendent permission.
+ *
+ * Arguments:
+ * attr - attribute name return string, attribute is assumed to be
+ * ZFS_MAX_DELEG_NAME long.
+ * type - type of entry to construct
+ * inheritchr - inheritance type (local,descendent, or NA for create and
+ * permission set definitions
+ * data - is either a permission set name or a 64 bit uid/gid.
+ */
+void
+zfs_deleg_whokey(char *attr, char type, char inheritchr, void *data)
+{
+ int len = ZFS_MAX_DELEG_NAME;
+ uint64_t *id = data;
+
+ switch (type) {
+ case ZFS_DELEG_USER:
+ case ZFS_DELEG_GROUP:
+ case ZFS_DELEG_USER_SETS:
+ case ZFS_DELEG_GROUP_SETS:
+ (void) snprintf(attr, len, "%c%c%c%lld", type, inheritchr,
+ ZFS_DELEG_FIELD_SEP_CHR, (longlong_t)*id);
+ break;
+ case ZFS_DELEG_NAMED_SET_SETS:
+ case ZFS_DELEG_NAMED_SET:
+ (void) snprintf(attr, len, "%c-%c%s", type,
+ ZFS_DELEG_FIELD_SEP_CHR, (char *)data);
+ break;
+ case ZFS_DELEG_CREATE:
+ case ZFS_DELEG_CREATE_SETS:
+ (void) snprintf(attr, len, "%c-%c", type,
+ ZFS_DELEG_FIELD_SEP_CHR);
+ break;
+ default:
+ (void) snprintf(attr, len, "%c%c%c", type, inheritchr,
+ ZFS_DELEG_FIELD_SEP_CHR);
+ }
+}
diff --git a/usr/src/common/zfs/zfs_deleg.h b/usr/src/common/zfs/zfs_deleg.h
new file mode 100644
index 0000000000..9a5daf2438
--- /dev/null
+++ b/usr/src/common/zfs/zfs_deleg.h
@@ -0,0 +1,60 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _ZFS_DELEG_H
+#define _ZFS_DELEG_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/fs/zfs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZFS_DELEG_SET_NAME_CHR '@' /* set name lead char */
+#define ZFS_DELEG_FIELD_SEP_CHR '$' /* field separator */
+
+/*
+ * Max name length for a delegation attribute
+ */
+#define ZFS_MAX_DELEG_NAME 128
+
+#define ZFS_DELEG_LOCAL 'l'
+#define ZFS_DELEG_DESCENDENT 'd'
+#define ZFS_DELEG_NA '-'
+
+extern char *zfs_deleg_perm_tab[];
+int zfs_deleg_type(char *attr);
+
+int zfs_deleg_verify_nvlist(nvlist_t *nvlist);
+void zfs_deleg_whokey(char *attr, char type,
+ char checkflag, void *data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZFS_DELEG_H */
diff --git a/usr/src/common/zfs/zfs_namecheck.c b/usr/src/common/zfs/zfs_namecheck.c
index 2004d860d3..769a22a04e 100644
--- a/usr/src/common/zfs/zfs_namecheck.c
+++ b/usr/src/common/zfs/zfs_namecheck.c
@@ -44,7 +44,9 @@
#endif
#include <sys/param.h>
+#include <sys/nvpair.h>
#include "zfs_namecheck.h"
+#include "zfs_deleg.h"
static int
valid_char(char c)
@@ -90,6 +92,32 @@ snapshot_namecheck(const char *path, namecheck_err_t *why, char *what)
return (0);
}
+
+/*
+ * Permissions set name must start with the letter '@' followed by the
+ * same character restrictions as snapshot names, except that the name
+ * cannot exceed 64 characters.
+ */
+int
+permset_namecheck(const char *path, namecheck_err_t *why, char *what)
+{
+ if (strlen(path) >= ZFS_PERMSET_MAXLEN) {
+ if (why)
+ *why = NAME_ERR_TOOLONG;
+ return (-1);
+ }
+
+ if (path[0] != '@') {
+ if (why) {
+ *why = NAME_ERR_NO_AT;
+ *what = path[0];
+ }
+ return (-1);
+ }
+
+ return (snapshot_namecheck(&path[1], why, what));
+}
+
/*
* Dataset names must be of the following form:
*
@@ -114,6 +142,7 @@ dataset_namecheck(const char *path, namecheck_err_t *why, char *what)
* If ZFS_MAXNAMELEN value is changed, make sure to cleanup all
* places using MAXNAMELEN.
*/
+
if (strlen(path) >= MAXNAMELEN) {
if (why)
*why = NAME_ERR_TOOLONG;
diff --git a/usr/src/common/zfs/zfs_namecheck.h b/usr/src/common/zfs/zfs_namecheck.h
index 7e0cda974c..30fe8be9a8 100644
--- a/usr/src/common/zfs/zfs_namecheck.h
+++ b/usr/src/common/zfs/zfs_namecheck.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -42,12 +42,16 @@ typedef enum {
NAME_ERR_RESERVED, /* entire name is reserved */
NAME_ERR_DISKLIKE, /* reserved disk name (c[0-9].*) */
NAME_ERR_TOOLONG, /* name is too long */
+ NAME_ERR_NO_AT, /* permission set is missing '@' */
} namecheck_err_t;
+#define ZFS_PERMSET_MAXLEN 64
+
int pool_namecheck(const char *, namecheck_err_t *, char *);
int dataset_namecheck(const char *, namecheck_err_t *, char *);
int dataset_name_hidden(const char *);
int snapshot_namecheck(const char *, namecheck_err_t *, char *);
+int permset_namecheck(const char *, namecheck_err_t *, char *);
#ifdef __cplusplus
}
diff --git a/usr/src/common/zfs/zfs_prop.c b/usr/src/common/zfs/zfs_prop.c
index 0d00e0aa2c..887f20a2e1 100644
--- a/usr/src/common/zfs/zfs_prop.c
+++ b/usr/src/common/zfs/zfs_prop.c
@@ -53,6 +53,7 @@
#include <sys/zfs_ioctl.h>
#include "zfs_prop.h"
+#include "zfs_deleg.h"
#if defined(_KERNEL)
#include <sys/systm.h>
@@ -79,110 +80,131 @@ typedef struct {
const char *pd_colname;
boolean_t pd_rightalign;
boolean_t pd_visible;
+ const char *pd_perm;
} prop_desc_t;
static prop_desc_t zfs_prop_table[] = {
{ "type", prop_type_string, 0, NULL, prop_readonly,
ZFS_TYPE_ANY, "filesystem | volume | snapshot", "TYPE", B_TRUE,
- B_TRUE },
+ B_TRUE, ZFS_DELEG_PERM_NONE },
{ "creation", prop_type_number, 0, NULL, prop_readonly,
- ZFS_TYPE_ANY, "<date>", "CREATION", B_FALSE, B_TRUE },
+ ZFS_TYPE_ANY, "<date>", "CREATION", B_FALSE, B_TRUE,
+ ZFS_DELEG_PERM_NONE },
{ "used", prop_type_number, 0, NULL, prop_readonly,
- ZFS_TYPE_ANY, "<size>", "USED", B_TRUE, B_TRUE },
+ ZFS_TYPE_ANY, "<size>", "USED", B_TRUE, B_TRUE,
+ ZFS_DELEG_PERM_NONE },
{ "available", prop_type_number, 0, NULL, prop_readonly,
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size>", "AVAIL", B_TRUE,
- B_TRUE },
+ B_TRUE, ZFS_DELEG_PERM_NONE },
{ "referenced", prop_type_number, 0, NULL, prop_readonly,
ZFS_TYPE_ANY,
- "<size>", "REFER", B_TRUE, B_TRUE },
+ "<size>", "REFER", B_TRUE, B_TRUE, ZFS_DELEG_PERM_NONE },
{ "compressratio", prop_type_number, 0, NULL, prop_readonly,
ZFS_TYPE_ANY, "<1.00x or higher if compressed>", "RATIO", B_TRUE,
- B_TRUE },
+ B_TRUE, ZFS_DELEG_PERM_NONE },
{ "mounted", prop_type_boolean, 0, NULL, prop_readonly,
- ZFS_TYPE_FILESYSTEM, "yes | no | -", "MOUNTED", B_TRUE, B_TRUE },
+ ZFS_TYPE_FILESYSTEM, "yes | no | -", "MOUNTED", B_TRUE, B_TRUE,
+ ZFS_DELEG_PERM_NONE },
{ "origin", prop_type_string, 0, NULL, prop_readonly,
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<snapshot>", "ORIGIN",
- B_FALSE, B_TRUE },
+ B_FALSE, B_TRUE, ZFS_DELEG_PERM_NONE },
{ "quota", prop_type_number, 0, NULL, prop_default,
- ZFS_TYPE_FILESYSTEM, "<size> | none", "QUOTA", B_TRUE, B_TRUE },
+ ZFS_TYPE_FILESYSTEM, "<size> | none", "QUOTA", B_TRUE, B_TRUE,
+ ZFS_DELEG_PERM_QUOTA },
{ "reservation", prop_type_number, 0, NULL, prop_default,
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
- "<size> | none", "RESERV", B_TRUE, B_TRUE },
+ "<size> | none", "RESERV", B_TRUE, B_TRUE,
+ ZFS_DELEG_PERM_RESERVATION },
{ "volsize", prop_type_number, 0, NULL, prop_default,
- ZFS_TYPE_VOLUME, "<size>", "VOLSIZE", B_TRUE, B_TRUE },
+ ZFS_TYPE_VOLUME, "<size>", "VOLSIZE", B_TRUE, B_TRUE,
+ ZFS_DELEG_PERM_VOLSIZE },
{ "volblocksize", prop_type_number, 8192, NULL, prop_readonly,
ZFS_TYPE_VOLUME, "512 to 128k, power of 2", "VOLBLOCK", B_TRUE,
- B_TRUE },
+ B_TRUE, ZFS_DELEG_PERM_NONE },
{ "recordsize", prop_type_number, SPA_MAXBLOCKSIZE, NULL,
prop_inherit,
ZFS_TYPE_FILESYSTEM,
- "512 to 128k, power of 2", "RECSIZE", B_TRUE, B_TRUE },
+ "512 to 128k, power of 2", "RECSIZE", B_TRUE, B_TRUE,
+ ZFS_DELEG_PERM_RECORDSIZE },
{ "mountpoint", prop_type_string, 0, "/", prop_inherit,
ZFS_TYPE_FILESYSTEM,
- "<path> | legacy | none", "MOUNTPOINT", B_FALSE, B_TRUE },
+ "<path> | legacy | none", "MOUNTPOINT", B_FALSE, B_TRUE,
+ ZFS_DELEG_PERM_MOUNTPOINT },
{ "sharenfs", prop_type_string, 0, "off", prop_inherit,
ZFS_TYPE_FILESYSTEM,
- "on | off | share(1M) options", "SHARENFS", B_FALSE, B_TRUE },
+ "on | off | share(1M) options", "SHARENFS", B_FALSE, B_TRUE,
+ ZFS_DELEG_PERM_SHARENFS },
{ "checksum", prop_type_index, ZIO_CHECKSUM_DEFAULT, "on",
prop_inherit, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
"on | off | fletcher2 | fletcher4 | sha256", "CHECKSUM", B_TRUE,
- B_TRUE },
+ B_TRUE, ZFS_DELEG_PERM_CHECKSUM },
{ "compression", prop_type_index, ZIO_COMPRESS_DEFAULT, "off",
prop_inherit, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
- "on | off | lzjb | gzip | gzip-[1-9]", "COMPRESS", B_TRUE, B_TRUE },
+ "on | off | lzjb | gzip | gzip-[1-9]", "COMPRESS", B_TRUE, B_TRUE,
+ ZFS_DELEG_PERM_COMPRESSION },
{ "atime", prop_type_boolean, 1, NULL, prop_inherit,
ZFS_TYPE_FILESYSTEM,
- "on | off", "ATIME", B_TRUE, B_TRUE },
+ "on | off", "ATIME", B_TRUE, B_TRUE, ZFS_DELEG_PERM_ATIME },
{ "devices", prop_type_boolean, 1, NULL, prop_inherit,
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT,
- "on | off", "DEVICES", B_TRUE, B_TRUE },
+ "on | off", "DEVICES", B_TRUE, B_TRUE, ZFS_DELEG_PERM_DEVICES },
{ "exec", prop_type_boolean, 1, NULL, prop_inherit,
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT,
- "on | off", "EXEC", B_TRUE, B_TRUE },
+ "on | off", "EXEC", B_TRUE, B_TRUE, ZFS_DELEG_PERM_EXEC },
{ "setuid", prop_type_boolean, 1, NULL, prop_inherit,
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, "on | off", "SETUID",
- B_TRUE, B_TRUE },
+ B_TRUE, B_TRUE, ZFS_DELEG_PERM_SETUID },
{ "readonly", prop_type_boolean, 0, NULL, prop_inherit,
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
- "on | off", "RDONLY", B_TRUE, B_TRUE },
+ "on | off", "RDONLY", B_TRUE, B_TRUE, ZFS_DELEG_PERM_READONLY },
{ "zoned", prop_type_boolean, 0, NULL, prop_inherit,
ZFS_TYPE_FILESYSTEM,
- "on | off", "ZONED", B_TRUE, B_TRUE },
+ "on | off", "ZONED", B_TRUE, B_TRUE, ZFS_DELEG_PERM_ZONED },
{ "snapdir", prop_type_index, ZFS_SNAPDIR_HIDDEN, "hidden",
prop_inherit,
ZFS_TYPE_FILESYSTEM,
- "hidden | visible", "SNAPDIR", B_TRUE, B_TRUE },
+ "hidden | visible", "SNAPDIR", B_TRUE, B_TRUE,
+ ZFS_DELEG_PERM_SNAPDIR },
{ "aclmode", prop_type_index, ZFS_ACL_GROUPMASK, "groupmask",
prop_inherit, ZFS_TYPE_FILESYSTEM,
- "discard | groupmask | passthrough", "ACLMODE", B_TRUE, B_TRUE },
+ "discard | groupmask | passthrough", "ACLMODE", B_TRUE,
+ B_TRUE, ZFS_DELEG_PERM_ACLMODE },
{ "aclinherit", prop_type_index, ZFS_ACL_SECURE, "secure",
prop_inherit, ZFS_TYPE_FILESYSTEM,
"discard | noallow | secure | passthrough", "ACLINHERIT", B_TRUE,
- B_TRUE },
+ B_TRUE, ZFS_DELEG_PERM_ACLINHERIT },
{ "createtxg", prop_type_number, 0, NULL, prop_readonly,
- ZFS_TYPE_ANY, NULL, NULL, B_FALSE, B_FALSE },
+ ZFS_TYPE_ANY, NULL, NULL, B_FALSE, B_FALSE, ZFS_DELEG_PERM_NONE },
{ "name", prop_type_string, 0, NULL, prop_readonly,
- ZFS_TYPE_ANY, NULL, "NAME", B_FALSE, B_FALSE },
+ ZFS_TYPE_ANY, NULL, "NAME", B_FALSE, B_FALSE, ZFS_DELEG_PERM_NONE },
{ "canmount", prop_type_boolean, 1, NULL, prop_default,
ZFS_TYPE_FILESYSTEM,
- "on | off", "CANMOUNT", B_TRUE, B_TRUE },
+ "on | off", "CANMOUNT", B_TRUE, B_TRUE, ZFS_DELEG_PERM_CANMOUNT },
{ "shareiscsi", prop_type_string, 0, "off", prop_inherit,
ZFS_TYPE_ANY,
- "on | off | type=<type>", "SHAREISCSI", B_FALSE, B_TRUE },
+ "on | off | type=<type>", "SHAREISCSI", B_FALSE, B_TRUE,
+ ZFS_DELEG_PERM_SHAREISCSI },
{ "iscsioptions", prop_type_string, 0, NULL, prop_inherit,
- ZFS_TYPE_VOLUME, NULL, "ISCSIOPTIONS", B_FALSE, B_FALSE },
+ ZFS_TYPE_VOLUME, NULL, "ISCSIOPTIONS", B_FALSE, B_FALSE,
+ ZFS_DELEG_PERM_NONE },
{ "xattr", prop_type_boolean, 1, NULL, prop_inherit,
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT,
- "on | off", "XATTR", B_TRUE, B_TRUE },
+ "on | off", "XATTR", B_TRUE, B_TRUE, ZFS_DELEG_PERM_XATTR },
{ "numclones", prop_type_number, 0, NULL, prop_readonly,
- ZFS_TYPE_SNAPSHOT, NULL, NULL, B_FALSE, B_FALSE },
+ ZFS_TYPE_SNAPSHOT, NULL, NULL, B_FALSE, B_FALSE,
+ ZFS_DELEG_PERM_NONE },
{ "copies", prop_type_index, 1, "1", prop_inherit,
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
- "1 | 2 | 3", "COPIES", B_TRUE, B_TRUE },
+ "1 | 2 | 3", "COPIES", B_TRUE, B_TRUE, ZFS_DELEG_PERM_COPIES },
{ "bootfs", prop_type_string, 0, NULL, prop_default,
- ZFS_TYPE_POOL, "<filesystem>", "BOOTFS", B_FALSE, B_TRUE },
+ ZFS_TYPE_POOL, "<filesystem>", "BOOTFS", B_FALSE,
+ B_TRUE, ZFS_DELEG_PERM_NONE },
{ "autoreplace", prop_type_boolean, 0, NULL, prop_default,
- ZFS_TYPE_POOL, "on | off", "REPLACE", B_FALSE, B_TRUE },
+ ZFS_TYPE_POOL, "on | off", "REPLACE", B_FALSE, B_TRUE,
+ ZFS_DELEG_PERM_NONE },
+ { "delegation", prop_type_boolean, 1, NULL, prop_default,
+ ZFS_TYPE_POOL, "on | off", "DELEGATION", B_TRUE,
+ B_TRUE, ZFS_DELEG_PERM_NONE }
};
#define ZFS_PROP_COUNT ((sizeof (zfs_prop_table))/(sizeof (prop_desc_t)))
@@ -291,8 +313,9 @@ zfs_name_to_prop_cb(zfs_prop_t prop, void *cb_data)
{
const char *propname = cb_data;
- if (propname_match(propname, prop, strlen(propname)))
+ if (propname_match(propname, prop, strlen(propname))) {
return (prop);
+ }
return (ZFS_PROP_CONT);
}
@@ -328,6 +351,12 @@ zpool_name_to_prop(const char *propname)
return (zfs_name_to_prop_common(propname, ZFS_TYPE_POOL));
}
+const char *
+zfs_prop_perm(zfs_prop_t prop)
+{
+ return (zfs_prop_table[prop].pd_perm);
+}
+
/*
* For user property names, we allow all lowercase alphanumeric characters, plus
* a few useful punctuation characters.
diff --git a/usr/src/grub/grub-0.95/stage2/zfs-include/dsl_dir.h b/usr/src/grub/grub-0.95/stage2/zfs-include/dsl_dir.h
index 7fc8b1eaba..591e1978c9 100644
--- a/usr/src/grub/grub-0.95/stage2/zfs-include/dsl_dir.h
+++ b/usr/src/grub/grub-0.95/stage2/zfs-include/dsl_dir.h
@@ -44,7 +44,8 @@ typedef struct dsl_dir_phys {
/* Administrative reservation setting */
uint64_t dd_reserved;
uint64_t dd_props_zapobj;
- uint64_t dd_pad[21]; /* pad out to 256 bytes for good measure */
+ uint64_t dd_deleg_zapobj; /* dataset permissions */
+ uint64_t dd_pad[20]; /* pad out to 256 bytes for good measure */
} dsl_dir_phys_t;
#endif /* _SYS_DSL_DIR_H */
diff --git a/usr/src/grub/grub-0.95/stage2/zfs-include/zfs.h b/usr/src/grub/grub-0.95/stage2/zfs-include/zfs.h
index a8773b23c1..c590a2f932 100644
--- a/usr/src/grub/grub-0.95/stage2/zfs-include/zfs.h
+++ b/usr/src/grub/grub-0.95/stage2/zfs-include/zfs.h
@@ -36,7 +36,8 @@
#define ZFS_VERSION_5 5ULL
#define ZFS_VERSION_6 6ULL
#define ZFS_VERSION_7 7ULL
-#define ZFS_VERSION ZFS_VERSION_7
+#define ZFS_VERSION_8 8ULL
+#define ZFS_VERSION ZFS_VERSION_8
/*
* The following are configuration names used in the nvlist describing a pool's
diff --git a/usr/src/lib/libiscsitgt/Makefile.com b/usr/src/lib/libiscsitgt/Makefile.com
index 1a160df7de..3d4559c7fd 100644
--- a/usr/src/lib/libiscsitgt/Makefile.com
+++ b/usr/src/lib/libiscsitgt/Makefile.com
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -42,7 +42,8 @@ SRCS = $(OBJECTS:%.o=$(SRCDIR)/%.c)
$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC)
CFLAGS += $(CCVERBOSE)
-CPPFLAGS += -I/usr/include/libxml2 -I$(SRCDIR)
+CPPFLAGS += -I/usr/include/libxml2 -I$(SRCDIR) \
+ -I../../../cmd/iscsi/iscsitgtd
.KEEP_STATE:
diff --git a/usr/src/lib/libiscsitgt/common/gen.c b/usr/src/lib/libiscsitgt/common/gen.c
index 93fc956643..706378d4f6 100644
--- a/usr/src/lib/libiscsitgt/common/gen.c
+++ b/usr/src/lib/libiscsitgt/common/gen.c
@@ -52,8 +52,8 @@ tgt_door_call(char *str, int smf_flags)
{
tgt_node_t *n = NULL;
door_arg_t d;
- int s,
- allocated;
+ int s;
+ int allocated;
xmlTextReaderPtr r;
char *door_buf = NULL;
@@ -194,8 +194,8 @@ is_auto_enabled(void)
static Boolean_t
check_and_online(int smf_flags)
{
- int i,
- fd;
+ int i;
+ int fd;
door_arg_t d;
if (!is_online()) {
@@ -245,3 +245,13 @@ check_and_online(int smf_flags)
}
return (False);
}
+
+/*
+ * Not using Boolean_t here, since that is a
+ * private type to the library
+ */
+int
+iscsitgt_svc_online()
+{
+ return ((is_online() == True) ? 0 : 1);
+}
diff --git a/usr/src/lib/libiscsitgt/common/if_zfs.c b/usr/src/lib/libiscsitgt/common/if_zfs.c
index ffaca97b79..3528872286 100644
--- a/usr/src/lib/libiscsitgt/common/if_zfs.c
+++ b/usr/src/lib/libiscsitgt/common/if_zfs.c
@@ -35,6 +35,7 @@
#include <strings.h>
#include <errno.h>
#include <libscf.h>
+#include <errcode.h>
#include "iscsitgt_impl.h"
@@ -55,19 +56,27 @@ iscsitgt_zfs_share(const char *dataset)
tgt_buf_add_tag(&str, "create", Tag_End);
if ((n = tgt_door_call(str, SMF_TEMPORARY)) == NULL) {
- errno = EINVAL;
+ if (iscsitgt_svc_online() != 0) {
+ errno = EPERM;
+ } else {
+ errno = EINVAL;
+ }
free(str);
return (-1);
}
+
if (strcmp(n->x_name, XML_ELEMENT_ERROR) == 0 &&
- tgt_find_value_int(n, XML_ELEMENT_CODE, &code) == True &&
- code == 1000) {
- free(str);
- tgt_node_free(n);
- return (0);
+ tgt_find_value_int(n, XML_ELEMENT_CODE, &code) == True) {
+ if (code == 1000) {
+ free(str);
+ tgt_node_free(n);
+ return (0);
+ } else {
+ errno = (code == ERR_NO_PERMISSION) ? EPERM : EINVAL;
+ }
+ } else {
+ errno = EINVAL;
}
-
- errno = EINVAL;
free(str);
tgt_node_free(n);
return (-1);
@@ -94,17 +103,22 @@ iscsitgt_zfs_unshare(const char *dataset)
free(str);
return (-1);
}
+
if (strcmp(n->x_name, XML_ELEMENT_ERROR) == 0 &&
- tgt_find_value_int(n, XML_ELEMENT_CODE, &code) == True &&
- code == 1000) {
- free(str);
- tgt_node_free(n);
- return (0);
+ tgt_find_value_int(n, XML_ELEMENT_CODE, &code) == True) {
+ if (code == 1000) {
+ free(str);
+ tgt_node_free(n);
+ return (0);
+ } else {
+ errno = (code == ERR_NO_PERMISSION) ? EPERM : EINVAL;
+ }
+ } else {
+ errno = EINVAL;
}
free(str);
tgt_node_free(n);
- errno = EINVAL;
return (-1);
}
diff --git a/usr/src/lib/libiscsitgt/common/libiscsitgt.h b/usr/src/lib/libiscsitgt/common/libiscsitgt.h
index 690ac009b5..cf01a62fea 100644
--- a/usr/src/lib/libiscsitgt/common/libiscsitgt.h
+++ b/usr/src/lib/libiscsitgt/common/libiscsitgt.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -566,6 +566,11 @@ void iscsit_list_tpgt_free(iscsit_tpgt_t *t);
iscsit_admin_t *iscsit_list_adm(iscsit_handle_t h);
void iscsit_list_adm_free(iscsit_admin_t *t);
+/*
+ * Misc functions
+ */
+int iscsitgt_svc_online();
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/lib/libiscsitgt/common/mapfile-vers b/usr/src/lib/libiscsitgt/common/mapfile-vers
index 8702abfdfb..11ceede106 100644
--- a/usr/src/lib/libiscsitgt/common/mapfile-vers
+++ b/usr/src/lib/libiscsitgt/common/mapfile-vers
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -27,6 +27,7 @@
SUNWprivate_1.1 {
global:
+ iscsitgt_svc_online;
iscsitgt_zfs_is_shared;
iscsitgt_zfs_share;
iscsitgt_zfs_unshare;
diff --git a/usr/src/lib/libshare/common/libshare.c b/usr/src/lib/libshare/common/libshare.c
index 6809ab92cf..25213e97a4 100644
--- a/usr/src/lib/libshare/common/libshare.c
+++ b/usr/src/lib/libshare/common/libshare.c
@@ -1367,10 +1367,10 @@ sa_disable_share(sa_share_t share, char *protocol)
shared = sa_get_share_attr(share, "shared");
if (protocol != NULL) {
- ret = sa_proto_unshare(protocol, path);
+ ret = sa_proto_unshare(share, protocol, path);
} else {
/* need to do all protocols */
- ret = sa_proto_unshare("nfs", path);
+ ret = sa_proto_unshare(share, "nfs", path);
}
if (ret == SA_OK)
(void) sa_set_share_attr(share, "shared", NULL);
diff --git a/usr/src/lib/libshare/common/libshare.h b/usr/src/lib/libshare/common/libshare.h
index 754ef61c69..c78b0822c6 100644
--- a/usr/src/lib/libshare/common/libshare.h
+++ b/usr/src/lib/libshare/common/libshare.h
@@ -216,7 +216,7 @@ extern int sa_delete_sharetab(char *, char *);
/* ZFS functions */
extern int sa_zfs_is_shared(sa_handle_t, char *);
extern int sa_group_is_zfs(sa_group_t);
-
+extern int sa_path_is_zfs(char *);
/* SA Handle specific functions */
extern sa_handle_t sa_find_group_handle(sa_group_t);
diff --git a/usr/src/lib/libshare/common/libshare_impl.h b/usr/src/lib/libshare/common/libshare_impl.h
index 2eef7559c9..a428110a81 100644
--- a/usr/src/lib/libshare/common/libshare_impl.h
+++ b/usr/src/lib/libshare/common/libshare_impl.h
@@ -38,6 +38,8 @@
#include <libscf.h>
#include <scfutil.h>
#include <libzfs.h>
+#include <sharefs/share.h>
+#include "sharetab.h"
#ifdef __cplusplus
extern "C" {
@@ -58,7 +60,7 @@ struct sa_plugin_ops {
int (*sa_init)();
void (*sa_fini)();
int (*sa_share)(sa_share_t); /* start sharing */
- int (*sa_unshare)(char *); /* stop sharing */
+ int (*sa_unshare)(sa_share_t, char *); /* stop sharing */
int (*sa_valid_prop)(sa_property_t, sa_optionset_t);
int (*sa_valid_space)(char *); /* is name valid optionspace? */
int (*sa_security_prop)(char *); /* property is security */
@@ -91,7 +93,7 @@ typedef struct propertylist {
} property_list_t;
extern int sa_proto_share(char *, sa_share_t);
-extern int sa_proto_unshare(char *, char *);
+extern int sa_proto_unshare(sa_share_t, char *, char *);
extern int sa_proto_valid_prop(char *, sa_property_t, sa_optionset_t);
extern int sa_proto_security_prop(char *, char *);
extern int sa_proto_legacy_opts(char *, sa_group_t, char *);
@@ -113,10 +115,15 @@ extern int sa_delete_share(scfutilhandle_t *, sa_group_t, sa_share_t);
extern int sa_delete_instance(scfutilhandle_t *, char *);
extern int sa_create_pgroup(scfutilhandle_t *, char *);
extern int sa_delete_pgroup(scfutilhandle_t *, char *);
+extern void sa_fillshare(sa_share_t share, char *proto, struct share *sh);
+extern void sa_emptyshare(struct share *sh);
/* ZFS functions */
extern int sa_get_zfs_shares(sa_handle_t, char *);
extern int sa_zfs_update(sa_share_t);
+extern int sa_share_zfs(sa_share_t, char *, share_t *, void *, boolean_t);
+extern int sa_sharetab_fill_zfs(sa_share_t share, struct share *sh,
+ char *proto);
/* plugin specific functions */
extern int proto_plugin_init();
diff --git a/usr/src/lib/libshare/common/libshare_zfs.c b/usr/src/lib/libshare/common/libshare_zfs.c
index b7ac6c0d74..8beb621524 100644
--- a/usr/src/lib/libshare/common/libshare_zfs.c
+++ b/usr/src/lib/libshare/common/libshare_zfs.c
@@ -254,7 +254,7 @@ get_zfs_dataset(sa_handle_impl_t impl_handle, char *path)
/* canmount must be set */
canmount[0] = '\0';
- if (!zfs_prop_get(zlist[i], ZFS_PROP_CANMOUNT, canmount,
+ if (zfs_prop_get(zlist[i], ZFS_PROP_CANMOUNT, canmount,
sizeof (canmount), NULL, NULL, 0, B_FALSE) != 0 ||
strcmp(canmount, "off") == 0)
continue;
@@ -914,3 +914,76 @@ sa_path_is_zfs(char *path)
sa_free_fstype(fstype);
return (ret);
}
+
+int
+sa_sharetab_fill_zfs(sa_share_t share, share_t *sh, char *proto)
+{
+ char *path;
+
+ path = sa_get_share_attr(share, "path");
+ if (path != NULL) {
+ (void) memset(sh, 0, sizeof (sh));
+ (void) sa_fillshare(share, proto, sh);
+ return (0);
+ } else
+ return (1);
+}
+
+#define SMAX(i, j) \
+ if ((j) > (i)) { \
+ (i) = (j); \
+ }
+
+int
+sa_share_zfs(sa_share_t share, char *path, share_t *sh,
+ void *exportdata, boolean_t on)
+{
+ libzfs_handle_t *libhandle;
+ sa_group_t group;
+ sa_handle_t sahandle;
+ char *dataset;
+ int err = EINVAL;
+ int i, j;
+
+ /*
+ * First find the dataset name
+ */
+ if ((group = sa_get_parent_group(share)) == NULL) {
+ return (SA_SYSTEM_ERR);
+ }
+ if ((sahandle = sa_find_group_handle(group)) == NULL) {
+ return (SA_SYSTEM_ERR);
+ }
+
+ if ((dataset = get_zfs_dataset(sahandle, path)) == NULL) {
+ return (SA_SYSTEM_ERR);
+ }
+
+ libhandle = libzfs_init();
+ if (libhandle != NULL) {
+
+ i = (sh->sh_path ? strlen(sh->sh_path) : 0);
+ sh->sh_size = i;
+
+ j = (sh->sh_res ? strlen(sh->sh_res) : 0);
+ sh->sh_size += j;
+ SMAX(i, j);
+
+ j = (sh->sh_fstype ? strlen(sh->sh_fstype) : 0);
+ sh->sh_size += j;
+ SMAX(i, j);
+
+ j = (sh->sh_opts ? strlen(sh->sh_opts) : 0);
+ sh->sh_size += j;
+ SMAX(i, j);
+
+ j = (sh->sh_descr ? strlen(sh->sh_descr) : 0);
+ sh->sh_size += j;
+ SMAX(i, j);
+ err = zfs_deleg_share_nfs(libhandle, dataset, path,
+ exportdata, sh, i, on);
+ libzfs_fini(libhandle);
+ }
+ free(dataset);
+ return (err);
+}
diff --git a/usr/src/lib/libshare/common/libsharecore.c b/usr/src/lib/libshare/common/libsharecore.c
index d3bf9f4f6d..cbef53feab 100644
--- a/usr/src/lib/libshare/common/libsharecore.c
+++ b/usr/src/lib/libshare/common/libsharecore.c
@@ -1832,12 +1832,12 @@ sa_free_derived_security(sa_security_t security)
*/
/*
- * fillshare(share, proto, sh)
+ * sa_fillshare(share, proto, sh)
*
* Fill the struct share with values obtained from the share object.
*/
-static void
-fillshare(sa_share_t share, char *proto, struct share *sh)
+void
+sa_fillshare(sa_share_t share, char *proto, struct share *sh)
{
char *groupname = NULL;
char *value;
@@ -1916,13 +1916,13 @@ fillshare(sa_share_t share, char *proto, struct share *sh)
}
/*
- * emptyshare(sh)
+ * sa_emptyshare(sh)
*
* Free the strings in the non-NULL members of sh.
*/
-static void
-emptyshare(struct share *sh)
+void
+sa_emptyshare(struct share *sh)
{
if (sh->sh_path != NULL)
free(sh->sh_path);
@@ -1962,9 +1962,9 @@ sa_update_sharetab(sa_share_t share, char *proto)
/*
* Fill in share structure and send it to the kernel.
*/
- (void) fillshare(share, proto, &sh);
+ (void) sa_fillshare(share, proto, &sh);
(void) sharefs(SHAREFS_ADD, &sh);
- emptyshare(&sh);
+ sa_emptyshare(&sh);
sa_free_attr_string(path);
}
diff --git a/usr/src/lib/libshare/common/mapfile-vers b/usr/src/lib/libshare/common/mapfile-vers
index eb71ae23fb..317623b3b4 100644
--- a/usr/src/lib/libshare/common/mapfile-vers
+++ b/usr/src/lib/libshare/common/mapfile-vers
@@ -62,10 +62,14 @@ SUNWprivate {
sa_commit_properties;
sa_parse_legacy_options;
sa_zfs_is_shared;
+ sa_share_zfs;
+ sa_path_is_zfs;
sa_get_derived_optionset;
sa_move_share;
sa_group_is_zfs;
sa_update_config;
+ sa_sharetab_fill_zfs;
+ sa_emptyshare;
sa_get_share_attr;
sa_create_optionset;
sa_valid_property;
diff --git a/usr/src/lib/libshare/common/plugin.c b/usr/src/lib/libshare/common/plugin.c
index ab682e6f10..83bc4d5d94 100644
--- a/usr/src/lib/libshare/common/plugin.c
+++ b/usr/src/lib/libshare/common/plugin.c
@@ -240,13 +240,13 @@ sa_proto_share(char *proto, sa_share_t share)
*/
int
-sa_proto_unshare(char *proto, char *path)
+sa_proto_unshare(sa_share_t share, char *proto, char *path)
{
struct sa_plugin_ops *ops = find_protocol(proto);
int ret = SA_INVALID_PROTOCOL;
if (ops != NULL && ops->sa_unshare != NULL)
- ret = ops->sa_unshare(path);
+ ret = ops->sa_unshare(share, path);
return (ret);
}
diff --git a/usr/src/lib/libshare/nfs/libshare_nfs.c b/usr/src/lib/libshare/nfs/libshare_nfs.c
index f7b8e8ecba..ec25f19efe 100644
--- a/usr/src/lib/libshare/nfs/libshare_nfs.c
+++ b/usr/src/lib/libshare/nfs/libshare_nfs.c
@@ -49,6 +49,7 @@
#include "libshare_nfs.h"
#include <rpcsvc/daemon_utils.h>
#include <nfs/nfs.h>
+#include <nfs/nfssys.h>
/* should really be in some global place */
#define DEF_WIN 30000
@@ -56,12 +57,13 @@
int debug = 0;
+#define NFS_SERVER_SVC "svc:/network/nfs/server:default"
/* internal functions */
static int nfs_init();
static void nfs_fini();
static int nfs_enable_share(sa_share_t);
-static int nfs_disable_share(char *);
+static int nfs_disable_share(sa_share_t, char *);
static int nfs_validate_property(sa_property_t, sa_optionset_t);
static int nfs_validate_security_mode(char *);
static int nfs_is_security_opt(char *);
@@ -1616,6 +1618,7 @@ nfs_enable_share(sa_share_t share)
char *path;
int err = SA_OK;
int i;
+ int iszfs;
/* Don't drop core if the NFS module isn't loaded. */
(void) signal(SIGSYS, SIG_IGN);
@@ -1625,6 +1628,7 @@ nfs_enable_share(sa_share_t share)
if (path == NULL)
return (SA_NO_SUCH_PATH);
+ iszfs = sa_path_is_zfs(path);
/*
* find the optionsets and security sets. There may not be
* any or there could be one or two for each of optionset and
@@ -1741,18 +1745,67 @@ nfs_enable_share(sa_share_t share)
* call the exportfs system call which is implemented
* via the nfssys() call as the EXPORTFS subfunction.
*/
- if ((err = exportfs(path, &export)) < 0) {
+ if (iszfs) {
+ struct exportfs_args ea;
+ share_t sh;
+ char *str;
+ priv_set_t *priv_effective;
+ int privileged;
+
+ /*
+ * If we aren't a privileged user
+ * and NFS server service isn't running
+ * then print out an error message
+ * and return EPERM
+ */
+
+ priv_effective = priv_allocset();
+ (void) getppriv(PRIV_EFFECTIVE, priv_effective);
+
+ privileged = (priv_isfullset(priv_effective) == B_TRUE);
+ priv_freeset(priv_effective);
+
+ if (!privileged &&
+ (str = smf_get_state(NFS_SERVER_SVC)) != NULL) {
+ err = 0;
+ if (strcmp(str, SCF_STATE_STRING_ONLINE) != 0) {
+ (void) printf(dgettext(TEXT_DOMAIN,
+ "NFS: Cannot share remote "
+ "filesystem: %s\n"), path);
+ (void) printf(dgettext(TEXT_DOMAIN,
+ "NFS: Service needs to be enabled "
+ "by a privileged user\n"));
+ err = SA_SYSTEM_ERR;
+ errno = EPERM;
+ }
+ free(str);
+ }
+
+ if (err == 0) {
+ ea.dname = path;
+ ea.uex = &export;
+
+ sa_sharetab_fill_zfs(share, &sh, "nfs");
+ err = sa_share_zfs(share, path, &sh, &ea, B_TRUE);
+ sa_emptyshare(&sh);
+ }
+ } else {
+ err = exportfs(path, &export);
+ }
+
+ if (err < 0) {
err = SA_SYSTEM_ERR;
switch (errno) {
case EREMOTE:
(void) printf(dgettext(TEXT_DOMAIN,
- "NFS: Cannot share remote "
- "filesystem: %s\n"), path);
+ "NFS: Cannot share filesystems "
+ "in non-global zones: %s\n"), path);
+ err = SA_NOT_SUPPORTED;
break;
case EPERM:
if (getzoneid() != GLOBAL_ZONEID) {
(void) printf(dgettext(TEXT_DOMAIN,
- "NFS: Cannot share filesystems "
+ "NFS: Cannot share file systems "
"in non-global zones: %s\n"), path);
err = SA_NOT_SUPPORTED;
break;
@@ -1764,7 +1817,9 @@ nfs_enable_share(sa_share_t share)
}
} else {
/* update sharetab with an add/modify */
- (void) sa_update_sharetab(share, "nfs");
+ if (!iszfs) {
+ (void) sa_update_sharetab(share, "nfs");
+ }
}
if (err == SA_OK) {
@@ -1817,38 +1872,56 @@ out:
* done? We only do basic errors for now.
*/
static int
-nfs_disable_share(char *share)
+nfs_disable_share(sa_share_t share, char *path)
{
int err;
int ret = SA_OK;
+ int iszfs;
+
+
+ if (path != NULL) {
+ iszfs = sa_path_is_zfs(path);
+
+ if (iszfs) {
+ struct exportfs_args ea;
+ share_t sh = { 0 };
+
+ ea.dname = path;
+ ea.uex = NULL;
+ sh.sh_path = path;
+ sh.sh_fstype = "nfs";
- if (share != NULL) {
- err = exportfs(share, NULL);
+ err = sa_share_zfs(share, path, &sh, &ea, B_FALSE);
+ } else
+ err = exportfs(path, NULL);
if (err < 0) {
/*
- * TBD: only an error in some cases - need
- * better analysis
+ * TBD: only an error in some
+ * cases - need better analysis
*/
+
switch (errno) {
case EPERM:
case EACCES:
ret = SA_NO_PERMISSION;
- if (getzoneid() != GLOBAL_ZONEID)
+ if (getzoneid() != GLOBAL_ZONEID) {
ret = SA_NOT_SUPPORTED;
+ }
break;
case EINVAL:
case ENOENT:
ret = SA_NO_SUCH_PATH;
- break;
+ break;
default:
ret = SA_SYSTEM_ERR;
- break;
+ break;
}
}
if (ret == SA_OK || ret == SA_NO_SUCH_PATH) {
- (void) sa_delete_sharetab(share, "nfs");
+ if (!iszfs)
+ (void) sa_delete_sharetab(path, "nfs");
/* just in case it was logged */
- (void) nfslogtab_deactivate(share);
+ (void) nfslogtab_deactivate(path);
}
}
return (ret);
diff --git a/usr/src/lib/libzfs/Makefile.com b/usr/src/lib/libzfs/Makefile.com
index 952965eed6..531487193c 100644
--- a/usr/src/lib/libzfs/Makefile.com
+++ b/usr/src/lib/libzfs/Makefile.com
@@ -28,7 +28,7 @@
LIBRARY= libzfs.a
VERS= .1
-OBJS_SHARED= zfs_namecheck.o zfs_prop.o
+OBJS_SHARED= zfs_namecheck.o zfs_prop.o zfs_deleg.o
OBJS_COMMON= libzfs_dataset.o libzfs_util.o libzfs_graph.o libzfs_mount.o \
libzfs_pool.o libzfs_changelist.o libzfs_config.o libzfs_import.o \
libzfs_status.o
@@ -49,7 +49,7 @@ INCS += -I../../../common/zfs
C99MODE= -xc99=%all
C99LMODE= -Xc99=%all
-LDLIBS += -lc -lm -ldevinfo -ldevid -lgen -lnvpair -luutil -lefi
+LDLIBS += -lc -lm -ldevinfo -ldevid -lgen -lnvpair -luutil -lavl -lefi
CPPFLAGS += $(INCS) -D_REENTRANT
SRCS= $(OBJS_COMMON:%.o=$(SRCDIR)/%.c) \
diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h
index 1a27fa14fb..06ea0a885c 100644
--- a/usr/src/lib/libzfs/common/libzfs.h
+++ b/usr/src/lib/libzfs/common/libzfs.h
@@ -35,6 +35,9 @@
#include <sys/types.h>
#include <sys/varargs.h>
#include <sys/fs/zfs.h>
+#include <sys/avl.h>
+#include <libuutil.h>
+#include <ucred.h>
#ifdef __cplusplus
extern "C" {
@@ -101,10 +104,53 @@ enum {
EZFS_OPENFAILED, /* open of device failed */
EZFS_NOCAP, /* couldn't get capacity */
EZFS_LABELFAILED, /* write of label failed */
+ EZFS_ISCSISVCUNAVAIL, /* iscsi service unavailable */
+ EZFS_BADWHO, /* invalid permission who */
+ EZFS_BADPERM, /* invalid permission */
+ EZFS_BADPERMSET, /* invalid permission set name */
+ EZFS_PERMSET_CIRCULAR, /* circular dependency on permset */
+ EZFS_NODELEGATION, /* delegated administration is disabled */
+ EZFS_PERMRDONLY, /* pemissions are readonly */
EZFS_UNKNOWN
};
/*
+ * The following data structures are all part
+ * of the zfs_allow_t data structure which is
+ * used for printing 'allow' permissions.
+ * It is a linked list of zfs_allow_t's which
+ * then contain avl tree's for user/group/sets/...
+ * and each one of the entries in those trees have
+ * avl tree's for the permissions they belong to and
+ * whether they are local,descendent or local+descendent
+ * permissions. The AVL trees are used primarily for
+ * sorting purposes, but also so that we can quickly find
+ * a given user and or permission.
+ */
+typedef struct zfs_perm_node {
+ avl_node_t z_node;
+ char z_pname[MAXPATHLEN];
+} zfs_perm_node_t;
+
+typedef struct zfs_allow_node {
+ avl_node_t z_node;
+ char z_key[MAXPATHLEN]; /* name, such as joe */
+ avl_tree_t z_localdescend; /* local+descendent perms */
+ avl_tree_t z_local; /* local permissions */
+ avl_tree_t z_descend; /* descendent permissions */
+} zfs_allow_node_t;
+
+typedef struct zfs_allow {
+ struct zfs_allow *z_next;
+ char z_setpoint[MAXPATHLEN];
+ avl_tree_t z_sets;
+ avl_tree_t z_crperms;
+ avl_tree_t z_user;
+ avl_tree_t z_group;
+ avl_tree_t z_everyone;
+} zfs_allow_t;
+
+/*
* Basic handle types
*/
typedef struct zfs_handle zfs_handle_t;
@@ -247,14 +293,16 @@ extern nvlist_t *zpool_find_import(libzfs_handle_t *, int, char **);
/*
* Miscellaneous pool functions
*/
+struct zfs_cmd;
+
extern char *zpool_vdev_name(libzfs_handle_t *, zpool_handle_t *, nvlist_t *);
extern int zpool_upgrade(zpool_handle_t *);
extern int zpool_get_history(zpool_handle_t *, nvlist_t **);
-extern void zpool_log_history(libzfs_handle_t *, int, char **, const char *,
- boolean_t, boolean_t);
+extern void zpool_stage_history(libzfs_handle_t *, int, char **,
+ boolean_t zfs_cmd, boolean_t pool_create);
extern void zpool_obj_to_path(zpool_handle_t *, uint64_t, uint64_t, char *,
size_t len);
-
+extern int zfs_ioctl(libzfs_handle_t *, int, struct zfs_cmd *);
/*
* Basic handle manipulations. These functions do not create or destroy the
* underlying datasets, only the references to them.
@@ -368,6 +416,16 @@ extern boolean_t zfs_dataset_exists(libzfs_handle_t *, const char *,
zfs_type_t);
/*
+ * dataset permission functions.
+ */
+extern int zfs_perm_set(zfs_handle_t *, nvlist_t *);
+extern int zfs_perm_remove(zfs_handle_t *, nvlist_t *);
+extern int zfs_build_perms(zfs_handle_t *, char *, char *,
+ zfs_deleg_who_type_t, zfs_deleg_inherit_t, nvlist_t **nvlist_t);
+extern int zfs_perm_get(zfs_handle_t *, zfs_allow_t **);
+extern void zfs_free_allows(zfs_allow_t *);
+
+/*
* Mount support functions.
*/
extern boolean_t is_mounted(libzfs_handle_t *, const char *special, char **);
@@ -393,6 +451,9 @@ extern int zfs_unshareall_nfs(zfs_handle_t *);
extern boolean_t zfs_is_shared_iscsi(zfs_handle_t *);
extern int zfs_share_iscsi(zfs_handle_t *);
extern int zfs_unshare_iscsi(zfs_handle_t *);
+extern int zfs_iscsi_perm_check(libzfs_handle_t *, char *, ucred_t *);
+extern int zfs_deleg_share_nfs(libzfs_handle_t *, char *, char *,
+ void *, void *, int, boolean_t);
/*
* When dealing with nvlists, verify() is extremely useful
diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c
index 93ffe03980..5cea7c3fea 100644
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c
@@ -41,6 +41,12 @@
#include <sys/mntent.h>
#include <sys/mnttab.h>
#include <sys/mount.h>
+#include <sys/avl.h>
+#include <priv.h>
+#include <pwd.h>
+#include <grp.h>
+#include <stddef.h>
+#include <ucred.h>
#include <sys/spa.h>
#include <sys/zio.h>
@@ -50,6 +56,7 @@
#include "zfs_namecheck.h"
#include "zfs_prop.h"
#include "libzfs_impl.h"
+#include "zfs_deleg.h"
static int create_parents(libzfs_handle_t *, char *, int);
static int zvol_create_link_common(libzfs_handle_t *, const char *, int);
@@ -307,16 +314,25 @@ zfs_handle_t *
make_dataset_handle(libzfs_handle_t *hdl, const char *path)
{
zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
+ char *logstr;
if (zhp == NULL)
return (NULL);
zhp->zfs_hdl = hdl;
+ /*
+ * Preserve history log string.
+ * any changes performed here will be
+ * logged as an internal event.
+ */
+ logstr = zhp->zfs_hdl->libzfs_log_str;
+ zhp->zfs_hdl->libzfs_log_str = NULL;
top:
(void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
if (get_stats(zhp) != 0) {
+ zhp->zfs_hdl->libzfs_log_str = logstr;
free(zhp);
return (NULL);
}
@@ -356,6 +372,7 @@ top:
* never existed.
*/
if (ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc) == 0) {
+ zhp->zfs_hdl->libzfs_log_str = logstr;
free(zhp);
errno = ENOENT;
return (NULL);
@@ -382,6 +399,7 @@ top:
else
abort(); /* we should never see any other types */
+ zhp->zfs_hdl->libzfs_log_str = logstr;
return (zhp);
}
@@ -1120,6 +1138,742 @@ error:
return (NULL);
}
+static int
+zfs_get_perm_who(const char *who, zfs_deleg_who_type_t *who_type,
+ uint64_t *ret_who)
+{
+ struct passwd *pwd;
+ struct group *grp;
+ uid_t id;
+
+ if (*who_type == ZFS_DELEG_EVERYONE || *who_type == ZFS_DELEG_CREATE ||
+ *who_type == ZFS_DELEG_NAMED_SET) {
+ *ret_who = -1;
+ return (0);
+ }
+ if (who == NULL && !(*who_type == ZFS_DELEG_EVERYONE))
+ return (EZFS_BADWHO);
+
+ if (*who_type == ZFS_DELEG_WHO_UNKNOWN &&
+ strcmp(who, "everyone") == 0) {
+ *ret_who = -1;
+ *who_type = ZFS_DELEG_EVERYONE;
+ return (0);
+ }
+
+ pwd = getpwnam(who);
+ grp = getgrnam(who);
+
+ if ((*who_type == ZFS_DELEG_USER) && pwd) {
+ *ret_who = pwd->pw_uid;
+ } else if ((*who_type == ZFS_DELEG_GROUP) && grp) {
+ *ret_who = grp->gr_gid;
+ } else if (pwd) {
+ *ret_who = pwd->pw_uid;
+ *who_type = ZFS_DELEG_USER;
+ } else if (grp) {
+ *ret_who = grp->gr_gid;
+ *who_type = ZFS_DELEG_GROUP;
+ } else {
+ char *end;
+
+ id = strtol(who, &end, 10);
+ if (errno != 0 || *end != '\0') {
+ return (EZFS_BADWHO);
+ } else {
+ *ret_who = id;
+ if (*who_type == ZFS_DELEG_WHO_UNKNOWN)
+ *who_type = ZFS_DELEG_USER;
+ }
+ }
+
+ return (0);
+}
+
+static void
+zfs_perms_add_to_nvlist(nvlist_t *who_nvp, char *name, nvlist_t *perms_nvp)
+{
+ if (perms_nvp != NULL) {
+ verify(nvlist_add_nvlist(who_nvp,
+ name, perms_nvp) == 0);
+ } else {
+ verify(nvlist_add_boolean(who_nvp, name) == 0);
+ }
+}
+
+static void
+helper(zfs_deleg_who_type_t who_type, uint64_t whoid, char *whostr,
+ zfs_deleg_inherit_t inherit, nvlist_t *who_nvp, nvlist_t *perms_nvp,
+ nvlist_t *sets_nvp)
+{
+ boolean_t do_perms, do_sets;
+ char name[ZFS_MAX_DELEG_NAME];
+
+ do_perms = (nvlist_next_nvpair(perms_nvp, NULL) != NULL);
+ do_sets = (nvlist_next_nvpair(sets_nvp, NULL) != NULL);
+
+ if (!do_perms && !do_sets)
+ do_perms = do_sets = B_TRUE;
+
+ if (do_perms) {
+ zfs_deleg_whokey(name, who_type, inherit,
+ (who_type == ZFS_DELEG_NAMED_SET) ?
+ whostr : (void *)&whoid);
+ zfs_perms_add_to_nvlist(who_nvp, name, perms_nvp);
+ }
+ if (do_sets) {
+ zfs_deleg_whokey(name, toupper(who_type), inherit,
+ (who_type == ZFS_DELEG_NAMED_SET) ?
+ whostr : (void *)&whoid);
+ zfs_perms_add_to_nvlist(who_nvp, name, sets_nvp);
+ }
+}
+
+static void
+zfs_perms_add_who_nvlist(nvlist_t *who_nvp, uint64_t whoid, void *whostr,
+ nvlist_t *perms_nvp, nvlist_t *sets_nvp,
+ zfs_deleg_who_type_t who_type, zfs_deleg_inherit_t inherit)
+{
+ if (who_type == ZFS_DELEG_NAMED_SET || who_type == ZFS_DELEG_CREATE) {
+ helper(who_type, whoid, whostr, 0,
+ who_nvp, perms_nvp, sets_nvp);
+ } else {
+ if (inherit & ZFS_DELEG_PERM_LOCAL) {
+ helper(who_type, whoid, whostr, ZFS_DELEG_LOCAL,
+ who_nvp, perms_nvp, sets_nvp);
+ }
+ if (inherit & ZFS_DELEG_PERM_DESCENDENT) {
+ helper(who_type, whoid, whostr, ZFS_DELEG_DESCENDENT,
+ who_nvp, perms_nvp, sets_nvp);
+ }
+ }
+}
+
+/*
+ * Construct nvlist to pass down to kernel for setting/removing permissions.
+ *
+ * The nvlist is constructed as a series of nvpairs with an optional embedded
+ * nvlist of permissions to remove or set. The topmost nvpairs are the actual
+ * base attribute named stored in the dsl.
+ * Arguments:
+ *
+ * whostr: is a comma separated list of users, groups, or a single set name.
+ * whostr may be null for everyone or create perms.
+ * who_type: is the type of entry in whostr. Typically this will be
+ * ZFS_DELEG_WHO_UNKNOWN.
+ * perms: comman separated list of permissions. May be null if user
+ * is requested to remove permissions by who.
+ * inherit: Specifies the inheritance of the permissions. Will be either
+ * ZFS_DELEG_PERM_LOCAL and/or ZFS_DELEG_PERM_DESCENDENT.
+ * nvp The constructed nvlist to pass to zfs_perm_set().
+ * The output nvp will look something like this.
+ * ul$1234 -> {create ; destroy }
+ * Ul$1234 -> { @myset }
+ * s-$@myset - { snapshot; checksum; compression }
+ */
+int
+zfs_build_perms(zfs_handle_t *zhp, char *whostr, char *perms,
+ zfs_deleg_who_type_t who_type, zfs_deleg_inherit_t inherit, nvlist_t **nvp)
+{
+ nvlist_t *who_nvp;
+ nvlist_t *perms_nvp = NULL;
+ nvlist_t *sets_nvp = NULL;
+ char errbuf[1024];
+ char *who_tok;
+ int error;
+
+ *nvp = NULL;
+
+ if (perms) {
+ /* Make sure permission string doesn't have an '=' sign in it */
+ if (strchr(perms, '=') != NULL) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN,
+ "permissions can't contain equal sign : '%s'"),
+ perms);
+ return (zfs_error(zhp->zfs_hdl, EZFS_BADPERM, errbuf));
+ }
+
+ if ((error = nvlist_alloc(&perms_nvp,
+ NV_UNIQUE_NAME, 0)) != 0) {
+ return (1);
+ }
+ if ((error = nvlist_alloc(&sets_nvp,
+ NV_UNIQUE_NAME, 0)) != 0) {
+ nvlist_free(perms_nvp);
+ return (1);
+ }
+ }
+
+ if ((error = nvlist_alloc(&who_nvp, NV_UNIQUE_NAME, 0)) != 0) {
+ if (perms_nvp)
+ nvlist_free(perms_nvp);
+ if (sets_nvp)
+ nvlist_free(sets_nvp);
+ return (1);
+ }
+
+ if (who_type == ZFS_DELEG_NAMED_SET) {
+ namecheck_err_t why;
+ char what;
+
+ if ((error = permset_namecheck(whostr, &why, &what)) != 0) {
+ switch (why) {
+ case NAME_ERR_NO_AT:
+ zfs_error_aux(zhp->zfs_hdl,
+ dgettext(TEXT_DOMAIN,
+ "set definition must begin with an '@' "
+ "character"));
+ }
+ return (zfs_error(zhp->zfs_hdl,
+ EZFS_BADPERMSET, whostr));
+ }
+ }
+
+ /*
+ * Build up nvlist(s) of permissions. Two nvlists are maintained.
+ * The first nvlist perms_nvp will have normal permissions and the
+ * other sets_nvp will have only permssion set names in it.
+ */
+
+
+ while (perms && *perms != '\0') {
+ char *value;
+ char *perm_name;
+ nvlist_t *update_nvp;
+ int perm_num;
+ char canonical_name[64];
+ char *canonicalp = canonical_name;
+
+
+ update_nvp = perms_nvp;
+
+ perm_num = getsubopt(&perms, zfs_deleg_perm_tab, &value);
+ if (perm_num == -1) {
+ zfs_prop_t prop;
+
+ prop = zfs_name_to_prop(value);
+ if (prop != ZFS_PROP_INVAL) {
+ (void) snprintf(canonical_name,
+ sizeof (canonical_name), "%s",
+ zfs_prop_to_name(prop));
+ perm_num = getsubopt(&canonicalp,
+ zfs_deleg_perm_tab, &value);
+ }
+ }
+ if (perm_num != -1) {
+ perm_name = zfs_deleg_perm_tab[perm_num];
+ } else { /* check and see if permission is a named set */
+ if (value[0] == '@') {
+
+ /*
+ * make sure permssion set isn't defined
+ * in terms of itself. ie.
+ * @set1 = create,destroy,@set1
+ */
+ if (who_type == ZFS_DELEG_NAMED_SET &&
+ strcmp(value, whostr) == 0) {
+ nvlist_free(who_nvp);
+ nvlist_free(perms_nvp);
+ if (sets_nvp)
+ nvlist_free(sets_nvp);
+ (void) snprintf(errbuf,
+ sizeof (errbuf),
+ dgettext(TEXT_DOMAIN,
+ "Invalid permission %s"), value);
+ return (zfs_error(zhp->zfs_hdl,
+ EZFS_PERMSET_CIRCULAR, errbuf));
+ }
+ update_nvp = sets_nvp;
+ perm_name = value;
+ } else {
+ nvlist_free(who_nvp);
+ nvlist_free(perms_nvp);
+ if (sets_nvp)
+ nvlist_free(sets_nvp);
+ return (zfs_error(zhp->zfs_hdl,
+ EZFS_BADPERM, value));
+ }
+ }
+ verify(nvlist_add_boolean(update_nvp, perm_name) == 0);
+ }
+
+ if (whostr && who_type != ZFS_DELEG_CREATE) {
+ who_tok = strtok(whostr, ",");
+ if (who_tok == NULL) {
+ nvlist_free(who_nvp);
+ nvlist_free(perms_nvp);
+ if (sets_nvp)
+ nvlist_free(sets_nvp);
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "Who string is NULL"),
+ whostr);
+ return (zfs_error(zhp->zfs_hdl, EZFS_BADWHO, errbuf));
+ }
+ }
+
+ /*
+ * Now create the nvlist(s)
+ */
+ do {
+ uint64_t who_id;
+
+ error = zfs_get_perm_who(who_tok, &who_type,
+ &who_id);
+ if (error) {
+ nvlist_free(who_nvp);
+ nvlist_free(perms_nvp);
+ if (sets_nvp)
+ nvlist_free(sets_nvp);
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN,
+ "Unable to determine uid/gid for "
+ "%s "), who_tok);
+ return (zfs_error(zhp->zfs_hdl, EZFS_BADWHO, errbuf));
+ }
+
+ /*
+ * add entries for both local and descendent when required
+ */
+
+ zfs_perms_add_who_nvlist(who_nvp, who_id, who_tok,
+ perms_nvp, sets_nvp, who_type, inherit);
+
+ } while (who_tok = strtok(NULL, ","));
+ *nvp = who_nvp;
+ return (0);
+}
+
+static int
+zfs_perm_set_common(zfs_handle_t *zhp, nvlist_t *nvp, boolean_t unset)
+{
+ zfs_cmd_t zc = { 0 };
+ int error;
+ size_t sz;
+ char errbuf[1024];
+
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "Cannot update 'allows' for '%s'"),
+ zhp->zfs_name);
+
+ if (zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, nvp, &sz))
+ return (-1);
+
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+ zc.zc_perm_action = unset;
+
+ error = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SET_FSACL, &zc);
+ if (error && errno == ENOTSUP) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ gettext("Pool must be upgraded to use 'allow/unallow'"));
+ zcmd_free_nvlists(&zc);
+ return (zfs_error(zhp->zfs_hdl, EZFS_BADVERSION, errbuf));
+ } else if (error) {
+ return (zfs_standard_error(zhp->zfs_hdl, errno, errbuf));
+ }
+ zcmd_free_nvlists(&zc);
+
+ return (error);
+}
+
+int
+zfs_perm_set(zfs_handle_t *zhp, nvlist_t *nvp)
+{
+ return (zfs_perm_set_common(zhp, nvp, B_FALSE));
+}
+
+int
+zfs_perm_remove(zfs_handle_t *zhp, nvlist_t *perms)
+{
+ return (zfs_perm_set_common(zhp, perms, B_TRUE));
+}
+
+static int
+perm_compare(const void *arg1, const void *arg2)
+{
+ const zfs_perm_node_t *node1 = arg1;
+ const zfs_perm_node_t *node2 = arg2;
+ int ret;
+
+ ret = strcmp(node1->z_pname, node2->z_pname);
+
+ if (ret > 0)
+ return (1);
+ if (ret < 0)
+ return (-1);
+ else
+ return (0);
+}
+
+static void
+zfs_destroy_perm_tree(avl_tree_t *tree)
+{
+ zfs_perm_node_t *permnode;
+ void *cookie;
+
+ cookie = NULL;
+ while ((permnode = avl_destroy_nodes(tree, &cookie)) != NULL) {
+ avl_remove(tree, permnode);
+ free(permnode);
+ }
+}
+
+static void
+zfs_destroy_tree(avl_tree_t *tree)
+{
+ zfs_allow_node_t *allownode;
+ void *cookie;
+
+ cookie = NULL;
+ while ((allownode = avl_destroy_nodes(tree, &cookie)) != NULL) {
+ zfs_destroy_perm_tree(&allownode->z_localdescend);
+ zfs_destroy_perm_tree(&allownode->z_local);
+ zfs_destroy_perm_tree(&allownode->z_descend);
+ avl_remove(tree, allownode);
+ free(allownode);
+ }
+}
+
+void
+zfs_free_allows(zfs_allow_t *allow)
+{
+ zfs_allow_t *allownext;
+ zfs_allow_t *freeallow;
+
+ allownext = allow;
+ while (allownext) {
+ zfs_destroy_tree(&allownext->z_sets);
+ zfs_destroy_tree(&allownext->z_crperms);
+ zfs_destroy_tree(&allownext->z_user);
+ zfs_destroy_tree(&allownext->z_group);
+ zfs_destroy_tree(&allownext->z_everyone);
+ freeallow = allownext;
+ allownext = allownext->z_next;
+ free(freeallow);
+ }
+}
+
+static zfs_allow_t *
+zfs_alloc_perm_tree(zfs_handle_t *zhp, zfs_allow_t *prev, char *setpoint)
+{
+ zfs_allow_t *ptree;
+
+ if ((ptree = zfs_alloc(zhp->zfs_hdl,
+ sizeof (zfs_allow_t))) == NULL) {
+ return (NULL);
+ }
+
+ (void) strlcpy(ptree->z_setpoint, setpoint, sizeof (ptree->z_setpoint));
+ avl_create(&ptree->z_sets,
+ perm_compare, sizeof (zfs_allow_node_t),
+ offsetof(zfs_allow_node_t, z_node));
+ avl_create(&ptree->z_crperms,
+ perm_compare, sizeof (zfs_allow_node_t),
+ offsetof(zfs_allow_node_t, z_node));
+ avl_create(&ptree->z_user,
+ perm_compare, sizeof (zfs_allow_node_t),
+ offsetof(zfs_allow_node_t, z_node));
+ avl_create(&ptree->z_group,
+ perm_compare, sizeof (zfs_allow_node_t),
+ offsetof(zfs_allow_node_t, z_node));
+ avl_create(&ptree->z_everyone,
+ perm_compare, sizeof (zfs_allow_node_t),
+ offsetof(zfs_allow_node_t, z_node));
+
+ if (prev)
+ prev->z_next = ptree;
+ ptree->z_next = NULL;
+ return (ptree);
+}
+
+/*
+ * Add permissions to the appropriate AVL permission tree.
+ * The appropriate tree may not be the requested tree.
+ * For example if ld indicates a local permission, but
+ * same permission also exists as a descendent permission
+ * then the permission will be removed from the descendent
+ * tree and add the the local+descendent tree.
+ */
+static int
+zfs_coalesce_perm(zfs_handle_t *zhp, zfs_allow_node_t *allownode,
+ char *perm, char ld)
+{
+ zfs_perm_node_t pnode, *permnode, *permnode2;
+ zfs_perm_node_t *newnode;
+ avl_index_t where, where2;
+ avl_tree_t *tree, *altree;
+
+ (void) strlcpy(pnode.z_pname, perm, sizeof (pnode.z_pname));
+
+ if (ld == ZFS_DELEG_NA) {
+ tree = &allownode->z_localdescend;
+ altree = &allownode->z_descend;
+ } else if (ld == ZFS_DELEG_LOCAL) {
+ tree = &allownode->z_local;
+ altree = &allownode->z_descend;
+ } else {
+ tree = &allownode->z_descend;
+ altree = &allownode->z_local;
+ }
+ permnode = avl_find(tree, &pnode, &where);
+ permnode2 = avl_find(altree, &pnode, &where2);
+
+ if (permnode2) {
+ avl_remove(altree, permnode2);
+ free(permnode2);
+ if (permnode == NULL) {
+ tree = &allownode->z_localdescend;
+ }
+ }
+
+ /*
+ * Now insert new permission in either requested location
+ * local/descendent or into ld when perm will exist in both.
+ */
+ if (permnode == NULL) {
+ if ((newnode = zfs_alloc(zhp->zfs_hdl,
+ sizeof (zfs_perm_node_t))) == NULL) {
+ return (-1);
+ }
+ *newnode = pnode;
+ avl_add(tree, newnode);
+ }
+ return (0);
+}
+/*
+ * Uggh, this is going to be a bit complicated.
+ * we have an nvlist coming out of the kernel that
+ * will indicate where the permission is set and then
+ * it will contain allow of the various "who's", and what
+ * their permissions are. To further complicate this
+ * we will then have to coalesce the local,descendent
+ * and local+descendent permissions where appropriate.
+ * The kernel only knows about a permission as being local
+ * or descendent, but not both.
+ *
+ * In order to make this easier for zfs_main to deal with
+ * a series of AVL trees will be used to maintain
+ * all of this, primarily for sorting purposes as well
+ * as the ability to quickly locate a specific entry.
+ *
+ * What we end up with are tree's for sets, create perms,
+ * user, groups and everyone. With each of those trees
+ * we have subtrees for local, descendent and local+descendent
+ * permissions.
+ */
+int
+zfs_perm_get(zfs_handle_t *zhp, zfs_allow_t **zfs_perms)
+{
+ zfs_cmd_t zc = { 0 };
+ int error;
+ nvlist_t *nvlist;
+ nvlist_t *permnv, *sourcenv;
+ nvpair_t *who_pair, *source_pair;
+ nvpair_t *perm_pair;
+ char errbuf[1024];
+ zfs_allow_t *zallowp, *newallowp;
+ char ld;
+ char *nvpname;
+ uid_t uid;
+ gid_t gid;
+ avl_tree_t *tree;
+ avl_index_t where;
+
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+
+ if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
+ return (-1);
+
+ while (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_GET_FSACL, &zc) != 0) {
+ if (errno == ENOMEM) {
+ if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, &zc) != 0) {
+ zcmd_free_nvlists(&zc);
+ return (-1);
+ }
+ } else if (errno == ENOTSUP) {
+ zcmd_free_nvlists(&zc);
+ (void) snprintf(errbuf, sizeof (errbuf),
+ gettext("Pool must be upgraded to use 'allow'"));
+ return (zfs_error(zhp->zfs_hdl,
+ EZFS_BADVERSION, errbuf));
+ } else {
+ zcmd_free_nvlists(&zc);
+ return (-1);
+ }
+ }
+
+ if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &nvlist) != 0) {
+ zcmd_free_nvlists(&zc);
+ return (-1);
+ }
+
+ zcmd_free_nvlists(&zc);
+
+ source_pair = nvlist_next_nvpair(nvlist, NULL);
+
+ if (source_pair == NULL) {
+ *zfs_perms = NULL;
+ return (0);
+ }
+
+ *zfs_perms = zfs_alloc_perm_tree(zhp, NULL, nvpair_name(source_pair));
+ if (*zfs_perms == NULL) {
+ return (0);
+ }
+
+ zallowp = *zfs_perms;
+
+ for (;;) {
+ struct passwd *pwd;
+ struct group *grp;
+ zfs_allow_node_t *allownode;
+ zfs_allow_node_t findallownode;
+ zfs_allow_node_t *newallownode;
+
+ (void) strlcpy(zallowp->z_setpoint,
+ nvpair_name(source_pair),
+ sizeof (zallowp->z_setpoint));
+
+ if ((error = nvpair_value_nvlist(source_pair, &sourcenv)) != 0)
+ goto abort;
+
+ /*
+ * Make sure nvlist is composed correctly
+ */
+ if (zfs_deleg_verify_nvlist(sourcenv)) {
+ goto abort;
+ }
+
+ who_pair = nvlist_next_nvpair(sourcenv, NULL);
+ if (who_pair == NULL) {
+ goto abort;
+ }
+
+ do {
+ error = nvpair_value_nvlist(who_pair, &permnv);
+ if (error) {
+ goto abort;
+ }
+
+ /*
+ * First build up the key to use
+ * for looking up in the various
+ * who trees.
+ */
+ ld = nvpair_name(who_pair)[1];
+ nvpname = nvpair_name(who_pair);
+ switch (nvpair_name(who_pair)[0]) {
+ case ZFS_DELEG_USER:
+ case ZFS_DELEG_USER_SETS:
+ tree = &zallowp->z_user;
+ uid = atol(&nvpname[3]);
+ pwd = getpwuid(uid);
+ (void) snprintf(findallownode.z_key,
+ sizeof (findallownode.z_key), "user %s",
+ (pwd) ? pwd->pw_name :
+ &nvpair_name(who_pair)[3]);
+ break;
+ case ZFS_DELEG_GROUP:
+ case ZFS_DELEG_GROUP_SETS:
+ tree = &zallowp->z_group;
+ gid = atol(&nvpname[3]);
+ grp = getgrgid(gid);
+ (void) snprintf(findallownode.z_key,
+ sizeof (findallownode.z_key), "group %s",
+ (grp) ? grp->gr_name :
+ &nvpair_name(who_pair)[3]);
+ break;
+ case ZFS_DELEG_CREATE:
+ case ZFS_DELEG_CREATE_SETS:
+ tree = &zallowp->z_crperms;
+ (void) strlcpy(findallownode.z_key, "",
+ sizeof (findallownode.z_key));
+ break;
+ case ZFS_DELEG_EVERYONE:
+ case ZFS_DELEG_EVERYONE_SETS:
+ (void) snprintf(findallownode.z_key,
+ sizeof (findallownode.z_key), "everyone");
+ tree = &zallowp->z_everyone;
+ break;
+ case ZFS_DELEG_NAMED_SET:
+ case ZFS_DELEG_NAMED_SET_SETS:
+ (void) snprintf(findallownode.z_key,
+ sizeof (findallownode.z_key), "%s",
+ &nvpair_name(who_pair)[3]);
+ tree = &zallowp->z_sets;
+ break;
+ }
+
+ /*
+ * Place who in tree
+ */
+ allownode = avl_find(tree, &findallownode, &where);
+ if (allownode == NULL) {
+ if ((newallownode = zfs_alloc(zhp->zfs_hdl,
+ sizeof (zfs_allow_node_t))) == NULL) {
+ goto abort;
+ }
+ avl_create(&newallownode->z_localdescend,
+ perm_compare,
+ sizeof (zfs_perm_node_t),
+ offsetof(zfs_perm_node_t, z_node));
+ avl_create(&newallownode->z_local,
+ perm_compare,
+ sizeof (zfs_perm_node_t),
+ offsetof(zfs_perm_node_t, z_node));
+ avl_create(&newallownode->z_descend,
+ perm_compare,
+ sizeof (zfs_perm_node_t),
+ offsetof(zfs_perm_node_t, z_node));
+ (void) strlcpy(newallownode->z_key,
+ findallownode.z_key,
+ sizeof (findallownode.z_key));
+ avl_insert(tree, newallownode, where);
+ allownode = newallownode;
+ }
+
+ /*
+ * Now iterate over the permissions and
+ * place them in the appropriate local,
+ * descendent or local+descendent tree.
+ *
+ * The permissions are added to the tree
+ * via zfs_coalesce_perm().
+ */
+ perm_pair = nvlist_next_nvpair(permnv, NULL);
+ if (perm_pair == NULL)
+ goto abort;
+ do {
+ if (zfs_coalesce_perm(zhp, allownode,
+ nvpair_name(perm_pair), ld) != 0)
+ goto abort;
+ } while (perm_pair = nvlist_next_nvpair(permnv,
+ perm_pair));
+ } while (who_pair = nvlist_next_nvpair(sourcenv, who_pair));
+
+ source_pair = nvlist_next_nvpair(nvlist, source_pair);
+ if (source_pair == NULL)
+ break;
+
+ /*
+ * allocate another node from the link list of
+ * zfs_allow_t structures
+ */
+ newallowp = zfs_alloc_perm_tree(zhp, zallowp,
+ nvpair_name(source_pair));
+ if (newallowp == NULL) {
+ goto abort;
+ }
+ zallowp = newallowp;
+ }
+ nvlist_free(nvlist);
+ return (0);
+abort:
+ zfs_free_allows(*zfs_perms);
+ nvlist_free(nvlist);
+ return (-1);
+}
+
/*
* Given a property name and value, set the property for the given dataset.
*/
@@ -1174,7 +1928,7 @@ zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
if (zcmd_write_src_nvlist(hdl, &zc, nvl, NULL) != 0)
goto error;
- ret = ioctl(hdl->libzfs_fd, ZFS_IOC_SET_PROP, &zc);
+ ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
if (ret != 0) {
switch (errno) {
@@ -1283,8 +2037,7 @@ zfs_prop_inherit(zfs_handle_t *zhp, const char *propname)
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
- if (ioctl(zhp->zfs_hdl->libzfs_fd,
- ZFS_IOC_SET_PROP, &zc) != 0)
+ if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SET_PROP, &zc) != 0)
return (zfs_standard_error(hdl, errno, errbuf));
return (0);
@@ -1336,8 +2089,7 @@ zfs_prop_inherit(zfs_handle_t *zhp, const char *propname)
if ((ret = changelist_prefix(cl)) != 0)
goto error;
- if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd,
- ZFS_IOC_SET_PROP, &zc)) != 0) {
+ if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SET_PROP, &zc)) != 0) {
return (zfs_standard_error(hdl, errno, errbuf));
} else {
@@ -2220,7 +2972,7 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
nvlist_free(props);
/* create the dataset */
- ret = ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE, &zc);
+ ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc);
if (ret == 0 && type == ZFS_TYPE_VOLUME) {
ret = zvol_create_link(hdl, path);
@@ -2292,10 +3044,13 @@ zfs_destroy(zfs_handle_t *zhp)
if (ZFS_IS_VOLUME(zhp)) {
/*
- * Unconditionally unshare this zvol ignoring failure as it
- * indicates only that the volume wasn't shared initially.
+ * If user doesn't have permissions to unshare volume, then
+ * abort the request. This would only happen for a
+ * non-privileged user.
*/
- (void) zfs_unshare_iscsi(zhp);
+ if (zfs_unshare_iscsi(zhp) != 0) {
+ return (-1);
+ }
if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0)
return (-1);
@@ -2305,7 +3060,7 @@ zfs_destroy(zfs_handle_t *zhp)
zc.zc_objset_type = DMU_OST_ZFS;
}
- if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc) != 0) {
+ if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) {
return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,
dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
zhp->zfs_name));
@@ -2379,7 +3134,7 @@ zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname)
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
- ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY_SNAPS, &zc);
+ ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc);
if (ret != 0) {
char errbuf[1024];
@@ -2454,7 +3209,7 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
(void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name));
(void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value));
- ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_CREATE, &zc);
+ ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc);
zcmd_free_nvlists(&zc);
@@ -2605,7 +3360,7 @@ zfs_promote(zfs_handle_t *zhp)
(void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_clone_of,
sizeof (zc.zc_value));
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- ret = ioctl(hdl->libzfs_fd, ZFS_IOC_PROMOTE, &zc);
+ ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc);
if (ret != 0) {
int save_errno = errno;
@@ -2704,15 +3459,21 @@ zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive)
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
(void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value));
+ if (ZFS_IS_VOLUME(zhp))
+ zc.zc_objset_type = DMU_OST_ZVOL;
+ else
+ zc.zc_objset_type = DMU_OST_ZFS;
zc.zc_cookie = recursive;
- ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SNAPSHOT, &zc);
+ ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc);
/*
* if it was recursive, the one that actually failed will be in
* zc.zc_name.
*/
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
+ if (ret != 0)
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+ "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
+
if (ret == 0 && recursive) {
struct createdata cd;
@@ -2723,8 +3484,13 @@ zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive)
if (ret == 0 && zhp->zfs_type == ZFS_TYPE_VOLUME) {
ret = zvol_create_link(zhp->zfs_hdl, path);
if (ret != 0) {
- (void) ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY,
- &zc);
+ (void) zfs_standard_error(hdl, errno,
+ dgettext(TEXT_DOMAIN,
+ "Volume successfully snapshotted, but device links "
+ "were not created"));
+ free(parent);
+ zfs_close(zhp);
+ return (-1);
}
}
@@ -2815,6 +3581,7 @@ create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
for (cp = target + prefixlen + 1;
cp = strchr(cp, '/'); *cp = '/', cp++) {
const char *opname;
+ char *logstr;
*cp = '\0';
@@ -2826,10 +3593,15 @@ create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
}
opname = dgettext(TEXT_DOMAIN, "create");
+ logstr = hdl->libzfs_log_str;
+ hdl->libzfs_log_str = NULL;
if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,
- NULL) != 0)
+ NULL) != 0) {
+ hdl->libzfs_log_str = logstr;
goto ancestorerr;
+ }
+ hdl->libzfs_log_str = logstr;
opname = dgettext(TEXT_DOMAIN, "open");
h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
if (h == NULL)
@@ -2985,7 +3757,11 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, int isprefix,
return (-1);
}
} else {
- (void) zvol_remove_link(hdl, h->zfs_name);
+ if (zvol_remove_link(hdl, h->zfs_name) != 0) {
+ zfs_close(h);
+ return (-1);
+ }
+
}
}
zfs_close(h);
@@ -3032,7 +3808,7 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, int isprefix,
}
if (dryrun)
return (0);
- err = ioctl_err = ioctl(hdl->libzfs_fd, ZFS_IOC_RECVBACKUP, &zc);
+ err = ioctl_err = zfs_ioctl(hdl, ZFS_IOC_RECVBACKUP, &zc);
if (ioctl_err != 0) {
switch (errno) {
case ENODEV:
@@ -3149,6 +3925,7 @@ rollback_destroy(zfs_handle_t *zhp, void *data)
zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
cbp->cb_create) {
+ char *logstr;
cbp->cb_dependent = B_TRUE;
if (zfs_iter_dependents(zhp, B_FALSE, rollback_destroy,
@@ -3156,10 +3933,13 @@ rollback_destroy(zfs_handle_t *zhp, void *data)
cbp->cb_error = 1;
cbp->cb_dependent = B_FALSE;
+ logstr = zhp->zfs_hdl->libzfs_log_str;
+ zhp->zfs_hdl->libzfs_log_str = NULL;
if (zfs_destroy(zhp) != 0)
cbp->cb_error = 1;
else
changelist_remove(zhp, cbp->cb_clp);
+ zhp->zfs_hdl->libzfs_log_str = logstr;
}
} else {
if (zfs_destroy(zhp) != 0)
@@ -3202,8 +3982,7 @@ do_rollback(zfs_handle_t *zhp)
* condition where the user has taken a snapshot since we verified that
* this was the most recent.
*/
- if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_ROLLBACK,
- &zc)) != 0) {
+ if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) {
(void) zfs_standard_error_fmt(zhp->zfs_hdl, errno,
dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
zhp->zfs_name);
@@ -3465,7 +4244,7 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive)
zc.zc_cookie = recursive;
- if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_RENAME, &zc)) != 0) {
+ if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) {
/*
* if it was recursive, the one that actually failed will
* be in zc.zc_name
@@ -3540,6 +4319,8 @@ zvol_create_link_common(libzfs_handle_t *hdl, const char *dataset, int ifexists)
{
zfs_cmd_t zc = { 0 };
di_devlink_handle_t dhdl;
+ priv_set_t *priv_effective;
+ int privileged;
(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
@@ -3576,17 +4357,51 @@ zvol_create_link_common(libzfs_handle_t *hdl, const char *dataset, int ifexists)
}
/*
- * Call devfsadm and wait for the links to magically appear.
+ * If privileged call devfsadm and wait for the links to
+ * magically appear.
+ * Otherwise, print out an informational message.
*/
- if ((dhdl = di_devlink_init(ZFS_DRIVER, DI_MAKE_LINK)) == NULL) {
- zfs_error_aux(hdl, strerror(errno));
- (void) zfs_error_fmt(hdl, EZFS_DEVLINKS,
- dgettext(TEXT_DOMAIN, "cannot create device links "
- "for '%s'"), dataset);
- (void) ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc);
- return (-1);
+
+ priv_effective = priv_allocset();
+ (void) getppriv(PRIV_EFFECTIVE, priv_effective);
+ privileged = (priv_isfullset(priv_effective) == B_TRUE);
+ priv_freeset(priv_effective);
+
+ if (privileged) {
+ if ((dhdl = di_devlink_init(ZFS_DRIVER,
+ DI_MAKE_LINK)) == NULL) {
+ zfs_error_aux(hdl, strerror(errno));
+ (void) zfs_standard_error_fmt(hdl, EZFS_DEVLINKS,
+ dgettext(TEXT_DOMAIN, "cannot create device links "
+ "for '%s'"), dataset);
+ (void) ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc);
+ return (-1);
+ } else {
+ (void) di_devlink_fini(&dhdl);
+ }
} else {
- (void) di_devlink_fini(&dhdl);
+ char pathname[MAXPATHLEN];
+ struct stat64 statbuf;
+ int i;
+
+#define MAX_WAIT 10
+
+ /*
+ * This is the poor mans way of waiting for the link
+ * to show up. If after 10 seconds we still don't
+ * have it, then print out a message.
+ */
+ (void) snprintf(pathname, sizeof (pathname), "/dev/zvol/dsk/%s",
+ dataset);
+
+ for (i = 0; i != MAX_WAIT; i++) {
+ if (stat64(pathname, &statbuf) == 0)
+ break;
+ (void) sleep(1);
+ }
+ if (i == MAX_WAIT)
+ (void) printf(gettext("%s may not be immediately "
+ "available\n"), pathname);
}
return (0);
@@ -3917,3 +4732,68 @@ zfs_expand_proplist(zfs_handle_t *zhp, zfs_proplist_t **plp)
return (0);
}
+
+int
+zfs_iscsi_perm_check(libzfs_handle_t *hdl, char *dataset, ucred_t *cred)
+{
+ zfs_cmd_t zc = { 0 };
+ nvlist_t *nvp;
+ size_t sz;
+ gid_t gid;
+ uid_t uid;
+ const gid_t *groups;
+ int group_cnt;
+ int error;
+
+ if (nvlist_alloc(&nvp, NV_UNIQUE_NAME, 0) != 0)
+ return (no_memory(hdl));
+
+ uid = ucred_geteuid(cred);
+ gid = ucred_getegid(cred);
+ group_cnt = ucred_getgroups(cred, &groups);
+
+ if (uid == (uid_t)-1 || gid == (uid_t)-1 || group_cnt == (uid_t)-1)
+ return (1);
+
+ if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_UID, uid) != 0) {
+ nvlist_free(nvp);
+ return (1);
+ }
+
+ if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_GID, gid) != 0) {
+ nvlist_free(nvp);
+ return (1);
+ }
+
+ if (nvlist_add_uint32_array(nvp,
+ ZFS_DELEG_PERM_GROUPS, (uint32_t *)groups, group_cnt) != 0) {
+ nvlist_free(nvp);
+ return (1);
+ }
+ (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
+
+ if (zcmd_write_src_nvlist(hdl, &zc, nvp, &sz))
+ return (-1);
+
+ error = ioctl(hdl->libzfs_fd, ZFS_IOC_ISCSI_PERM_CHECK, &zc);
+ nvlist_free(nvp);
+ return (error);
+}
+
+int
+zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path,
+ void *export, void *sharetab, int sharemax, boolean_t share_on)
+{
+ zfs_cmd_t zc = { 0 };
+ int error;
+
+ (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
+ (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
+ zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab;
+ zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export;
+ zc.zc_share.z_sharetype = share_on;
+ zc.zc_share.z_sharemax = sharemax;
+
+ error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc);
+ return (error);
+}
diff --git a/usr/src/lib/libzfs/common/libzfs_impl.h b/usr/src/lib/libzfs/common/libzfs_impl.h
index 20042e8c90..76f4823f29 100644
--- a/usr/src/lib/libzfs/common/libzfs_impl.h
+++ b/usr/src/lib/libzfs/common/libzfs_impl.h
@@ -33,6 +33,7 @@
#include <sys/fs/zfs.h>
#include <sys/zfs_ioctl.h>
#include <sys/zfs_acl.h>
+#include <sys/spa.h>
#include <sys/nvpair.h>
#include <libuutil.h>
@@ -54,6 +55,8 @@ struct libzfs_handle {
int libzfs_desc_active;
char libzfs_action[1024];
char libzfs_desc[1024];
+ char *libzfs_log_str;
+ int libzfs_log_type;
int libzfs_printerr;
void *libzfs_sharehdl; /* libshare handle */
};
diff --git a/usr/src/lib/libzfs/common/libzfs_mount.c b/usr/src/lib/libzfs/common/libzfs_mount.c
index 39cc9403c8..eb301116b3 100644
--- a/usr/src/lib/libzfs/common/libzfs_mount.c
+++ b/usr/src/lib/libzfs/common/libzfs_mount.c
@@ -85,6 +85,7 @@
static int (*iscsitgt_zfs_share)(const char *);
static int (*iscsitgt_zfs_unshare)(const char *);
static int (*iscsitgt_zfs_is_shared)(const char *);
+static int (*iscsitgt_svc_online)();
#pragma init(zfs_iscsi_init)
static void
@@ -99,10 +100,13 @@ zfs_iscsi_init(void)
(iscsitgt_zfs_unshare = (int (*)(const char *))dlsym(libiscsitgt,
"iscsitgt_zfs_unshare")) == NULL ||
(iscsitgt_zfs_is_shared = (int (*)(const char *))dlsym(libiscsitgt,
- "iscsitgt_zfs_is_shared")) == NULL) {
+ "iscsitgt_zfs_is_shared")) == NULL ||
+ (iscsitgt_svc_online = (int (*)(const char *))dlsym(libiscsitgt,
+ "iscsitgt_svc_online")) == NULL) {
iscsitgt_zfs_share = NULL;
iscsitgt_zfs_unshare = NULL;
iscsitgt_zfs_is_shared = NULL;
+ iscsitgt_svc_online = NULL;
}
}
@@ -284,6 +288,9 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
if (errno == EBUSY) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"mountpoint or dataset is busy"));
+ } else if (errno == EPERM) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "Insufficient privileges"));
} else {
zfs_error_aux(hdl, strerror(errno));
}
@@ -793,8 +800,15 @@ remove_mountpoint(zfs_handle_t *zhp)
boolean_t
zfs_is_shared_iscsi(zfs_handle_t *zhp)
{
- return (iscsitgt_zfs_is_shared != NULL &&
- iscsitgt_zfs_is_shared(zhp->zfs_name) != 0);
+
+ /*
+ * If iscsi deamon isn't running then we aren't shared
+ */
+ if (iscsitgt_svc_online && iscsitgt_svc_online() == 1)
+ return (0);
+ else
+ return (iscsitgt_zfs_is_shared != NULL &&
+ iscsitgt_zfs_is_shared(zhp->zfs_name) != 0);
}
int
@@ -812,9 +826,20 @@ zfs_share_iscsi(zfs_handle_t *zhp)
strcmp(shareopts, "off") == 0)
return (0);
- if (iscsitgt_zfs_share == NULL || iscsitgt_zfs_share(dataset) != 0)
- return (zfs_error_fmt(hdl, EZFS_SHAREISCSIFAILED,
+ if (iscsitgt_zfs_share == NULL || iscsitgt_zfs_share(dataset) != 0) {
+ int error = EZFS_SHAREISCSIFAILED;
+
+ /*
+ * If service isn't availabele and EPERM was
+ * returned then use special error.
+ */
+ if (iscsitgt_svc_online && errno == EPERM &&
+ (iscsitgt_svc_online() != 0))
+ error = EZFS_ISCSISVCUNAVAIL;
+
+ return (zfs_error_fmt(hdl, error,
dgettext(TEXT_DOMAIN, "cannot share '%s'"), dataset));
+ }
return (0);
}
@@ -836,9 +861,13 @@ zfs_unshare_iscsi(zfs_handle_t *zhp)
* we should return success in that case.
*/
if (iscsitgt_zfs_unshare == NULL ||
- (iscsitgt_zfs_unshare(dataset) != 0 && errno != ENODEV))
+ (iscsitgt_zfs_unshare(dataset) != 0 && errno != ENODEV)) {
+ if (errno == EPERM)
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "Insufficient privileges to unshare iscsi"));
return (zfs_error_fmt(hdl, EZFS_UNSHAREISCSIFAILED,
dgettext(TEXT_DOMAIN, "cannot unshare '%s'"), dataset));
+ }
return (0);
}
@@ -991,7 +1020,8 @@ zvol_cb(const char *dataset, void *data)
(zhp = zfs_open(hdl, dataset, ZFS_TYPE_VOLUME)) == NULL)
return (0);
- (void) zfs_unshare_iscsi(zhp);
+ if (zfs_unshare_iscsi(zhp) != 0)
+ return (-1);
zfs_close(zhp);
diff --git a/usr/src/lib/libzfs/common/libzfs_pool.c b/usr/src/lib/libzfs/common/libzfs_pool.c
index cf08df2ebc..9c1d197f4d 100644
--- a/usr/src/lib/libzfs/common/libzfs_pool.c
+++ b/usr/src/lib/libzfs/common/libzfs_pool.c
@@ -409,7 +409,7 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
if (altroot != NULL)
(void) strlcpy(zc.zc_value, altroot, sizeof (zc.zc_value));
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_CREATE, &zc) != 0) {
+ if (zfs_ioctl(hdl, ZFS_IOC_POOL_CREATE, &zc) != 0) {
zcmd_free_nvlists(&zc);
switch (errno) {
@@ -451,7 +451,6 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
return (zpool_standard_error(hdl, errno, msg));
}
}
-
zcmd_free_nvlists(&zc);
/*
@@ -493,7 +492,7 @@ zpool_destroy(zpool_handle_t *zhp)
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_POOL_DESTROY, &zc) != 0) {
+ if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_DESTROY, &zc) != 0) {
(void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
"cannot destroy '%s'"), zhp->zpool_name);
@@ -547,7 +546,7 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
return (-1);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_ADD, &zc) != 0) {
+ if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_VDEV_ADD, &zc) != 0) {
switch (errno) {
case EBUSY:
/*
@@ -621,7 +620,7 @@ zpool_export(zpool_handle_t *zhp)
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_POOL_EXPORT, &zc) != 0)
+ if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_EXPORT, &zc) != 0)
return (zpool_standard_error_fmt(zhp->zpool_hdl, errno,
dgettext(TEXT_DOMAIN, "cannot export '%s'"),
zhp->zpool_name));
@@ -675,7 +674,7 @@ zpool_import(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
return (-1);
ret = 0;
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_IMPORT, &zc) != 0) {
+ if (zfs_ioctl(hdl, ZFS_IOC_POOL_IMPORT, &zc) != 0) {
char desc[1024];
if (newname == NULL)
(void) snprintf(desc, sizeof (desc),
@@ -705,6 +704,7 @@ zpool_import(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
ret = -1;
} else {
zpool_handle_t *zhp;
+
/*
* This should never fail, but play it safe anyway.
*/
@@ -714,8 +714,10 @@ zpool_import(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
ret = zpool_create_zvol_links(zhp);
zpool_close(zhp);
}
+
}
+
zcmd_free_nvlists(&zc);
return (ret);
}
@@ -733,7 +735,7 @@ zpool_scrub(zpool_handle_t *zhp, pool_scrub_type_t type)
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
zc.zc_cookie = type;
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_POOL_SCRUB, &zc) == 0)
+ if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_SCRUB, &zc) == 0)
return (0);
(void) snprintf(msg, sizeof (msg),
@@ -893,7 +895,7 @@ zpool_vdev_online(zpool_handle_t *zhp, const char *path, int flags,
zc.zc_obj = flags;
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_SET_STATE, &zc) != 0)
+ if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_VDEV_SET_STATE, &zc) != 0)
return (zpool_standard_error(hdl, errno, msg));
*newstate = zc.zc_cookie;
@@ -927,7 +929,7 @@ zpool_vdev_offline(zpool_handle_t *zhp, const char *path, boolean_t istmp)
zc.zc_cookie = VDEV_STATE_OFFLINE;
zc.zc_obj = istmp ? ZFS_OFFLINE_TEMPORARY : 0;
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
+ if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
return (0);
switch (errno) {
@@ -1105,7 +1107,7 @@ zpool_vdev_attach(zpool_handle_t *zhp,
if (zcmd_write_src_nvlist(hdl, &zc, nvroot, NULL) != 0)
return (-1);
- ret = ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_ATTACH, &zc);
+ ret = zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_VDEV_ATTACH, &zc);
zcmd_free_nvlists(&zc);
@@ -1206,7 +1208,7 @@ zpool_vdev_detach(zpool_handle_t *zhp, const char *path)
verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0);
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_VDEV_DETACH, &zc) == 0)
+ if (zfs_ioctl(hdl, ZFS_IOC_VDEV_DETACH, &zc) == 0)
return (0);
switch (errno) {
@@ -1261,7 +1263,7 @@ zpool_vdev_remove(zpool_handle_t *zhp, const char *path)
verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0);
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_VDEV_REMOVE, &zc) == 0)
+ if (zfs_ioctl(hdl, ZFS_IOC_VDEV_REMOVE, &zc) == 0)
return (0);
return (zpool_standard_error(hdl, errno, msg));
@@ -1300,7 +1302,7 @@ zpool_clear(zpool_handle_t *zhp, const char *path)
&zc.zc_guid) == 0);
}
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_CLEAR, &zc) == 0)
+ if (zfs_ioctl(hdl, ZFS_IOC_CLEAR, &zc) == 0)
return (0);
return (zpool_standard_error(hdl, errno, msg));
@@ -1780,11 +1782,10 @@ zpool_upgrade(zpool_handle_t *zhp)
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) strcpy(zc.zc_name, zhp->zpool_name);
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_UPGRADE, &zc) != 0)
+ if (zfs_ioctl(hdl, ZFS_IOC_POOL_UPGRADE, &zc) != 0)
return (zpool_standard_error_fmt(hdl, errno,
dgettext(TEXT_DOMAIN, "cannot upgrade '%s'"),
zhp->zpool_name));
-
return (0);
}
@@ -1797,39 +1798,32 @@ zpool_upgrade(zpool_handle_t *zhp)
* poolname. 'argc' and 'argv' are used to construct the command string.
*/
void
-zpool_log_history(libzfs_handle_t *hdl, int argc, char **argv, const char *path,
- boolean_t pool, boolean_t pool_create)
+zpool_stage_history(libzfs_handle_t *hdl, int argc, char **argv,
+ boolean_t zfs_cmd, boolean_t pool_create)
{
- char cmd_buf[HIS_MAX_RECORD_LEN];
- char *dspath;
- zfs_cmd_t zc = { 0 };
+ char *cmd_buf;
int i;
- /* construct the command string */
- (void) strcpy(cmd_buf, pool ? "zpool" : "zfs");
- for (i = 0; i < argc; i++) {
- if (strlen(cmd_buf) + 1 + strlen(argv[i]) > HIS_MAX_RECORD_LEN)
- break;
- (void) strcat(cmd_buf, " ");
- (void) strcat(cmd_buf, argv[i]);
+ if (hdl->libzfs_log_str != NULL) {
+ free(hdl->libzfs_log_str);
}
- /* figure out the poolname */
- dspath = strpbrk(path, "/@");
- if (dspath == NULL) {
- (void) strcpy(zc.zc_name, path);
- } else {
- (void) strncpy(zc.zc_name, path, dspath - path);
- zc.zc_name[dspath-path] = '\0';
- }
-
- zc.zc_history = (uint64_t)(uintptr_t)cmd_buf;
- zc.zc_history_len = strlen(cmd_buf);
+ if ((hdl->libzfs_log_str = zfs_alloc(hdl, HIS_MAX_RECORD_LEN)) == NULL)
+ return;
- /* overloading zc_history_offset */
- zc.zc_history_offset = pool_create;
+ hdl->libzfs_log_type =
+ (pool_create == B_TRUE) ? LOG_CMD_POOL_CREATE : LOG_CMD_NORMAL;
+ cmd_buf = hdl->libzfs_log_str;
- (void) ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_LOG_HISTORY, &zc);
+ /* construct the command string */
+ (void) strlcpy(cmd_buf, zfs_cmd ? "zfs" : "zpool",
+ HIS_MAX_RECORD_LEN);
+ for (i = 1; i < argc; i++) {
+ if (strlen(cmd_buf) + 1 + strlen(argv[i]) > HIS_MAX_RECORD_LEN)
+ break;
+ (void) strlcat(cmd_buf, " ", HIS_MAX_RECORD_LEN);
+ (void) strlcat(cmd_buf, argv[i], HIS_MAX_RECORD_LEN);
+ }
}
/*
@@ -2221,7 +2215,7 @@ zpool_set_prop(zpool_handle_t *zhp, const char *propname, const char *propval)
if (zcmd_write_src_nvlist(zhp->zpool_hdl, &zc, nvl, NULL) != 0)
return (-1);
- ret = ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_POOL_SET_PROPS, &zc);
+ ret = zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_SET_PROPS, &zc);
zcmd_free_nvlists(&zc);
if (ret)
@@ -2308,6 +2302,7 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *propbuf,
(void) strlcpy(propbuf, strvalue, proplen);
break;
+ case ZPOOL_PROP_DELEGATION:
case ZPOOL_PROP_AUTOREPLACE:
if (nvlist_lookup_nvlist(zhp->zpool_props,
zpool_prop_to_name(prop), &nvp) != 0) {
diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c
index 2ae3e2e6ce..029cfbdf45 100644
--- a/usr/src/lib/libzfs/common/libzfs_util.c
+++ b/usr/src/lib/libzfs/common/libzfs_util.c
@@ -133,6 +133,10 @@ libzfs_error_description(libzfs_handle_t *hdl)
return (dgettext(TEXT_DOMAIN, "unshare(1M) failed"));
case EZFS_SHARENFSFAILED:
return (dgettext(TEXT_DOMAIN, "share(1M) failed"));
+ case EZFS_ISCSISVCUNAVAIL:
+ return (dgettext(TEXT_DOMAIN,
+ "iscsitgt service need to be enabled by "
+ "a privileged user"));
case EZFS_DEVLINKS:
return (dgettext(TEXT_DOMAIN, "failed to create /dev links"));
case EZFS_PERM:
@@ -176,6 +180,21 @@ libzfs_error_description(libzfs_handle_t *hdl)
"disk capacity information could not be retrieved"));
case EZFS_LABELFAILED:
return (dgettext(TEXT_DOMAIN, "write of label failed"));
+ case EZFS_BADWHO:
+ return (dgettext(TEXT_DOMAIN, "invalid user/group"));
+ case EZFS_BADPERM:
+ return (dgettext(TEXT_DOMAIN, "invalid permission"));
+ case EZFS_BADPERMSET:
+ return (dgettext(TEXT_DOMAIN, "invalid permission set name"));
+ case EZFS_PERMSET_CIRCULAR:
+ return (dgettext(TEXT_DOMAIN,
+ "Cannot define a permission set in terms of itself"));
+ case EZFS_NODELEGATION:
+ return (dgettext(TEXT_DOMAIN, "delegated administration is "
+ "disabled on pool"));
+ case EZFS_PERMRDONLY:
+ return (dgettext(TEXT_DOMAIN, "snapshot permissions cannot be"
+ " modified"));
case EZFS_UNKNOWN:
return (dgettext(TEXT_DOMAIN, "unknown error"));
default:
@@ -256,6 +275,10 @@ zfs_common_error(libzfs_handle_t *hdl, int error, const char *fmt,
zfs_verror(hdl, EZFS_PERM, fmt, ap);
return (-1);
+ case ECANCELED:
+ zfs_verror(hdl, EZFS_NODELEGATION, fmt, ap);
+ return (-1);
+
case EIO:
zfs_verror(hdl, EZFS_IO, fmt, ap);
return (-1);
@@ -315,11 +338,14 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
"dataset is busy"));
zfs_verror(hdl, EZFS_BUSY, fmt, ap);
break;
-
+ case EROFS:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "snapshot permissions cannot be modified"));
+ zfs_verror(hdl, EZFS_PERMRDONLY, fmt, ap);
+ break;
case ENAMETOOLONG:
zfs_verror(hdl, EZFS_NAMETOOLONG, fmt, ap);
break;
-
default:
zfs_error_aux(hdl, strerror(errno));
zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
@@ -389,6 +415,11 @@ zpool_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
zfs_verror(hdl, EZFS_POOL_INVALARG, fmt, ap);
break;
+ case ENOSPC:
+ case EDQUOT:
+ zfs_verror(hdl, EZFS_NOSPC, fmt, ap);
+ return (-1);
+
default:
zfs_error_aux(hdl, strerror(error));
zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
@@ -537,6 +568,8 @@ libzfs_fini(libzfs_handle_t *hdl)
if (hdl->libzfs_sharetab)
(void) fclose(hdl->libzfs_sharetab);
zfs_uninit_libshare(hdl);
+ if (hdl->libzfs_log_str)
+ (void) free(hdl->libzfs_log_str);
namespace_clear(hdl);
free(hdl);
}
@@ -852,3 +885,21 @@ libzfs_print_one_property(const char *name, libzfs_get_cbdata_t *cbp,
(void) printf("\n");
}
+
+int
+zfs_ioctl(libzfs_handle_t *hdl, int request, zfs_cmd_t *zc)
+{
+ int error;
+
+ zc->zc_history = (uint64_t)(uintptr_t)hdl->libzfs_log_str;
+ zc->zc_history_offset = hdl->libzfs_log_type;
+ error = ioctl(hdl->libzfs_fd, request, zc);
+ if (hdl->libzfs_log_str) {
+ free(hdl->libzfs_log_str);
+ hdl->libzfs_log_str = NULL;
+ }
+ zc->zc_history = 0;
+ zc->zc_history_offset = 0;
+
+ return (error);
+}
diff --git a/usr/src/lib/libzfs/common/mapfile-vers b/usr/src/lib/libzfs/common/mapfile-vers
index af27ecb1c0..d870f339f8 100644
--- a/usr/src/lib/libzfs/common/mapfile-vers
+++ b/usr/src/lib/libzfs/common/mapfile-vers
@@ -34,20 +34,24 @@ SUNWprivate_1.1 {
libzfs_init;
libzfs_print_on_error;
libzfs_print_one_property;
+ zfs_build_perms;
zfs_clone;
zfs_close;
zfs_create;
zfs_create_ancestors;
zfs_dataset_exists;
+ zfs_deleg_share_nfs;
zfs_destroy;
zfs_destroy_snaps;
zfs_expand_proplist;
+ zfs_free_allows;
zfs_free_proplist;
zfs_get_handle;
zfs_get_name;
zfs_get_user_props;
zfs_get_proplist;
zfs_get_type;
+ zfs_iscsi_perm_check;
zfs_is_mounted;
zfs_is_shared;
zfs_is_shared_iscsi;
@@ -64,6 +68,9 @@ SUNWprivate_1.1 {
zfs_nicestrtonum;
zfs_open;
zfs_path_to_zhandle;
+ zfs_perm_get;
+ zfs_perm_remove;
+ zfs_perm_set;
zfs_promote;
zfs_prop_align_right;
zfs_prop_column_name;
@@ -131,8 +138,8 @@ SUNWprivate_1.1 {
zpool_in_use;
zpool_iter;
zpool_label_disk;
- zpool_log_history;
zpool_mount_datasets;
+ zpool_name_to_prop;
zpool_obj_to_path;
zpool_open;
zpool_open_canfail;
@@ -144,6 +151,7 @@ SUNWprivate_1.1 {
zpool_remove_zvol_links;
zpool_scrub;
zpool_set_prop;
+ zpool_stage_history;
zpool_unmount_datasets;
zpool_upgrade;
zpool_vdev_attach;
diff --git a/usr/src/lib/libzpool/Makefile.com b/usr/src/lib/libzpool/Makefile.com
index 6d1254186f..6a51a25230 100644
--- a/usr/src/lib/libzpool/Makefile.com
+++ b/usr/src/lib/libzpool/Makefile.com
@@ -54,6 +54,7 @@ LIBS += $(LINTLIB)
INCS += -I../common
INCS += -I../../../uts/common/fs/zfs
INCS += -I../../../common/zfs
+INCS += -I../../../common
$(LINTLIB) := SRCS= $(SRCDIR)/$(LINTSRC)
diff --git a/usr/src/lib/libzpool/common/kernel.c b/usr/src/lib/libzpool/common/kernel.c
index 6410efa5f6..c601d1811e 100644
--- a/usr/src/lib/libzpool/common/kernel.c
+++ b/usr/src/lib/libzpool/common/kernel.c
@@ -800,3 +800,45 @@ z_compress_level(void *dst, size_t *dstlen, const void *src, size_t srclen,
return (ret);
}
+
+uid_t
+crgetuid(cred_t *cr)
+{
+ return (0);
+}
+
+gid_t
+crgetgid(cred_t *cr)
+{
+ return (0);
+}
+
+int
+crgetngroups(cred_t *cr)
+{
+ return (0);
+}
+
+gid_t *
+crgetgroups(cred_t *cr)
+{
+ return (NULL);
+}
+
+int
+zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr)
+{
+ return (0);
+}
+
+int
+zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr)
+{
+ return (0);
+}
+
+int
+zfs_secpolicy_destroy_perms(const char *name, cred_t *cr)
+{
+ return (0);
+}
diff --git a/usr/src/lib/libzpool/common/sys/zfs_context.h b/usr/src/lib/libzpool/common/sys/zfs_context.h
index e30f1c704d..4f3bdc6f36 100644
--- a/usr/src/lib/libzpool/common/sys/zfs_context.h
+++ b/usr/src/lib/libzpool/common/sys/zfs_context.h
@@ -63,6 +63,7 @@ extern "C" {
#include <time.h>
#include <sys/note.h>
#include <sys/types.h>
+#include <sys/cred.h>
#include <sys/sysmacros.h>
#include <sys/bitmap.h>
#include <sys/resource.h>
@@ -250,6 +251,11 @@ extern int rw_tryupgrade(krwlock_t *rwlp);
extern void rw_exit(krwlock_t *rwlp);
#define rw_downgrade(rwlp) do { } while (0)
+extern uid_t crgetuid(cred_t *cr);
+extern gid_t crgetgid(cred_t *cr);
+extern int crgetngroups(cred_t *cr);
+extern gid_t *crgetgroups(cred_t *cr);
+
/*
* Condition variables
*/
@@ -448,6 +454,11 @@ extern int kobj_read_file(struct _buf *file, char *buf, unsigned size,
unsigned off);
extern void kobj_close_file(struct _buf *file);
extern int kobj_get_filesize(struct _buf *file, uint64_t *size);
+extern int zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr);
+extern int zfs_secpolicy_rename_perms(const char *from, const char *to,
+ cred_t *cr);
+extern int zfs_secpolicy_destroy_perms(const char *name, cred_t *cr);
+extern zoneid_t getzoneid(void);
#ifdef __cplusplus
}
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index e148047d34..7ccf67bb41 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -932,6 +932,7 @@ ZFS_COMMON_OBJS += \
dsl_pool.o \
dsl_synctask.o \
dmu_zfetch.o \
+ dsl_deleg.o \
dsl_prop.o \
fletcher.o \
gzip.o \
@@ -971,7 +972,8 @@ ZFS_COMMON_OBJS += \
ZFS_SHARED_OBJS += \
zfs_namecheck.o \
- zfs_prop.o
+ zfs_prop.o \
+ zfs_deleg.o
ZFS_OBJS += \
$(ZFS_COMMON_OBJS) \
diff --git a/usr/src/uts/common/fs/nfs/nfs_sys.c b/usr/src/uts/common/fs/nfs/nfs_sys.c
index 73b2f996d5..f42361def0 100644
--- a/usr/src/uts/common/fs/nfs/nfs_sys.c
+++ b/usr/src/uts/common/fs/nfs/nfs_sys.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
@@ -75,6 +75,19 @@ size_t nfs4_dss_buflen = 0;
/* This filled in by nfssrv:_init() */
int (*nfs_srv_dss_func)(char *, size_t) = NULL;
+int
+nfs_export(void *arg)
+{
+ STRUCT_DECL(exportfs_args, ea);
+
+ if (!INGLOBALZONE(curproc))
+ return (set_errno(EPERM));
+ STRUCT_INIT(ea, get_udatamodel());
+ if (copyin(arg, STRUCT_BUF(ea), STRUCT_SIZE(ea)))
+ return (set_errno(EFAULT));
+
+ return (exportfs(STRUCT_BUF(ea), get_udatamodel(), CRED()));
+}
int
nfssys(enum nfssys_op opcode, void *arg)
@@ -189,15 +202,7 @@ nfssys(enum nfssys_op opcode, void *arg)
}
case EXPORTFS: { /* export a file system */
- STRUCT_DECL(exportfs_args, ea);
-
- if (!INGLOBALZONE(curproc))
- return (set_errno(EPERM));
- STRUCT_INIT(ea, get_udatamodel());
- if (copyin(arg, STRUCT_BUF(ea), STRUCT_SIZE(ea)))
- return (set_errno(EFAULT));
-
- error = exportfs(STRUCT_BUF(ea), get_udatamodel(), CRED());
+ error = nfs_export(arg);
break;
}
diff --git a/usr/src/uts/common/fs/sharefs/sharetab.c b/usr/src/uts/common/fs/sharefs/sharetab.c
index 57b0b2dbe0..53d4ae4a2b 100644
--- a/usr/src/uts/common/fs/sharefs/sharetab.c
+++ b/usr/src/uts/common/fs/sharefs/sharetab.c
@@ -40,6 +40,7 @@
#include <sys/vtrace.h>
#include <sys/cmn_err.h>
#include <sys/atomic.h>
+#include <sys/policy.h>
#include <sharefs/sharefs.h>
@@ -173,13 +174,12 @@ sharefs_remove(share_t *sh, sharefs_lens_t *shl)
* Now walk down the hash table and find the entry to free!
*/
for (p = NULL, s = sht->s_buckets[iHash].ssh_sh;
- s != NULL;
- s = s->sh_next) {
+ s != NULL; s = s->sh_next) {
/*
* We need exact matches.
*/
if (strcmp(sh->sh_path, s->sh_path) == 0 &&
- strlen(s->sh_path) == iPath) {
+ strlen(s->sh_path) == iPath) {
if (p) {
p->sh_next = s->sh_next;
} else {
@@ -269,14 +269,11 @@ sharefs_add(share_t *sh, sharefs_lens_t *shl)
if (shl) {
sh->sh_size = shl->shl_path + shl->shl_res +
- shl->shl_fstype + shl->shl_opts +
- shl->shl_descr;
+ shl->shl_fstype + shl->shl_opts + shl->shl_descr;
} else {
sh->sh_size = strlen(sh->sh_path) +
- strlen(sh->sh_res) +
- strlen(sh->sh_fstype) +
- strlen(sh->sh_opts) +
- strlen(sh->sh_descr);
+ strlen(sh->sh_res) + strlen(sh->sh_fstype) +
+ strlen(sh->sh_opts) + strlen(sh->sh_descr);
}
/*
@@ -289,8 +286,7 @@ sharefs_add(share_t *sh, sharefs_lens_t *shl)
* Now walk down the hash table and add the new entry!
*/
for (p = NULL, s = sht->s_buckets[iHash].ssh_sh;
- s != NULL;
- s = s->sh_next) {
+ s != NULL; s = s->sh_next) {
/*
* We need exact matches.
*
@@ -299,7 +295,7 @@ sharefs_add(share_t *sh, sharefs_lens_t *shl)
* being asked to replace an existing entry.
*/
if (strcmp(sh->sh_path, s->sh_path) == 0 &&
- strlen(s->sh_path) == iPath) {
+ strlen(s->sh_path) == iPath) {
if (p) {
p->sh_next = sh;
} else {
@@ -365,7 +361,7 @@ sharefs_sharetab_init(void)
}
int
-sharefs(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen)
+sharefs_impl(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen)
{
int error = 0;
size_t len;
@@ -407,8 +403,7 @@ sharefs(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen)
* Initialize the data pointers.
*/
STRUCT_INIT(u_sh, model);
- if (copyin(sh_in, STRUCT_BUF(u_sh),
- STRUCT_SIZE(u_sh))) {
+ if (copyin(sh_in, STRUCT_BUF(u_sh), STRUCT_SIZE(u_sh))) {
return (set_errno(EFAULT));
}
@@ -468,3 +463,12 @@ cleanup:
return ((error != 0) ? set_errno(error) : 0);
}
+
+int
+sharefs(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen)
+{
+ if (secpolicy_sys_config(CRED(), B_FALSE) != 0)
+ return (set_errno(EPERM));
+
+ return (sharefs_impl(opcode, sh_in, iMaxLen));
+}
diff --git a/usr/src/uts/common/fs/zfs/dmu.c b/usr/src/uts/common/fs/zfs/dmu.c
index 8a7e3192e9..4fbba8ab92 100644
--- a/usr/src/uts/common/fs/zfs/dmu.c
+++ b/usr/src/uts/common/fs/zfs/dmu.c
@@ -78,7 +78,8 @@ const dmu_object_type_info_t dmu_ot[DMU_OT_NUMTYPES] = {
{ zap_byteswap, TRUE, "persistent error log" },
{ byteswap_uint8_array, TRUE, "SPA history" },
{ byteswap_uint64_array, TRUE, "SPA history offsets" },
- { zap_byteswap, TRUE, "Pool properties" },
+ { zap_byteswap, TRUE, "Pool properties" },
+ { zap_byteswap, TRUE, "DSL permissions" }
};
int
diff --git a/usr/src/uts/common/fs/zfs/dmu_objset.c b/usr/src/uts/common/fs/zfs/dmu_objset.c
index 378fe8c15b..25530d59fb 100644
--- a/usr/src/uts/common/fs/zfs/dmu_objset.c
+++ b/usr/src/uts/common/fs/zfs/dmu_objset.c
@@ -25,6 +25,7 @@
#pragma ident "%Z%%M% %I% %E% SMI"
+#include <sys/cred.h>
#include <sys/zfs_context.h>
#include <sys/dmu_objset.h>
#include <sys/dsl_dir.h>
@@ -32,6 +33,7 @@
#include <sys/dsl_prop.h>
#include <sys/dsl_pool.h>
#include <sys/dsl_synctask.h>
+#include <sys/dsl_deleg.h>
#include <sys/dnode.h>
#include <sys/dbuf.h>
#include <sys/zvol.h>
@@ -40,6 +42,7 @@
#include <sys/zap.h>
#include <sys/zil.h>
#include <sys/dmu_impl.h>
+#include <sys/zfs_ioctl.h>
spa_t *
@@ -443,14 +446,14 @@ dmu_objset_create_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
}
struct oscarg {
- void (*userfunc)(objset_t *os, void *arg, dmu_tx_t *tx);
+ void (*userfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx);
void *userarg;
dsl_dataset_t *clone_parent;
const char *lastname;
dmu_objset_type_t type;
};
-/* ARGSUSED */
+/*ARGSUSED*/
static int
dmu_objset_create_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
@@ -478,11 +481,12 @@ dmu_objset_create_check(void *arg1, void *arg2, dmu_tx_t *tx)
if (oa->clone_parent->ds_phys->ds_num_children == 0)
return (EINVAL);
}
+
return (0);
}
static void
-dmu_objset_create_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+dmu_objset_create_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
dsl_dir_t *dd = arg1;
struct oscarg *oa = arg2;
@@ -506,15 +510,24 @@ dmu_objset_create_sync(void *arg1, void *arg2, dmu_tx_t *tx)
ds, bp, oa->type, tx);
if (oa->userfunc)
- oa->userfunc(&osi->os, oa->userarg, tx);
+ oa->userfunc(&osi->os, oa->userarg, cr, tx);
}
+
+ /*
+ * Create create time permission if any?
+ */
+ dsl_deleg_set_create_perms(ds->ds_dir, tx, cr);
+
+ spa_history_internal_log(LOG_DS_CREATE, dd->dd_pool->dp_spa,
+ tx, cr, "dataset = %llu", dsobj);
+
dsl_dataset_close(ds, DS_MODE_STANDARD | DS_MODE_READONLY, FTAG);
}
int
dmu_objset_create(const char *name, dmu_objset_type_t type,
objset_t *clone_parent,
- void (*func)(objset_t *os, void *arg, dmu_tx_t *tx), void *arg)
+ void (*func)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx), void *arg)
{
dsl_dir_t *pdd;
const char *tail;
@@ -536,6 +549,7 @@ dmu_objset_create(const char *name, dmu_objset_type_t type,
oa.userarg = arg;
oa.lastname = tail;
oa.type = type;
+
if (clone_parent != NULL) {
/*
* You can't clone to a different type.
@@ -598,6 +612,7 @@ struct snaparg {
dsl_sync_task_group_t *dstg;
char *snapname;
char failed[MAXPATHLEN];
+ boolean_t checkperms;
};
static int
@@ -610,6 +625,15 @@ dmu_objset_snapshot_one(char *name, void *arg)
(void) strcpy(sn->failed, name);
+ /*
+ * Check permissions only when requested. This only applies when
+ * doing a recursive snapshot. The permission checks for the starting
+ * dataset have already been performed in zfs_secpolicy_snapshot()
+ */
+ if (sn->checkperms == B_TRUE &&
+ (err = zfs_secpolicy_snapshot_perms(name, CRED())))
+ return (err);
+
err = dmu_objset_open(name, DMU_OST_ANY, DS_MODE_STANDARD, &os);
if (err != 0)
return (err);
@@ -665,9 +689,11 @@ dmu_objset_snapshot(char *fsname, char *snapname, boolean_t recursive)
sn.snapname = snapname;
if (recursive) {
+ sn.checkperms = B_TRUE;
err = dmu_objset_find(fsname,
dmu_objset_snapshot_one, &sn, DS_FIND_CHILDREN);
} else {
+ sn.checkperms = B_FALSE;
err = dmu_objset_snapshot_one(fsname, &sn);
}
diff --git a/usr/src/uts/common/fs/zfs/dmu_send.c b/usr/src/uts/common/fs/zfs/dmu_send.c
index 0a0dde12ae..4107286fd4 100644
--- a/usr/src/uts/common/fs/zfs/dmu_send.c
+++ b/usr/src/uts/common/fs/zfs/dmu_send.c
@@ -335,11 +335,15 @@ replay_incremental_check(void *arg1, void *arg2, dmu_tx_t *tx)
/* ARGSUSED */
static void
-replay_incremental_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+replay_incremental_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
dmu_buf_will_dirty(ds->ds_dbuf, tx);
ds->ds_phys->ds_flags |= DS_FLAG_INCONSISTENT;
+
+ spa_history_internal_log(LOG_DS_REPLAY_INC_SYNC,
+ ds->ds_dir->dd_pool->dp_spa, tx, cr, "dataset = %lld",
+ ds->ds_phys->ds_dir_obj);
}
/* ARGSUSED */
@@ -367,7 +371,7 @@ replay_full_check(void *arg1, void *arg2, dmu_tx_t *tx)
}
static void
-replay_full_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+replay_full_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
dsl_dir_t *dd = arg1;
struct drr_begin *drrb = arg2;
@@ -379,7 +383,6 @@ replay_full_sync(void *arg1, void *arg2, dmu_tx_t *tx)
*cp = '\0';
dsobj = dsl_dataset_create_sync(dd, strrchr(drrb->drr_toname, '/') + 1,
NULL, tx);
- *cp = '@';
VERIFY(0 == dsl_dataset_open_obj(dd->dd_pool, dsobj, NULL,
DS_MODE_EXCLUSIVE, FTAG, &ds));
@@ -390,6 +393,12 @@ replay_full_sync(void *arg1, void *arg2, dmu_tx_t *tx)
dmu_buf_will_dirty(ds->ds_dbuf, tx);
ds->ds_phys->ds_flags |= DS_FLAG_INCONSISTENT;
+ spa_history_internal_log(LOG_DS_REPLAY_FULL_SYNC,
+ ds->ds_dir->dd_pool->dp_spa, tx, cr, "dataset = %lld",
+ ds->ds_phys->ds_dir_obj);
+
+ *cp = '@';
+
dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG);
}
@@ -411,7 +420,7 @@ replay_end_check(void *arg1, void *arg2, dmu_tx_t *tx)
}
static void
-replay_end_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+replay_end_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
objset_t *os = arg1;
struct drr_begin *drrb = arg2;
@@ -420,7 +429,7 @@ replay_end_sync(void *arg1, void *arg2, dmu_tx_t *tx)
snapname = strchr(drrb->drr_toname, '@') + 1;
- dsl_dataset_snapshot_sync(os, snapname, tx);
+ dsl_dataset_snapshot_sync(os, snapname, cr, tx);
/* set snapshot's creation time and guid */
hds = os->os->os_dsl_dataset;
@@ -434,6 +443,10 @@ replay_end_sync(void *arg1, void *arg2, dmu_tx_t *tx)
ds->ds_phys->ds_guid = drrb->drr_toguid;
ds->ds_phys->ds_flags &= ~DS_FLAG_INCONSISTENT;
+ /* log the end of the receive */
+ spa_history_internal_log(LOG_DS_RECEIVE, ds->ds_dir->dd_pool->dp_spa,
+ tx, cr, "dataset = %llu", ds->ds_phys->ds_dir_obj);
+
dsl_dataset_close(ds, DS_MODE_PRIMARY, FTAG);
dmu_buf_will_dirty(hds->ds_dbuf, tx);
diff --git a/usr/src/uts/common/fs/zfs/dsl_dataset.c b/usr/src/uts/common/fs/zfs/dsl_dataset.c
index 7d4689f335..7a36057402 100644
--- a/usr/src/uts/common/fs/zfs/dsl_dataset.c
+++ b/usr/src/uts/common/fs/zfs/dsl_dataset.c
@@ -38,6 +38,8 @@
#include <sys/unique.h>
#include <sys/zfs_context.h>
#include <sys/zfs_ioctl.h>
+#include <sys/spa.h>
+#include <sys/sunddi.h>
static dsl_checkfunc_t dsl_dataset_destroy_begin_check;
static dsl_syncfunc_t dsl_dataset_destroy_begin_sync;
@@ -966,7 +968,7 @@ dsl_dataset_rollback_check(void *arg1, void *arg2, dmu_tx_t *tx)
/* ARGSUSED */
static void
-dsl_dataset_rollback_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+dsl_dataset_rollback_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
@@ -1017,6 +1019,9 @@ dsl_dataset_rollback_sync(void *arg1, void *arg2, dmu_tx_t *tx)
dmu_buf_will_dirty(ds->ds_prev->ds_dbuf, tx);
ds->ds_prev->ds_phys->ds_unique_bytes = 0;
}
+
+ spa_history_internal_log(LOG_DS_ROLLBACK, ds->ds_dir->dd_pool->dp_spa,
+ tx, cr, "dataset = %llu", ds->ds_object);
}
/* ARGSUSED */
@@ -1039,13 +1044,17 @@ dsl_dataset_destroy_begin_check(void *arg1, void *arg2, dmu_tx_t *tx)
/* ARGSUSED */
static void
-dsl_dataset_destroy_begin_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+dsl_dataset_destroy_begin_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
+ dsl_pool_t *dp = ds->ds_dir->dd_pool;
/* Mark it as inconsistent on-disk, in case we crash */
dmu_buf_will_dirty(ds->ds_dbuf, tx);
ds->ds_phys->ds_flags |= DS_FLAG_INCONSISTENT;
+
+ spa_history_internal_log(LOG_DS_DESTROY_BEGIN, dp->dp_spa, tx,
+ cr, "dataset = %llu", ds->ds_object);
}
/* ARGSUSED */
@@ -1079,7 +1088,7 @@ dsl_dataset_destroy_check(void *arg1, void *arg2, dmu_tx_t *tx)
}
static void
-dsl_dataset_destroy_sync(void *arg1, void *tag, dmu_tx_t *tx)
+dsl_dataset_destroy_sync(void *arg1, void *tag, cred_t *cr, dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
uint64_t used = 0, compressed = 0, uncompressed = 0;
@@ -1320,6 +1329,9 @@ dsl_dataset_destroy_sync(void *arg1, void *tag, dmu_tx_t *tx)
dsl_dataset_close(ds_prev, DS_MODE_NONE, FTAG);
spa_clear_bootfs(dp->dp_spa, ds->ds_object, tx);
+ spa_history_internal_log(LOG_DS_DESTROY, dp->dp_spa, tx,
+ cr, "dataset = %llu", ds->ds_object);
+
dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, tag);
VERIFY(0 == dmu_object_free(mos, obj, tx));
@@ -1365,7 +1377,7 @@ dsl_dataset_snapshot_check(void *arg1, void *arg2, dmu_tx_t *tx)
}
void
-dsl_dataset_snapshot_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+dsl_dataset_snapshot_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
objset_t *os = arg1;
dsl_dataset_t *ds = os->os->os_dsl_dataset;
@@ -1438,6 +1450,9 @@ dsl_dataset_snapshot_sync(void *arg1, void *arg2, dmu_tx_t *tx)
VERIFY(0 == dsl_dataset_open_obj(dp,
ds->ds_phys->ds_prev_snap_obj, snapname,
DS_MODE_NONE, ds, &ds->ds_prev));
+
+ spa_history_internal_log(LOG_DS_SNAPSHOT, dp->dp_spa, tx, cr,
+ "dataset = %llu", ds->ds_object);
}
void
@@ -1554,10 +1569,11 @@ dsl_dataset_snapshot_rename_check(void *arg1, void *arg2, dmu_tx_t *tx)
}
static void
-dsl_dataset_snapshot_rename_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+dsl_dataset_snapshot_rename_sync(void *arg1, void *arg2,
+ cred_t *cr, dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
- char *newsnapname = arg2;
+ const char *newsnapname = arg2;
dsl_dir_t *dd = ds->ds_dir;
objset_t *mos = dd->dd_pool->dp_meta_objset;
dsl_dataset_t *hds;
@@ -1579,6 +1595,8 @@ dsl_dataset_snapshot_rename_sync(void *arg1, void *arg2, dmu_tx_t *tx)
ds->ds_snapname, 8, 1, &ds->ds_object, tx);
ASSERT3U(err, ==, 0);
+ spa_history_internal_log(LOG_DS_RENAME, dd->dd_pool->dp_spa, tx,
+ cr, "dataset = %llu", ds->ds_object);
dsl_dataset_close(hds, DS_MODE_NONE, FTAG);
}
@@ -1600,6 +1618,16 @@ dsl_snapshot_rename_one(char *name, void *arg)
cp = name + strlen(name);
*cp = '@';
(void) strcpy(cp + 1, ra->oldsnap);
+
+ /*
+ * For recursive snapshot renames the parent won't be changing
+ * so we just pass name for both the to/from argument.
+ */
+ if (err = zfs_secpolicy_rename_perms(name, name, CRED())) {
+ (void) strcpy(ra->failed, name);
+ return (err);
+ }
+
err = dsl_dataset_open(name, DS_MODE_READONLY | DS_MODE_STANDARD,
ra->dstg, &ds);
if (err == ENOENT) {
@@ -1678,7 +1706,8 @@ dsl_recursive_rename(char *oldname, const char *newname)
dsl_dataset_close(ds, DS_MODE_STANDARD, ra->dstg);
}
- (void) strcpy(oldname, ra->failed);
+ if (err)
+ (void) strcpy(oldname, ra->failed);
dsl_sync_task_group_destroy(ra->dstg);
kmem_free(ra, sizeof (struct renamearg));
@@ -1743,6 +1772,7 @@ struct promotearg {
uint64_t newnext_obj, snapnames_obj;
};
+/* ARGSUSED */
static int
dsl_dataset_promote_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
@@ -1885,7 +1915,7 @@ out:
}
static void
-dsl_dataset_promote_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+dsl_dataset_promote_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
dsl_dataset_t *hds = arg1;
struct promotearg *pa = arg2;
@@ -1968,6 +1998,10 @@ dsl_dataset_promote_sync(void *arg1, void *arg2, dmu_tx_t *tx)
dsl_dir_diduse_space(dd, pa->used, pa->comp, pa->uncomp, tx);
pivot_ds->ds_phys->ds_unique_bytes = pa->unique;
+ /* log history record */
+ spa_history_internal_log(LOG_DS_PROMOTE, dd->dd_pool->dp_spa, tx,
+ cr, "dataset = %llu", ds->ds_object);
+
dsl_dir_close(pdd, FTAG);
dsl_dataset_close(pivot_ds, DS_MODE_EXCLUSIVE, FTAG);
kmem_free(name, MAXPATHLEN);
diff --git a/usr/src/uts/common/fs/zfs/dsl_deleg.c b/usr/src/uts/common/fs/zfs/dsl_deleg.c
new file mode 100644
index 0000000000..e8fc0df28c
--- /dev/null
+++ b/usr/src/uts/common/fs/zfs/dsl_deleg.c
@@ -0,0 +1,744 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * DSL permissions are stored in a two level zap attribute
+ * mechanism. The first level identifies the "class" of
+ * entry. The class is identified by the first 2 letters of
+ * the attribute. The second letter "l" or "d" identifies whether
+ * it is a local or descendent permission. The first letter
+ * identifies the type of entry.
+ *
+ * ul$<id> identifies permssions granted locally for this userid.
+ * ud$<id> identifies permissions granted on descendent datasets for
+ * this userid.
+ * Ul$<id> identifies permission sets granted locally for this userid.
+ * Ud$<id> identifies permission sets granted on descendent datasets for
+ * this userid.
+ * gl$<id> identifies permissions granted locally for this groupid.
+ * gd$<id> identifies permissions granted on descendent datasets for
+ * this groupid.
+ * Gl$<id> identifies permission sets granted locally for this groupid.
+ * Gd$<id> identifies permission sets granted on descendent datasets for
+ * this groupid.
+ * el$ identifies permissions granted locally for everyone.
+ * ed$ identifies permissions granted on descendent datasets
+ * for everyone.
+ * El$ identifies permission sets granted locally for everyone.
+ * Ed$ identifies permission sets granted to descendent datasets for
+ * everyone.
+ * c-$ identifies permission to create at dataset creation time.
+ * C-$ identifies permission sets to grant locally at dataset creation
+ * time.
+ * s-$@<name> permissions defined in specified set @<name>
+ * S-$@<name> Sets defined in named set @<name>
+ *
+ * Each of the above entiies points to another zap attribute that contains one
+ * attribute for each allowed permission, such as create, destroy,...
+ * All of the "upper" case class types will specify permission set names
+ * rather than permissions.
+ *
+ * Basically it looks something like this:
+ * ul$12 -> ZAP OBJ -> permissions...
+ *
+ * The ZAP OBJ is referred to as the jump object.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/dmu.h>
+#include <sys/dmu_objset.h>
+#include <sys/dmu_tx.h>
+#include <sys/dsl_dataset.h>
+#include <sys/dsl_dir.h>
+#include <sys/dsl_prop.h>
+#include <sys/dsl_synctask.h>
+#include <sys/dsl_deleg.h>
+#include <sys/spa.h>
+#include <sys/spa_impl.h>
+#include <sys/zio_checksum.h> /* for the default checksum value */
+#include <sys/zap.h>
+#include <sys/fs/zfs.h>
+#include <sys/cred.h>
+#include <sys/sunddi.h>
+
+#include "zfs_deleg.h"
+
+/*
+ * Validate that user is allowed to delegate specified permissions.
+ *
+ * In order to delegate "create" you must have create"
+ * and "allow".
+ */
+int
+dsl_deleg_can_allow(char *ddname, nvlist_t *nvp, cred_t *cr)
+{
+ nvpair_t *whopair = NULL;
+ int error = 0;
+
+ if ((error = dsl_deleg_access(ddname,
+ ZFS_DELEG_PERM_ALLOW, cr)) != 0)
+ return (error);
+
+ while (whopair = nvlist_next_nvpair(nvp, whopair)) {
+ nvlist_t *perms;
+ nvpair_t *permpair = NULL;
+
+ VERIFY(nvpair_value_nvlist(whopair, &perms) == 0);
+
+ while (permpair = nvlist_next_nvpair(perms, permpair)) {
+ const char *perm = nvpair_name(permpair);
+
+ if (strcmp(perm, ZFS_DELEG_PERM_ALLOW) == 0)
+ return (EPERM);
+
+ if ((error = dsl_deleg_access(ddname,
+ perm, cr)) != 0)
+ return (error);
+ }
+ }
+ return (error);
+}
+
+/*
+ * Validate that user is allowed to unallow specified permissions. They
+ * must have the 'allow' permission, and even then can only unallow
+ * perms for their uid.
+ */
+int
+dsl_deleg_can_unallow(char *ddname, nvlist_t *nvp, cred_t *cr)
+{
+ nvpair_t *whopair = NULL;
+ int error;
+
+ if ((error = dsl_deleg_access(ddname,
+ ZFS_DELEG_PERM_ALLOW, cr)) != 0)
+ return (error);
+
+ while (whopair = nvlist_next_nvpair(nvp, whopair)) {
+ zfs_deleg_who_type_t type = nvpair_name(whopair)[0];
+ char idstr[32];
+
+ if (type != ZFS_DELEG_USER &&
+ type != ZFS_DELEG_USER_SETS)
+ return (EPERM);
+
+ (void) snprintf(idstr, sizeof (idstr), "%lld",
+ (longlong_t)crgetuid(cr));
+ if (strcmp(idstr, &nvpair_name(whopair)[3]) != 0)
+ return (EPERM);
+
+ continue;
+ }
+ return (0);
+}
+
+typedef struct {
+ nvlist_t *p_nvp;
+ boolean_t p_unset;
+} perm_args_t;
+
+static void
+dsl_deleg_set_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
+{
+ dsl_dir_t *dd = arg1;
+ perm_args_t *pa = arg2;
+ objset_t *mos = dd->dd_pool->dp_meta_objset;
+ nvpair_t *whopair = NULL;
+ uint64_t zapobj = dd->dd_phys->dd_deleg_zapobj;
+
+ if (zapobj == 0) {
+ if (pa->p_unset)
+ return;
+ dmu_buf_will_dirty(dd->dd_dbuf, tx);
+ zapobj = dd->dd_phys->dd_deleg_zapobj = zap_create(mos,
+ DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
+ }
+
+ while (whopair = nvlist_next_nvpair(pa->p_nvp, whopair)) {
+ const char *whokey = nvpair_name(whopair);
+ nvlist_t *perms;
+ nvpair_t *permpair = NULL;
+ uint64_t jumpobj;
+
+ if (nvpair_value_nvlist(whopair, &perms) != 0) {
+ if (zap_lookup(mos, zapobj, whokey, 8,
+ 1, &jumpobj) == 0) {
+ (void) zap_remove(mos, zapobj, whokey, tx);
+ VERIFY(0 == zap_destroy(mos, jumpobj, tx));
+ }
+ spa_history_internal_log(LOG_DS_PERM_WHO_REMOVE,
+ dd->dd_pool->dp_spa, tx, cr,
+ "%s dataset = %llu", whokey,
+ dd->dd_phys->dd_head_dataset_obj);
+ continue;
+ }
+
+ if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) != 0) {
+ /*
+ * If object doesn't exist and we are removing
+ * it, then just continue to next item in nvlist
+ */
+ if (pa->p_unset == 1)
+ continue;
+ jumpobj = zap_create(mos, DMU_OT_DSL_PERMS,
+ DMU_OT_NONE, 0, tx);
+ VERIFY(zap_update(mos, zapobj,
+ whokey, 8, 1, &jumpobj, tx) == 0);
+ }
+
+ while (permpair = nvlist_next_nvpair(perms, permpair)) {
+ const char *perm = nvpair_name(permpair);
+ uint64_t n = 0;
+
+ if (pa->p_unset) {
+ (void) zap_remove(mos, jumpobj, perm, tx);
+ if (zap_count(mos, jumpobj, &n) == 0 && !n) {
+ (void) zap_remove(mos, zapobj,
+ whokey, tx);
+ VERIFY(0 == zap_destroy(mos,
+ jumpobj, tx));
+ }
+ } else {
+ VERIFY(zap_update(mos, jumpobj,
+ perm, 8, 1, &n, tx) == 0);
+ }
+ spa_history_internal_log((pa->p_unset == B_FALSE) ?
+ LOG_DS_PERM_UPDATE : LOG_DS_PERM_REMOVE,
+ dd->dd_pool->dp_spa, tx, cr,
+ "%s %s dataset = %llu", whokey, perm,
+ dd->dd_phys->dd_head_dataset_obj);
+ }
+ }
+}
+
+int
+dsl_deleg_set(const char *ddname, nvlist_t *nvp, boolean_t unset)
+{
+ dsl_dir_t *dd;
+ int error;
+ perm_args_t pa;
+ nvpair_t *whopair = NULL;
+ int blocks_modified = 0;
+
+ error = dsl_dir_open(ddname, FTAG, &dd, NULL);
+ if (error)
+ return (error);
+
+ if (spa_version(dmu_objset_spa(dd->dd_pool->dp_meta_objset)) <
+ ZFS_VERSION_DELEGATED_PERMS) {
+ dsl_dir_close(dd, FTAG);
+ return (ENOTSUP);
+ }
+
+ while (whopair = nvlist_next_nvpair(nvp, whopair))
+ blocks_modified++;
+
+ pa.p_nvp = nvp;
+ pa.p_unset = unset;
+
+ error = dsl_sync_task_do(dd->dd_pool, NULL, dsl_deleg_set_sync,
+ dd, &pa, blocks_modified);
+ dsl_dir_close(dd, FTAG);
+
+ return (error);
+}
+
+/*
+ * Find all 'allow' permissions from a given point and then continue
+ * traversing up to the root.
+ *
+ * This function constructs an nvlist of nvlists.
+ * each setpoint is an nvlist composed of an nvlist of an nvlist
+ * of the individual * users/groups/everyone/create
+ * permissions.
+ *
+ * The nvlist will look like this.
+ *
+ * { source fsname -> { whokeys { permissions,...}, ...}}
+ *
+ * The fsname nvpairs will be arranged in a bottom up order. For example,
+ * if we have the following structure a/b/c then the nvpairs for the fsnames
+ * will be ordered a/b/c, a/b, a.
+ */
+int
+dsl_deleg_get(const char *ddname, nvlist_t **nvp)
+{
+ dsl_dir_t *dd, *startdd;
+ dsl_pool_t *dp;
+ int error;
+ objset_t *mos;
+
+ error = dsl_dir_open(ddname, FTAG, &startdd, NULL);
+ if (error)
+ return (error);
+
+ dp = startdd->dd_pool;
+ mos = dp->dp_meta_objset;
+
+ VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+
+ rw_enter(&dp->dp_config_rwlock, RW_READER);
+ for (dd = startdd; dd != NULL; dd = dd->dd_parent) {
+ zap_cursor_t basezc;
+ zap_attribute_t baseza;
+ nvlist_t *sp_nvp;
+ uint64_t n;
+ char source[MAXNAMELEN];
+
+ if (dd->dd_phys->dd_deleg_zapobj &&
+ (zap_count(mos, dd->dd_phys->dd_deleg_zapobj,
+ &n) == 0) && n) {
+ VERIFY(nvlist_alloc(&sp_nvp,
+ NV_UNIQUE_NAME, KM_SLEEP) == 0);
+ } else {
+ continue;
+ }
+
+ for (zap_cursor_init(&basezc, mos,
+ dd->dd_phys->dd_deleg_zapobj);
+ zap_cursor_retrieve(&basezc, &baseza) == 0;
+ zap_cursor_advance(&basezc)) {
+ zap_cursor_t zc;
+ zap_attribute_t za;
+ nvlist_t *perms_nvp;
+
+ ASSERT(baseza.za_integer_length == 8);
+ ASSERT(baseza.za_num_integers == 1);
+
+ VERIFY(nvlist_alloc(&perms_nvp,
+ NV_UNIQUE_NAME, KM_SLEEP) == 0);
+ for (zap_cursor_init(&zc, mos, baseza.za_first_integer);
+ zap_cursor_retrieve(&zc, &za) == 0;
+ zap_cursor_advance(&zc)) {
+ VERIFY(nvlist_add_boolean(perms_nvp,
+ za.za_name) == 0);
+ }
+ zap_cursor_fini(&zc);
+ VERIFY(nvlist_add_nvlist(sp_nvp, baseza.za_name,
+ perms_nvp) == 0);
+ nvlist_free(perms_nvp);
+ }
+
+ zap_cursor_fini(&basezc);
+
+ dsl_dir_name(dd, source);
+ VERIFY(nvlist_add_nvlist(*nvp, source, sp_nvp) == 0);
+ nvlist_free(sp_nvp);
+ }
+ rw_exit(&dp->dp_config_rwlock);
+
+ dsl_dir_close(startdd, FTAG);
+ return (0);
+}
+
+/*
+ * Routines for dsl_deleg_access() -- access checking.
+ */
+typedef struct perm_set {
+ avl_node_t p_node;
+ char p_setname[ZFS_MAX_DELEG_NAME];
+ boolean_t p_matched;
+} perm_set_t;
+
+static int
+perm_set_compare(const void *arg1, const void *arg2)
+{
+ const perm_set_t *node1 = arg1;
+ const perm_set_t *node2 = arg2;
+ int val;
+
+ val = strcmp(node1->p_setname, node2->p_setname);
+ if (val == 0)
+ return (0);
+ return (val > 0 ? 1 : -1);
+}
+
+/*
+ * Determine whether a specified permission exists.
+ *
+ * First the base attribute has to be retrieved. i.e. ul$12
+ * Once the base object has been retrieved the actual permission
+ * is lookup up in the zap object the base object points to.
+ *
+ * Return 0 if permission exists, ENOENT if there is no whokey, EPERM if
+ * there is no perm in that jumpobj.
+ */
+static int
+dsl_check_access(objset_t *mos, uint64_t zapobj,
+ char type, char checkflag, void *valp, const char *perm)
+{
+ int error;
+ uint64_t jumpobj, zero;
+ char whokey[ZFS_MAX_DELEG_NAME];
+
+ zfs_deleg_whokey(whokey, type, checkflag, valp);
+ error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj);
+ if (error == 0) {
+ error = zap_lookup(mos, jumpobj, perm, 8, 1, &zero);
+ if (error == ENOENT)
+ error = EPERM;
+ }
+ return (error);
+}
+
+/*
+ * check a specified user/group for a requested permission
+ */
+static int
+dsl_check_user_access(objset_t *os, uint64_t zapobj, const char *perm,
+ int checkflag, cred_t *cr)
+{
+ const gid_t *gids;
+ int ngids;
+ int i;
+ uint64_t id;
+
+ /* check for user */
+ id = crgetuid(cr);
+ if (dsl_check_access(os, zapobj,
+ ZFS_DELEG_USER, checkflag, &id, perm) == 0)
+ return (0);
+
+ /* check for users primary group */
+ id = crgetgid(cr);
+ if (dsl_check_access(os, zapobj,
+ ZFS_DELEG_GROUP, checkflag, &id, perm) == 0)
+ return (0);
+
+ /* check for everyone entry */
+ id = -1;
+ if (dsl_check_access(os, zapobj,
+ ZFS_DELEG_EVERYONE, checkflag, &id, perm) == 0)
+ return (0);
+
+ /* check each supplemental group user is a member of */
+ ngids = crgetngroups(cr);
+ gids = crgetgroups(cr);
+ for (i = 0; i != ngids; i++) {
+ id = gids[i];
+ if (dsl_check_access(os, zapobj,
+ ZFS_DELEG_GROUP, checkflag, &id, perm) == 0)
+ return (0);
+ }
+
+ return (EPERM);
+}
+
+/*
+ * Iterate over the sets specified in the specified zapobj
+ * and load them into the permsets avl tree.
+ */
+static int
+dsl_load_sets(objset_t *mos, uint64_t zapobj,
+ char type, char checkflag, void *valp, avl_tree_t *avl)
+{
+ zap_cursor_t zc;
+ zap_attribute_t za;
+ perm_set_t *permnode;
+ avl_index_t idx;
+ uint64_t jumpobj;
+ int error;
+ char whokey[ZFS_MAX_DELEG_NAME];
+
+ zfs_deleg_whokey(whokey, type, checkflag, valp);
+
+ error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj);
+ if (error != 0)
+ return (error);
+
+ for (zap_cursor_init(&zc, mos, jumpobj);
+ zap_cursor_retrieve(&zc, &za) == 0;
+ zap_cursor_advance(&zc)) {
+ permnode = kmem_alloc(sizeof (perm_set_t), KM_SLEEP);
+ (void) strlcpy(permnode->p_setname, za.za_name,
+ sizeof (permnode->p_setname));
+ permnode->p_matched = B_FALSE;
+
+ if (avl_find(avl, permnode, &idx) == NULL) {
+ avl_insert(avl, permnode, idx);
+ } else {
+ kmem_free(permnode, sizeof (perm_set_t));
+ }
+ }
+ zap_cursor_fini(&zc);
+ return (0);
+}
+
+/*
+ * Load all permissions user based on cred belongs to.
+ */
+static void
+dsl_load_user_sets(objset_t *mos, uint64_t zapobj, avl_tree_t *avl,
+ char checkflag, cred_t *cr)
+{
+ const gid_t *gids;
+ int ngids, i;
+ uint64_t id;
+
+ id = crgetuid(cr);
+ (void) dsl_load_sets(mos, zapobj,
+ ZFS_DELEG_USER_SETS, checkflag, &id, avl);
+
+ id = crgetgid(cr);
+ (void) dsl_load_sets(mos, zapobj,
+ ZFS_DELEG_GROUP_SETS, checkflag, &id, avl);
+
+ (void) dsl_load_sets(mos, zapobj,
+ ZFS_DELEG_EVERYONE_SETS, checkflag, NULL, avl);
+
+ ngids = crgetngroups(cr);
+ gids = crgetgroups(cr);
+ for (i = 0; i != ngids; i++) {
+ id = gids[i];
+ (void) dsl_load_sets(mos, zapobj,
+ ZFS_DELEG_GROUP_SETS, checkflag, &id, avl);
+ }
+}
+
+/*
+ * Check if user has requested permission.
+ */
+int
+dsl_deleg_access(const char *ddname, const char *perm, cred_t *cr)
+{
+ dsl_dir_t *dd, *startdd;
+ dsl_pool_t *dp;
+ void *cookie;
+ int error;
+ char checkflag = ZFS_DELEG_LOCAL;
+ const char *tail;
+ objset_t *mos;
+ avl_tree_t permsets;
+ perm_set_t *setnode;
+
+ /*
+ * Use tail so that zfs_ioctl() code doesn't have
+ * to always to to figure out parent name in order
+ * to do access check. for example renaming a snapshot
+ */
+ error = dsl_dir_open(ddname, FTAG, &startdd, &tail);
+ if (error)
+ return (error);
+
+ if (tail && tail[0] != '@') {
+ dsl_dir_close(startdd, FTAG);
+ return (ENOENT);
+ }
+ dp = startdd->dd_pool;
+ mos = dp->dp_meta_objset;
+
+ if (dsl_delegation_on(mos) == B_FALSE) {
+ dsl_dir_close(startdd, FTAG);
+ return (ECANCELED);
+ }
+
+ if (spa_version(dmu_objset_spa(dp->dp_meta_objset)) <
+ ZFS_VERSION_DELEGATED_PERMS) {
+ dsl_dir_close(startdd, FTAG);
+ return (EPERM);
+ }
+
+ avl_create(&permsets, perm_set_compare, sizeof (perm_set_t),
+ offsetof(perm_set_t, p_node));
+
+ rw_enter(&dp->dp_config_rwlock, RW_READER);
+ for (dd = startdd; dd != NULL; dd = dd->dd_parent,
+ checkflag = ZFS_DELEG_DESCENDENT) {
+ uint64_t zapobj;
+ boolean_t expanded;
+
+ /*
+ * If not in global zone then make sure
+ * the zoned property is set
+ */
+ if (!INGLOBALZONE(curproc)) {
+ uint64_t zoned;
+
+ if (dsl_prop_get_ds_locked(dd,
+ zfs_prop_to_name(ZFS_PROP_ZONED),
+ 8, 1, &zoned, NULL) != 0)
+ break;
+
+ /*
+ * if zoned property isn't set then break
+ * out and return EPERM.
+ */
+ if (!zoned)
+ break;
+ }
+ zapobj = dd->dd_phys->dd_deleg_zapobj;
+
+ if (zapobj == 0)
+ continue;
+
+ dsl_load_user_sets(mos, zapobj, &permsets, checkflag, cr);
+ setnode = avl_first(&permsets);
+again:
+ expanded = B_FALSE;
+ for (setnode = avl_first(&permsets); setnode;
+ setnode = AVL_NEXT(&permsets, setnode)) {
+
+ if (setnode->p_matched == B_TRUE)
+ continue;
+
+ /* See if this set directly grants this permission */
+ error = dsl_check_access(mos, zapobj,
+ ZFS_DELEG_NAMED_SET, 0, setnode->p_setname, perm);
+ if (error == 0)
+ goto success;
+ if (error == EPERM)
+ setnode->p_matched = B_TRUE;
+
+ /* See if this set includes other sets */
+ error = dsl_load_sets(mos, zapobj,
+ ZFS_DELEG_NAMED_SET_SETS, 0,
+ setnode->p_setname, &permsets);
+ if (error == 0)
+ setnode->p_matched = expanded = B_TRUE;
+ }
+ /*
+ * If we expanded any sets, that will define more sets,
+ * which we need to check.
+ */
+ if (expanded)
+ goto again;
+
+ error = dsl_check_user_access(mos, zapobj, perm, checkflag, cr);
+ if (error == 0)
+ goto success;
+ }
+ error = EPERM;
+success:
+ rw_exit(&dp->dp_config_rwlock);
+ dsl_dir_close(startdd, FTAG);
+
+ cookie = NULL;
+ while ((setnode = avl_destroy_nodes(&permsets, &cookie)) != NULL) {
+ /* These sets were used but never defined! */
+ kmem_free(setnode, sizeof (perm_set_t));
+ }
+
+ return (error);
+}
+
+/*
+ * Other routines.
+ */
+
+static void
+copy_create_perms(objset_t *mos, uint64_t pzapobj, dsl_dir_t *dd,
+ boolean_t dosets, uint64_t uid, dmu_tx_t *tx)
+{
+ int error;
+ uint64_t jumpobj, pjumpobj;
+ uint64_t zero = 0;
+ uint64_t zapobj = dd->dd_phys->dd_deleg_zapobj;
+ zap_cursor_t zc;
+ zap_attribute_t za;
+ char whokey[ZFS_MAX_DELEG_NAME];
+
+ zfs_deleg_whokey(whokey,
+ dosets ? ZFS_DELEG_CREATE_SETS : ZFS_DELEG_CREATE,
+ ZFS_DELEG_LOCAL, NULL);
+ error = zap_lookup(mos, pzapobj, whokey, 8, 1, &pjumpobj);
+ if (error != 0)
+ return;
+
+ zfs_deleg_whokey(whokey,
+ dosets ? ZFS_DELEG_USER_SETS : ZFS_DELEG_USER,
+ ZFS_DELEG_LOCAL, &uid);
+
+ if (zapobj == 0) {
+ dmu_buf_will_dirty(dd->dd_dbuf, tx);
+ zapobj = dd->dd_phys->dd_deleg_zapobj = zap_create(mos,
+ DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
+ }
+
+ if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) == ENOENT) {
+ jumpobj = zap_create(mos, DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
+ VERIFY(zap_add(mos, zapobj, whokey, 8, 1, &jumpobj, tx) == 0);
+ }
+
+ for (zap_cursor_init(&zc, mos, pjumpobj);
+ zap_cursor_retrieve(&zc, &za) == 0;
+ zap_cursor_advance(&zc)) {
+ ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1);
+
+ VERIFY(zap_update(mos, jumpobj, za.za_name,
+ 8, 1, &zero, tx) == 0);
+ }
+ zap_cursor_fini(&zc);
+}
+
+/*
+ * set all create time permission on new dataset.
+ */
+void
+dsl_deleg_set_create_perms(dsl_dir_t *sdd, dmu_tx_t *tx, cred_t *cr)
+{
+ dsl_dir_t *dd;
+ objset_t *mos = sdd->dd_pool->dp_meta_objset;
+
+ if (spa_version(dmu_objset_spa(sdd->dd_pool->dp_meta_objset)) <
+ ZFS_VERSION_DELEGATED_PERMS)
+ return;
+
+ for (dd = sdd->dd_parent; dd != NULL; dd = dd->dd_parent) {
+ uint64_t pobj = dd->dd_phys->dd_deleg_zapobj;
+
+ if (pobj == 0)
+ continue;
+
+ copy_create_perms(mos, pobj, sdd, B_FALSE, crgetuid(cr), tx);
+ copy_create_perms(mos, pobj, sdd, B_TRUE, crgetuid(cr), tx);
+ }
+}
+
+int
+dsl_deleg_destroy(objset_t *mos, uint64_t zapobj, dmu_tx_t *tx)
+{
+ zap_cursor_t zc;
+ zap_attribute_t za;
+
+ if (zapobj == 0)
+ return (0);
+
+ for (zap_cursor_init(&zc, mos, zapobj);
+ zap_cursor_retrieve(&zc, &za) == 0;
+ zap_cursor_advance(&zc)) {
+ ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1);
+ VERIFY(0 == zap_destroy(mos, za.za_first_integer, tx));
+ }
+ zap_cursor_fini(&zc);
+ VERIFY(0 == zap_destroy(mos, zapobj, tx));
+ return (0);
+}
+
+boolean_t
+dsl_delegation_on(objset_t *os)
+{
+ return (os->os->os_spa->spa_delegation);
+}
diff --git a/usr/src/uts/common/fs/zfs/dsl_dir.c b/usr/src/uts/common/fs/zfs/dsl_dir.c
index 5e563b6329..d15c7772c3 100644
--- a/usr/src/uts/common/fs/zfs/dsl_dir.c
+++ b/usr/src/uts/common/fs/zfs/dsl_dir.c
@@ -31,14 +31,17 @@
#include <sys/dsl_dir.h>
#include <sys/dsl_prop.h>
#include <sys/dsl_synctask.h>
+#include <sys/dsl_deleg.h>
#include <sys/spa.h>
#include <sys/zap.h>
#include <sys/zio.h>
#include <sys/arc.h>
+#include <sys/sunddi.h>
#include "zfs_namecheck.h"
static uint64_t dsl_dir_estimated_space(dsl_dir_t *dd);
-static void dsl_dir_set_reservation_sync(void *arg1, void *arg2, dmu_tx_t *tx);
+static void dsl_dir_set_reservation_sync(void *arg1, void *arg2,
+ cred_t *cr, dmu_tx_t *tx);
/* ARGSUSED */
@@ -461,7 +464,7 @@ dsl_dir_destroy_check(void *arg1, void *arg2, dmu_tx_t *tx)
}
void
-dsl_dir_destroy_sync(void *arg1, void *tag, dmu_tx_t *tx)
+dsl_dir_destroy_sync(void *arg1, void *tag, cred_t *cr, dmu_tx_t *tx)
{
dsl_dir_t *dd = arg1;
objset_t *mos = dd->dd_pool->dp_meta_objset;
@@ -472,12 +475,13 @@ dsl_dir_destroy_sync(void *arg1, void *tag, dmu_tx_t *tx)
/* Remove our reservation. */
val = 0;
- dsl_dir_set_reservation_sync(dd, &val, tx);
+ dsl_dir_set_reservation_sync(dd, &val, cr, tx);
ASSERT3U(dd->dd_used_bytes, ==, 0);
ASSERT3U(dd->dd_phys->dd_reserved, ==, 0);
VERIFY(0 == zap_destroy(mos, dd->dd_phys->dd_child_dir_zapobj, tx));
VERIFY(0 == zap_destroy(mos, dd->dd_phys->dd_props_zapobj, tx));
+ VERIFY(0 == dsl_deleg_destroy(mos, dd->dd_phys->dd_deleg_zapobj, tx));
VERIFY(0 == zap_remove(mos,
dd->dd_parent->dd_phys->dd_child_dir_zapobj, dd->dd_myname, tx));
@@ -904,7 +908,6 @@ dsl_dir_diduse_space(dsl_dir_t *dd,
}
}
-/* ARGSUSED */
static int
dsl_dir_set_quota_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
@@ -935,8 +938,9 @@ dsl_dir_set_quota_check(void *arg1, void *arg2, dmu_tx_t *tx)
return (err);
}
+/* ARGSUSED */
static void
-dsl_dir_set_quota_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+dsl_dir_set_quota_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
dsl_dir_t *dd = arg1;
uint64_t *quotap = arg2;
@@ -947,6 +951,10 @@ dsl_dir_set_quota_sync(void *arg1, void *arg2, dmu_tx_t *tx)
mutex_enter(&dd->dd_lock);
dd->dd_phys->dd_quota = new_quota;
mutex_exit(&dd->dd_lock);
+
+ spa_history_internal_log(LOG_DS_QUOTA, dd->dd_pool->dp_spa,
+ tx, cr, "%lld dataset = %llu ",
+ (longlong_t)new_quota, dd->dd_phys->dd_head_dataset_obj);
}
int
@@ -970,7 +978,6 @@ dsl_dir_set_quota(const char *ddname, uint64_t quota)
return (err);
}
-/* ARGSUSED */
static int
dsl_dir_set_reservation_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
@@ -1011,8 +1018,9 @@ dsl_dir_set_reservation_check(void *arg1, void *arg2, dmu_tx_t *tx)
return (0);
}
+/* ARGSUSED */
static void
-dsl_dir_set_reservation_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+dsl_dir_set_reservation_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
dsl_dir_t *dd = arg1;
uint64_t *reservationp = arg2;
@@ -1033,6 +1041,10 @@ dsl_dir_set_reservation_sync(void *arg1, void *arg2, dmu_tx_t *tx)
/* Roll up this additional usage into our ancestors */
dsl_dir_diduse_space(dd->dd_parent, delta, 0, 0, tx);
}
+
+ spa_history_internal_log(LOG_DS_RESERVATION, dd->dd_pool->dp_spa,
+ tx, cr, "%lld dataset = %llu",
+ (longlong_t)new_reservation, dd->dd_phys->dd_head_dataset_obj);
}
int
@@ -1084,7 +1096,7 @@ struct renamearg {
const char *mynewname;
};
-/* ARGSUSED */
+/*ARGSUSED*/
static int
dsl_dir_rename_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
@@ -1125,7 +1137,7 @@ dsl_dir_rename_check(void *arg1, void *arg2, dmu_tx_t *tx)
}
static void
-dsl_dir_rename_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+dsl_dir_rename_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
dsl_dir_t *dd = arg1;
struct renamearg *ra = arg2;
@@ -1164,6 +1176,9 @@ dsl_dir_rename_sync(void *arg1, void *arg2, dmu_tx_t *tx)
err = zap_add(mos, ra->newparent->dd_phys->dd_child_dir_zapobj,
dd->dd_myname, 8, 1, &dd->dd_object, tx);
ASSERT3U(err, ==, 0);
+
+ spa_history_internal_log(LOG_DS_RENAME, dd->dd_pool->dp_spa,
+ tx, cr, "dataset = %llu", dd->dd_phys->dd_head_dataset_obj);
}
int
@@ -1189,7 +1204,6 @@ dsl_dir_rename(dsl_dir_t *dd, const char *newname)
goto out;
}
-
err = dsl_sync_task_do(dd->dd_pool,
dsl_dir_rename_check, dsl_dir_rename_sync, dd, &ra, 3);
diff --git a/usr/src/uts/common/fs/zfs/dsl_prop.c b/usr/src/uts/common/fs/zfs/dsl_prop.c
index 2fff66d06b..103e80b8f6 100644
--- a/usr/src/uts/common/fs/zfs/dsl_prop.c
+++ b/usr/src/uts/common/fs/zfs/dsl_prop.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -162,6 +162,16 @@ dsl_prop_get_ds(dsl_dir_t *dd, const char *propname,
return (err);
}
+/*
+ * Get property when config lock is already held.
+ */
+int dsl_prop_get_ds_locked(dsl_dir_t *dd, const char *propname,
+ int intsz, int numints, void *buf, char *setpoint)
+{
+ ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
+ return (dsl_prop_get_impl(dd, propname, intsz, numints, buf, setpoint));
+}
+
int
dsl_prop_get(const char *ddname, const char *propname,
int intsz, int numints, void *buf, char *setpoint)
@@ -316,7 +326,7 @@ struct prop_set_arg {
static void
-dsl_prop_set_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+dsl_prop_set_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
dsl_dir_t *dd = arg1;
struct prop_set_arg *psa = arg2;
@@ -324,6 +334,8 @@ dsl_prop_set_sync(void *arg1, void *arg2, dmu_tx_t *tx)
uint64_t zapobj = dd->dd_phys->dd_props_zapobj;
uint64_t intval;
int isint;
+ char valbuf[32];
+ char *valstr;
isint = (dodefault(psa->name, 8, 1, &intval) == 0);
@@ -345,6 +357,17 @@ dsl_prop_set_sync(void *arg1, void *arg2, dmu_tx_t *tx)
dsl_prop_changed_notify(dd->dd_pool,
dd->dd_object, psa->name, intval, TRUE);
}
+ if (isint) {
+ (void) snprintf(valbuf, sizeof (valbuf),
+ "%lld", (longlong_t)intval);
+ valstr = valbuf;
+ } else {
+ valstr = (char *)psa->buf;
+ }
+ spa_history_internal_log((psa->numints == 0) ? LOG_DS_INHERIT :
+ LOG_DS_PROPSET, dd->dd_pool->dp_spa, tx, cr,
+ "%s=%s dataset = %llu", psa->name, valstr,
+ dd->dd_phys->dd_head_dataset_obj);
}
int
diff --git a/usr/src/uts/common/fs/zfs/dsl_synctask.c b/usr/src/uts/common/fs/zfs/dsl_synctask.c
index 17deb569c4..e1b5b49faa 100644
--- a/usr/src/uts/common/fs/zfs/dsl_synctask.c
+++ b/usr/src/uts/common/fs/zfs/dsl_synctask.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -30,6 +30,7 @@
#include <sys/dsl_pool.h>
#include <sys/dsl_dir.h>
#include <sys/dsl_synctask.h>
+#include <sys/cred.h>
#define DST_AVG_BLKSHIFT 14
@@ -67,6 +68,7 @@ dsl_sync_task_create(dsl_sync_task_group_t *dstg,
dst->dst_syncfunc = syncfunc;
dst->dst_arg1 = arg1;
dst->dst_arg2 = arg2;
+ dst->dst_cr = CRED();
list_insert_tail(&dstg->dstg_tasks, dst);
dstg->dstg_space += blocks_modified << DST_AVG_BLKSHIFT;
@@ -171,7 +173,8 @@ dsl_sync_task_group_sync(dsl_sync_task_group_t *dstg, dmu_tx_t *tx)
*/
for (dst = list_head(&dstg->dstg_tasks); dst;
dst = list_next(&dstg->dstg_tasks, dst)) {
- dst->dst_syncfunc(dst->dst_arg1, dst->dst_arg2, tx);
+ dst->dst_syncfunc(dst->dst_arg1, dst->dst_arg2,
+ dst->dst_cr, tx);
}
}
rw_exit(&dstg->dstg_pool->dp_config_rwlock);
diff --git a/usr/src/uts/common/fs/zfs/spa.c b/usr/src/uts/common/fs/zfs/spa.c
index c7b1bdc361..72015135e7 100644
--- a/usr/src/uts/common/fs/zfs/spa.c
+++ b/usr/src/uts/common/fs/zfs/spa.c
@@ -721,6 +721,8 @@ spa_load(spa_t *spa, nvlist_t *config, spa_load_state_t state, int mosconfig)
spa_config_exit(spa, FTAG);
}
+ spa->spa_delegation = zfs_prop_default_numeric(ZPOOL_PROP_DELEGATION);
+
error = zap_lookup(spa->spa_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
DMU_POOL_PROPS, sizeof (uint64_t), 1, &spa->spa_pool_props_object);
@@ -740,6 +742,10 @@ spa_load(spa_t *spa, nvlist_t *config, spa_load_state_t state, int mosconfig)
spa->spa_pool_props_object,
zpool_prop_to_name(ZPOOL_PROP_AUTOREPLACE),
sizeof (uint64_t), 1, &autoreplace);
+ (void) zap_lookup(spa->spa_meta_objset,
+ spa->spa_pool_props_object,
+ zpool_prop_to_name(ZPOOL_PROP_DELEGATION),
+ sizeof (uint64_t), 1, &spa->spa_delegation);
}
/*
@@ -1258,6 +1264,7 @@ spa_create(const char *pool, nvlist_t *nvroot, const char *altroot)
dmu_tx_commit(tx);
spa->spa_bootfs = zpool_prop_default_numeric(ZPOOL_PROP_BOOTFS);
+ spa->spa_delegation = zfs_prop_default_numeric(ZPOOL_PROP_DELEGATION);
spa->spa_sync_on = B_TRUE;
txg_sync_start(spa->spa_dsl_pool);
@@ -2957,7 +2964,7 @@ spa_sync_config_object(spa_t *spa, dmu_tx_t *tx)
}
static void
-spa_sync_props(void *arg1, void *arg2, dmu_tx_t *tx)
+spa_sync_props(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
spa_t *spa = arg1;
nvlist_t *nvp = arg2;
@@ -2982,13 +2989,23 @@ spa_sync_props(void *arg1, void *arg2, dmu_tx_t *tx)
nvpair = NULL;
while ((nvpair = nvlist_next_nvpair(nvp, nvpair))) {
switch (zpool_name_to_prop(nvpair_name(nvpair))) {
+ case ZPOOL_PROP_DELEGATION:
+ VERIFY(nvlist_lookup_uint64(nvp,
+ nvpair_name(nvpair), &intval) == 0);
+ VERIFY(zap_update(mos,
+ spa->spa_pool_props_object,
+ nvpair_name(nvpair), 8, 1,
+ &intval, tx) == 0);
+ spa->spa_delegation = intval;
+ break;
case ZPOOL_PROP_BOOTFS:
VERIFY(nvlist_lookup_uint64(nvp,
nvpair_name(nvpair), &spa->spa_bootfs) == 0);
+ intval = spa->spa_bootfs;
VERIFY(zap_update(mos,
spa->spa_pool_props_object,
zpool_prop_to_name(ZPOOL_PROP_BOOTFS), 8, 1,
- &spa->spa_bootfs, tx) == 0);
+ &intval, tx) == 0);
break;
case ZPOOL_PROP_AUTOREPLACE:
@@ -3000,6 +3017,10 @@ spa_sync_props(void *arg1, void *arg2, dmu_tx_t *tx)
&intval, tx) == 0);
break;
}
+ spa_history_internal_log(LOG_POOL_PROPSET,
+ spa, tx, cr, "%s %lld %s",
+ nvpair_name(nvpair), intval,
+ spa->spa_name);
}
}
diff --git a/usr/src/uts/common/fs/zfs/spa_history.c b/usr/src/uts/common/fs/zfs/spa_history.c
index 66428013a7..7d44242596 100644
--- a/usr/src/uts/common/fs/zfs/spa_history.c
+++ b/usr/src/uts/common/fs/zfs/spa_history.c
@@ -26,9 +26,18 @@
#pragma ident "%Z%%M% %I% %E% SMI"
+#include <sys/spa.h>
#include <sys/spa_impl.h>
#include <sys/zap.h>
#include <sys/dsl_synctask.h>
+#include <sys/dmu_tx.h>
+#include <sys/dmu_objset.h>
+#include <sys/utsname.h>
+#include <sys/cmn_err.h>
+#include <sys/sunddi.h>
+#ifdef _KERNEL
+#include <sys/zone.h>
+#endif
/*
* Routines to manage the on-disk history log.
@@ -59,16 +68,6 @@
* and permanently lost.
*/
-typedef enum history_log_type {
- LOG_CMD_CREATE,
- LOG_CMD_NO_CREATE
-} history_log_type_t;
-
-typedef struct history_arg {
- const char *ha_history_str;
- history_log_type_t ha_log_type;
-} history_arg_t;
-
/* convert a logical offset to physical */
static uint64_t
spa_history_log_to_phys(uint64_t log_off, spa_history_phys_t *shpp)
@@ -156,8 +155,9 @@ spa_history_write(spa_t *spa, void *buf, uint64_t len, spa_history_phys_t *shpp,
/* see if we need to reset logical BOF */
while (shpp->sh_phys_max_off - shpp->sh_pool_create_len -
(shpp->sh_eof - shpp->sh_bof) <= len) {
- if ((err = spa_history_advance_bof(spa, shpp)) != 0)
+ if ((err = spa_history_advance_bof(spa, shpp)) != 0) {
return (err);
+ }
}
phys_eof = spa_history_log_to_phys(shpp->sh_eof, shpp);
@@ -175,11 +175,21 @@ spa_history_write(spa_t *spa, void *buf, uint64_t len, spa_history_phys_t *shpp,
return (0);
}
+static char *
+spa_history_zone()
+{
+#ifdef _KERNEL
+ return (curproc->p_zone->zone_name);
+#else
+ return ("global");
+#endif
+}
+
/*
* Write out a history event.
*/
void
-spa_history_log_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+spa_history_log_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
spa_t *spa = arg1;
history_arg_t *hap = arg2;
@@ -222,16 +232,36 @@ spa_history_log_sync(void *arg1, void *arg2, dmu_tx_t *tx)
}
#endif
- /* construct a nvlist of the current time and cmd string */
VERIFY(nvlist_alloc(&nvrecord, NV_UNIQUE_NAME, KM_SLEEP) == 0);
VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_TIME,
gethrestime_sec()) == 0);
- VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_CMD, history_str) == 0);
+ VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_WHO,
+ (uint64_t)crgetuid(cr)) == 0);
+ if (hap->ha_zone[0] != '\0')
+ VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_ZONE,
+ hap->ha_zone) == 0);
+#ifdef _KERNEL
+ VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_HOST,
+ utsname.nodename) == 0);
+#endif
+ if (hap->ha_log_type == LOG_CMD_POOL_CREATE ||
+ hap->ha_log_type == LOG_CMD_NORMAL) {
+ VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_CMD,
+ history_str) == 0);
+ } else {
+ VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_INT_EVENT,
+ hap->ha_event) == 0);
+ VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_TXG,
+ tx->tx_txg) == 0);
+ VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_INT_STR,
+ history_str) == 0);
+ }
+
VERIFY(nvlist_pack(nvrecord, &record_packed, &reclen,
NV_ENCODE_XDR, KM_SLEEP) == 0);
mutex_enter(&spa->spa_history_lock);
- if (hap->ha_log_type == LOG_CMD_CREATE)
+ if (hap->ha_log_type == LOG_CMD_POOL_CREATE)
VERIFY(shpp->sh_eof == shpp->sh_pool_create_len);
/* write out the packed length as little endian */
@@ -240,7 +270,7 @@ spa_history_log_sync(void *arg1, void *arg2, dmu_tx_t *tx)
if (!ret)
ret = spa_history_write(spa, record_packed, reclen, shpp, tx);
- if (!ret && hap->ha_log_type == LOG_CMD_CREATE) {
+ if (!ret && hap->ha_log_type == LOG_CMD_POOL_CREATE) {
shpp->sh_pool_create_len += sizeof (le_len) + reclen;
shpp->sh_bof = shpp->sh_pool_create_len;
}
@@ -255,12 +285,13 @@ spa_history_log_sync(void *arg1, void *arg2, dmu_tx_t *tx)
* Write out a history event.
*/
int
-spa_history_log(spa_t *spa, const char *history_str, uint64_t pool_create)
+spa_history_log(spa_t *spa, const char *history_str, history_log_type_t what)
{
history_arg_t ha;
ha.ha_history_str = history_str;
- ha.ha_log_type = pool_create ? LOG_CMD_CREATE : LOG_CMD_NO_CREATE;
+ ha.ha_log_type = what;
+ (void) strlcpy(ha.ha_zone, spa_history_zone(), sizeof (ha.ha_zone));
return (dsl_sync_task_do(spa_get_dsl(spa), NULL, spa_history_log_sync,
spa, &ha, 0));
}
@@ -352,3 +383,25 @@ spa_history_get(spa_t *spa, uint64_t *offp, uint64_t *len, char *buf)
dmu_buf_rele(dbp, FTAG);
return (err);
}
+
+void
+spa_history_internal_log(history_internal_events_t event, spa_t *spa,
+ dmu_tx_t *tx, cred_t *cr, const char *fmt, ...)
+{
+ history_arg_t ha;
+ char *str;
+ va_list adx;
+
+ str = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP);
+
+ va_start(adx, fmt);
+ (void) vsnprintf(str, HIS_MAX_RECORD_LEN, fmt, adx);
+ va_end(adx);
+
+ ha.ha_log_type = LOG_INTERNAL;
+ ha.ha_history_str = str;
+ ha.ha_event = event;
+ ha.ha_zone[0] = '\0';
+ spa_history_log_sync(spa, &ha, cr, tx);
+ kmem_free(str, HIS_MAX_RECORD_LEN);
+}
diff --git a/usr/src/uts/common/fs/zfs/sys/dmu.h b/usr/src/uts/common/fs/zfs/sys/dmu.h
index a4eae81543..eba1e64dc4 100644
--- a/usr/src/uts/common/fs/zfs/sys/dmu.h
+++ b/usr/src/uts/common/fs/zfs/sys/dmu.h
@@ -39,6 +39,7 @@
#include <sys/inttypes.h>
#include <sys/types.h>
#include <sys/param.h>
+#include <sys/cred.h>
#ifdef __cplusplus
extern "C" {
@@ -108,7 +109,7 @@ typedef enum dmu_object_type {
DMU_OT_SPA_HISTORY, /* UINT8 */
DMU_OT_SPA_HISTORY_OFFSETS, /* spa_his_phys_t */
DMU_OT_POOL_PROPS, /* ZAP */
-
+ DMU_OT_DSL_PERMS, /* ZAP */
DMU_OT_NUMTYPES
} dmu_object_type_t;
@@ -159,7 +160,7 @@ void dmu_objset_close(objset_t *os);
int dmu_objset_evict_dbufs(objset_t *os, int try);
int dmu_objset_create(const char *name, dmu_objset_type_t type,
objset_t *clone_parent,
- void (*func)(objset_t *os, void *arg, dmu_tx_t *tx), void *arg);
+ void (*func)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx), void *arg);
int dmu_objset_destroy(const char *name);
int dmu_snapshots_destroy(char *fsname, char *snapname);
int dmu_objset_rollback(const char *name);
diff --git a/usr/src/uts/common/fs/zfs/sys/dmu_objset.h b/usr/src/uts/common/fs/zfs/sys/dmu_objset.h
index 8293a3b407..a401930af8 100644
--- a/usr/src/uts/common/fs/zfs/sys/dmu_objset.h
+++ b/usr/src/uts/common/fs/zfs/sys/dmu_objset.h
@@ -96,7 +96,7 @@ int dmu_objset_open(const char *name, dmu_objset_type_t type, int mode,
void dmu_objset_close(objset_t *os);
int dmu_objset_create(const char *name, dmu_objset_type_t type,
objset_t *clone_parent,
- void (*func)(objset_t *os, void *arg, dmu_tx_t *tx), void *arg);
+ void (*func)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx), void *arg);
int dmu_objset_destroy(const char *name);
int dmu_objset_rollback(const char *name);
int dmu_objset_snapshot(char *fsname, char *snapname, boolean_t recursive);
diff --git a/usr/src/uts/common/fs/zfs/sys/dsl_deleg.h b/usr/src/uts/common/fs/zfs/sys/dsl_deleg.h
new file mode 100644
index 0000000000..87a8526716
--- /dev/null
+++ b/usr/src/uts/common/fs/zfs/sys/dsl_deleg.h
@@ -0,0 +1,88 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_DSL_PERMS_H
+#define _SYS_DSL_PERMS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/dmu.h>
+#include <sys/dsl_pool.h>
+#include <sys/zfs_context.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZFS_DELEG_PERM_NONE ""
+#define ZFS_DELEG_PERM_CREATE "create"
+#define ZFS_DELEG_PERM_DESTROY "destroy"
+#define ZFS_DELEG_PERM_SNAPSHOT "snapshot"
+#define ZFS_DELEG_PERM_ROLLBACK "rollback"
+#define ZFS_DELEG_PERM_CLONE "clone"
+#define ZFS_DELEG_PERM_PROMOTE "promote"
+#define ZFS_DELEG_PERM_RENAME "rename"
+#define ZFS_DELEG_PERM_MOUNT "mount"
+#define ZFS_DELEG_PERM_SHARE "share"
+#define ZFS_DELEG_PERM_SEND "send"
+#define ZFS_DELEG_PERM_RECEIVE "receive"
+#define ZFS_DELEG_PERM_QUOTA "quota"
+#define ZFS_DELEG_PERM_RESERVATION "reservation"
+#define ZFS_DELEG_PERM_VOLSIZE "volsize"
+#define ZFS_DELEG_PERM_RECORDSIZE "recordsize"
+#define ZFS_DELEG_PERM_MOUNTPOINT "mountpoint"
+#define ZFS_DELEG_PERM_SHARENFS "sharenfs"
+#define ZFS_DELEG_PERM_CHECKSUM "checksum"
+#define ZFS_DELEG_PERM_COMPRESSION "compression"
+#define ZFS_DELEG_PERM_ATIME "atime"
+#define ZFS_DELEG_PERM_DEVICES "devices"
+#define ZFS_DELEG_PERM_EXEC "exec"
+#define ZFS_DELEG_PERM_SETUID "setuid"
+#define ZFS_DELEG_PERM_READONLY "readonly"
+#define ZFS_DELEG_PERM_ZONED "zoned"
+#define ZFS_DELEG_PERM_SNAPDIR "snapdir"
+#define ZFS_DELEG_PERM_ACLMODE "aclmode"
+#define ZFS_DELEG_PERM_ACLINHERIT "aclinherit"
+#define ZFS_DELEG_PERM_ALLOW "allow"
+#define ZFS_DELEG_PERM_CANMOUNT "canmount"
+#define ZFS_DELEG_PERM_USERPROP "userprop"
+#define ZFS_DELEG_PERM_SHAREISCSI "shareiscsi"
+#define ZFS_DELEG_PERM_XATTR "xattr"
+#define ZFS_DELEG_PERM_COPIES "copies"
+
+int dsl_deleg_get(const char *ddname, nvlist_t **nvp);
+int dsl_deleg_set(const char *ddname, nvlist_t *nvp, boolean_t unset);
+int dsl_deleg_access(const char *ddname, const char *perm, cred_t *cr);
+void dsl_deleg_set_create_perms(dsl_dir_t *dd, dmu_tx_t *tx, cred_t *cr);
+int dsl_deleg_can_allow(char *ddname, nvlist_t *nvp, cred_t *cr);
+int dsl_deleg_can_unallow(char *ddname, nvlist_t *nvp, cred_t *cr);
+int dsl_deleg_destroy(objset_t *os, uint64_t zapobj, dmu_tx_t *tx);
+boolean_t dsl_delegation_on(objset_t *os);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_DSL_PERMS_H */
diff --git a/usr/src/uts/common/fs/zfs/sys/dsl_dir.h b/usr/src/uts/common/fs/zfs/sys/dsl_dir.h
index e0595d3c36..bcab488f3b 100644
--- a/usr/src/uts/common/fs/zfs/sys/dsl_dir.h
+++ b/usr/src/uts/common/fs/zfs/sys/dsl_dir.h
@@ -58,7 +58,8 @@ typedef struct dsl_dir_phys {
/* Administrative reservation setting */
uint64_t dd_reserved;
uint64_t dd_props_zapobj;
- uint64_t dd_pad[21]; /* pad out to 256 bytes for good measure */
+ uint64_t dd_deleg_zapobj; /* dataset delegation permissions */
+ uint64_t dd_pad[20]; /* pad out to 256 bytes for good measure */
} dsl_dir_phys_t;
struct dsl_dir {
diff --git a/usr/src/uts/common/fs/zfs/sys/dsl_prop.h b/usr/src/uts/common/fs/zfs/sys/dsl_prop.h
index d2debff8b8..0bfe409fbe 100644
--- a/usr/src/uts/common/fs/zfs/sys/dsl_prop.h
+++ b/usr/src/uts/common/fs/zfs/sys/dsl_prop.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -60,6 +60,8 @@ int dsl_prop_get(const char *ddname, const char *propname,
int dsl_prop_get_integer(const char *ddname, const char *propname,
uint64_t *valuep, char *setpoint);
int dsl_prop_get_all(objset_t *os, nvlist_t **nvp);
+int dsl_prop_get_ds_locked(dsl_dir_t *dd, const char *propname,
+ int intsz, int numints, void *buf, char *setpoint);
int dsl_prop_set(const char *ddname, const char *propname,
int intsz, int numints, const void *buf);
diff --git a/usr/src/uts/common/fs/zfs/sys/dsl_synctask.h b/usr/src/uts/common/fs/zfs/sys/dsl_synctask.h
index e695b182f7..9125c30b42 100644
--- a/usr/src/uts/common/fs/zfs/sys/dsl_synctask.h
+++ b/usr/src/uts/common/fs/zfs/sys/dsl_synctask.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -38,7 +38,7 @@ extern "C" {
struct dsl_pool;
typedef int (dsl_checkfunc_t)(void *, void *, dmu_tx_t *);
-typedef void (dsl_syncfunc_t)(void *, void *, dmu_tx_t *);
+typedef void (dsl_syncfunc_t)(void *, void *, cred_t *, dmu_tx_t *);
typedef struct dsl_sync_task {
list_node_t dst_node;
@@ -46,6 +46,7 @@ typedef struct dsl_sync_task {
dsl_syncfunc_t *dst_syncfunc;
void *dst_arg1;
void *dst_arg2;
+ cred_t *dst_cr;
int dst_err;
} dsl_sync_task_t;
diff --git a/usr/src/uts/common/fs/zfs/sys/spa.h b/usr/src/uts/common/fs/zfs/sys/spa.h
index 6675fe5462..a79e17effb 100644
--- a/usr/src/uts/common/fs/zfs/sys/spa.h
+++ b/usr/src/uts/common/fs/zfs/sys/spa.h
@@ -438,11 +438,28 @@ extern boolean_t spa_has_spare(spa_t *, uint64_t guid);
extern uint64_t bp_get_dasize(spa_t *spa, const blkptr_t *bp);
/* history logging */
+typedef enum history_log_type {
+ LOG_CMD_POOL_CREATE,
+ LOG_CMD_NORMAL,
+ LOG_INTERNAL
+} history_log_type_t;
+
+typedef struct history_arg {
+ const char *ha_history_str;
+ history_log_type_t ha_log_type;
+ history_internal_events_t ha_event;
+ char ha_zone[MAXPATHLEN];
+} history_arg_t;
+
+extern char *spa_his_ievent_table[];
+
extern void spa_history_create_obj(spa_t *spa, dmu_tx_t *tx);
extern int spa_history_get(spa_t *spa, uint64_t *offset, uint64_t *len_read,
char *his_buf);
extern int spa_history_log(spa_t *spa, const char *his_buf,
- uint64_t pool_create);
+ history_log_type_t what);
+void spa_history_internal_log(history_internal_events_t event, spa_t *spa,
+ dmu_tx_t *tx, cred_t *cr, const char *fmt, ...);
/* error handling */
struct zbookmark;
diff --git a/usr/src/uts/common/fs/zfs/sys/spa_impl.h b/usr/src/uts/common/fs/zfs/sys/spa_impl.h
index 9f4368cdcf..0d155d7c0c 100644
--- a/usr/src/uts/common/fs/zfs/sys/spa_impl.h
+++ b/usr/src/uts/common/fs/zfs/sys/spa_impl.h
@@ -150,6 +150,7 @@ struct spa {
kmutex_t spa_props_lock; /* property lock */
uint64_t spa_pool_props_object; /* object for properties */
uint64_t spa_bootfs; /* default boot filesystem */
+ boolean_t spa_delegation; /* delegation on/off */
/*
* spa_refcnt must be the last element because it changes size based on
* compilation options. In order for the MDB module to function
diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_ctldir.h b/usr/src/uts/common/fs/zfs/sys/zfs_ctldir.h
index 78d82ccbe2..8f1cb74d94 100644
--- a/usr/src/uts/common/fs/zfs/sys/zfs_ctldir.h
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_ctldir.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -56,6 +55,7 @@ void zfsctl_fini(void);
int zfsctl_rename_snapshot(const char *from, const char *to);
int zfsctl_destroy_snapshot(const char *snapname, int force);
int zfsctl_umount_snapshots(vfs_t *, int, cred_t *);
+int zfsctl_unmount_snap(vnode_t *dvp, const char *name, int force, cred_t *cr);
int zfsctl_root_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp,
int flags, vnode_t *rdir, cred_t *cr);
diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h
index 765804c46e..8228fe4709 100644
--- a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h
@@ -31,6 +31,7 @@
#include <sys/cred.h>
#include <sys/dmu.h>
#include <sys/zio.h>
+#include <sys/dsl_deleg.h>
#ifdef __cplusplus
extern "C" {
@@ -109,12 +110,20 @@ typedef struct zinject_record {
uint32_t zi_error;
uint64_t zi_type;
uint32_t zi_freq;
+ uint32_t zi_pad; /* pad out to 64 bit alignment */
} zinject_record_t;
#define ZINJECT_NULL 0x1
#define ZINJECT_FLUSH_ARC 0x2
#define ZINJECT_UNLOAD_SPA 0x4
+typedef struct zfs_share {
+ uint64_t z_exportdata;
+ uint64_t z_sharedata;
+ uint64_t z_sharetype; /* 0 = share, 1 = unshare */
+ uint64_t z_sharemax; /* max length of share string */
+} zfs_share_t;
+
typedef struct zfs_cmd {
char zc_name[MAXPATHLEN];
char zc_value[MAXPATHLEN * 2];
@@ -127,21 +136,17 @@ typedef struct zfs_cmd {
uint64_t zc_cred;
uint64_t zc_dev;
uint64_t zc_objset_type;
- uint64_t zc_history; /* really (char *) */
- uint64_t zc_history_len;
+ uint64_t zc_perm_action;
+ uint64_t zc_history; /* really (char *) */
+ uint64_t zc_history_len;
uint64_t zc_history_offset;
uint64_t zc_obj;
+ zfs_share_t zc_share;
dmu_objset_stats_t zc_objset_stats;
struct drr_begin zc_begin_record;
zinject_record_t zc_inject_record;
} zfs_cmd_t;
-typedef struct zfs_create_data {
- cred_t *zc_cred;
- dev_t zc_dev;
- nvlist_t *zc_props;
-} zfs_create_data_t;
-
#define ZVOL_MAX_MINOR (1 << 16)
#define ZFS_MIN_MINOR (ZVOL_MAX_MINOR + 1)
@@ -149,7 +154,10 @@ typedef struct zfs_create_data {
extern dev_info_t *zfs_dip;
-extern int zfs_secpolicy_write(const char *dataset, cred_t *cr);
+extern int zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr);
+extern int zfs_secpolicy_rename_perms(const char *from,
+ const char *to, cred_t *cr);
+extern int zfs_secpolicy_destroy_perms(const char *name, cred_t *cr);
extern int zfs_busy(void);
extern int zfs_unmount_snap(char *, void *);
diff --git a/usr/src/uts/common/fs/zfs/sys/zvol.h b/usr/src/uts/common/fs/zfs/sys/zvol.h
index 2a221bd436..34f1ca1c31 100644
--- a/usr/src/uts/common/fs/zfs/sys/zvol.h
+++ b/usr/src/uts/common/fs/zfs/sys/zvol.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -39,7 +39,7 @@ extern "C" {
extern int zvol_check_volsize(uint64_t volsize, uint64_t blocksize);
extern int zvol_check_volblocksize(uint64_t volblocksize);
extern int zvol_get_stats(objset_t *os, nvlist_t *nv);
-extern void zvol_create_cb(objset_t *os, void *arg, dmu_tx_t *tx);
+extern void zvol_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx);
extern int zvol_create_minor(const char *, dev_t);
extern int zvol_remove_minor(const char *);
extern int zvol_set_volsize(const char *, dev_t, uint64_t);
diff --git a/usr/src/uts/common/fs/zfs/zfs_ctldir.c b/usr/src/uts/common/fs/zfs/zfs_ctldir.c
index f48dd98436..d842fafa2a 100644
--- a/usr/src/uts/common/fs/zfs/zfs_ctldir.c
+++ b/usr/src/uts/common/fs/zfs/zfs_ctldir.c
@@ -63,6 +63,7 @@
#include <sys/gfs.h>
#include <sys/stat.h>
#include <sys/dmu.h>
+#include <sys/dsl_deleg.h>
#include <sys/mount.h>
typedef struct {
@@ -411,7 +412,7 @@ zfsctl_snapshot_zname(vnode_t *vp, const char *name, int len, char *zname)
return (0);
}
-static int
+int
zfsctl_unmount_snap(vnode_t *dvp, const char *name, int force, cred_t *cr)
{
zfsctl_snapdir_t *sdp = dvp->v_data;
@@ -514,10 +515,13 @@ zfsctl_snapdir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
err = zfsctl_snapshot_zname(sdvp, snm, MAXNAMELEN, from);
if (err)
return (err);
- err = zfs_secpolicy_write(from, cr);
+
+ err = zfsctl_snapshot_zname(tdvp, tnm, MAXNAMELEN, to);
if (err)
return (err);
+ if (err = zfs_secpolicy_rename_perms(from, to, cr))
+ return (err);
/*
* Cannot move snapshots out of the snapdir.
*/
@@ -527,10 +531,6 @@ zfsctl_snapdir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
if (strcmp(snm, tnm) == 0)
return (0);
- err = zfsctl_snapshot_zname(tdvp, tnm, MAXNAMELEN, to);
- if (err)
- return (err);
-
mutex_enter(&sdp->sd_lock);
search.se_name = (char *)snm;
@@ -559,13 +559,13 @@ zfsctl_snapdir_remove(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr)
err = zfsctl_snapshot_zname(dvp, name, MAXNAMELEN, snapname);
if (err)
return (err);
- err = zfs_secpolicy_write(snapname, cr);
- if (err)
+
+ if (err = zfs_secpolicy_destroy_perms(snapname, cr))
return (err);
mutex_enter(&sdp->sd_lock);
- err = zfsctl_unmount_snap(dvp, name, 0, cr);
+ err = zfsctl_unmount_snap(dvp, name, MS_FORCE, cr);
if (err) {
mutex_exit(&sdp->sd_lock);
return (err);
@@ -578,6 +578,35 @@ zfsctl_snapdir_remove(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr)
return (err);
}
+/* ARGSUSED */
+static int
+zfsctl_snapdir_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp,
+ cred_t *cr)
+{
+ zfsvfs_t *zfsvfs = dvp->v_vfsp->vfs_data;
+ char name[MAXNAMELEN];
+ int err;
+ static enum symfollow follow = NO_FOLLOW;
+ static enum uio_seg seg = UIO_SYSSPACE;
+
+ dmu_objset_name(zfsvfs->z_os, name);
+
+ *vpp = NULL;
+
+ err = zfs_secpolicy_snapshot_perms(name, cr);
+ if (err)
+ return (err);
+
+ if (err == 0) {
+ err = dmu_objset_snapshot(name, dirname, B_FALSE);
+ if (err)
+ return (err);
+ err = lookupnameat(dirname, seg, follow, NULL, vpp, dvp);
+ }
+
+ return (err);
+}
+
/*
* Lookup entry point for the 'snapshot' directory. Try to open the
* snapshot if it exist, creating the pseudo filesystem vnode as necessary.
@@ -796,6 +825,7 @@ static const fs_operation_def_t zfsctl_tops_snapdir[] = {
{ VOPNAME_ACCESS, { .vop_access = zfsctl_common_access } },
{ VOPNAME_RENAME, { .vop_rename = zfsctl_snapdir_rename } },
{ VOPNAME_RMDIR, { .vop_rmdir = zfsctl_snapdir_remove } },
+ { VOPNAME_MKDIR, { .vop_mkdir = zfsctl_snapdir_mkdir } },
{ VOPNAME_READDIR, { .vop_readdir = gfs_vop_readdir } },
{ VOPNAME_LOOKUP, { .vop_lookup = zfsctl_snapdir_lookup } },
{ VOPNAME_SEEK, { .vop_seek = fs_seek } },
diff --git a/usr/src/uts/common/fs/zfs/zfs_ioctl.c b/usr/src/uts/common/fs/zfs/zfs_ioctl.c
index fdbfb189e3..560a943cd7 100644
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c
@@ -47,6 +47,8 @@
#include <sys/dsl_dir.h>
#include <sys/dsl_dataset.h>
#include <sys/dsl_prop.h>
+#include <sys/dsl_deleg.h>
+#include <sys/dmu_objset.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sunldi.h>
@@ -59,9 +61,11 @@
#include <sys/fs/zfs.h>
#include <sys/zfs_ctldir.h>
#include <sys/zvol.h>
+#include <sharefs/share.h>
#include "zfs_namecheck.h"
#include "zfs_prop.h"
+#include "zfs_deleg.h"
extern struct modlfs zfs_modlfs;
@@ -72,7 +76,7 @@ ldi_ident_t zfs_li = NULL;
dev_info_t *zfs_dip;
typedef int zfs_ioc_func_t(zfs_cmd_t *);
-typedef int zfs_secpolicy_func_t(const char *, cred_t *);
+typedef int zfs_secpolicy_func_t(zfs_cmd_t *, cred_t *);
typedef struct zfs_ioc_vec {
zfs_ioc_func_t *zvec_func;
@@ -81,7 +85,8 @@ typedef struct zfs_ioc_vec {
no_name,
pool_name,
dataset_name
- } zvec_namecheck;
+ } zvec_namecheck;
+ boolean_t zvec_his_log;
} zfs_ioc_vec_t;
/* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */
@@ -120,13 +125,49 @@ __dprintf(const char *file, const char *func, int line, const char *fmt, ...)
char *, newfile, char *, func, int, line, char *, buf);
}
+static void
+zfs_log_history(zfs_cmd_t *zc)
+{
+ spa_t *spa;
+ char poolname[MAXNAMELEN];
+ char *buf, *cp;
+
+ if (zc->zc_history == NULL)
+ return;
+
+ buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP);
+ if (copyinstr((void *)(uintptr_t)zc->zc_history,
+ buf, HIS_MAX_RECORD_LEN, NULL) != 0) {
+ kmem_free(buf, HIS_MAX_RECORD_LEN);
+ return;
+ }
+
+ buf[HIS_MAX_RECORD_LEN -1] = '\0';
+
+ (void) strlcpy(poolname, zc->zc_name, sizeof (poolname));
+ cp = strpbrk(poolname, "/@");
+ if (cp != NULL)
+ *cp = '\0';
+
+ if (spa_open(poolname, &spa, FTAG) != 0) {
+ kmem_free(buf, HIS_MAX_RECORD_LEN);
+ return;
+ }
+
+ if (spa_version(spa) >= ZFS_VERSION_ZPOOL_HISTORY)
+ (void) spa_history_log(spa, buf, zc->zc_history_offset);
+
+ spa_close(spa, FTAG);
+ kmem_free(buf, HIS_MAX_RECORD_LEN);
+}
+
/*
* Policy for top-level read operations (list pools). Requires no privileges,
* and can be used in the local zone, as there is no associated dataset.
*/
/* ARGSUSED */
static int
-zfs_secpolicy_none(const char *unused1, cred_t *cr)
+zfs_secpolicy_none(zfs_cmd_t *zc, cred_t *cr)
{
return (0);
}
@@ -137,10 +178,10 @@ zfs_secpolicy_none(const char *unused1, cred_t *cr)
*/
/* ARGSUSED */
static int
-zfs_secpolicy_read(const char *dataset, cred_t *cr)
+zfs_secpolicy_read(zfs_cmd_t *zc, cred_t *cr)
{
if (INGLOBALZONE(curproc) ||
- zone_dataset_visible(dataset, NULL))
+ zone_dataset_visible(zc->zc_name, NULL))
return (0);
return (ENOENT);
@@ -184,47 +225,340 @@ zfs_dozonecheck(const char *dataset, cred_t *cr)
return (0);
}
-/*
- * Policy for dataset write operations (create children, set properties, etc).
- * Requires SYS_MOUNT privilege, and must be writable in the local zone.
- */
int
-zfs_secpolicy_write(const char *dataset, cred_t *cr)
+zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr)
{
int error;
- if (error = zfs_dozonecheck(dataset, cr))
+ error = zfs_dozonecheck(name, cr);
+ if (error == 0) {
+ error = secpolicy_zfs(cr);
+ if (error) {
+ error = dsl_deleg_access(name, perm, cr);
+ }
+ }
+ return (error);
+}
+
+static int
+zfs_secpolicy_setprop(const char *name, zfs_prop_t prop, cred_t *cr)
+{
+ int error = 0;
+
+ /*
+ * Check permissions for special properties.
+ */
+ switch (prop) {
+ case ZFS_PROP_ZONED:
+ /*
+ * Disallow setting of 'zoned' from within a local zone.
+ */
+ if (!INGLOBALZONE(curproc))
+ return (EPERM);
+ break;
+
+ case ZFS_PROP_QUOTA:
+ if (error =
+ zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_QUOTA, cr))
+ return (error);
+
+ if (!INGLOBALZONE(curproc)) {
+ uint64_t zoned;
+ char setpoint[MAXNAMELEN];
+ int dslen;
+ /*
+ * Unprivileged users are allowed to modify the
+ * quota on things *under* (ie. contained by)
+ * the thing they own.
+ */
+ if (dsl_prop_get_integer(name, "zoned", &zoned,
+ setpoint))
+ return (EPERM);
+ if (!zoned) /* this shouldn't happen */
+ return (EPERM);
+ dslen = strlen(name);
+ if (dslen <= strlen(setpoint))
+ return (EPERM);
+ }
+ default:
+ error = zfs_secpolicy_write_perms(name,
+ zfs_prop_perm(prop), cr);
+ }
+
+ return (error);
+}
+
+int
+zfs_secpolicy_fsacl(zfs_cmd_t *zc, cred_t *cr)
+{
+ int error;
+
+ error = zfs_dozonecheck(zc->zc_name, cr);
+ if (error)
return (error);
- return (secpolicy_zfs(cr));
+ /*
+ * permission to set permissions will be evaluated later in
+ * dsl_deleg_can_allow()
+ */
+ return (0);
+}
+
+int
+zfs_secpolicy_rollback(zfs_cmd_t *zc, cred_t *cr)
+{
+ int error;
+ error = zfs_secpolicy_write_perms(zc->zc_name,
+ ZFS_DELEG_PERM_ROLLBACK, cr);
+ if (error == 0)
+ error = zfs_secpolicy_write_perms(zc->zc_name,
+ ZFS_DELEG_PERM_MOUNT, cr);
+ return (error);
+}
+
+int
+zfs_secpolicy_send(zfs_cmd_t *zc, cred_t *cr)
+{
+ return (zfs_secpolicy_write_perms(zc->zc_name,
+ ZFS_DELEG_PERM_SEND, cr));
+}
+
+int
+zfs_secpolicy_share(zfs_cmd_t *zc, cred_t *cr)
+{
+ if (!INGLOBALZONE(curproc))
+ return (EPERM);
+
+ if (secpolicy_nfs(CRED()) == 0) {
+ return (0);
+ } else {
+ vnode_t *vp;
+ int error;
+
+ if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
+ NO_FOLLOW, NULL, &vp)) != 0)
+ return (error);
+
+ /* Now make sure mntpnt and dataset are ZFS */
+
+ if (vp->v_vfsp->vfs_fstype != zfsfstype ||
+ (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
+ zc->zc_name) != 0)) {
+ VN_RELE(vp);
+ return (EPERM);
+ }
+
+ VN_RELE(vp);
+ return (dsl_deleg_access(zc->zc_name,
+ ZFS_DELEG_PERM_SHARE, cr));
+ }
}
-/*
- * Policy for operations that want to write a dataset's parent:
- * create, destroy, snapshot, clone, restore.
- */
static int
-zfs_secpolicy_parent(const char *dataset, cred_t *cr)
+zfs_get_parent(const char *datasetname, char *parent, int parentsize)
{
- char parentname[MAXNAMELEN];
char *cp;
/*
* Remove the @bla or /bla from the end of the name to get the parent.
*/
- (void) strncpy(parentname, dataset, sizeof (parentname));
- cp = strrchr(parentname, '@');
+ (void) strncpy(parent, datasetname, parentsize);
+ cp = strrchr(parent, '@');
if (cp != NULL) {
cp[0] = '\0';
} else {
- cp = strrchr(parentname, '/');
+ cp = strrchr(parent, '/');
if (cp == NULL)
return (ENOENT);
cp[0] = '\0';
+ }
+
+ return (0);
+}
+
+int
+zfs_secpolicy_destroy_perms(const char *name, cred_t *cr)
+{
+ int error;
+
+ if ((error = zfs_secpolicy_write_perms(name,
+ ZFS_DELEG_PERM_MOUNT, cr)) != 0)
+ return (error);
+
+ return (zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_DESTROY, cr));
+}
+
+static int
+zfs_secpolicy_destroy(zfs_cmd_t *zc, cred_t *cr)
+{
+ return (zfs_secpolicy_destroy_perms(zc->zc_name, cr));
+}
+
+/*
+ * Must have sys_config privilege to check the iscsi permission
+ */
+/* ARGSUSED */
+static int
+zfs_secpolicy_iscsi(zfs_cmd_t *zc, cred_t *cr)
+{
+ return (secpolicy_zfs(cr));
+}
+
+int
+zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr)
+{
+ char parentname[MAXNAMELEN];
+ int error;
+
+ if ((error = zfs_secpolicy_write_perms(from,
+ ZFS_DELEG_PERM_RENAME, cr)) != 0)
+ return (error);
+
+ if ((error = zfs_secpolicy_write_perms(from,
+ ZFS_DELEG_PERM_MOUNT, cr)) != 0)
+ return (error);
+
+ if ((error = zfs_get_parent(to, parentname,
+ sizeof (parentname))) != 0)
+ return (error);
+
+ if ((error = zfs_secpolicy_write_perms(parentname,
+ ZFS_DELEG_PERM_CREATE, cr)) != 0)
+ return (error);
+
+ if ((error = zfs_secpolicy_write_perms(parentname,
+ ZFS_DELEG_PERM_MOUNT, cr)) != 0)
+ return (error);
+
+ return (error);
+}
+
+static int
+zfs_secpolicy_rename(zfs_cmd_t *zc, cred_t *cr)
+{
+ return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr));
+}
+
+static int
+zfs_secpolicy_promote(zfs_cmd_t *zc, cred_t *cr)
+{
+ char parentname[MAXNAMELEN];
+ objset_t *clone;
+ int error;
+
+ error = zfs_secpolicy_write_perms(zc->zc_name,
+ ZFS_DELEG_PERM_PROMOTE, cr);
+ if (error)
+ return (error);
+
+ error = dmu_objset_open(zc->zc_name, DMU_OST_ANY,
+ DS_MODE_STANDARD | DS_MODE_READONLY, &clone);
+
+ if (error == 0) {
+ dsl_dataset_t *pclone = NULL;
+ dsl_dir_t *dd;
+ dd = clone->os->os_dsl_dataset->ds_dir;
+
+ rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
+ error = dsl_dataset_open_obj(dd->dd_pool,
+ dd->dd_phys->dd_clone_parent_obj, NULL,
+ DS_MODE_NONE, FTAG, &pclone);
+ rw_exit(&dd->dd_pool->dp_config_rwlock);
+ if (error) {
+ dmu_objset_close(clone);
+ return (error);
+ }
+
+ error = zfs_secpolicy_write_perms(zc->zc_name,
+ ZFS_DELEG_PERM_MOUNT, cr);
+
+ dsl_dataset_name(pclone, parentname);
+ dmu_objset_close(clone);
+ dsl_dataset_close(pclone, DS_MODE_NONE, FTAG);
+ if (error == 0)
+ error = zfs_secpolicy_write_perms(parentname,
+ ZFS_DELEG_PERM_PROMOTE, cr);
+ }
+ return (error);
+}
+
+static int
+zfs_secpolicy_receive(zfs_cmd_t *zc, cred_t *cr)
+{
+ int error;
+
+ if ((error = zfs_secpolicy_write_perms(zc->zc_name,
+ ZFS_DELEG_PERM_RECEIVE, cr)) != 0)
+ return (error);
+
+ if ((error = zfs_secpolicy_write_perms(zc->zc_name,
+ ZFS_DELEG_PERM_MOUNT, cr)) != 0)
+ return (error);
+
+ return (zfs_secpolicy_write_perms(zc->zc_name,
+ ZFS_DELEG_PERM_CREATE, cr));
+}
+
+int
+zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr)
+{
+ int error;
+
+ if ((error = zfs_secpolicy_write_perms(name,
+ ZFS_DELEG_PERM_SNAPSHOT, cr)) != 0)
+ return (error);
+
+ error = zfs_secpolicy_write_perms(name,
+ ZFS_DELEG_PERM_MOUNT, cr);
+
+ return (error);
+}
+
+static int
+zfs_secpolicy_snapshot(zfs_cmd_t *zc, cred_t *cr)
+{
+
+ return (zfs_secpolicy_snapshot_perms(zc->zc_name, cr));
+}
+
+
+
+static int
+zfs_secpolicy_create(zfs_cmd_t *zc, cred_t *cr)
+{
+ char parentname[MAXNAMELEN];
+ int error;
+
+ if ((error = zfs_get_parent(zc->zc_name, parentname,
+ sizeof (parentname))) != 0)
+ return (error);
+ if (zc->zc_value[0] != '\0') {
+ if ((error = zfs_secpolicy_write_perms(zc->zc_value,
+ ZFS_DELEG_PERM_CLONE, cr)) != 0)
+ return (error);
}
- return (zfs_secpolicy_write(parentname, cr));
+ if ((error = zfs_secpolicy_write_perms(parentname,
+ ZFS_DELEG_PERM_CREATE, cr)) != 0)
+ return (error);
+
+ error = zfs_secpolicy_write_perms(parentname,
+ ZFS_DELEG_PERM_MOUNT, cr);
+
+ return (error);
+}
+
+static int
+zfs_secpolicy_umount(zfs_cmd_t *zc, cred_t *cr)
+{
+ int error;
+
+ error = secpolicy_fs_unmount(cr, NULL);
+ if (error) {
+ error = dsl_deleg_access(zc->zc_name, ZFS_DELEG_PERM_MOUNT, cr);
+ }
+ return (error);
}
/*
@@ -233,7 +567,7 @@ zfs_secpolicy_parent(const char *dataset, cred_t *cr)
*/
/* ARGSUSED */
static int
-zfs_secpolicy_config(const char *unused, cred_t *cr)
+zfs_secpolicy_config(zfs_cmd_t *zc, cred_t *cr)
{
if (secpolicy_sys_config(cr, B_FALSE) != 0)
return (EPERM);
@@ -242,11 +576,27 @@ zfs_secpolicy_config(const char *unused, cred_t *cr)
}
/*
+ * Just like zfs_secpolicy_config, except that we will check for
+ * mount permission on the dataset for permission to create/remove
+ * the minor nodes.
+ */
+static int
+zfs_secpolicy_minor(zfs_cmd_t *zc, cred_t *cr)
+{
+ if (secpolicy_sys_config(cr, B_FALSE) != 0) {
+ return (dsl_deleg_access(zc->zc_name,
+ ZFS_DELEG_PERM_MOUNT, cr));
+ }
+
+ return (0);
+}
+
+/*
* Policy for fault injection. Requires all privileges.
*/
/* ARGSUSED */
static int
-zfs_secpolicy_inject(const char *unused, cred_t *cr)
+zfs_secpolicy_inject(zfs_cmd_t *zc, cred_t *cr)
{
return (secpolicy_zinject(cr));
}
@@ -330,7 +680,10 @@ zfs_ioc_pool_create(zfs_cmd_t *zc)
static int
zfs_ioc_pool_destroy(zfs_cmd_t *zc)
{
- return (spa_destroy(zc->zc_name));
+ int error;
+ zfs_log_history(zc);
+ error = spa_destroy(zc->zc_name);
+ return (error);
}
static int
@@ -358,7 +711,10 @@ zfs_ioc_pool_import(zfs_cmd_t *zc)
static int
zfs_ioc_pool_export(zfs_cmd_t *zc)
{
- return (spa_export(zc->zc_name, NULL));
+ int error;
+ zfs_log_history(zc);
+ error = spa_export(zc->zc_name, NULL);
+ return (error);
}
static int
@@ -472,7 +828,6 @@ zfs_ioc_pool_upgrade(zfs_cmd_t *zc)
return (error);
spa_upgrade(spa);
-
spa_close(spa, FTAG);
return (error);
@@ -500,7 +855,8 @@ zfs_ioc_pool_get_history(zfs_cmd_t *zc)
hist_buf = kmem_alloc(size, KM_SLEEP);
if ((error = spa_history_get(spa, &zc->zc_history_offset,
&zc->zc_history_len, hist_buf)) == 0) {
- error = xcopyout(hist_buf, (char *)(uintptr_t)zc->zc_history,
+ error = xcopyout(hist_buf,
+ (char *)(uintptr_t)zc->zc_history,
zc->zc_history_len);
}
@@ -510,45 +866,6 @@ zfs_ioc_pool_get_history(zfs_cmd_t *zc)
}
static int
-zfs_ioc_pool_log_history(zfs_cmd_t *zc)
-{
- spa_t *spa;
- char *history_str = NULL;
- size_t size;
- int error;
-
- size = zc->zc_history_len;
- if (size == 0 || size > HIS_MAX_RECORD_LEN)
- return (EINVAL);
-
- if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
- return (error);
-
- if (spa_version(spa) < ZFS_VERSION_ZPOOL_HISTORY) {
- spa_close(spa, FTAG);
- return (ENOTSUP);
- }
-
- /* add one for the NULL delimiter */
- size++;
- history_str = kmem_alloc(size, KM_SLEEP);
- if ((error = xcopyin((void *)(uintptr_t)zc->zc_history, history_str,
- size)) != 0) {
- spa_close(spa, FTAG);
- kmem_free(history_str, size);
- return (error);
- }
- history_str[size - 1] = '\0';
-
- error = spa_history_log(spa, history_str, zc->zc_history_offset);
-
- spa_close(spa, FTAG);
- kmem_free(history_str, size);
-
- return (error);
-}
-
-static int
zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc)
{
int error;
@@ -600,7 +917,6 @@ zfs_ioc_vdev_add(zfs_cmd_t *zc)
error = spa_vdev_add(spa, config);
nvlist_free(config);
}
-
spa_close(spa, FTAG);
return (error);
}
@@ -863,10 +1179,10 @@ zfs_set_prop_nvlist(const char *name, dev_t dev, cred_t *cr, nvlist_t *nvl)
zfs_prop_t prop;
uint64_t intval;
char *strval;
- char buf[MAXNAMELEN];
- const char *p;
- spa_t *spa;
+ /*
+ * First validate permission to set all of the properties
+ */
elem = NULL;
while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
propname = nvpair_name(elem);
@@ -881,18 +1197,18 @@ zfs_set_prop_nvlist(const char *name, dev_t dev, cred_t *cr, nvlist_t *nvl)
nvpair_type(elem) != DATA_TYPE_STRING)
return (EINVAL);
- VERIFY(nvpair_value_string(elem, &strval) == 0);
- error = dsl_prop_set(name, propname, 1,
- strlen(strval) + 1, strval);
- if (error == 0)
- continue;
- else
- return (error);
+ error = zfs_secpolicy_write_perms(name,
+ ZFS_DELEG_PERM_USERPROP, cr);
+ if (error) {
+ return (EPERM);
+ }
+ continue;
}
/*
- * Check permissions for special properties.
+ * Check permissions for special properties
*/
+
switch (prop) {
case ZFS_PROP_ZONED:
/*
@@ -936,6 +1252,10 @@ zfs_set_prop_nvlist(const char *name, dev_t dev, cred_t *cr, nvlist_t *nvl)
nvpair_value_uint64(elem, &intval) == 0 &&
intval >= ZIO_COMPRESS_GZIP_1 &&
intval <= ZIO_COMPRESS_GZIP_9) {
+ char buf[MAXNAMELEN];
+ spa_t *spa;
+ const char *p;
+
if ((p = strchr(name, '/')) == NULL) {
p = name;
} else {
@@ -956,6 +1276,25 @@ zfs_set_prop_nvlist(const char *name, dev_t dev, cred_t *cr, nvlist_t *nvl)
}
break;
}
+ if ((error = zfs_secpolicy_setprop(name, prop, cr)) != 0)
+ return (error);
+ }
+
+ elem = NULL;
+ while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
+ propname = nvpair_name(elem);
+
+ if ((prop = zfs_name_to_prop(propname)) ==
+ ZFS_PROP_INVAL) {
+
+ VERIFY(nvpair_value_string(elem, &strval) == 0);
+ error = dsl_prop_set(name, propname, 1,
+ strlen(strval) + 1, strval);
+ if (error == 0)
+ continue;
+ else
+ return (error);
+ }
switch (prop) {
case ZFS_PROP_QUOTA:
@@ -1060,6 +1399,7 @@ zfs_ioc_set_prop(zfs_cmd_t *zc)
error = zfs_set_prop_nvlist(zc->zc_name, zc->zc_dev,
(cred_t *)(uintptr_t)zc->zc_cred, nvl);
+
nvlist_free(nvl);
return (error);
}
@@ -1070,6 +1410,7 @@ zfs_ioc_pool_set_props(zfs_cmd_t *zc)
nvlist_t *nvl;
int error, reset_bootfs = 0;
uint64_t objnum;
+ uint64_t intval;
zpool_prop_t prop;
nvpair_t *elem;
char *propname, *strval;
@@ -1105,6 +1446,11 @@ zfs_ioc_pool_set_props(zfs_cmd_t *zc)
}
switch (prop) {
+ case ZPOOL_PROP_DELEGATION:
+ VERIFY(nvpair_value_uint64(elem, &intval) == 0);
+ if (intval > 1)
+ error = EINVAL;
+ break;
case ZPOOL_PROP_BOOTFS:
/*
* A bootable filesystem can not be on a RAIDZ pool
@@ -1183,6 +1529,106 @@ zfs_ioc_pool_get_props(zfs_cmd_t *zc)
}
static int
+zfs_ioc_iscsi_perm_check(zfs_cmd_t *zc)
+{
+ nvlist_t *nvp;
+ int error;
+ uint32_t uid;
+ uint32_t gid;
+ uint32_t *groups;
+ uint_t group_cnt;
+ cred_t *usercred;
+
+ if ((error = get_nvlist(zc, &nvp)) != 0) {
+ return (error);
+ }
+
+ if ((error = nvlist_lookup_uint32(nvp,
+ ZFS_DELEG_PERM_UID, &uid)) != 0) {
+ nvlist_free(nvp);
+ return (EPERM);
+ }
+
+ if ((error = nvlist_lookup_uint32(nvp,
+ ZFS_DELEG_PERM_GID, &gid)) != 0) {
+ nvlist_free(nvp);
+ return (EPERM);
+ }
+
+ if ((error = nvlist_lookup_uint32_array(nvp, ZFS_DELEG_PERM_GROUPS,
+ &groups, &group_cnt)) != 0) {
+ nvlist_free(nvp);
+ return (EPERM);
+ }
+ usercred = cralloc();
+ if ((crsetugid(usercred, uid, gid) != 0) ||
+ (crsetgroups(usercred, group_cnt, (gid_t *)groups) != 0)) {
+ nvlist_free(nvp);
+ crfree(usercred);
+ return (EPERM);
+ }
+ nvlist_free(nvp);
+ error = dsl_deleg_access(zc->zc_name,
+ ZFS_DELEG_PERM_SHAREISCSI, usercred);
+ crfree(usercred);
+ return (error);
+}
+
+static int
+zfs_ioc_set_fsacl(zfs_cmd_t *zc)
+{
+ int error;
+ nvlist_t *fsaclnv = NULL;
+ cred_t *cr;
+
+ if ((error = get_nvlist(zc, &fsaclnv)) != 0)
+ return (error);
+
+ /*
+ * Verify nvlist is constructed correctly
+ */
+ if ((error = zfs_deleg_verify_nvlist(fsaclnv)) != 0) {
+ nvlist_free(fsaclnv);
+ return (EINVAL);
+ }
+
+ /*
+ * If we don't have PRIV_SYS_MOUNT, then validate
+ * that user is allowed to hand out each permission in
+ * the nvlist(s)
+ */
+
+ cr = (cred_t *)(uintptr_t)zc->zc_cred;
+ error = secpolicy_zfs(cr);
+ if (error) {
+ if (zc->zc_perm_action == B_FALSE)
+ error = dsl_deleg_can_allow(zc->zc_name, fsaclnv, cr);
+ else
+ error = dsl_deleg_can_unallow(zc->zc_name, fsaclnv, cr);
+ }
+
+ if (error == 0)
+ error = dsl_deleg_set(zc->zc_name, fsaclnv, zc->zc_perm_action);
+
+ nvlist_free(fsaclnv);
+ return (error);
+}
+
+static int
+zfs_ioc_get_fsacl(zfs_cmd_t *zc)
+{
+ nvlist_t *nvp;
+ int error;
+
+ if ((error = dsl_deleg_get(zc->zc_name, &nvp)) == 0) {
+ error = put_nvlist(zc, nvp);
+ nvlist_free(nvp);
+ }
+
+ return (error);
+}
+
+static int
zfs_ioc_create_minor(zfs_cmd_t *zc)
{
return (zvol_create_minor(zc->zc_name, zc->zc_dev));
@@ -1219,11 +1665,11 @@ zfs_get_vfs(const char *resource)
return (vfs_found);
}
+/* ARGSUSED */
static void
-zfs_create_cb(objset_t *os, void *arg, dmu_tx_t *tx)
+zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
{
- zfs_create_data_t *zc = arg;
- zfs_create_fs(os, (cred_t *)(uintptr_t)zc->zc_cred, tx);
+ zfs_create_fs(os, cr, tx);
}
static int
@@ -1231,8 +1677,8 @@ zfs_ioc_create(zfs_cmd_t *zc)
{
objset_t *clone;
int error = 0;
- zfs_create_data_t cbdata = { 0 };
- void (*cbfunc)(objset_t *os, void *arg, dmu_tx_t *tx);
+ nvlist_t *nvprops = NULL;
+ void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx);
dmu_objset_type_t type = zc->zc_objset_type;
switch (type) {
@@ -1252,51 +1698,48 @@ zfs_ioc_create(zfs_cmd_t *zc)
return (EINVAL);
if (zc->zc_nvlist_src != NULL &&
- (error = get_nvlist(zc, &cbdata.zc_props)) != 0)
+ (error = get_nvlist(zc, &nvprops)) != 0)
return (error);
- cbdata.zc_cred = (cred_t *)(uintptr_t)zc->zc_cred;
- cbdata.zc_dev = (dev_t)zc->zc_dev;
-
if (zc->zc_value[0] != '\0') {
/*
* We're creating a clone of an existing snapshot.
*/
zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0) {
- nvlist_free(cbdata.zc_props);
+ nvlist_free(nvprops);
return (EINVAL);
}
error = dmu_objset_open(zc->zc_value, type,
DS_MODE_STANDARD | DS_MODE_READONLY, &clone);
if (error) {
- nvlist_free(cbdata.zc_props);
+ nvlist_free(nvprops);
return (error);
}
error = dmu_objset_create(zc->zc_name, type, clone, NULL, NULL);
dmu_objset_close(clone);
} else {
if (cbfunc == NULL) {
- nvlist_free(cbdata.zc_props);
+ nvlist_free(nvprops);
return (EINVAL);
}
if (type == DMU_OST_ZVOL) {
uint64_t volsize, volblocksize;
- if (cbdata.zc_props == NULL ||
- nvlist_lookup_uint64(cbdata.zc_props,
+ if (nvprops == NULL ||
+ nvlist_lookup_uint64(nvprops,
zfs_prop_to_name(ZFS_PROP_VOLSIZE),
&volsize) != 0) {
- nvlist_free(cbdata.zc_props);
+ nvlist_free(nvprops);
return (EINVAL);
}
- if ((error = nvlist_lookup_uint64(cbdata.zc_props,
+ if ((error = nvlist_lookup_uint64(nvprops,
zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
&volblocksize)) != 0 && error != ENOENT) {
- nvlist_free(cbdata.zc_props);
+ nvlist_free(nvprops);
return (EINVAL);
}
@@ -1308,13 +1751,13 @@ zfs_ioc_create(zfs_cmd_t *zc)
volblocksize)) != 0 ||
(error = zvol_check_volsize(volsize,
volblocksize)) != 0) {
- nvlist_free(cbdata.zc_props);
+ nvlist_free(nvprops);
return (error);
}
}
error = dmu_objset_create(zc->zc_name, type, NULL, cbfunc,
- &cbdata);
+ nvprops);
}
/*
@@ -1323,11 +1766,11 @@ zfs_ioc_create(zfs_cmd_t *zc)
if (error == 0) {
if ((error = zfs_set_prop_nvlist(zc->zc_name,
zc->zc_dev, (cred_t *)(uintptr_t)zc->zc_cred,
- cbdata.zc_props)) != 0)
+ nvprops)) != 0)
(void) dmu_objset_destroy(zc->zc_name);
}
- nvlist_free(cbdata.zc_props);
+ nvlist_free(nvprops);
return (error);
}
@@ -1613,49 +2056,120 @@ zfs_ioc_promote(zfs_cmd_t *zc)
return (dsl_dataset_promote(zc->zc_name));
}
+/*
+ * We don't want to have a hard dependency
+ * against some special symbols in sharefs
+ * and nfs. Determine them if needed when
+ * the first file system is shared.
+ * Neither sharefs or nfs are unloadable modules.
+ */
+int (*zexport_fs)(void *arg);
+int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t);
+
+int zfs_share_inited;
+ddi_modhandle_t nfs_mod;
+ddi_modhandle_t sharefs_mod;
+kmutex_t zfs_share_lock;
+
+static int
+zfs_ioc_share(zfs_cmd_t *zc)
+{
+ int error;
+ int opcode;
+
+ if (zfs_share_inited == 0) {
+ mutex_enter(&zfs_share_lock);
+ nfs_mod = ddi_modopen("fs/nfs", KRTLD_MODE_FIRST, &error);
+ sharefs_mod = ddi_modopen("fs/sharefs",
+ KRTLD_MODE_FIRST, &error);
+ if (nfs_mod == NULL || sharefs_mod == NULL) {
+ mutex_exit(&zfs_share_lock);
+ return (ENOSYS);
+ }
+ if (zexport_fs == NULL && ((zexport_fs = (int (*)(void *))
+ ddi_modsym(nfs_mod, "nfs_export", &error)) == NULL)) {
+ mutex_exit(&zfs_share_lock);
+ return (ENOSYS);
+ }
+
+ if (zshare_fs == NULL && ((zshare_fs =
+ (int (*)(enum sharefs_sys_op, share_t *, uint32_t))
+ ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) {
+ mutex_exit(&zfs_share_lock);
+ return (ENOSYS);
+ }
+ zfs_share_inited = 1;
+ mutex_exit(&zfs_share_lock);
+ }
+
+ if (error = zexport_fs((void *)(uintptr_t)zc->zc_share.z_exportdata))
+ return (error);
+
+ opcode = (zc->zc_share.z_sharetype == B_TRUE) ?
+ SHAREFS_ADD : SHAREFS_REMOVE;
+
+ error = zshare_fs(opcode,
+ (void *)(uintptr_t)zc->zc_share.z_sharedata,
+ zc->zc_share.z_sharemax);
+
+ return (error);
+
+}
+
+/*
+ * pool destroy and pool export don't log the history as part of zfsdev_ioctl,
+ * but rather zfs_ioc_pool_create, and zfs_ioc_pool_export do the loggin
+ * of those commands.
+ */
static zfs_ioc_vec_t zfs_ioc_vec[] = {
- { zfs_ioc_pool_create, zfs_secpolicy_config, pool_name },
- { zfs_ioc_pool_destroy, zfs_secpolicy_config, pool_name },
- { zfs_ioc_pool_import, zfs_secpolicy_config, pool_name },
- { zfs_ioc_pool_export, zfs_secpolicy_config, pool_name },
- { zfs_ioc_pool_configs, zfs_secpolicy_none, no_name },
- { zfs_ioc_pool_stats, zfs_secpolicy_read, pool_name },
- { zfs_ioc_pool_tryimport, zfs_secpolicy_config, no_name },
- { zfs_ioc_pool_scrub, zfs_secpolicy_config, pool_name },
- { zfs_ioc_pool_freeze, zfs_secpolicy_config, no_name },
- { zfs_ioc_pool_upgrade, zfs_secpolicy_config, pool_name },
- { zfs_ioc_pool_get_history, zfs_secpolicy_config, pool_name },
- { zfs_ioc_pool_log_history, zfs_secpolicy_config, pool_name },
- { zfs_ioc_vdev_add, zfs_secpolicy_config, pool_name },
- { zfs_ioc_vdev_remove, zfs_secpolicy_config, pool_name },
- { zfs_ioc_vdev_set_state, zfs_secpolicy_config, pool_name },
- { zfs_ioc_vdev_attach, zfs_secpolicy_config, pool_name },
- { zfs_ioc_vdev_detach, zfs_secpolicy_config, pool_name },
- { zfs_ioc_vdev_setpath, zfs_secpolicy_config, pool_name },
- { zfs_ioc_objset_stats, zfs_secpolicy_read, dataset_name },
- { zfs_ioc_dataset_list_next, zfs_secpolicy_read, dataset_name },
- { zfs_ioc_snapshot_list_next, zfs_secpolicy_read, dataset_name },
- { zfs_ioc_set_prop, zfs_secpolicy_write, dataset_name },
- { zfs_ioc_create_minor, zfs_secpolicy_config, dataset_name },
- { zfs_ioc_remove_minor, zfs_secpolicy_config, dataset_name },
- { zfs_ioc_create, zfs_secpolicy_parent, dataset_name },
- { zfs_ioc_destroy, zfs_secpolicy_parent, dataset_name },
- { zfs_ioc_rollback, zfs_secpolicy_write, dataset_name },
- { zfs_ioc_rename, zfs_secpolicy_write, dataset_name },
- { zfs_ioc_recvbackup, zfs_secpolicy_write, dataset_name },
- { zfs_ioc_sendbackup, zfs_secpolicy_write, dataset_name },
- { zfs_ioc_inject_fault, zfs_secpolicy_inject, no_name },
- { zfs_ioc_clear_fault, zfs_secpolicy_inject, no_name },
- { zfs_ioc_inject_list_next, zfs_secpolicy_inject, no_name },
- { zfs_ioc_error_log, zfs_secpolicy_inject, pool_name },
- { zfs_ioc_clear, zfs_secpolicy_config, pool_name },
- { zfs_ioc_promote, zfs_secpolicy_write, dataset_name },
- { zfs_ioc_destroy_snaps, zfs_secpolicy_write, dataset_name },
- { zfs_ioc_snapshot, zfs_secpolicy_write, dataset_name },
- { zfs_ioc_dsobj_to_dsname, zfs_secpolicy_config, pool_name },
- { zfs_ioc_obj_to_path, zfs_secpolicy_config, no_name },
- { zfs_ioc_pool_set_props, zfs_secpolicy_config, pool_name },
- { zfs_ioc_pool_get_props, zfs_secpolicy_read, pool_name },
+ { zfs_ioc_pool_create, zfs_secpolicy_config, pool_name, B_TRUE },
+ { zfs_ioc_pool_destroy, zfs_secpolicy_config, pool_name, B_FALSE },
+ { zfs_ioc_pool_import, zfs_secpolicy_config, pool_name, B_TRUE },
+ { zfs_ioc_pool_export, zfs_secpolicy_config, pool_name, B_FALSE },
+ { zfs_ioc_pool_configs, zfs_secpolicy_none, no_name, B_FALSE },
+ { zfs_ioc_pool_stats, zfs_secpolicy_read, pool_name, B_FALSE },
+ { zfs_ioc_pool_tryimport, zfs_secpolicy_config, no_name, B_FALSE },
+ { zfs_ioc_pool_scrub, zfs_secpolicy_config, pool_name, B_TRUE },
+ { zfs_ioc_pool_freeze, zfs_secpolicy_config, no_name, B_FALSE },
+ { zfs_ioc_pool_upgrade, zfs_secpolicy_config, pool_name, B_TRUE },
+ { zfs_ioc_pool_get_history, zfs_secpolicy_config, pool_name, B_FALSE },
+ { zfs_ioc_vdev_add, zfs_secpolicy_config, pool_name, B_TRUE },
+ { zfs_ioc_vdev_remove, zfs_secpolicy_config, pool_name, B_TRUE },
+ { zfs_ioc_vdev_set_state, zfs_secpolicy_config, pool_name, B_TRUE },
+ { zfs_ioc_vdev_attach, zfs_secpolicy_config, pool_name, B_TRUE },
+ { zfs_ioc_vdev_detach, zfs_secpolicy_config, pool_name, B_TRUE },
+ { zfs_ioc_vdev_setpath, zfs_secpolicy_config, pool_name, B_FALSE },
+ { zfs_ioc_objset_stats, zfs_secpolicy_read, dataset_name, B_FALSE },
+ { zfs_ioc_dataset_list_next, zfs_secpolicy_read,
+ dataset_name, B_FALSE },
+ { zfs_ioc_snapshot_list_next, zfs_secpolicy_read,
+ dataset_name, B_FALSE },
+ { zfs_ioc_set_prop, zfs_secpolicy_none, dataset_name, B_TRUE },
+ { zfs_ioc_create_minor, zfs_secpolicy_minor, dataset_name, B_FALSE },
+ { zfs_ioc_remove_minor, zfs_secpolicy_minor, dataset_name, B_FALSE },
+ { zfs_ioc_create, zfs_secpolicy_create, dataset_name, B_TRUE },
+ { zfs_ioc_destroy, zfs_secpolicy_destroy, dataset_name, B_TRUE },
+ { zfs_ioc_rollback, zfs_secpolicy_rollback, dataset_name, B_TRUE },
+ { zfs_ioc_rename, zfs_secpolicy_rename, dataset_name, B_TRUE },
+ { zfs_ioc_recvbackup, zfs_secpolicy_receive, dataset_name, B_TRUE },
+ { zfs_ioc_sendbackup, zfs_secpolicy_send, dataset_name, B_TRUE },
+ { zfs_ioc_inject_fault, zfs_secpolicy_inject, no_name, B_FALSE },
+ { zfs_ioc_clear_fault, zfs_secpolicy_inject, no_name, B_FALSE },
+ { zfs_ioc_inject_list_next, zfs_secpolicy_inject, no_name, B_FALSE },
+ { zfs_ioc_error_log, zfs_secpolicy_inject, pool_name, B_FALSE },
+ { zfs_ioc_clear, zfs_secpolicy_config, pool_name, B_TRUE },
+ { zfs_ioc_promote, zfs_secpolicy_promote, dataset_name, B_TRUE },
+ { zfs_ioc_destroy_snaps, zfs_secpolicy_destroy, dataset_name, B_TRUE },
+ { zfs_ioc_snapshot, zfs_secpolicy_snapshot, dataset_name, B_TRUE },
+ { zfs_ioc_dsobj_to_dsname, zfs_secpolicy_config, pool_name, B_FALSE },
+ { zfs_ioc_obj_to_path, zfs_secpolicy_config, no_name, B_FALSE },
+ { zfs_ioc_pool_set_props, zfs_secpolicy_config, pool_name, B_TRUE },
+ { zfs_ioc_pool_get_props, zfs_secpolicy_read, pool_name, B_FALSE },
+ { zfs_ioc_set_fsacl, zfs_secpolicy_fsacl, dataset_name, B_TRUE },
+ { zfs_ioc_get_fsacl, zfs_secpolicy_read, dataset_name, B_FALSE },
+ { zfs_ioc_iscsi_perm_check, zfs_secpolicy_iscsi,
+ dataset_name, B_FALSE },
+ { zfs_ioc_share, zfs_secpolicy_share, dataset_name, B_FALSE }
};
static int
@@ -1680,7 +2194,7 @@ zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp)
if (error == 0) {
zc->zc_cred = (uintptr_t)cr;
zc->zc_dev = dev;
- error = zfs_ioc_vec[vec].zvec_secpolicy(zc->zc_name, cr);
+ error = zfs_ioc_vec[vec].zvec_secpolicy(zc, cr);
}
/*
@@ -1709,8 +2223,11 @@ zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp)
error = zfs_ioc_vec[vec].zvec_func(zc);
rc = xcopyout(zc, (void *)arg, sizeof (zfs_cmd_t));
- if (error == 0)
+ if (error == 0) {
error = rc;
+ if (zfs_ioc_vec[vec].zvec_his_log == B_TRUE)
+ zfs_log_history(zc);
+ }
kmem_free(zc, sizeof (zfs_cmd_t));
return (error);
@@ -1840,6 +2357,7 @@ _init(void)
error = ldi_ident_from_mod(&modlinkage, &zfs_li);
ASSERT(error == 0);
+ mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL);
return (0);
}
@@ -1858,9 +2376,14 @@ _fini(void)
zvol_fini();
zfs_fini();
spa_fini();
+ if (zfs_share_inited) {
+ (void) ddi_modclose(nfs_mod);
+ (void) ddi_modclose(sharefs_mod);
+ }
ldi_ident_release(zfs_li);
zfs_li = NULL;
+ mutex_destroy(&zfs_share_lock);
return (error);
}
diff --git a/usr/src/uts/common/fs/zfs/zfs_vfsops.c b/usr/src/uts/common/fs/zfs/zfs_vfsops.c
index ae26f77e35..40915bfec5 100644
--- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c
+++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c
@@ -31,7 +31,6 @@
#include <sys/sysmacros.h>
#include <sys/kmem.h>
#include <sys/pathname.h>
-#include <sys/acl.h>
#include <sys/vnode.h>
#include <sys/vfs.h>
#include <sys/vfs_opreg.h>
@@ -46,6 +45,7 @@
#include <sys/dmu.h>
#include <sys/dsl_prop.h>
#include <sys/dsl_dataset.h>
+#include <sys/dsl_deleg.h>
#include <sys/spa.h>
#include <sys/zap.h>
#include <sys/varargs.h>
@@ -53,6 +53,7 @@
#include <sys/atomic.h>
#include <sys/mkdev.h>
#include <sys/modctl.h>
+#include <sys/refstr.h>
#include <sys/zfs_ioctl.h>
#include <sys/zfs_ctldir.h>
#include <sys/bootconf.h>
@@ -884,8 +885,42 @@ zfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
osname = spn.pn_path;
- if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
- goto out;
+ /*
+ * Check for mount privilege?
+ *
+ * If we don't have privilege then see if
+ * we have local permission to allow it
+ */
+ error = secpolicy_fs_mount(cr, mvp, vfsp);
+ if (error) {
+ error = dsl_deleg_access(osname, ZFS_DELEG_PERM_MOUNT, cr);
+ if (error == 0) {
+ vattr_t vattr;
+
+ /*
+ * Make sure user is the owner of the mount point
+ * or has sufficient privileges.
+ */
+
+ vattr.va_mask = AT_UID;
+
+ if (VOP_GETATTR(mvp, &vattr, 0, cr)) {
+ goto out;
+ }
+
+ if (error = secpolicy_vnode_owner(cr, vattr.va_uid)) {
+ goto out;
+ }
+
+ if (error = VOP_ACCESS(mvp, VWRITE, 0, cr)) {
+ goto out;
+ }
+
+ secpolicy_fs_mount_clearopts(cr, vfsp);
+ } else {
+ goto out;
+ }
+ }
/*
* Refuse to mount a filesystem if we are in a local zone and the
@@ -992,9 +1027,13 @@ zfs_umount(vfs_t *vfsp, int fflag, cred_t *cr)
zfsvfs_t *zfsvfs = vfsp->vfs_data;
int ret;
- if ((ret = secpolicy_fs_unmount(cr, vfsp)) != 0)
- return (ret);
-
+ ret = secpolicy_fs_unmount(cr, vfsp);
+ if (ret) {
+ ret = dsl_deleg_access((char *)refstr_value(vfsp->vfs_resource),
+ ZFS_DELEG_PERM_MOUNT, cr);
+ if (ret)
+ return (ret);
+ }
(void) dnlc_purge_vfsp(vfsp, 0);
@@ -1003,8 +1042,9 @@ zfs_umount(vfs_t *vfsp, int fflag, cred_t *cr)
* dataset itself.
*/
if (zfsvfs->z_ctldir != NULL &&
- (ret = zfsctl_umount_snapshots(vfsp, fflag, cr)) != 0)
+ (ret = zfsctl_umount_snapshots(vfsp, fflag, cr)) != 0) {
return (ret);
+ }
if (fflag & MS_FORCE) {
vfsp->vfs_flag |= VFS_UNMOUNTED;
diff --git a/usr/src/uts/common/fs/zfs/zfs_vnops.c b/usr/src/uts/common/fs/zfs/zfs_vnops.c
index 8f27afe9ca..8213b1a32e 100644
--- a/usr/src/uts/common/fs/zfs/zfs_vnops.c
+++ b/usr/src/uts/common/fs/zfs/zfs_vnops.c
@@ -1830,6 +1830,7 @@ zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr)
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
znode_phys_t *pzp = zp->z_phys;
int error;
+ uint64_t links;
ZFS_ENTER(zfsvfs);
@@ -1845,7 +1846,11 @@ zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr)
vap->va_gid = zp->z_phys->zp_gid;
vap->va_fsid = zp->z_zfsvfs->z_vfs->vfs_dev;
vap->va_nodeid = zp->z_id;
- vap->va_nlink = MIN(pzp->zp_links, UINT32_MAX); /* nlink_t limit! */
+ if ((vp->v_flag & VROOT) && zfs_show_ctldir(zp))
+ links = pzp->zp_links + 1;
+ else
+ links = pzp->zp_links;
+ vap->va_nlink = MIN(links, UINT32_MAX); /* nlink_t limit! */
vap->va_size = pzp->zp_size;
vap->va_rdev = vp->v_rdev;
vap->va_seq = zp->z_seq;
diff --git a/usr/src/uts/common/fs/zfs/zfs_znode.c b/usr/src/uts/common/fs/zfs/zfs_znode.c
index 4350f45cf6..be7522ee80 100644
--- a/usr/src/uts/common/fs/zfs/zfs_znode.c
+++ b/usr/src/uts/common/fs/zfs/zfs_znode.c
@@ -306,10 +306,6 @@ zfs_init_fs(zfsvfs_t *zfsvfs, znode_t **zpp, cred_t *cr)
ASSERT(zfsvfs->z_root != 0);
/*
- * Create the per mount vop tables.
- */
-
- /*
* Initialize zget mutex's
*/
for (i = 0; i != ZFS_OBJ_MTX_SZ; i++)
@@ -1088,8 +1084,8 @@ zfs_create_fs(objset_t *os, cred_t *cr, dmu_tx_t *tx)
vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE;
vattr.va_type = VDIR;
vattr.va_mode = S_IFDIR|0755;
- vattr.va_uid = 0;
- vattr.va_gid = 3;
+ vattr.va_uid = crgetuid(cr);
+ vattr.va_gid = crgetgid(cr);
rootzp = kmem_cache_alloc(znode_cache, KM_SLEEP);
rootzp->z_zfsvfs = &zfsvfs;
diff --git a/usr/src/uts/common/fs/zfs/zvol.c b/usr/src/uts/common/fs/zfs/zvol.c
index b7562a5267..2d9cc65ef8 100644
--- a/usr/src/uts/common/fs/zfs/zvol.c
+++ b/usr/src/uts/common/fs/zfs/zvol.c
@@ -219,16 +219,17 @@ zvol_minor_lookup(const char *name)
return (zv);
}
+/* ARGSUSED */
void
-zvol_create_cb(objset_t *os, void *arg, dmu_tx_t *tx)
+zvol_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
{
- zfs_create_data_t *zc = arg;
+ nvlist_t *nvprops = arg;
int error;
uint64_t volblocksize, volsize;
- VERIFY(nvlist_lookup_uint64(zc->zc_props,
+ VERIFY(nvlist_lookup_uint64(nvprops,
zfs_prop_to_name(ZFS_PROP_VOLSIZE), &volsize) == 0);
- if (nvlist_lookup_uint64(zc->zc_props,
+ if (nvlist_lookup_uint64(nvprops,
zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), &volblocksize) != 0)
volblocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
@@ -236,9 +237,9 @@ zvol_create_cb(objset_t *os, void *arg, dmu_tx_t *tx)
* These properites must be removed from the list so the generic
* property setting step won't apply to them.
*/
- VERIFY(nvlist_remove_all(zc->zc_props,
+ VERIFY(nvlist_remove_all(nvprops,
zfs_prop_to_name(ZFS_PROP_VOLSIZE)) == 0);
- (void) nvlist_remove_all(zc->zc_props,
+ (void) nvlist_remove_all(nvprops,
zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE));
error = dmu_object_claim(os, ZVOL_OBJ, DMU_OT_ZVOL, volblocksize,
diff --git a/usr/src/uts/common/os/policy.c b/usr/src/uts/common/os/policy.c
index 7025453518..a49226267d 100644
--- a/usr/src/uts/common/os/policy.c
+++ b/usr/src/uts/common/os/policy.c
@@ -296,7 +296,7 @@ priv_policy_errmsg(const cred_t *cr, int priv, const char *msg)
}
curthread->t_pdmsg = mprintf(fmt, cmd, me->p_pid, pname,
- cr->cr_uid, curthread->t_sysnum, msg, sym, off);
+ cr->cr_uid, curthread->t_sysnum, msg, sym, off);
curthread->t_post_sys = 1;
} else {
@@ -412,7 +412,7 @@ secpolicy_require_set(const cred_t *cr, const priv_set_t *req, const char *msg)
priv_set_t pset;
if (req == PRIV_FULLSET ? HAS_ALLPRIVS(cr) : priv_issubset(req,
- &CR_OEPRIV(cr))) {
+ &CR_OEPRIV(cr))) {
return (0);
}
@@ -438,7 +438,7 @@ secpolicy_require_set(const cred_t *cr, const priv_set_t *req, const char *msg)
if (pfound != -1) {
/* Multiple missing privs */
priv_policy_errmsg(cr, PRIV_MULTIPLE,
- msg);
+ msg);
return (EACCES);
}
pfound = priv;
@@ -598,6 +598,37 @@ secpolicy_fs_common(cred_t *cr, vnode_t *mvp, const vfs_t *vfsp,
return (PRIV_POLICY(cr, PRIV_SYS_MOUNT, allzone, EPERM, NULL));
}
+void
+secpolicy_fs_mount_clearopts(cred_t *cr, struct vfs *vfsp)
+{
+ boolean_t amsuper = HAS_ALLZONEPRIVS(cr);
+
+ /*
+ * check; if we don't have either "nosuid" or
+ * both "nosetuid" and "nodevices", then we add
+ * "nosuid"; this depends on how the current
+ * implementation works (it first checks nosuid). In a
+ * zone, a user with all zone privileges can mount with
+ * "setuid" but never with "devices".
+ */
+ if (!vfs_optionisset(vfsp, MNTOPT_NOSUID, NULL) &&
+ (!vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL) ||
+ !vfs_optionisset(vfsp, MNTOPT_NOSETUID, NULL))) {
+ if (crgetzoneid(cr) == GLOBAL_ZONEID || !amsuper)
+ vfs_setmntopt(vfsp, MNTOPT_NOSUID, NULL, 0);
+ else
+ vfs_setmntopt(vfsp, MNTOPT_NODEVICES, NULL, 0);
+ }
+ /*
+ * If we're not the local super user, we set the "restrict"
+ * option to indicate to automountd that this mount should
+ * be handled with care.
+ */
+ if (!amsuper)
+ vfs_setmntopt(vfsp, MNTOPT_RESTRICT, NULL, 0);
+
+}
+
extern vnode_t *rootvp;
extern vfs_t *rootvfs;
@@ -621,33 +652,9 @@ secpolicy_fs_mount(cred_t *cr, vnode_t *mvp, struct vfs *vfsp)
error = secpolicy_fs_common(cr, mvp, vfsp, &needoptchk);
if (error == 0 && needoptchk) {
- boolean_t amsuper = HAS_ALLZONEPRIVS(cr);
-
- /*
- * Third check; if we don't have either "nosuid" or
- * both "nosetuid" and "nodevices", then we add
- * "nosuid"; this depends on how the current
- * implementation works (it first checks nosuid). In a
- * zone, a user with all zone privileges can mount with
- * "setuid" but never with "devices".
- */
- if (!vfs_optionisset(vfsp, MNTOPT_NOSUID, NULL) &&
- (!vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL) ||
- !vfs_optionisset(vfsp, MNTOPT_NOSETUID, NULL))) {
- if (crgetzoneid(cr) == GLOBAL_ZONEID || !amsuper)
- vfs_setmntopt(vfsp, MNTOPT_NOSUID, NULL, 0);
- else
- vfs_setmntopt(vfsp, MNTOPT_NODEVICES, NULL, 0);
- }
- /*
- * If we're not the local super user, we set the "restrict"
- * option to indicate to automountd that this mount should
- * be handled with care.
- */
- if (!amsuper)
- vfs_setmntopt(vfsp, MNTOPT_RESTRICT, NULL, 0);
-
+ secpolicy_fs_mount_clearopts(cr, vfsp);
}
+
return (error);
}
@@ -943,7 +950,7 @@ secpolicy_setid_setsticky_clear(vnode_t *vp, vattr_t *vap, const vattr_t *ovap,
*/
if (vp->v_type != VDIR && (vap->va_mode & S_ISVTX) != 0 &&
secpolicy_vnode_stky_modify(cr) != 0) {
- vap->va_mode &= ~S_ISVTX;
+ vap->va_mode &= ~S_ISVTX;
}
/*
@@ -952,7 +959,7 @@ secpolicy_setid_setsticky_clear(vnode_t *vp, vattr_t *vap, const vattr_t *ovap,
*/
if ((vap->va_mode & S_ISGID) != 0 &&
secpolicy_vnode_setids_setgids(cr, ovap->va_gid) != 0) {
- vap->va_mode &= ~S_ISGID;
+ vap->va_mode &= ~S_ISGID;
}
return (0);
diff --git a/usr/src/uts/common/sys/fs/zfs.h b/usr/src/uts/common/sys/fs/zfs.h
index f2d72f9ae7..1023e31c16 100644
--- a/usr/src/uts/common/sys/fs/zfs.h
+++ b/usr/src/uts/common/sys/fs/zfs.h
@@ -28,8 +28,6 @@
#pragma ident "%Z%%M% %I% %E% SMI"
-#include <sys/types.h>
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -98,6 +96,7 @@ typedef enum {
ZFS_PROP_COPIES,
ZPOOL_PROP_BOOTFS,
ZPOOL_PROP_AUTOREPLACE,
+ ZPOOL_PROP_DELEGATION,
ZPOOL_PROP_NAME
} zfs_prop_t;
@@ -119,6 +118,32 @@ typedef enum {
#define ZFS_SRC_ALL 0x1f
+typedef enum {
+ ZFS_DELEG_WHO_UNKNOWN = 0,
+ ZFS_DELEG_USER = 'u',
+ ZFS_DELEG_USER_SETS = 'U',
+ ZFS_DELEG_GROUP = 'g',
+ ZFS_DELEG_GROUP_SETS = 'G',
+ ZFS_DELEG_EVERYONE = 'e',
+ ZFS_DELEG_EVERYONE_SETS = 'E',
+ ZFS_DELEG_CREATE = 'c',
+ ZFS_DELEG_CREATE_SETS = 'C',
+ ZFS_DELEG_NAMED_SET = 's',
+ ZFS_DELEG_NAMED_SET_SETS = 'S'
+} zfs_deleg_who_type_t;
+
+typedef enum {
+ ZFS_DELEG_NONE = 0,
+ ZFS_DELEG_PERM_LOCAL = 1,
+ ZFS_DELEG_PERM_DESCENDENT = 2,
+ ZFS_DELEG_PERM_LOCALDESCENDENT = 3,
+ ZFS_DELEG_PERM_CREATE = 4
+} zfs_deleg_inherit_t;
+
+#define ZFS_DELEG_PERM_UID "uid"
+#define ZFS_DELEG_PERM_GID "gid"
+#define ZFS_DELEG_PERM_GROUPS "groups"
+
/*
* The following functions are shared between libzfs and the kernel.
*/
@@ -134,6 +159,7 @@ int zfs_prop_inheritable(zfs_prop_t);
int zfs_prop_string_to_index(zfs_prop_t, const char *, uint64_t *);
int zfs_prop_index_to_string(zfs_prop_t, uint64_t, const char **);
uint64_t zpool_prop_default_numeric(zpool_prop_t);
+const char *zfs_prop_perm(zfs_prop_t);
/*
* Property Iterator
@@ -153,13 +179,14 @@ extern zpool_prop_t zpool_prop_iter(zpool_prop_f, void *, boolean_t);
#define ZFS_VERSION_5 5ULL
#define ZFS_VERSION_6 6ULL
#define ZFS_VERSION_7 7ULL
+#define ZFS_VERSION_8 8ULL
/*
* When bumping up ZFS_VERSION, make sure GRUB ZFS understand the on-disk
* format change. Go to usr/src/grub/grub-0.95/stage2/{zfs-include/, fsys_zfs*},
* and do the appropriate changes.
*/
-#define ZFS_VERSION ZFS_VERSION_7
-#define ZFS_VERSION_STRING "7"
+#define ZFS_VERSION ZFS_VERSION_8
+#define ZFS_VERSION_STRING "8"
/*
* Symbolic names for the changes that caused a ZFS_VERSION switch.
@@ -183,6 +210,7 @@ extern zpool_prop_t zpool_prop_iter(zpool_prop_f, void *, boolean_t);
#define ZFS_VERSION_GZIP_COMPRESSION ZFS_VERSION_5
#define ZFS_VERSION_BOOTFS ZFS_VERSION_6
#define ZFS_VERSION_SLOGS ZFS_VERSION_7
+#define ZFS_VERSION_DELEGATED_PERMS ZFS_VERSION_8
/*
* The following are configuration names used in the nvlist describing a pool's
@@ -391,7 +419,6 @@ typedef enum zfs_ioc {
ZFS_IOC_POOL_FREEZE,
ZFS_IOC_POOL_UPGRADE,
ZFS_IOC_POOL_GET_HISTORY,
- ZFS_IOC_POOL_LOG_HISTORY,
ZFS_IOC_VDEV_ADD,
ZFS_IOC_VDEV_REMOVE,
ZFS_IOC_VDEV_SET_STATE,
@@ -421,7 +448,11 @@ typedef enum zfs_ioc {
ZFS_IOC_DSOBJ_TO_DSNAME,
ZFS_IOC_OBJ_TO_PATH,
ZFS_IOC_POOL_SET_PROPS,
- ZFS_IOC_POOL_GET_PROPS
+ ZFS_IOC_POOL_GET_PROPS,
+ ZFS_IOC_SET_FSACL,
+ ZFS_IOC_GET_FSACL,
+ ZFS_IOC_ISCSI_PERM_CHECK,
+ ZFS_IOC_SHARE
} zfs_ioc_t;
/*
@@ -450,6 +481,12 @@ typedef enum {
#define ZPOOL_HIST_RECORD "history record"
#define ZPOOL_HIST_TIME "history time"
#define ZPOOL_HIST_CMD "history command"
+#define ZPOOL_HIST_WHO "history who"
+#define ZPOOL_HIST_ZONE "history zone"
+#define ZPOOL_HIST_HOST "history hostname"
+#define ZPOOL_HIST_TXG "history txg"
+#define ZPOOL_HIST_INT_EVENT "history internal event"
+#define ZPOOL_HIST_INT_STR "history internal str"
/*
* Flags for ZFS_IOC_VDEV_SET_STATE
@@ -484,6 +521,44 @@ typedef enum {
#define ZFS_EV_VDEV_PATH "vdev_path"
#define ZFS_EV_VDEV_GUID "vdev_guid"
+typedef enum history_internal_events {
+ LOG_NO_EVENT = 0,
+ LOG_POOL_CREATE,
+ LOG_POOL_VDEV_ADD,
+ LOG_POOL_REMOVE,
+ LOG_POOL_DESTROY,
+ LOG_POOL_EXPORT,
+ LOG_POOL_IMPORT,
+ LOG_POOL_VDEV_ATTACH,
+ LOG_POOL_VDEV_REPLACE,
+ LOG_POOL_VDEV_DETACH,
+ LOG_POOL_VDEV_ONLINE,
+ LOG_POOL_VDEV_OFFLINE,
+ LOG_POOL_UPGRADE,
+ LOG_POOL_CLEAR,
+ LOG_POOL_SCRUB,
+ LOG_POOL_PROPSET,
+ LOG_DS_CREATE,
+ LOG_DS_CLONE,
+ LOG_DS_DESTROY,
+ LOG_DS_DESTROY_BEGIN,
+ LOG_DS_INHERIT,
+ LOG_DS_PROPSET,
+ LOG_DS_QUOTA,
+ LOG_DS_PERM_UPDATE,
+ LOG_DS_PERM_REMOVE,
+ LOG_DS_PERM_WHO_REMOVE,
+ LOG_DS_PROMOTE,
+ LOG_DS_RECEIVE,
+ LOG_DS_RENAME,
+ LOG_DS_RESERVATION,
+ LOG_DS_REPLAY_INC_SYNC,
+ LOG_DS_REPLAY_FULL_SYNC,
+ LOG_DS_ROLLBACK,
+ LOG_DS_SNAPSHOT,
+ LOG_END
+} history_internal_events_t;
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/sys/policy.h b/usr/src/uts/common/sys/policy.h
index 4bea4a5c0c..06dd3edd87 100644
--- a/usr/src/uts/common/sys/policy.h
+++ b/usr/src/uts/common/sys/policy.h
@@ -149,6 +149,7 @@ int secpolicy_vnode_stky_modify(const cred_t *);
int secpolicy_zinject(const cred_t *);
int secpolicy_zfs(const cred_t *);
void secpolicy_setid_clear(vattr_t *, cred_t *);
+void secpolicy_fs_mount_clearopts(cred_t *, struct vfs *);
int secpolicy_setid_setsticky_clear(vnode_t *, vattr_t *,
const vattr_t *, cred_t *);