diff options
author | marks <none@none> | 2007-06-26 07:44:24 -0700 |
---|---|---|
committer | marks <none@none> | 2007-06-26 07:44:24 -0700 |
commit | ecd6cf800b63704be73fb264c3f5b6e0dafc068d (patch) | |
tree | ec83d040bc56ee0a46e9533a645e832e58d8ba86 /usr/src/lib | |
parent | 8ac1b93fdff76ea81638299d410b2d474240ee2b (diff) | |
download | illumos-joyent-ecd6cf800b63704be73fb264c3f5b6e0dafc068d.tar.gz |
PSARC/2006/465 ZFS Delegated Administration
PSARC/2006/577 zpool property to disable delegation
PSARC/2006/625 Enhancements to zpool history
PSARC/2007/228 ZFS delegation amendments
PSARC/2007/295 ZFS Delegated Administration Addendum
6280676 restore "owner" property
6349470 investigate non-root restore/backup
6572465 'zpool set bootfs=...' records history as 'zfs set bootfs=...'
Diffstat (limited to 'usr/src/lib')
24 files changed, 1414 insertions, 144 deletions
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 } |