+ return (0);
+}
+
+/*
+ * Utility function to get the given numeric property. Does no validation that
+ * the given property is the appropriate type; should only be used with
+ * hard-coded property types.
+ */
+uint64_t
+zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop)
+{
+ char *source;
+ uint64_t val;
+
+ (void) get_numeric_property(zhp, prop, NULL, &source, &val);
+
+ return (val);
+}
+
+int
+zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val)
+{
+ char buf[64];
+
+ (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val);
+ return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf));
+}
+
+/*
+ * Similar to zfs_prop_get(), but returns the value as an integer.
+ */
+int
+zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value,
+ zprop_source_t *src, char *statbuf, size_t statlen)
+{
+ char *source;
+
+ /*
+ * Check to see if this property applies to our object
+ */
+ if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) {
+ return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE,
+ dgettext(TEXT_DOMAIN, "cannot get property '%s'"),
+ zfs_prop_to_name(prop)));
+ }
+
+ if (src)
+ *src = ZPROP_SRC_NONE;
+
+ if (get_numeric_property(zhp, prop, src, &source, value) != 0)
+ return (-1);
+
+ get_source(zhp, src, source, statbuf, statlen);
+
+ return (0);
+}
+
+#ifdef HAVE_IDMAP
+static int
+idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser,
+ char **domainp, idmap_rid_t *ridp)
+{
+ idmap_get_handle_t *get_hdl = NULL;
+ idmap_stat status;
+ int err = EINVAL;
+
+ if (idmap_get_create(&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);
+ return (err);
+}
+#endif /* HAVE_IDMAP */
+
+/*
+ * 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;
+ char *numericsid = NULL;
+ 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, '@')) {
+#ifdef HAVE_IDMAP
+ /*
+ * It's a SID name (eg "user@domain") that needs to be
+ * turned into S-1-domainID-RID.
+ */
+ directory_error_t e;
+ if (zoned && getzoneid() == GLOBAL_ZONEID)
+ return (ENOENT);
+ if (isuser) {
+ e = directory_sid_from_user_name(NULL,
+ cp, &numericsid);
+ } else {
+ e = directory_sid_from_group_name(NULL,
+ cp, &numericsid);
+ }
+ if (e != NULL) {
+ directory_error_free(e);
+ return (ENOENT);
+ }
+ if (numericsid == NULL)
+ return (ENOENT);
+ cp = numericsid;
+ /* will be further decoded below */
+#else
+ return (ENOSYS);
+#endif /* HAVE_IDMAP */
+ }
+
+ if (strncmp(cp, "S-1-", 4) == 0) {
+ /* It's a numeric SID (eg "S-1-234-567-89") */
+ (void) strlcpy(domain, cp, domainlen);
+ cp = strrchr(domain, '-');
+ *cp = '\0';
+ cp++;
+
+ errno = 0;
+ *ridp = strtoull(cp, &end, 10);
+ if (numericsid) {
+ free(numericsid);
+ numericsid = NULL;
+ }
+ 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 {
+#ifdef HAVE_IDMAP
+ /* 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) strlcpy(domain, mapdomain, domainlen);
+ *ridp = rid;
+ } else {
+ *ridp = id;
+ }
+#else
+ return (ENOSYS);
+#endif /* HAVE_IDMAP */
+ }
+
+ ASSERT3P(numericsid, ==, NULL);
+ return (0);
+}
+
+static int
+zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname,
+ uint64_t *propvalue, zfs_userquota_prop_t *typep)
+{
+ int err;
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+
+ (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));