summaryrefslogtreecommitdiff
path: root/usr/src/lib
diff options
context:
space:
mode:
authorMatthew Ahrens <Matthew.Ahrens@Sun.COM>2009-04-18 13:41:47 -0700
committerMatthew Ahrens <Matthew.Ahrens@Sun.COM>2009-04-18 13:41:47 -0700
commit148434217c040ea38dc844384f6ba68d9b325906 (patch)
treeed04bcf57eac8c6bd1eacab471a79499bac86504 /usr/src/lib
parenteead73cfdc384282a25862c27aed73c597fc10a9 (diff)
downloadillumos-gate-148434217c040ea38dc844384f6ba68d9b325906.tar.gz
PSARC/2009/204 ZFS user/group quotas & space accounting
6501037 want user/group quotas on ZFS 6830813 zfs list -t all fails assertion 6827260 assertion failed in arc_read(): hdr == pbuf->b_hdr 6815592 panic: No such hold X on refcount Y from zfs_znode_move 6759986 zfs list shows temporary %clone when doing online zfs recv
Diffstat (limited to 'usr/src/lib')
-rw-r--r--usr/src/lib/Makefile6
-rw-r--r--usr/src/lib/libzfs/Makefile.com7
-rw-r--r--usr/src/lib/libzfs/common/libzfs.h19
-rw-r--r--usr/src/lib/libzfs/common/libzfs_changelist.c4
-rw-r--r--usr/src/lib/libzfs/common/libzfs_dataset.c1136
-rw-r--r--usr/src/lib/libzfs/common/libzfs_graph.c11
-rw-r--r--usr/src/lib/libzfs/common/libzfs_sendrecv.c20
-rw-r--r--usr/src/lib/libzfs/common/libzfs_util.c2
-rw-r--r--usr/src/lib/libzfs/common/mapfile-vers12
-rw-r--r--usr/src/lib/pyzfs/Makefile54
-rw-r--r--usr/src/lib/pyzfs/Makefile.com65
-rw-r--r--usr/src/lib/pyzfs/common/__init__.py28
-rw-r--r--usr/src/lib/pyzfs/common/allow.py394
-rw-r--r--usr/src/lib/pyzfs/common/dataset.py205
-rw-r--r--usr/src/lib/pyzfs/common/groupspace.py29
-rw-r--r--usr/src/lib/pyzfs/common/ioctl.c599
-rw-r--r--usr/src/lib/pyzfs/common/mapfile-vers45
-rw-r--r--usr/src/lib/pyzfs/common/unallow.py28
-rw-r--r--usr/src/lib/pyzfs/common/userspace.py277
-rw-r--r--usr/src/lib/pyzfs/common/util.py138
-rw-r--r--usr/src/lib/pyzfs/i386/Makefile27
-rw-r--r--usr/src/lib/pyzfs/sparc/Makefile27
22 files changed, 2265 insertions, 868 deletions
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index 9ca22955f7..fc576c86fb 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -238,6 +238,7 @@ SUBDIRS += \
libzpool \
libzfs \
libzfs_jni \
+ pyzfs \
libmapid \
brand \
policykit \
@@ -346,6 +347,7 @@ MSGSUBDIRS= \
mms \
mpss \
pam_modules \
+ pyzfs \
rpcsec_gss
$(CLOSED_BUILD)MSGSUBDIRS += \
$(CLOSED)/lib/smartcard
@@ -601,7 +603,8 @@ librestart: libuutil libscf
pkcs11: libcryptoutil
print: libldap5
udapl/udapl_tavor: udapl/libdat
-libzfs: libdevinfo libdevid libgen libnvpair libuutil libiscsitgt
+libzfs: libdevinfo libdevid libgen libnvpair libuutil libiscsitgt \
+ libavl libefi libidmap libsec
libzfs_jni: libdiskmgt libnvpair libzfs
libzpool: libavl libumem libnvpair
libsec: libavl libidmap
@@ -615,6 +618,7 @@ libvscan: libscf
scsi: libnvpair
mpapi: libpthread libdevinfo libsysevent libnvpair
libgrubmgmt: libdevinfo libzfs libfstyp
+pyzfs: libnvpair libsec libidmap libzfs
#
# The reason this rule checks for the existence of the
diff --git a/usr/src/lib/libzfs/Makefile.com b/usr/src/lib/libzfs/Makefile.com
index 742bf3610f..6602481348 100644
--- a/usr/src/lib/libzfs/Makefile.com
+++ b/usr/src/lib/libzfs/Makefile.com
@@ -19,11 +19,9 @@
# CDDL HEADER END
#
#
-# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
-#
LIBRARY= libzfs.a
VERS= .1
@@ -50,7 +48,8 @@ INCS += -I../../../common/zfs
C99MODE= -xc99=%all
C99LMODE= -Xc99=%all
-LDLIBS += -lc -lm -ldevinfo -ldevid -lgen -lnvpair -luutil -lavl -lefi
+LDLIBS += -lc -lm -ldevinfo -ldevid -lgen -lnvpair -luutil -lavl -lefi \
+ -lidmap -lsec
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 8c83d5d946..a41005b255 100644
--- a/usr/src/lib/libzfs/common/libzfs.h
+++ b/usr/src/lib/libzfs/common/libzfs.h
@@ -370,6 +370,8 @@ extern int zfs_prop_get(zfs_handle_t *, zfs_prop_t, char *, size_t,
zprop_source_t *, char *, size_t, boolean_t);
extern int zfs_prop_get_numeric(zfs_handle_t *, zfs_prop_t, uint64_t *,
zprop_source_t *, char *, size_t);
+extern int zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
+ char *propbuf, int proplen, boolean_t literal);
extern uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t);
extern int zfs_prop_inherit(zfs_handle_t *, const char *);
extern const char *zfs_prop_values(zfs_prop_t);
@@ -457,6 +459,12 @@ extern int zfs_send(zfs_handle_t *, const char *, const char *,
boolean_t, boolean_t, boolean_t, boolean_t, int);
extern int zfs_promote(zfs_handle_t *);
+typedef void (*zfs_userspace_cb_t)(void *arg, const char *domain,
+ uid_t rid, uint64_t space);
+
+extern int zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
+ zfs_userspace_cb_t func, void *arg);
+
typedef struct recvflags {
/* print informational messages (ie, -v was specified) */
int verbose : 1;
@@ -495,17 +503,6 @@ extern boolean_t zfs_dataset_exists(libzfs_handle_t *, const char *,
extern int zfs_spa_version(zfs_handle_t *, int *);
/*
- * 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 *);
-extern void zfs_deleg_permissions(void);
-
-/*
* Mount support functions.
*/
extern boolean_t is_mounted(libzfs_handle_t *, const char *special, char **);
diff --git a/usr/src/lib/libzfs/common/libzfs_changelist.c b/usr/src/lib/libzfs/common/libzfs_changelist.c
index b905bc6cb6..de87a8c692 100644
--- a/usr/src/lib/libzfs/common/libzfs_changelist.c
+++ b/usr/src/lib/libzfs/common/libzfs_changelist.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Portions Copyright 2007 Ramprakash Jelari
@@ -621,8 +621,6 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags,
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
} else if (prop == ZFS_PROP_VOLSIZE) {
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
- } else if (prop == ZFS_PROP_VERSION) {
- clp->cl_prop = ZFS_PROP_MOUNTPOINT;
} else {
clp->cl_prop = prop;
}
diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c
index 41f1734734..ad2a74861b 100644
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c
@@ -45,6 +45,8 @@
#include <grp.h>
#include <stddef.h>
#include <ucred.h>
+#include <idmap.h>
+#include <aclutils.h>
#include <sys/spa.h>
#include <sys/zap.h>
@@ -56,6 +58,8 @@
#include "zfs_deleg.h"
static int zvol_create_link_common(libzfs_handle_t *, const char *, int);
+static int userquota_propname_decode(const char *propname, boolean_t zoned,
+ zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp);
/*
* Given a single type (not a mask of types), return the type in a human
@@ -121,8 +125,8 @@ path_to_str(const char *path, int types)
/*
* Validate a ZFS path. This is used even before trying to open the dataset, to
- * provide a more meaningful error message. We place a more useful message in
- * 'buf' detailing exactly why the name was not valid.
+ * provide a more meaningful error message. We call zfs_error_aux() to
+ * explain exactly why the name was not valid.
*/
static int
zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
@@ -346,6 +350,10 @@ put_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc)
return (-1);
}
+ /*
+ * XXX Why do we store the user props separately, in addition to
+ * storing them in zfs_props?
+ */
if ((userprops = process_user_props(zhp, allprops)) == NULL) {
nvlist_free(allprops);
return (-1);
@@ -772,23 +780,18 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
return (NULL);
}
+ /*
+ * Make sure this property is valid and applies to this type.
+ */
+
elem = NULL;
while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
const char *propname = nvpair_name(elem);
- /*
- * Make sure this property is valid and applies to this type.
- */
- if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) {
- if (!zfs_prop_user(propname)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid property '%s'"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
+ prop = zfs_name_to_prop(propname);
+ if (prop == ZPROP_INVAL && zfs_prop_user(propname)) {
/*
- * If this is a user property, make sure it's a
+ * This is a user property: make sure it's a
* string, and that it's less than ZAP_MAXNAMELEN.
*/
if (nvpair_type(elem) != DATA_TYPE_STRING) {
@@ -814,6 +817,10 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
continue;
}
+ /*
+ * Currently, only user properties can be modified on
+ * snapshots.
+ */
if (type == ZFS_TYPE_SNAPSHOT) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"this property can not be modified for snapshots"));
@@ -821,6 +828,80 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
goto error;
}
+ if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) {
+ zfs_userquota_prop_t uqtype;
+ char newpropname[128];
+ char domain[128];
+ uint64_t rid;
+ uint64_t valary[3];
+
+ if (userquota_propname_decode(propname, zoned,
+ &uqtype, domain, sizeof (domain), &rid) != 0) {
+ zfs_error_aux(hdl,
+ dgettext(TEXT_DOMAIN,
+ "'%s' has an invalid user/group name"),
+ propname);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+
+ if (uqtype != ZFS_PROP_USERQUOTA &&
+ uqtype != ZFS_PROP_GROUPQUOTA) {
+ zfs_error_aux(hdl,
+ dgettext(TEXT_DOMAIN, "'%s' is readonly"),
+ propname);
+ (void) zfs_error(hdl, EZFS_PROPREADONLY,
+ errbuf);
+ goto error;
+ }
+
+ if (nvpair_type(elem) == DATA_TYPE_STRING) {
+ (void) nvpair_value_string(elem, &strval);
+ if (strcmp(strval, "none") == 0) {
+ intval = 0;
+ } else if (zfs_nicestrtonum(hdl,
+ strval, &intval) != 0) {
+ (void) zfs_error(hdl,
+ EZFS_BADPROP, errbuf);
+ goto error;
+ }
+ } else if (nvpair_type(elem) ==
+ DATA_TYPE_UINT64) {
+ (void) nvpair_value_uint64(elem, &intval);
+ if (intval == 0) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "use 'none' to disable "
+ "userquota/groupquota"));
+ goto error;
+ }
+ } else {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "'%s' must be a number"), propname);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+
+ (void) snprintf(newpropname, sizeof (newpropname),
+ "%s%s", zfs_userquota_prop_prefixes[uqtype],
+ domain);
+ valary[0] = uqtype;
+ valary[1] = rid;
+ valary[2] = intval;
+ if (nvlist_add_uint64_array(ret, newpropname,
+ valary, 3) != 0) {
+ (void) no_memory(hdl);
+ goto error;
+ }
+ continue;
+ }
+
+ if (prop == ZPROP_INVAL) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "invalid property '%s'"), propname);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+
if (!zfs_prop_valid_for_type(prop, type)) {
zfs_error_aux(hdl,
dgettext(TEXT_DOMAIN, "'%s' does not "
@@ -960,7 +1041,7 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
} else if (getzoneid() != GLOBAL_ZONEID) {
/*
* If zoned property is 'off', this must be in
- * a globle zone. If not, something is wrong.
+ * a global zone. If not, something is wrong.
*/
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"'%s' cannot be set while dataset "
@@ -1144,808 +1225,6 @@ 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: common 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, *perm;
- int error;
-
- *nvp = NULL;
-
- if (perms) {
- 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) {
- nvlist_free(who_nvp);
- if (perms_nvp)
- nvlist_free(perms_nvp);
- if (sets_nvp)
- nvlist_free(sets_nvp);
-
- 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.
- */
- for (perm = strtok(perms, ","); perm; perm = strtok(NULL, ",")) {
- const char *perm_canonical = zfs_deleg_canonicalize_perm(perm);
-
- if (perm_canonical) {
- verify(nvlist_add_boolean(perms_nvp,
- perm_canonical) == 0);
- } else if (perm[0] == '@') {
- verify(nvlist_add_boolean(sets_nvp, perm) == 0);
- } else {
- nvlist_free(who_nvp);
- nvlist_free(perms_nvp);
- nvlist_free(sets_nvp);
- return (zfs_error(zhp->zfs_hdl, EZFS_BADPERM, perm));
- }
- }
-
- if (whostr && who_type != ZFS_DELEG_CREATE) {
- who_tok = strtok(whostr, ",");
- if (who_tok == NULL) {
- nvlist_free(who_nvp);
- if (perms_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);
- if (perms_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;
- 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))
- 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 = NULL;
-
- while ((permnode = avl_destroy_nodes(tree, &cookie)) != NULL)
- free(permnode);
- avl_destroy(tree);
-}
-
-static void
-zfs_destroy_tree(avl_tree_t *tree)
-{
- zfs_allow_node_t *allownode;
- void *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);
- free(allownode);
- }
- avl_destroy(tree);
-}
-
-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);
-}
-
-static char *
-zfs_deleg_perm_note(zfs_deleg_note_t note)
-{
- /*
- * Don't put newlines on end of lines
- */
- switch (note) {
- case ZFS_DELEG_NOTE_CREATE:
- return (dgettext(TEXT_DOMAIN,
- "Must also have the 'mount' ability"));
- case ZFS_DELEG_NOTE_DESTROY:
- return (dgettext(TEXT_DOMAIN,
- "Must also have the 'mount' ability"));
- case ZFS_DELEG_NOTE_SNAPSHOT:
- return (dgettext(TEXT_DOMAIN,
- "Must also have the 'mount' ability"));
- case ZFS_DELEG_NOTE_ROLLBACK:
- return (dgettext(TEXT_DOMAIN,
- "Must also have the 'mount' ability"));
- case ZFS_DELEG_NOTE_CLONE:
- return (dgettext(TEXT_DOMAIN, "Must also have the 'create' "
- "ability and 'mount'\n"
- "\t\t\t\tability in the origin file system"));
- case ZFS_DELEG_NOTE_PROMOTE:
- return (dgettext(TEXT_DOMAIN, "Must also have the 'mount'\n"
- "\t\t\t\tand 'promote' ability in the origin file system"));
- case ZFS_DELEG_NOTE_RENAME:
- return (dgettext(TEXT_DOMAIN, "Must also have the 'mount' "
- "and 'create' \n\t\t\t\tability in the new parent"));
- case ZFS_DELEG_NOTE_RECEIVE:
- return (dgettext(TEXT_DOMAIN, "Must also have the 'mount'"
- " and 'create' ability"));
- case ZFS_DELEG_NOTE_USERPROP:
- return (dgettext(TEXT_DOMAIN,
- "Allows changing any user property"));
- case ZFS_DELEG_NOTE_ALLOW:
- return (dgettext(TEXT_DOMAIN,
- "Must also have the permission that is being\n"
- "\t\t\t\tallowed"));
- case ZFS_DELEG_NOTE_MOUNT:
- return (dgettext(TEXT_DOMAIN,
- "Allows mount/umount of ZFS datasets"));
- case ZFS_DELEG_NOTE_SHARE:
- return (dgettext(TEXT_DOMAIN,
- "Allows sharing file systems over NFS or SMB\n"
- "\t\t\t\tprotocols"));
- case ZFS_DELEG_NOTE_NONE:
- default:
- return (dgettext(TEXT_DOMAIN, ""));
- }
-}
-
-typedef enum {
- ZFS_DELEG_SUBCOMMAND,
- ZFS_DELEG_PROP,
- ZFS_DELEG_OTHER
-} zfs_deleg_perm_type_t;
-
-/*
- * is the permission a subcommand or other?
- */
-zfs_deleg_perm_type_t
-zfs_deleg_perm_type(const char *perm)
-{
- if (strcmp(perm, "userprop") == 0)
- return (ZFS_DELEG_OTHER);
- else
- return (ZFS_DELEG_SUBCOMMAND);
-}
-
-static char *
-zfs_deleg_perm_type_str(zfs_deleg_perm_type_t type)
-{
- switch (type) {
- case ZFS_DELEG_SUBCOMMAND:
- return (dgettext(TEXT_DOMAIN, "subcommand"));
- case ZFS_DELEG_PROP:
- return (dgettext(TEXT_DOMAIN, "property"));
- case ZFS_DELEG_OTHER:
- return (dgettext(TEXT_DOMAIN, "other"));
- }
- return ("");
-}
-
-/*ARGSUSED*/
-static int
-zfs_deleg_prop_cb(int prop, void *cb)
-{
- if (zfs_prop_delegatable(prop))
- (void) fprintf(stderr, "%-15s %-15s\n", zfs_prop_to_name(prop),
- zfs_deleg_perm_type_str(ZFS_DELEG_PROP));
-
- return (ZPROP_CONT);
-}
-
-void
-zfs_deleg_permissions(void)
-{
- int i;
-
- (void) fprintf(stderr, "\n%-15s %-15s\t%s\n\n", "NAME",
- "TYPE", "NOTES");
-
- /*
- * First print out the subcommands
- */
- for (i = 0; zfs_deleg_perm_tab[i].z_perm != NULL; i++) {
- (void) fprintf(stderr, "%-15s %-15s\t%s\n",
- zfs_deleg_perm_tab[i].z_perm,
- zfs_deleg_perm_type_str(
- zfs_deleg_perm_type(zfs_deleg_perm_tab[i].z_perm)),
- zfs_deleg_perm_note(zfs_deleg_perm_tab[i].z_note));
- }
-
- (void) zprop_iter(zfs_deleg_prop_cb, NULL, B_FALSE, B_TRUE,
- ZFS_TYPE_DATASET|ZFS_TYPE_VOLUME);
-}
-
/*
* Given a property name and value, set the property for the given dataset.
*/
@@ -2422,7 +1701,7 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
case PROP_TYPE_INDEX:
*val = getprop_uint64(zhp, prop, source);
/*
- * If we tried to use a defalut value for a
+ * If we tried to use a default value for a
* readonly property, it means that it was not
* present; return an error.
*/
@@ -2716,7 +1995,7 @@ zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val)
{
char buf[64];
- zfs_nicenum(val, buf, sizeof (buf));
+ (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val);
return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf));
}
@@ -2749,6 +2028,179 @@ zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value,
return (0);
}
+static int
+idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser,
+ char **domainp, idmap_rid_t *ridp)
+{
+ idmap_handle_t *idmap_hdl = NULL;
+ idmap_get_handle_t *get_hdl = NULL;
+ idmap_stat status;
+ int err = EINVAL;
+
+ if (idmap_init(&idmap_hdl) != IDMAP_SUCCESS)
+ goto out;
+ if (idmap_get_create(idmap_hdl, &get_hdl) != IDMAP_SUCCESS)
+ goto out;
+
+ if (isuser) {
+ err = idmap_get_sidbyuid(get_hdl, id,
+ IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);
+ } else {
+ err = idmap_get_sidbygid(get_hdl, id,
+ IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);
+ }
+ if (err == IDMAP_SUCCESS &&
+ idmap_get_mappings(get_hdl) == IDMAP_SUCCESS &&
+ status == IDMAP_SUCCESS)
+ err = 0;
+ else
+ err = EINVAL;
+out:
+ if (get_hdl)
+ idmap_get_destroy(get_hdl);
+ if (idmap_hdl)
+ (void) idmap_fini(idmap_hdl);
+ return (err);
+}
+
+/*
+ * convert the propname into parameters needed by kernel
+ * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829
+ * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789
+ */
+static int
+userquota_propname_decode(const char *propname, boolean_t zoned,
+ zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp)
+{
+ zfs_userquota_prop_t type;
+ char *cp, *end;
+ boolean_t isuser;
+
+ domain[0] = '\0';
+
+ /* Figure out the property type ({user|group}{quota|space}) */
+ for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) {
+ if (strncmp(propname, zfs_userquota_prop_prefixes[type],
+ strlen(zfs_userquota_prop_prefixes[type])) == 0)
+ break;
+ }
+ if (type == ZFS_NUM_USERQUOTA_PROPS)
+ return (EINVAL);
+ *typep = type;
+
+ isuser = (type == ZFS_PROP_USERQUOTA ||
+ type == ZFS_PROP_USERUSED);
+
+ cp = strchr(propname, '@') + 1;
+
+ if (strchr(cp, '@')) {
+ /*
+ * It's a SID name (eg "user@domain") that needs to be
+ * turned into S-1-domainID-RID. There should be a
+ * better way to do this, but for now just translate it
+ * to the (possibly ephemeral) uid and then back to the
+ * SID. This is like getsidname(noresolve=TRUE).
+ */
+ uid_t id;
+ idmap_rid_t rid;
+ char *mapdomain;
+
+ if (zoned && getzoneid() == GLOBAL_ZONEID)
+ return (ENOENT);
+ if (sid_to_id(cp, isuser, &id) != 0)
+ return (ENOENT);
+ if (idmap_id_to_numeric_domain_rid(id, isuser,
+ &mapdomain, &rid) != 0)
+ return (ENOENT);
+ (void) strlcpy(domain, mapdomain, domainlen);
+ *ridp = rid;
+ } else if (strncmp(cp, "S-1-", 4) == 0) {
+ /* It's a numeric SID (eg "S-1-234-567-89") */
+ (void) strcpy(domain, cp);
+ cp = strrchr(domain, '-');
+ *cp = '\0';
+ cp++;
+
+ errno = 0;
+ *ridp = strtoull(cp, &end, 10);
+ if (errno == 0 || *end != '\0')
+ return (EINVAL);
+ } else if (!isdigit(*cp)) {
+ /*
+ * It's a user/group name (eg "user") that needs to be
+ * turned into a uid/gid
+ */
+ if (zoned && getzoneid() == GLOBAL_ZONEID)
+ return (ENOENT);
+ if (isuser) {
+ struct passwd *pw;
+ pw = getpwnam(cp);
+ if (pw == NULL)
+ return (ENOENT);
+ *ridp = pw->pw_uid;
+ } else {
+ struct group *gr;
+ gr = getgrnam(cp);
+ if (gr == NULL)
+ return (ENOENT);
+ *ridp = gr->gr_gid;
+ }
+ } else {
+ /* It's a user/group ID (eg "12345"). */
+ uid_t id = strtoul(cp, &end, 10);
+ idmap_rid_t rid;
+ char *mapdomain;
+
+ if (*end != '\0')
+ return (EINVAL);
+ if (id > MAXUID) {
+ /* It's an ephemeral ID. */
+ if (idmap_id_to_numeric_domain_rid(id, isuser,
+ &mapdomain, &rid) != 0)
+ return (ENOENT);
+ (void) strcpy(domain, mapdomain);
+ *ridp = rid;
+ } else {
+ *ridp = id;
+ }
+ }
+
+ return (0);
+}
+
+int
+zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
+ char *propbuf, int proplen, boolean_t literal)
+{
+ int err;
+ zfs_cmd_t zc = { 0 };
+ zfs_userquota_prop_t type;
+
+ (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+
+ err = userquota_propname_decode(propname,
+ zfs_prop_get_int(zhp, ZFS_PROP_ZONED),
+ &type, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid);
+ zc.zc_objset_type = type;
+ if (err)
+ return (err);
+
+ err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc);
+ if (err)
+ return (err);
+
+ if (literal) {
+ (void) snprintf(propbuf, proplen, "%llu",
+ (u_longlong_t)zc.zc_cookie);
+ } else if (zc.zc_cookie == 0 &&
+ (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) {
+ (void) strlcpy(propbuf, "none", proplen);
+ } else {
+ zfs_nicenum(zc.zc_cookie, propbuf, proplen);
+ }
+ return (0);
+}
+
/*
* Returns the name of the given zfs handle.
*/
@@ -2826,12 +2278,6 @@ zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT,
&zc)) == 0) {
/*
- * Ignore private dataset names.
- */
- if (dataset_name_hidden(zc.zc_name))
- continue;
-
- /*
* Silently ignore errors, as the only plausible explanation is
* that the pool has since been removed.
*/
@@ -4488,7 +3934,12 @@ zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props)
zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr));
nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr);
- if (props[zfs_prop] == B_FALSE)
+ /*
+ * We leave user:props in the nvlist, so there will be
+ * some ZPROP_INVAL. To be extra safe, don't prune
+ * those.
+ */
+ if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE)
(void) nvlist_remove(zhp->zfs_props,
nvpair_name(curr), nvpair_type(curr));
curr = next;
@@ -4576,3 +4027,36 @@ zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path,
return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME,
oldname, newname));
}
+
+int
+zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
+ zfs_userspace_cb_t func, void *arg)
+{
+ zfs_cmd_t zc = { 0 };
+ int error;
+ zfs_useracct_t buf[100];
+
+ (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+
+ zc.zc_objset_type = type;
+ zc.zc_nvlist_dst = (uintptr_t)buf;
+
+ /* CONSTCOND */
+ while (1) {
+ zfs_useracct_t *zua = buf;
+
+ zc.zc_nvlist_dst_size = sizeof (buf);
+ error = ioctl(zhp->zfs_hdl->libzfs_fd,
+ ZFS_IOC_USERSPACE_MANY, &zc);
+ if (error || zc.zc_nvlist_dst_size == 0)
+ break;
+
+ while (zc.zc_nvlist_dst_size > 0) {
+ func(arg, zua->zu_domain, zua->zu_rid, zua->zu_space);
+ zua++;
+ zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t);
+ }
+ }
+
+ return (error);
+}
diff --git a/usr/src/lib/libzfs/common/libzfs_graph.c b/usr/src/lib/libzfs/common/libzfs_graph.c
index e7cbf23860..bc21c51ae2 100644
--- a/usr/src/lib/libzfs/common/libzfs_graph.c
+++ b/usr/src/lib/libzfs/common/libzfs_graph.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* Iterate over all children of the current object. This includes the normal
* dataset hierarchy, but also arbitrary hierarchies due to clones. We want to
@@ -399,13 +397,6 @@ iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
ioctl(hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0;
(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {
-
- /*
- * Ignore private dataset names.
- */
- if (dataset_name_hidden(zc.zc_name))
- continue;
-
/*
* Get statistics for this dataset, to determine the type of the
* dataset and clone statistics. If this fails, the dataset has
diff --git a/usr/src/lib/libzfs/common/libzfs_sendrecv.c b/usr/src/lib/libzfs/common/libzfs_sendrecv.c
index 5a2e2aeb6d..612a099146 100644
--- a/usr/src/lib/libzfs/common/libzfs_sendrecv.c
+++ b/usr/src/lib/libzfs/common/libzfs_sendrecv.c
@@ -237,6 +237,8 @@ send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv)
zfs_prop_t prop = zfs_name_to_prop(propname);
nvlist_t *propnv;
+ assert(zfs_prop_user(propname) || prop != ZPROP_INVAL);
+
if (!zfs_prop_user(propname) && zfs_prop_readonly(prop))
continue;
@@ -594,12 +596,18 @@ dump_filesystem(zfs_handle_t *zhp, void *arg)
zhp->zfs_name, sdd->fromsnap);
sdd->err = B_TRUE;
} else if (!sdd->seento) {
- (void) fprintf(stderr,
- "WARNING: could not send %s@%s:\n"
- "incremental source (%s@%s) "
- "is not earlier than it\n",
- zhp->zfs_name, sdd->tosnap,
- zhp->zfs_name, sdd->fromsnap);
+ if (sdd->fromsnap) {
+ (void) fprintf(stderr,
+ "WARNING: could not send %s@%s:\n"
+ "incremental source (%s@%s) "
+ "is not earlier than it\n",
+ zhp->zfs_name, sdd->tosnap,
+ zhp->zfs_name, sdd->fromsnap);
+ } else {
+ (void) fprintf(stderr, "WARNING: "
+ "could not send %s@%s: does not exist\n",
+ zhp->zfs_name, sdd->tosnap);
+ }
sdd->err = B_TRUE;
}
} else {
diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c
index 17d8db5c75..e1993ce0f2 100644
--- a/usr/src/lib/libzfs/common/libzfs_util.c
+++ b/usr/src/lib/libzfs/common/libzfs_util.c
@@ -1221,7 +1221,7 @@ addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp,
* dataset property,
*/
if (prop == ZPROP_INVAL && (type == ZFS_TYPE_POOL ||
- !zfs_prop_user(propname))) {
+ (!zfs_prop_user(propname) && !zfs_prop_userquota(propname)))) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"invalid property '%s'"), propname);
return (zfs_error(hdl, EZFS_BADPROP,
diff --git a/usr/src/lib/libzfs/common/mapfile-vers b/usr/src/lib/libzfs/common/mapfile-vers
index f7d0d2774e..80d7c45bdc 100644
--- a/usr/src/lib/libzfs/common/mapfile-vers
+++ b/usr/src/lib/libzfs/common/mapfile-vers
@@ -47,18 +47,15 @@ SUNWprivate_1.1 {
libzfs_mnttab_cache;
libzfs_print_on_error;
zfs_allocatable_devs;
- zfs_build_perms;
zfs_clone;
zfs_close;
zfs_create;
zfs_create_ancestors;
zfs_dataset_exists;
- zfs_deleg_permissions;
zfs_deleg_share_nfs;
zfs_destroy;
zfs_destroy_snaps;
zfs_expand_proplist;
- zfs_free_allows;
zfs_get_handle;
zfs_get_name;
zfs_get_pool_handle;
@@ -82,9 +79,6 @@ 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;
@@ -93,14 +87,18 @@ SUNWprivate_1.1 {
zfs_prop_get;
zfs_prop_get_int;
zfs_prop_get_numeric;
+ zfs_prop_get_table;
+ zfs_prop_get_userquota;
zfs_prop_inherit;
zfs_prop_inheritable;
+ zfs_prop_init;
zfs_prop_is_string;
zfs_prop_readonly;
zfs_prop_set;
zfs_prop_string_to_index;
zfs_prop_to_name;
zfs_prop_user;
+ zfs_prop_userquota;
zfs_prop_valid_for_type;
zfs_prop_values;
zfs_prune_proplist;
@@ -131,6 +129,8 @@ SUNWprivate_1.1 {
zfs_unshareall_bypath;
zfs_unshareall_nfs;
zfs_unshareall_smb;
+ zfs_userspace;
+ zfs_userquota_prop_prefixes;
zpool_add;
zpool_clear;
zpool_close;
diff --git a/usr/src/lib/pyzfs/Makefile b/usr/src/lib/pyzfs/Makefile
new file mode 100644
index 0000000000..c39ef5b9c7
--- /dev/null
+++ b/usr/src/lib/pyzfs/Makefile
@@ -0,0 +1,54 @@
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.lib
+
+SUBDIRS= $(MACH)
+XGETTEXT= $(GNUXGETTEXT)
+XGETFLAGS= $(GNUXGETFLAGS)
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+
+MSGFILES = `$(FIND) . -name '*.py' -o -name '*.c'`
+POFILE = pyzfs.po
+
+.KEEP_STATE:
+
+all install clean clobber lint: $(SUBDIRS)
+
+$(POFILE): pofile_MSGFILES
+
+_msg: $(MSGDOMAINPOFILE)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../../Makefile.msg.targ
diff --git a/usr/src/lib/pyzfs/Makefile.com b/usr/src/lib/pyzfs/Makefile.com
new file mode 100644
index 0000000000..adfaf729f7
--- /dev/null
+++ b/usr/src/lib/pyzfs/Makefile.com
@@ -0,0 +1,65 @@
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+LIBRARY = ioctl.a
+VERS =
+OBJECTS = ioctl.o
+
+PYSRCS= __init__.py util.py dataset.py \
+ allow.py unallow.py \
+ userspace.py groupspace.py
+
+
+include ../../Makefile.lib
+
+LIBLINKS =
+SRCDIR = ../common
+ROOTLIBDIR= $(ROOT)/usr/lib/python2.4/vendor-packages/zfs
+PYOBJS= $(PYSRCS:%.py=$(SRCDIR)/%.pyc)
+PYFILES= $(PYSRCS) $(PYSRCS:%.py=%.pyc)
+ROOTPYZFSFILES= $(PYFILES:%=$(ROOTLIBDIR)/%)
+
+C99MODE= -xc99=%all
+C99LMODE= -Xc99=%all
+
+LIBS = $(DYNLIB)
+LDLIBS += -lc -lnvpair -lsec -lidmap -lpython2.4 -lzfs
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -I/usr/include/python2.4
+CPPFLAGS += -I../../../uts/common/fs/zfs
+CPPFLAGS += -I../../../common/zfs
+
+.KEEP_STATE:
+
+all: $(PYOBJS) $(LIBS)
+
+install: all $(ROOTPYZFSFILES)
+
+$(ROOTLIBDIR)/%: %
+ $(INS.pyfile)
+
+lint: lintcheck
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/pyzfs/common/__init__.py b/usr/src/lib/pyzfs/common/__init__.py
new file mode 100644
index 0000000000..f4b0f53954
--- /dev/null
+++ b/usr/src/lib/pyzfs/common/__init__.py
@@ -0,0 +1,28 @@
+#! /usr/bin/python2.4
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+"""
+package which provides an administrative interface to ZFS
+"""
diff --git a/usr/src/lib/pyzfs/common/allow.py b/usr/src/lib/pyzfs/common/allow.py
new file mode 100644
index 0000000000..d3a03c7318
--- /dev/null
+++ b/usr/src/lib/pyzfs/common/allow.py
@@ -0,0 +1,394 @@
+#! /usr/bin/python2.4
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+"""This module implements the "zfs allow" and "zfs unallow" subcommands.
+The only public interface is the zfs.allow.do_allow() function."""
+
+import zfs.util
+import zfs.dataset
+import optparse
+import sys
+import pwd
+import grp
+import errno
+
+_ = zfs.util._
+
+class FSPerms(object):
+ """This class represents all the permissions that are set on a
+ particular filesystem (not including those inherited)."""
+
+ __slots__ = "create", "sets", "local", "descend", "ld"
+ __repr__ = zfs.util.default_repr
+
+ def __init__(self, raw):
+ """Create a FSPerms based on the dict of raw permissions
+ from zfs.ioctl.get_fsacl()."""
+ # set of perms
+ self.create = set()
+
+ # below are { "Ntype name": set(perms) }
+ # where N is a number that we just use for sorting,
+ # type is "user", "group", "everyone", or "" (for sets)
+ # name is a user, group, or set name, or "" (for everyone)
+ self.sets = dict()
+ self.local = dict()
+ self.descend = dict()
+ self.ld = dict()
+
+ # see the comment in dsl_deleg.c for the definition of whokey
+ for whokey in raw.keys():
+ perms = raw[whokey].keys()
+ whotypechr = whokey[0].lower()
+ ws = whokey[3:]
+ if whotypechr == "c":
+ self.create.update(perms)
+ elif whotypechr == "s":
+ nwho = "1" + ws
+ self.sets.setdefault(nwho, set()).update(perms)
+ else:
+ if whotypechr == "u":
+ try:
+ name = pwd.getpwuid(int(ws)).pw_name
+ except KeyError:
+ name = ws
+ nwho = "1user " + name
+ elif whotypechr == "g":
+ try:
+ name = grp.getgrgid(int(ws)).gr_name
+ except KeyError:
+ name = ws
+ nwho = "2group " + name
+ elif whotypechr == "e":
+ nwho = "3everyone"
+ else:
+ raise ValueError(whotypechr)
+
+ if whokey[1] == "l":
+ d = self.local
+ elif whokey[1] == "d":
+ d = self.descend
+ else:
+ raise ValueError(whokey[1])
+
+ d.setdefault(nwho, set()).update(perms)
+
+ # Find perms that are in both local and descend, and
+ # move them to ld.
+ for nwho in self.local:
+ if nwho not in self.descend:
+ continue
+ # note: these are set operations
+ self.ld[nwho] = self.local[nwho] & self.descend[nwho]
+ self.local[nwho] -= self.ld[nwho]
+ self.descend[nwho] -= self.ld[nwho]
+
+ @staticmethod
+ def __ldstr(d, header):
+ s = ""
+ for (nwho, perms) in sorted(d.items()):
+ # local and descend may have entries where perms
+ # is an empty set, due to consolidating all
+ # permissions into ld
+ if perms:
+ s += "\t%s %s\n" % \
+ (nwho[1:], ",".join(sorted(perms)))
+ if s:
+ s = header + s
+ return s
+
+ def __str__(self):
+ s = self.__ldstr(self.sets, _("Permission sets:\n"))
+
+ if self.create:
+ s += _("Create time permissions:\n")
+ s += "\t%s\n" % ",".join(sorted(self.create))
+
+ s += self.__ldstr(self.local, _("Local permissions:\n"))
+ s += self.__ldstr(self.descend, _("Descendent permissions:\n"))
+ s += self.__ldstr(self.ld, _("Local+Descendent permissions:\n"))
+ return s.rstrip()
+
+def args_to_perms(parser, options, who, perms):
+ """Return a dict of raw perms {"whostr" -> {"perm" -> None}}
+ based on the command-line input."""
+
+ # perms is not set if we are doing a "zfs unallow <who> <fs>" to
+ # remove all of someone's permissions
+ if perms:
+ setperms = dict(((p, None) for p in perms if p[0] == "@"))
+ baseperms = dict(((canonicalized_perm(p), None)
+ for p in perms if p[0] != "@"))
+ else:
+ setperms = None
+ baseperms = None
+
+ d = dict()
+
+ def storeperm(typechr, inheritchr, arg):
+ assert typechr in "ugecs"
+ assert inheritchr in "ld-"
+
+ def mkwhokey(t):
+ return "%c%c$%s" % (t, inheritchr, arg)
+
+ if baseperms or not perms:
+ d[mkwhokey(typechr)] = baseperms
+ if setperms or not perms:
+ d[mkwhokey(typechr.upper())] = setperms
+
+ def decodeid(w, toidfunc, fmt):
+ try:
+ return int(w)
+ except ValueError:
+ try:
+ return toidfunc(w)[2]
+ except KeyError:
+ parser.error(fmt % w)
+
+ if options.set:
+ storeperm("s", "-", who)
+ elif options.create:
+ storeperm("c", "-", "")
+ else:
+ for w in who:
+ if options.user:
+ id = decodeid(w, pwd.getpwnam,
+ _("invalid user %s"))
+ typechr = "u"
+ elif options.group:
+ id = decodeid(w, grp.getgrnam,
+ _("invalid group %s"))
+ typechr = "g"
+ elif w == "everyone":
+ id = ""
+ typechr = "e"
+ else:
+ try:
+ id = pwd.getpwnam(w)[2]
+ typechr = "u"
+ except KeyError:
+ try:
+ id = grp.getgrnam(w)[2]
+ typechr = "g"
+ except KeyError:
+ parser.error(_("invalid user/group %s") % w)
+ if options.local:
+ storeperm(typechr, "l", id)
+ if options.descend:
+ storeperm(typechr, "d", id)
+ return d
+
+perms_subcmd = dict(
+ create=_("Must also have the 'mount' ability"),
+ destroy=_("Must also have the 'mount' ability"),
+ snapshot=_("Must also have the 'mount' ability"),
+ rollback=_("Must also have the 'mount' ability"),
+ clone=_("""Must also have the 'create' ability and 'mount'
+\t\t\t\tability in the origin file system"""),
+ promote=_("""Must also have the 'mount'
+\t\t\t\tand 'promote' ability in the origin file system"""),
+ rename=_("""Must also have the 'mount' and 'create'
+\t\t\t\tability in the new parent"""),
+ receive=_("Must also have the 'mount' and 'create' ability"),
+ allow=_("Must also have the permission that is being\n\t\t\t\tallowed"),
+ mount=_("Allows mount/umount of ZFS datasets"),
+ share=_("Allows sharing file systems over NFS or SMB\n\t\t\t\tprotocols"),
+ send="",
+)
+
+perms_other = dict(
+ userprop=_("Allows changing any user property"),
+ userquota=_("Allows accessing any userquota@... property"),
+ groupquota=_("Allows accessing any groupquota@... property"),
+ userused=_("Allows reading any userused@... property"),
+ groupused=_("Allows reading any groupused@... property"),
+)
+
+def hasset(ds, setname):
+ """Return True if the given setname (string) is defined for this
+ ds (Dataset)."""
+ # It would be nice to cache the result of get_fsacl().
+ for raw in ds.get_fsacl().values():
+ for whokey in raw.keys():
+ if whokey[0].lower() == "s" and whokey[3:] == setname:
+ return True
+ return False
+
+def canonicalized_perm(permname):
+ """Return the canonical name (string) for this permission (string).
+ Raises ZFSError if it is not a valid permission."""
+ if permname in perms_subcmd.keys() or permname in perms_other.keys():
+ return permname
+ try:
+ return zfs.dataset.getpropobj(permname).name
+ except KeyError:
+ raise zfs.util.ZFSError(errno.EINVAL, permname,
+ _("invalid permission"))
+
+def print_perms():
+ """Print the set of supported permissions."""
+ print(_("\nThe following permissions are supported:\n"))
+ fmt = "%-16s %-14s\t%s"
+ print(fmt % (_("NAME"), _("TYPE"), _("NOTES")))
+
+ for (name, note) in sorted(perms_subcmd.iteritems()):
+ print(fmt % (name, _("subcommand"), note))
+
+ for (name, note) in sorted(perms_other.iteritems()):
+ print(fmt % (name, _("other"), note))
+
+ for (name, prop) in sorted(zfs.dataset.proptable.iteritems()):
+ if prop.visible and prop.delegatable():
+ print(fmt % (name, _("property"), ""))
+
+def do_allow():
+ """Implementes the "zfs allow" and "zfs unallow" subcommands."""
+ un = (sys.argv[1] == "unallow")
+
+ def usage(msg=None):
+ parser.print_help()
+ print_perms()
+ if msg:
+ print
+ parser.exit("zfs: error: " + msg)
+ else:
+ parser.exit()
+
+ if un:
+ u = _("""unallow [-rldug] <"everyone"|user|group>[,...]
+ [<perm|@setname>[,...]] <filesystem|volume>
+ unallow [-rld] -e [<perm|@setname>[,...]] <filesystem|volume>
+ unallow [-r] -c [<perm|@setname>[,...]] <filesystem|volume>
+ unallow [-r] -s @setname [<perm|@setname>[,...]] <filesystem|volume>""")
+ verb = _("remove")
+ sstr = _("undefine permission set")
+ else:
+ u = _("""allow <filesystem|volume>
+ allow [-ldug] <"everyone"|user|group>[,...] <perm|@setname>[,...]
+ <filesystem|volume>
+ allow [-ld] -e <perm|@setname>[,...] <filesystem|volume>
+ allow -c <perm|@setname>[,...] <filesystem|volume>
+ allow -s @setname <perm|@setname>[,...] <filesystem|volume>""")
+ verb = _("set")
+ sstr = _("define permission set")
+
+ parser = optparse.OptionParser(usage=u, prog="zfs")
+
+ parser.add_option("-l", action="store_true", dest="local",
+ help=_("%s permission locally") % verb)
+ parser.add_option("-d", action="store_true", dest="descend",
+ help=_("%s permission for descendents") % verb)
+ parser.add_option("-u", action="store_true", dest="user",
+ help=_("%s permission for user") % verb)
+ parser.add_option("-g", action="store_true", dest="group",
+ help=_("%s permission for group") % verb)
+ parser.add_option("-e", action="store_true", dest="everyone",
+ help=_("%s permission for everyone") % verb)
+ parser.add_option("-c", action="store_true", dest="create",
+ help=_("%s create time permissions") % verb)
+ parser.add_option("-s", action="store_true", dest="set", help=sstr)
+ if un:
+ parser.add_option("-r", action="store_true", dest="recursive",
+ help=_("remove permissions recursively"))
+
+ if len(sys.argv) == 3 and not un:
+ # just print the permissions on this fs
+
+ if sys.argv[2] == "-h":
+ # hack to make "zfs allow -h" work
+ usage()
+ ds = zfs.dataset.Dataset(sys.argv[2])
+
+ p = dict()
+ for (fs, raw) in ds.get_fsacl().items():
+ p[fs] = FSPerms(raw)
+
+ for fs in sorted(p.keys(), reverse=True):
+ s = _("---- Permissions on %s ") % fs
+ print(s + "-" * (70-len(s)))
+ print(p[fs])
+ return
+
+
+ (options, args) = parser.parse_args(sys.argv[2:])
+
+ if sum((bool(options.everyone), bool(options.user),
+ bool(options.group))) > 1:
+ parser.error(_("-u, -g, and -e are mutually exclusive"))
+
+ def mungeargs(expected_len):
+ if un and len(args) == expected_len-1:
+ return (None, args[expected_len-2])
+ elif len(args) == expected_len:
+ return (args[expected_len-2].split(","),
+ args[expected_len-1])
+ else:
+ usage(_("wrong number of parameters"))
+
+ if options.set:
+ if options.local or options.descend or options.user or \
+ options.group or options.everyone or options.create:
+ parser.error(_("invalid option combined with -s"))
+ if args[0][0] != "@":
+ parser.error(_("invalid set name: missing '@' prefix"))
+
+ (perms, fsname) = mungeargs(3)
+ who = args[0]
+ elif options.create:
+ if options.local or options.descend or options.user or \
+ options.group or options.everyone or options.set:
+ parser.error(_("invalid option combined with -c"))
+
+ (perms, fsname) = mungeargs(2)
+ who = None
+ elif options.everyone:
+ if options.user or options.group or \
+ options.create or options.set:
+ parser.error(_("invalid option combined with -e"))
+
+ (perms, fsname) = mungeargs(2)
+ who = ["everyone"]
+ else:
+ (perms, fsname) = mungeargs(3)
+ who = args[0].split(",")
+
+ if not options.local and not options.descend:
+ options.local = True
+ options.descend = True
+
+ d = args_to_perms(parser, options, who, perms)
+
+ ds = zfs.dataset.Dataset(fsname, snaps=False)
+
+ if not un and perms:
+ for p in perms:
+ if p[0] == "@" and not hasset(ds, p):
+ parser.error(_("set %s is not defined") % p)
+
+ ds.set_fsacl(un, d)
+ if un and options.recursive:
+ for child in ds.descendents():
+ child.set_fsacl(un, d)
diff --git a/usr/src/lib/pyzfs/common/dataset.py b/usr/src/lib/pyzfs/common/dataset.py
new file mode 100644
index 0000000000..b45173e01f
--- /dev/null
+++ b/usr/src/lib/pyzfs/common/dataset.py
@@ -0,0 +1,205 @@
+#! /usr/bin/python2.4
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+"""Implements the Dataset class, providing methods for manipulating ZFS
+datasets. Also implements the Property class, which describes ZFS
+properties."""
+
+import zfs.ioctl
+import zfs.util
+import errno
+
+_ = zfs.util._
+
+class Property(object):
+ """This class represents a ZFS property. It contains
+ information about the property -- if it's readonly, a number vs
+ string vs index, etc. Only native properties are represented by
+ this class -- not user properties (eg "user:prop") or userspace
+ properties (eg "userquota@joe")."""
+
+ __slots__ = "name", "number", "type", "default", "attr", "validtypes", \
+ "values", "colname", "rightalign", "visible", "indextable"
+ __repr__ = zfs.util.default_repr
+
+ def __init__(self, t):
+ """t is the tuple of information about this property
+ from zfs.ioctl.get_proptable, which should match the
+ members of zprop_desc_t (see zfs_prop.h)."""
+
+ self.name = t[0]
+ self.number = t[1]
+ self.type = t[2]
+ if self.type == "string":
+ self.default = t[3]
+ else:
+ self.default = t[4]
+ self.attr = t[5]
+ self.validtypes = t[6]
+ self.values = t[7]
+ self.colname = t[8]
+ self.rightalign = t[9]
+ self.visible = t[10]
+ self.indextable = t[11]
+
+ def delegatable(self):
+ """Return True if this property can be delegated with
+ "zfs allow"."""
+ return self.attr != "readonly"
+
+proptable = dict()
+for name, t in zfs.ioctl.get_proptable().iteritems():
+ proptable[name] = Property(t)
+del name, t
+
+def getpropobj(name):
+ """Return the Property object that is identified by the given
+ name string. It can be the full name, or the column name."""
+ try:
+ return proptable[name]
+ except KeyError:
+ for p in proptable.itervalues():
+ if p.colname and p.colname.lower() == name:
+ return p
+ raise
+
+class Dataset(object):
+ """Represents a ZFS dataset (filesystem, snapshot, zvol, clone, etc).
+
+ Generally, this class provides interfaces to the C functions in
+ zfs.ioctl which actually interface with the kernel to manipulate
+ datasets.
+
+ Unless otherwise noted, any method can raise a ZFSError to
+ indicate failure."""
+
+ __slots__ = "name", "__props"
+ __repr__ = zfs.util.default_repr
+
+ def __init__(self, name, props=None,
+ types=("filesystem", "volume"), snaps=True):
+ """Open the named dataset, checking that it exists and
+ is of the specified type.
+
+ name is the string name of this dataset.
+
+ props is the property settings dict from zfs.ioctl.next_dataset.
+
+ types is an iterable of strings specifying which types
+ of datasets are permitted. Accepted strings are
+ "filesystem" and "volume". Defaults to acceptying all
+ types.
+
+ snaps is a boolean specifying if snapshots are acceptable.
+
+ Raises a ZFSError if the dataset can't be accessed (eg
+ doesn't exist) or is not of the specified type.
+ """
+
+ self.name = name
+
+ e = zfs.util.ZFSError(errno.EINVAL,
+ _("cannot open %s") % name,
+ _("operation not applicable to datasets of this type"))
+ if "@" in name and not snaps:
+ raise e
+ if not props:
+ props = zfs.ioctl.dataset_props(name)
+ self.__props = props
+ if "volume" not in types and self.getprop("type") == 3:
+ raise e
+ if "filesystem" not in types and self.getprop("type") == 2:
+ raise e
+
+ def getprop(self, propname):
+ """Return the value of the given property for this dataset.
+
+ Currently only works for native properties (those with a
+ Property object.)
+
+ Raises KeyError if propname does not specify a native property.
+ Does not raise ZFSError.
+ """
+
+ p = getpropobj(propname)
+ try:
+ return self.__props[p.name]["value"]
+ except KeyError:
+ return p.default
+
+ def parent(self):
+ """Return a Dataset representing the parent of this one."""
+ return Dataset(self.name[:self.name.rindex("/")])
+
+ def descendents(self):
+ """A generator function which iterates over all
+ descendent Datasets (not including snapshots."""
+
+ cookie = 0
+ while True:
+ # next_dataset raises StopIteration when done
+ (name, cookie, props) = \
+ zfs.ioctl.next_dataset(self.name, False, cookie)
+ ds = Dataset(name, props)
+ yield ds
+ for child in ds.descendents():
+ yield child
+
+ def userspace(self, prop):
+ """A generator function which iterates over a
+ userspace-type property.
+
+ prop specifies which property ("userused@",
+ "userquota@", "groupused@", or "groupquota@").
+
+ returns 3-tuple of domain (string), rid (int), and space (int).
+ """
+
+ d = zfs.ioctl.userspace_many(self.name, prop)
+ for ((domain, rid), space) in d.iteritems():
+ yield (domain, rid, space)
+
+ def userspace_upgrade(self):
+ """Initialize the accounting information for
+ userused@... and groupused@... properties."""
+ return zfs.ioctl.userspace_upgrade(self.name)
+
+ def set_fsacl(self, un, d):
+ """Add to the "zfs allow"-ed permissions on this Dataset.
+
+ un is True if the specified permissions should be removed.
+
+ d is a dict specifying which permissions to add/remove:
+ { "whostr" -> None # remove all perms for this entity
+ "whostr" -> { "perm" -> None} # add/remove these perms
+ } """
+ return zfs.ioctl.set_fsacl(self.name, un, d)
+
+ def get_fsacl(self):
+ """Get the "zfs allow"-ed permissions on the Dataset.
+
+ Return a dict("whostr": { "perm" -> None })."""
+
+ return zfs.ioctl.get_fsacl(self.name)
diff --git a/usr/src/lib/pyzfs/common/groupspace.py b/usr/src/lib/pyzfs/common/groupspace.py
new file mode 100644
index 0000000000..7db4bf3e0c
--- /dev/null
+++ b/usr/src/lib/pyzfs/common/groupspace.py
@@ -0,0 +1,29 @@
+#! /usr/bin/python2.4
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+import zfs.userspace
+
+do_groupspace = zfs.userspace.do_userspace
+
diff --git a/usr/src/lib/pyzfs/common/ioctl.c b/usr/src/lib/pyzfs/common/ioctl.c
new file mode 100644
index 0000000000..7a10a4e25e
--- /dev/null
+++ b/usr/src/lib/pyzfs/common/ioctl.c
@@ -0,0 +1,599 @@
+/*
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <Python.h>
+#include <sys/zfs_ioctl.h>
+#include <sys/fs/zfs.h>
+#include <strings.h>
+#include <unistd.h>
+#include <libnvpair.h>
+#include <idmap.h>
+#include <zone.h>
+#include <libintl.h>
+#include <libzfs.h>
+#include "zfs_prop.h"
+
+static PyObject *ZFSError;
+static int zfsdevfd;
+
+#ifdef __lint
+#define dgettext(x, y) y
+#endif
+
+#define _(s) dgettext(TEXT_DOMAIN, s)
+
+extern int sid_to_id(char *sid, boolean_t user, uid_t *id);
+
+/*PRINTFLIKE1*/
+static void
+seterr(char *fmt, ...)
+{
+ char errstr[1024];
+ va_list v;
+
+ va_start(v, fmt);
+ (void) vsnprintf(errstr, sizeof (errstr), fmt, v);
+ va_end(v);
+
+ PyErr_SetObject(ZFSError, Py_BuildValue("is", errno, errstr));
+}
+
+static char cmdstr[HIS_MAX_RECORD_LEN];
+
+static int
+ioctl_with_cmdstr(int ioc, zfs_cmd_t *zc)
+{
+ int err;
+
+ if (cmdstr[0])
+ zc->zc_history = (uint64_t)(uintptr_t)cmdstr;
+ err = ioctl(zfsdevfd, ioc, zc);
+ cmdstr[0] = '\0';
+ return (err);
+}
+
+static PyObject *
+nvl2py(nvlist_t *nvl)
+{
+ PyObject *pyo;
+ nvpair_t *nvp;
+
+ pyo = PyDict_New();
+
+ for (nvp = nvlist_next_nvpair(nvl, NULL); nvp;
+ nvp = nvlist_next_nvpair(nvl, nvp)) {
+ PyObject *pyval;
+ char *sval;
+ uint64_t ival;
+ boolean_t bval;
+ nvlist_t *nval;
+
+ switch (nvpair_type(nvp)) {
+ case DATA_TYPE_STRING:
+ (void) nvpair_value_string(nvp, &sval);
+ pyval = Py_BuildValue("s", sval);
+ break;
+
+ case DATA_TYPE_UINT64:
+ (void) nvpair_value_uint64(nvp, &ival);
+ pyval = Py_BuildValue("K", ival);
+ break;
+
+ case DATA_TYPE_NVLIST:
+ (void) nvpair_value_nvlist(nvp, &nval);
+ pyval = nvl2py(nval);
+ break;
+
+ case DATA_TYPE_BOOLEAN:
+ Py_INCREF(Py_None);
+ pyval = Py_None;
+ break;
+
+ case DATA_TYPE_BOOLEAN_VALUE:
+ (void) nvpair_value_boolean_value(nvp, &bval);
+ pyval = Py_BuildValue("i", bval);
+ break;
+
+ default:
+ PyErr_SetNone(PyExc_ValueError);
+ Py_DECREF(pyo);
+ return (NULL);
+ }
+
+ PyDict_SetItemString(pyo, nvpair_name(nvp), pyval);
+ Py_DECREF(pyval);
+ }
+
+ return (pyo);
+}
+
+static nvlist_t *
+dict2nvl(PyObject *d)
+{
+ nvlist_t *nvl;
+ int err;
+ PyObject *key, *value;
+ int pos = 0;
+
+ if (!PyDict_Check(d)) {
+ PyErr_SetObject(PyExc_ValueError, d);
+ return (NULL);
+ }
+
+ err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0);
+ assert(err == 0);
+
+ while (PyDict_Next(d, &pos, &key, &value)) {
+ char *keystr = PyString_AsString(key);
+ if (keystr == NULL) {
+ PyErr_SetObject(PyExc_KeyError, key);
+ nvlist_free(nvl);
+ return (NULL);
+ }
+
+ if (PyDict_Check(value)) {
+ nvlist_t *valnvl = dict2nvl(value);
+ err = nvlist_add_nvlist(nvl, keystr, valnvl);
+ nvlist_free(valnvl);
+ } else if (value == Py_None) {
+ err = nvlist_add_boolean(nvl, keystr);
+ } else if (PyString_Check(value)) {
+ char *valstr = PyString_AsString(value);
+ err = nvlist_add_string(nvl, keystr, valstr);
+ } else if (PyInt_Check(value)) {
+ uint64_t valint = PyInt_AsUnsignedLongLongMask(value);
+ err = nvlist_add_uint64(nvl, keystr, valint);
+ } else if (PyBool_Check(value)) {
+ boolean_t valbool = value == Py_True ? B_TRUE : B_FALSE;
+ err = nvlist_add_boolean_value(nvl, keystr, valbool);
+ } else {
+ PyErr_SetObject(PyExc_ValueError, value);
+ nvlist_free(nvl);
+ return (NULL);
+ }
+ assert(err == 0);
+ }
+
+ return (nvl);
+}
+
+static PyObject *
+fakepropval(uint64_t value)
+{
+ PyObject *d = PyDict_New();
+ PyDict_SetItemString(d, "value", Py_BuildValue("K", value));
+ return (d);
+}
+
+static void
+add_ds_props(zfs_cmd_t *zc, PyObject *nvl)
+{
+ dmu_objset_stats_t *s = &zc->zc_objset_stats;
+ PyDict_SetItemString(nvl, "numclones",
+ fakepropval(s->dds_num_clones));
+ PyDict_SetItemString(nvl, "issnap",
+ fakepropval(s->dds_is_snapshot));
+ PyDict_SetItemString(nvl, "inconsistent",
+ fakepropval(s->dds_inconsistent));
+}
+
+/* On error, returns NULL but does not set python exception. */
+static PyObject *
+ioctl_with_dstnv(int ioc, zfs_cmd_t *zc)
+{
+ int nvsz = 2048;
+ void *nvbuf;
+ PyObject *pynv = NULL;
+
+again:
+ nvbuf = malloc(nvsz);
+ zc->zc_nvlist_dst_size = nvsz;
+ zc->zc_nvlist_dst = (uintptr_t)nvbuf;
+
+ if (ioctl(zfsdevfd, ioc, zc) == 0) {
+ nvlist_t *nvl;
+
+ errno = nvlist_unpack(nvbuf, zc->zc_nvlist_dst_size, &nvl, 0);
+ if (errno == 0) {
+ pynv = nvl2py(nvl);
+ nvlist_free(nvl);
+ }
+ } else if (errno == ENOMEM) {
+ free(nvbuf);
+ nvsz = zc->zc_nvlist_dst_size;
+ goto again;
+ }
+ free(nvbuf);
+ return (pynv);
+}
+
+static PyObject *
+py_next_dataset(PyObject *self, PyObject *args)
+{
+ int ioc;
+ uint64_t cookie;
+ zfs_cmd_t zc = { 0 };
+ int snaps;
+ char *name;
+ PyObject *nvl;
+ PyObject *ret = NULL;
+
+ if (!PyArg_ParseTuple(args, "siK", &name, &snaps, &cookie))
+ return (NULL);
+
+ (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
+ zc.zc_cookie = cookie;
+
+ if (snaps)
+ ioc = ZFS_IOC_SNAPSHOT_LIST_NEXT;
+ else
+ ioc = ZFS_IOC_DATASET_LIST_NEXT;
+
+ nvl = ioctl_with_dstnv(ioc, &zc);
+ if (nvl) {
+ add_ds_props(&zc, nvl);
+ ret = Py_BuildValue("sKO", zc.zc_name, zc.zc_cookie, nvl);
+ Py_DECREF(nvl);
+ } else if (errno == ESRCH) {
+ PyErr_SetNone(PyExc_StopIteration);
+ } else {
+ if (snaps)
+ seterr(_("cannot get snapshots of %s"), name);
+ else
+ seterr(_("cannot get child datasets of %s"), name);
+ }
+ return (ret);
+}
+
+static PyObject *
+py_dataset_props(PyObject *self, PyObject *args)
+{
+ zfs_cmd_t zc = { 0 };
+ int snaps;
+ char *name;
+ PyObject *nvl;
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return (NULL);
+
+ (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
+
+ nvl = ioctl_with_dstnv(ZFS_IOC_OBJSET_STATS, &zc);
+ if (nvl) {
+ add_ds_props(&zc, nvl);
+ } else {
+ seterr(_("cannot access dataset %s"), name);
+ }
+ return (nvl);
+}
+
+static PyObject *
+py_get_fsacl(PyObject *self, PyObject *args)
+{
+ zfs_cmd_t zc = { 0 };
+ char *name;
+ PyObject *nvl;
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return (NULL);
+
+ (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
+
+ nvl = ioctl_with_dstnv(ZFS_IOC_GET_FSACL, &zc);
+ if (nvl == NULL)
+ seterr(_("cannot get permissions on %s"), name);
+
+ return (nvl);
+}
+
+static PyObject *
+py_set_fsacl(PyObject *self, PyObject *args)
+{
+ int un;
+ size_t nvsz;
+ zfs_cmd_t zc = { 0 };
+ char *name, *nvbuf;
+ PyObject *dict, *file;
+ nvlist_t *nvl;
+ int err;
+
+ if (!PyArg_ParseTuple(args, "siO!", &name, &un,
+ &PyDict_Type, &dict))
+ return (NULL);
+
+ nvl = dict2nvl(dict);
+ if (nvl == NULL)
+ return (NULL);
+
+ err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE);
+ assert(err == 0);
+ nvbuf = malloc(nvsz);
+ err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0);
+ assert(err == 0);
+
+ (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
+ zc.zc_nvlist_src_size = nvsz;
+ zc.zc_nvlist_src = (uintptr_t)nvbuf;
+ zc.zc_perm_action = un;
+
+ err = ioctl_with_cmdstr(ZFS_IOC_SET_FSACL, &zc);
+ free(nvbuf);
+ if (err) {
+ seterr(_("cannot set permissions on %s"), name);
+ return (NULL);
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+py_userspace_many(PyObject *self, PyObject *args)
+{
+ zfs_cmd_t zc = { 0 };
+ zfs_userquota_prop_t type;
+ char *name, *propname;
+ int bufsz = 1<<20;
+ void *buf;
+ PyObject *dict, *file;
+ int error;
+
+ if (!PyArg_ParseTuple(args, "ss", &name, &propname))
+ return (NULL);
+
+ for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++)
+ if (strcmp(propname, zfs_userquota_prop_prefixes[type]) == 0)
+ break;
+ if (type == ZFS_NUM_USERQUOTA_PROPS) {
+ PyErr_SetString(PyExc_KeyError, propname);
+ return (NULL);
+ }
+
+ dict = PyDict_New();
+ buf = malloc(bufsz);
+
+ (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
+ zc.zc_objset_type = type;
+ zc.zc_cookie = 0;
+
+ while (1) {
+ zfs_useracct_t *zua = buf;
+
+ zc.zc_nvlist_dst = (uintptr_t)buf;
+ zc.zc_nvlist_dst_size = bufsz;
+
+ error = ioctl(zfsdevfd, ZFS_IOC_USERSPACE_MANY, &zc);
+ if (error || zc.zc_nvlist_dst_size == 0)
+ break;
+
+ while (zc.zc_nvlist_dst_size > 0) {
+ PyObject *pykey, *pyval;
+
+ pykey = Py_BuildValue("sI",
+ zua->zu_domain, zua->zu_rid);
+ pyval = Py_BuildValue("K", zua->zu_space);
+ PyDict_SetItem(dict, pykey, pyval);
+ Py_DECREF(pykey);
+ Py_DECREF(pyval);
+
+ zua++;
+ zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t);
+ }
+ }
+
+ free(buf);
+
+ if (error != 0) {
+ Py_DECREF(dict);
+ seterr(_("cannot get %s property on %s"), propname, name);
+ return (NULL);
+ }
+
+ return (dict);
+}
+
+static PyObject *
+py_userspace_upgrade(PyObject *self, PyObject *args)
+{
+ zfs_cmd_t zc = { 0 };
+ char *name;
+ int error;
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return (NULL);
+
+ (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
+ error = ioctl(zfsdevfd, ZFS_IOC_USERSPACE_UPGRADE, &zc);
+
+ if (error != 0) {
+ seterr(_("cannot initialize user accounting information on %s"),
+ name);
+ return (NULL);
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+py_sid_to_id(PyObject *self, PyObject *args)
+{
+ char *sid;
+ int err, isuser;
+ uid_t id;
+
+ if (!PyArg_ParseTuple(args, "si", &sid, &isuser))
+ return (NULL);
+
+ err = sid_to_id(sid, isuser, &id);
+ if (err) {
+ PyErr_SetString(PyExc_KeyError, sid);
+ return (NULL);
+ }
+
+ return (Py_BuildValue("I", id));
+}
+
+/*
+ * Translate the sid string ("S-1-...") to the user@domain name, if
+ * possible. There should be a better way to do this, but for now we
+ * just translate to the (possibly ephemeral) uid and then back again.
+ */
+static PyObject *
+py_sid_to_name(PyObject *self, PyObject *args)
+{
+ char *sid;
+ int err, isuser;
+ uid_t id;
+ char *name, *domain;
+ char buf[256];
+
+ if (!PyArg_ParseTuple(args, "si", &sid, &isuser))
+ return (NULL);
+
+ err = sid_to_id(sid, isuser, &id);
+ if (err) {
+ PyErr_SetString(PyExc_KeyError, sid);
+ return (NULL);
+ }
+
+ if (isuser) {
+ err = idmap_getwinnamebyuid(id,
+ IDMAP_REQ_FLG_USE_CACHE, &name, &domain);
+ } else {
+ err = idmap_getwinnamebygid(id,
+ IDMAP_REQ_FLG_USE_CACHE, &name, &domain);
+ }
+ if (err != IDMAP_SUCCESS) {
+ PyErr_SetString(PyExc_KeyError, sid);
+ return (NULL);
+ }
+ (void) snprintf(buf, sizeof (buf), "%s@%s", name, domain);
+ free(name);
+ free(domain);
+
+ return (Py_BuildValue("s", buf));
+}
+
+static PyObject *
+py_isglobalzone(PyObject *self, PyObject *args)
+{
+ return (Py_BuildValue("i", getzoneid() == GLOBAL_ZONEID));
+}
+
+static PyObject *
+py_set_cmdstr(PyObject *self, PyObject *args)
+{
+ char *str;
+
+ if (!PyArg_ParseTuple(args, "s", &str))
+ return (NULL);
+
+ (void) strlcpy(cmdstr, str, sizeof (cmdstr));
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+py_get_proptable(PyObject *self, PyObject *args)
+{
+ zprop_desc_t *t = zfs_prop_get_table();
+ PyObject *d = PyDict_New();
+ zfs_prop_t i;
+
+ for (i = 0; i < ZFS_NUM_PROPS; i++) {
+ zprop_desc_t *p = &t[i];
+ PyObject *tuple;
+ static const char *typetable[] =
+ {"number", "string", "index"};
+ static const char *attrtable[] =
+ {"default", "readonly", "inherit", "onetime"};
+ PyObject *indextable;
+
+ if (p->pd_proptype == PROP_TYPE_INDEX) {
+ const zprop_index_t *it = p->pd_table;
+ indextable = PyDict_New();
+ int j;
+ for (j = 0; it[j].pi_name; j++) {
+ PyDict_SetItemString(indextable,
+ it[j].pi_name,
+ Py_BuildValue("K", it[j].pi_value));
+ }
+ } else {
+ Py_INCREF(Py_None);
+ indextable = Py_None;
+ }
+
+ tuple = Py_BuildValue("sissKsissiiO",
+ p->pd_name, p->pd_propnum, typetable[p->pd_proptype],
+ p->pd_strdefault, p->pd_numdefault,
+ attrtable[p->pd_attr], p->pd_types,
+ p->pd_values, p->pd_colname,
+ p->pd_rightalign, p->pd_visible, indextable);
+ PyDict_SetItemString(d, p->pd_name, tuple);
+ Py_DECREF(tuple);
+ }
+
+ return (d);
+}
+
+static PyMethodDef zfsmethods[] = {
+ {"next_dataset", py_next_dataset, METH_VARARGS,
+ "Get next child dataset or snapshot."},
+ {"get_fsacl", py_get_fsacl, METH_VARARGS, "Get allowed permissions."},
+ {"set_fsacl", py_set_fsacl, METH_VARARGS, "Set allowed permissions."},
+ {"userspace_many", py_userspace_many, METH_VARARGS,
+ "Get user space accounting."},
+ {"userspace_upgrade", py_userspace_upgrade, METH_VARARGS,
+ "Upgrade fs to enable user space accounting."},
+ {"set_cmdstr", py_set_cmdstr, METH_VARARGS,
+ "Set command string for history logging."},
+ {"dataset_props", py_dataset_props, METH_VARARGS,
+ "Get dataset properties."},
+ {"get_proptable", py_get_proptable, METH_NOARGS,
+ "Get property table."},
+ /* Below are not really zfs-specific: */
+ {"sid_to_id", py_sid_to_id, METH_VARARGS, "Map SID to UID/GID."},
+ {"sid_to_name", py_sid_to_name, METH_VARARGS,
+ "Map SID to name@domain."},
+ {"isglobalzone", py_isglobalzone, METH_NOARGS,
+ "Determine if this is the global zone."},
+ {NULL, NULL, 0, NULL}
+};
+
+void
+initioctl(void)
+{
+ PyObject *zfs_ioctl = Py_InitModule("zfs.ioctl", zfsmethods);
+ PyObject *zfs_util = PyImport_ImportModule("zfs.util");
+ PyObject *devfile;
+
+ if (zfs_util == NULL)
+ return;
+
+ ZFSError = PyObject_GetAttrString(zfs_util, "ZFSError");
+ devfile = PyObject_GetAttrString(zfs_util, "dev");
+ zfsdevfd = PyObject_AsFileDescriptor(devfile);
+
+ zfs_prop_init();
+}
diff --git a/usr/src/lib/pyzfs/common/mapfile-vers b/usr/src/lib/pyzfs/common/mapfile-vers
new file mode 100644
index 0000000000..cc3d648b8a
--- /dev/null
+++ b/usr/src/lib/pyzfs/common/mapfile-vers
@@ -0,0 +1,45 @@
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+SUNWprivate {
+ global:
+ initioctl;
+ local:
+ *;
+};
diff --git a/usr/src/lib/pyzfs/common/unallow.py b/usr/src/lib/pyzfs/common/unallow.py
new file mode 100644
index 0000000000..1458dc1328
--- /dev/null
+++ b/usr/src/lib/pyzfs/common/unallow.py
@@ -0,0 +1,28 @@
+#! /usr/bin/python2.4
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+import zfs.allow
+
+do_unallow = zfs.allow.do_allow
diff --git a/usr/src/lib/pyzfs/common/userspace.py b/usr/src/lib/pyzfs/common/userspace.py
new file mode 100644
index 0000000000..93c65ca59d
--- /dev/null
+++ b/usr/src/lib/pyzfs/common/userspace.py
@@ -0,0 +1,277 @@
+#! /usr/bin/python2.4
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+"""This module implements the "zfs userspace" and "zfs groupspace" subcommands.
+The only public interface is the zfs.userspace.do_userspace() function."""
+
+import zfs.util
+import zfs.ioctl
+import zfs.dataset
+import optparse
+import sys
+import pwd
+import grp
+import errno
+
+_ = zfs.util._
+
+# map from property name prefix -> (field name, isgroup)
+props = {
+ "userused@": ("used", False),
+ "userquota@": ("quota", False),
+ "groupused@": ("used", True),
+ "groupquota@": ("quota", True),
+}
+
+def skiptype(options, prop):
+ """Return True if this property (eg "userquota@") should be skipped."""
+ (field, isgroup) = props[prop]
+ if field not in options.fields:
+ return True
+ if isgroup and "posixgroup" not in options.types and \
+ "smbgroup" not in options.types:
+ return True
+ if not isgroup and "posixuser" not in options.types and \
+ "smbuser" not in options.types:
+ return True
+ return False
+
+def updatemax(d, k, v):
+ d[k] = max(d.get(k, None), v)
+
+def new_entry(options, isgroup, domain, rid):
+ """Return a dict("field": value) for this domain (string) + rid (int)"""
+
+ if domain:
+ idstr = "%s-%u" % (domain, rid)
+ else:
+ idstr = "%u" % rid
+
+ (typename, mapfunc) = {
+ (1, 1): ("SMB Group", lambda id: zfs.ioctl.sid_to_name(id, 0)),
+ (1, 0): ("POSIX Group", lambda id: grp.getgrgid(int(id)).gr_name),
+ (0, 1): ("SMB User", lambda id: zfs.ioctl.sid_to_name(id, 1)),
+ (0, 0): ("POSIX User", lambda id: pwd.getpwuid(int(id)).pw_name)
+ }[isgroup, bool(domain)]
+
+ if typename.lower().replace(" ", "") not in options.types:
+ return None
+
+ v = dict()
+ v["type"] = typename
+
+ # python's getpwuid/getgrgid is confused by ephemeral uids
+ if not options.noname and rid < 1<<31:
+ try:
+ v["name"] = mapfunc(idstr)
+ except KeyError:
+ pass
+
+ if "name" not in v:
+ v["name"] = idstr
+ if not domain:
+ # it's just a number, so pad it with spaces so
+ # that it will sort numerically
+ v["name.sort"] = "%20d" % rid
+ # fill in default values
+ v["used"] = "0"
+ v["used.sort"] = 0
+ v["quota"] = "none"
+ v["quota.sort"] = 0
+ return v
+
+def process_one_raw(acct, maxfieldlen, options, prop, elem):
+ """Update the acct and maxfieldlen dicts to incorporate the
+ information from this elem from Dataset.userspace(prop)."""
+
+ (domain, rid, value) = elem
+ (field, isgroup) = props[prop]
+
+ if options.translate and domain:
+ try:
+ rid = zfs.ioctl.sid_to_id("%s-%u" % (domain, rid),
+ not isgroup)
+ domain = None
+ except KeyError:
+ pass;
+ key = (isgroup, domain, rid)
+
+ try:
+ v = acct[key]
+ except KeyError:
+ v = new_entry(options, isgroup, domain, rid)
+ if not v:
+ return
+ acct[key] = v
+
+ # Add our value to an existing value, which may be present if
+ # options.translate is set.
+ value = v[field + ".sort"] = value + v[field + ".sort"]
+
+ if options.parsable:
+ v[field] = str(value)
+ else:
+ v[field] = zfs.util.nicenum(value)
+ for k in v.keys():
+ # some of the .sort fields are integers, so have no len()
+ if isinstance(v[k], str):
+ updatemax(maxfieldlen, k, len(v[k]))
+
+def do_userspace():
+ """Implements the "zfs userspace" and "zfs groupspace" subcommands."""
+
+ def usage(msg=None):
+ parser.print_help()
+ if msg:
+ print
+ parser.exit("zfs: error: " + msg)
+ else:
+ parser.exit()
+
+ if sys.argv[1] == "userspace":
+ defaulttypes = "posixuser,smbuser"
+ else:
+ defaulttypes = "posixgroup,smbgroup"
+
+ fields = ("type", "name", "used", "quota")
+ ljustfields = ("type", "name")
+ types = ("all", "posixuser", "smbuser", "posixgroup", "smbgroup")
+
+ u = _("%s [-niHp] [-o field[,...]] [-sS field] ... \n") % sys.argv[1]
+ u += _(" [-t type[,...]] <filesystem|snapshot>")
+ parser = optparse.OptionParser(usage=u, prog="zfs")
+
+ parser.add_option("-n", action="store_true", dest="noname",
+ help=_("Print numeric ID instead of user/group name"))
+ parser.add_option("-i", action="store_true", dest="translate",
+ help=_("translate SID to posix (possibly ephemeral) ID"))
+ parser.add_option("-H", action="store_true", dest="noheaders",
+ help=_("no headers, tab delimited output"))
+ parser.add_option("-p", action="store_true", dest="parsable",
+ help=_("exact (parsable) numeric output"))
+ parser.add_option("-o", dest="fields", metavar="field[,...]",
+ default="type,name,used,quota",
+ help=_("print only these fields (eg type,name,used,quota)"))
+ parser.add_option("-s", dest="sortfields", metavar="field",
+ type="choice", choices=fields, default=list(),
+ action="callback", callback=zfs.util.append_with_opt,
+ help=_("sort field"))
+ parser.add_option("-S", dest="sortfields", metavar="field",
+ type="choice", choices=fields, #-s sets the default
+ action="callback", callback=zfs.util.append_with_opt,
+ help=_("reverse sort field"))
+ parser.add_option("-t", dest="types", metavar="type[,...]",
+ default=defaulttypes,
+ help=_("print only these types (eg posixuser,smbuser,posixgroup,smbgroup,all)"))
+
+ (options, args) = parser.parse_args(sys.argv[2:])
+ if len(args) != 1:
+ usage(_("wrong number of arguments"))
+ dsname = args[0]
+
+ options.fields = options.fields.split(",")
+ for f in options.fields:
+ if f not in fields:
+ usage(_("invalid field %s") % f)
+
+ options.types = options.types.split(",")
+ for t in options.types:
+ if t not in types:
+ usage(_("invalid type %s") % t)
+
+ if not options.sortfields:
+ options.sortfields = [("-s", "type"), ("-s", "name")]
+
+ if "all" in options.types:
+ options.types = types[1:]
+
+ ds = zfs.dataset.Dataset(dsname, types=("filesystem"))
+
+ if ds.getprop("zoned") and zfs.ioctl.isglobalzone():
+ options.noname = True
+
+ if not ds.getprop("useraccounting"):
+ print(_("Initializing accounting information on old filesystem, please wait..."))
+ ds.userspace_upgrade()
+
+ acct = dict()
+ maxfieldlen = dict()
+
+ # gather and process accounting information
+ for prop in props.keys():
+ if skiptype(options, prop):
+ continue;
+ for elem in ds.userspace(prop):
+ process_one_raw(acct, maxfieldlen, options, prop, elem)
+
+ # print out headers
+ if not options.noheaders:
+ line = str()
+ for field in options.fields:
+ # make sure the field header will fit
+ updatemax(maxfieldlen, field, len(field))
+
+ if field in ljustfields:
+ fmt = "%-*s "
+ else:
+ fmt = "%*s "
+ line += fmt % (maxfieldlen[field], field.upper())
+ print(line)
+
+ # custom sorting func
+ def cmpkey(val):
+ l = list()
+ for (opt, field) in options.sortfields:
+ try:
+ n = val[field + ".sort"]
+ except KeyError:
+ n = val[field]
+ if opt == "-S":
+ # reverse sorting
+ try:
+ n = -n
+ except TypeError:
+ # it's a string; decompose it
+ # into an array of integers,
+ # each one the negative of that
+ # character
+ n = [-ord(c) for c in n]
+ l.append(n)
+ return l
+
+ # print out data lines
+ for val in sorted(acct.itervalues(), key=cmpkey):
+ line = str()
+ for field in options.fields:
+ if options.noheaders:
+ line += val[field]
+ line += "\t"
+ else:
+ if field in ljustfields:
+ fmt = "%-*s "
+ else:
+ fmt = "%*s "
+ line += fmt % (maxfieldlen[field], val[field])
+ print(line)
diff --git a/usr/src/lib/pyzfs/common/util.py b/usr/src/lib/pyzfs/common/util.py
new file mode 100644
index 0000000000..14d05a8bc1
--- /dev/null
+++ b/usr/src/lib/pyzfs/common/util.py
@@ -0,0 +1,138 @@
+#! /usr/bin/python2.4
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+"""This module provides utility functions for ZFS.
+zfs.util.dev -- a file object of /dev/zfs """
+
+import gettext
+import errno
+import os
+# Note: this module (zfs.util) should not import zfs.ioctl, because that
+# would introduce a circular dependency
+
+errno.ECANCELED = 47
+errno.ENOTSUP = 48
+
+dev = open("/dev/zfs", "w")
+
+_ = gettext.translation("SUNW_OST_OSLIB", "/usr/lib/locale",
+ fallback=True).gettext
+
+def default_repr(self):
+ """A simple __repr__ function."""
+ if self.__slots__:
+ str = "<" + self.__class__.__name__
+ for v in self.__slots__:
+ str += " %s: %r" % (v, getattr(self, v))
+ return str + ">"
+ else:
+ return "<%s %s>" % \
+ (self.__class__.__name__, repr(self.__dict__))
+
+class ZFSError(StandardError):
+ """This exception class represents a potentially user-visible
+ ZFS error. If uncaught, it will be printed and the process will
+ exit with exit code 1.
+
+ errno -- the error number (eg, from ioctl(2))."""
+
+ __slots__ = "why", "task", "errno"
+ __repr__ = default_repr
+
+ def __init__(self, eno, task=None, why=None):
+ """Create a ZFS exception.
+ eno -- the error number (errno)
+ task -- a string describing the task that failed
+ why -- a string describing why it failed (defaults to
+ strerror(eno))"""
+
+ self.errno = eno
+ self.task = task
+ self.why = why
+
+ def __str__(self):
+ s = ""
+ if self.task:
+ s += self.task + ": "
+ if self.why:
+ s += self.why
+ else:
+ s += self.strerror
+ return s
+
+ __strs = {
+ errno.EPERM: _("permission denied"),
+ errno.ECANCELED:
+ _("delegated administration is disabled on pool"),
+ errno.EINTR: _("signal received"),
+ errno.EIO: _("I/O error"),
+ errno.ENOENT: _("dataset does not exist"),
+ errno.ENOSPC: _("out of space"),
+ errno.EEXIST: _("dataset already exists"),
+ errno.EBUSY: _("dataset is busy"),
+ errno.EROFS:
+ _("snapshot permissions cannot be modified"),
+ errno.ENAMETOOLONG: _("dataset name is too long"),
+ errno.ENOTSUP: _("unsupported version"),
+ errno.EAGAIN: _("pool I/O is currently suspended"),
+ }
+
+ __strs[errno.EACCES] = __strs[errno.EPERM]
+ __strs[errno.ENXIO] = __strs[errno.EIO]
+ __strs[errno.ENODEV] = __strs[errno.EIO]
+ __strs[errno.EDQUOT] = __strs[errno.ENOSPC]
+
+ @property
+ def strerror(self):
+ return ZFSError.__strs.get(self.errno, os.strerror(self.errno))
+
+def nicenum(num):
+ """Return a nice string (eg "1.23M") for this integer."""
+ index = 0;
+ n = num;
+
+ while n >= 1024:
+ n /= 1024
+ index += 1
+
+ u = " KMGTPE"[index]
+ if index == 0:
+ return "%u" % n;
+ elif n >= 100 or num & ((1024*index)-1) == 0:
+ # it's an exact multiple of its index, or it wouldn't
+ # fit as floating point, so print as an integer
+ return "%u%c" % (n, u)
+ else:
+ # due to rounding, it's tricky to tell what precision to
+ # use; try each precision and see which one fits
+ for i in (2, 1, 0):
+ s = "%.*f%c" % (i, float(num) / (1<<(10*index)), u)
+ if len(s) <= 5:
+ return s
+
+def append_with_opt(option, opt, value, parser):
+ """A function for OptionParser which appends a tuple (opt, value)."""
+ getattr(parser.values, option.dest).append((opt, value))
+
diff --git a/usr/src/lib/pyzfs/i386/Makefile b/usr/src/lib/pyzfs/i386/Makefile
new file mode 100644
index 0000000000..4ce880c2ae
--- /dev/null
+++ b/usr/src/lib/pyzfs/i386/Makefile
@@ -0,0 +1,27 @@
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS)
diff --git a/usr/src/lib/pyzfs/sparc/Makefile b/usr/src/lib/pyzfs/sparc/Makefile
new file mode 100644
index 0000000000..4ce880c2ae
--- /dev/null
+++ b/usr/src/lib/pyzfs/sparc/Makefile
@@ -0,0 +1,27 @@
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS)