diff options
Diffstat (limited to 'usr/src')
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 *); |