/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
*/
#include <ctype.h>
namecheck_err_t why;
char what;
+ (void) zfs_prop_get_table();
if (dataset_namecheck(path, &why, &what) != 0) {
if (hdl != NULL) {
switch (why) {
zhp->zfs_head_type = ZFS_TYPE_VOLUME;
else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM;
+ else if (zhp->zfs_dmustats.dds_type == DMU_OST_OTHER)
+ return (-1); /* zpios' and other testing datasets are
+ of this type, ignore if encountered */
else
abort();
return (zhp);
}
+static zfs_handle_t *
+make_dataset_simple_handle_zc(zfs_handle_t *pzhp, zfs_cmd_t *zc)
+{
+ zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
+
+ if (zhp == NULL)
+ return (NULL);
+
+ zhp->zfs_hdl = pzhp->zfs_hdl;
+ (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name));
+ zhp->zfs_head_type = pzhp->zfs_type;
+ zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
+ zhp->zpool_hdl = zpool_handle(zhp);
+
+ return (zhp);
+}
+
/*
* Opens the given snapshot, filesystem, or volume. The 'types'
* argument is a mask of acceptable types. The function will print an
/*FALLTHRU*/
-#ifdef HAVE_ZPL
case ZFS_PROP_SHARESMB:
case ZFS_PROP_SHARENFS:
/*
}
break;
-#endif /* HAVE_ZPL */
case ZFS_PROP_UTF8ONLY:
chosen_utf = (int)intval;
break;
}
}
+static boolean_t
+zfs_is_namespace_prop(zfs_prop_t prop)
+{
+ switch (prop) {
+
+ case ZFS_PROP_ATIME:
+ case ZFS_PROP_DEVICES:
+ case ZFS_PROP_EXEC:
+ case ZFS_PROP_SETUID:
+ case ZFS_PROP_READONLY:
+ case ZFS_PROP_XATTR:
+ case ZFS_PROP_NBMAND:
+ return (B_TRUE);
+
+ default:
+ return (B_FALSE);
+ }
+}
+
/*
* Given a property name and value, set the property for the given dataset.
*/
if (do_prefix)
ret = changelist_postfix(cl);
- /*
- * Refresh the statistics so the new property value
- * is reflected.
- */
- if (ret == 0)
+ if (ret == 0) {
+ /*
+ * Refresh the statistics so the new property
+ * value is reflected.
+ */
(void) get_stats(zhp);
+
+ /*
+ * Remount the filesystem to propagate the change
+ * if one of the options handled by the generic
+ * Linux namespace layer has been modified.
+ */
+ if (zfs_is_namespace_prop(prop) &&
+ zfs_is_mounted(zhp, NULL))
+ ret = zfs_mount(zhp, MNTOPT_REMOUNT, 0);
+ }
}
error:
* True DSL properties are stored in an nvlist. The following two functions
* extract them appropriately.
*/
-static uint64_t
+uint64_t
getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
{
nvlist_t *nv;
* zfs_prop_get_int() are built using this interface.
*
* Certain properties can be overridden using 'mount -o'. In this case, scan
- * the contents of the /etc/mnttab entry, searching for the appropriate options.
+ * the contents of the /etc/mtab entry, searching for the appropriate options.
* If they differ from the on-disk values, report the current values and mark
* the source "temporary".
*/
/*
* Because looking up the mount options is potentially expensive
- * (iterating over all of /etc/mnttab), we defer its calculation until
+ * (iterating over all of /etc/mtab), we defer its calculation until
* we're looking up a property which requires its presence.
*/
if (!zhp->zfs_mntcheck &&
}
break;
+ case ZFS_PROP_REFRATIO:
case ZFS_PROP_COMPRESSRATIO:
if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
return (-1);
* 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
+ * Eg: groupquota@staff -> ZFS_PROP_GROUPQUOTA, "", 1234
+ * Eg: groupused@staff -> ZFS_PROP_GROUPUSED, "", 1234
*/
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;
+ char *cp;
boolean_t isuser;
+ boolean_t isgroup;
+ struct passwd *pw;
+ struct group *gr;
domain[0] = '\0';
return (EINVAL);
*typep = type;
- isuser = (type == ZFS_PROP_USERQUOTA ||
- type == ZFS_PROP_USERUSED);
+ isuser = (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_USERUSED);
+ isgroup = (type == ZFS_PROP_GROUPQUOTA || type == ZFS_PROP_GROUPUSED);
cp = strchr(propname, '@') + 1;
- if (strchr(cp, '@')) {
+ if (isuser && (pw = getpwnam(cp)) != NULL) {
+ if (zoned && getzoneid() == GLOBAL_ZONEID)
+ return (ENOENT);
+ *ridp = pw->pw_uid;
+ } else if (isgroup && (gr = getgrnam(cp)) != NULL) {
+ if (zoned && getzoneid() == GLOBAL_ZONEID)
+ return (ENOENT);
+ *ridp = gr->gr_gid;
+ } else 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;
+ char *numericsid = NULL;
+ char *end;
+
if (zoned && getzoneid() == GLOBAL_ZONEID)
return (ENOENT);
if (isuser) {
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';
errno = 0;
*ridp = strtoull(cp, &end, 10);
- if (numericsid) {
- free(numericsid);
- numericsid = NULL;
- }
+ free(numericsid);
+
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
+ return (ENOSYS);
+#endif /* HAVE_IDMAP */
} else {
#ifdef HAVE_IDMAP
/* It's a user/group ID (eg "12345"). */
- uid_t id = strtoul(cp, &end, 10);
+ uid_t id;
idmap_rid_t rid;
char *mapdomain;
+ char *end;
+ id = strtoul(cp, &end, 10);
if (*end != '\0')
return (EINVAL);
if (id > MAXUID) {
#endif /* HAVE_IDMAP */
}
- ASSERT3P(numericsid, ==, NULL);
return (0);
}
* Iterate over all snapshots
*/
int
-zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+zfs_iter_snapshots(zfs_handle_t *zhp, boolean_t simple, zfs_iter_f func,
+ void *data)
{
zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
zfs_handle_t *nzhp;
if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
return (0);
+ zc.zc_simple = simple;
+
if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
return (-1);
while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
&zc)) == 0) {
- if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
- &zc)) == NULL) {
+ if (simple)
+ nzhp = make_dataset_simple_handle_zc(zhp, &zc);
+ else
+ nzhp = make_dataset_handle_zc(zhp->zfs_hdl, &zc);
+ if (nzhp == NULL)
continue;
- }
if ((ret = func(nzhp, data)) != 0) {
zcmd_free_nvlists(&zc);
if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
return (ret);
- return (zfs_iter_snapshots(zhp, func, data));
+ return (zfs_iter_snapshots(zhp, B_FALSE, func, data));
}
/*
goto ancestorerr;
}
-#ifdef HAVE_ZPL
if (zfs_mount(h, NULL, 0) != 0) {
opname = dgettext(TEXT_DOMAIN, "mount");
goto ancestorerr;
opname = dgettext(TEXT_DOMAIN, "share");
goto ancestorerr;
}
-#endif /* HAVE_ZPL */
zfs_close(h);
}
return (-1);
(void) zfs_prop_get(pzhp, ZFS_PROP_MOUNTPOINT, pd.cb_mountpoint,
sizeof (pd.cb_mountpoint), NULL, NULL, 0, FALSE);
- ret = zfs_iter_snapshots(pzhp, promote_snap_cb, &pd);
+ ret = zfs_iter_snapshots(pzhp, B_FALSE, promote_snap_cb, &pd);
if (ret != 0) {
zfs_close(pzhp);
return (-1);
if (ret != 0) {
int save_errno = errno;
- (void) zfs_iter_snapshots(pzhp, promote_snap_done_cb, &pd);
+ (void) zfs_iter_snapshots(pzhp, B_FALSE, promote_snap_done_cb,
+ &pd);
zfs_close(pzhp);
switch (save_errno) {
return (zfs_standard_error(hdl, save_errno, errbuf));
}
} else {
- (void) zfs_iter_snapshots(zhp, promote_snap_done_cb, &pd);
+ (void) zfs_iter_snapshots(zhp, B_FALSE, promote_snap_done_cb,
+ &pd);
}
zfs_close(pzhp);
zvol_remove_link(libzfs_handle_t *hdl, const char *dataset)
{
zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ int timeout = 3000; /* in milliseconds */
+ int error = 0;
+ int i;
(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc) != 0) {
+ /*
+ * Due to concurrent updates by udev the device may be reported as
+ * busy. In this case don't immediately fail. Instead briefly delay
+ * and retry the ioctl() which is now likely to succeed. If unable
+ * remove the link after timeout milliseconds return the failure.
+ */
+ for (i = 0; i < timeout; i++) {
+ error = ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc);
+ if (error && errno == EBUSY) {
+ usleep(1000);
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ if (error) {
switch (errno) {
case ENXIO:
/*
default:
return (zfs_standard_error_fmt(hdl, errno,
dgettext(TEXT_DOMAIN, "cannot remove device "
- "links for '%s'"), dataset));
+ "links for '%s': %s"), dataset, strerror(errno)));
}
}
return (0);
}
-#ifdef HAVE_ZPL
-int
-zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path,
- char *resource, void *export, void *sharetab,
- int sharemax, zfs_share_op_t operation)
-{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
- int error;
-
- (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
- if (resource)
- (void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string));
- zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab;
- zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export;
- zc.zc_share.z_sharetype = operation;
- zc.zc_share.z_sharemax = sharemax;
- error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc);
- return (error);
-}
-#endif /* HAVE_ZPL */
-
void
zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props)
{
return (0);
}
+int
+zfs_get_fsacl(zfs_handle_t *zhp, nvlist_t **nvl)
+{
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
+ int nvsz = 2048;
+ void *nvbuf;
+ int err = 0;
+ char errbuf[ZFS_MAXNAMELEN+32];
+
+ assert(zhp->zfs_type == ZFS_TYPE_VOLUME ||
+ zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
+
+tryagain:
+
+ nvbuf = malloc(nvsz);
+ if (nvbuf == NULL) {
+ err = (zfs_error(hdl, EZFS_NOMEM, strerror(errno)));
+ goto out;
+ }
+
+ zc.zc_nvlist_dst_size = nvsz;
+ zc.zc_nvlist_dst = (uintptr_t)nvbuf;
+
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN);
+
+ if (ioctl(hdl->libzfs_fd, ZFS_IOC_GET_FSACL, &zc) != 0) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot get permissions on '%s'"),
+ zc.zc_name);
+ switch (errno) {
+ case ENOMEM:
+ free(nvbuf);
+ nvsz = zc.zc_nvlist_dst_size;
+ goto tryagain;
+
+ case ENOTSUP:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "pool must be upgraded"));
+ err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
+ break;
+ case EINVAL:
+ err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
+ break;
+ case ENOENT:
+ err = zfs_error(hdl, EZFS_NOENT, errbuf);
+ break;
+ default:
+ err = zfs_standard_error_fmt(hdl, errno, errbuf);
+ break;
+ }
+ } else {
+ /* success */
+ int rc = nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0);
+ if (rc) {
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(
+ TEXT_DOMAIN, "cannot get permissions on '%s'"),
+ zc.zc_name);
+ err = zfs_standard_error_fmt(hdl, rc, errbuf);
+ }
+ }
+
+ free(nvbuf);
+out:
+ return (err);
+}
+
+int
+zfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl)
+{
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
+ char *nvbuf;
+ char errbuf[ZFS_MAXNAMELEN+32];
+ size_t nvsz;
+ int err;
+
+ assert(zhp->zfs_type == ZFS_TYPE_VOLUME ||
+ zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
+
+ 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);
+
+ zc.zc_nvlist_src_size = nvsz;
+ zc.zc_nvlist_src = (uintptr_t)nvbuf;
+ zc.zc_perm_action = un;
+
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+
+ if (zfs_ioctl(hdl, ZFS_IOC_SET_FSACL, &zc) != 0) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot set permissions on '%s'"),
+ zc.zc_name);
+ switch (errno) {
+ case ENOTSUP:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "pool must be upgraded"));
+ err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
+ break;
+ case EINVAL:
+ err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
+ break;
+ case ENOENT:
+ err = zfs_error(hdl, EZFS_NOENT, errbuf);
+ break;
+ default:
+ err = zfs_standard_error_fmt(hdl, errno, errbuf);
+ break;
+ }
+ }
+
+ free(nvbuf);
+
+ return (err);
+}
+
+int
+zfs_get_holds(zfs_handle_t *zhp, nvlist_t **nvl)
+{
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
+ int nvsz = 2048;
+ void *nvbuf;
+ int err = 0;
+ char errbuf[ZFS_MAXNAMELEN+32];
+
+ assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
+
+tryagain:
+
+ nvbuf = malloc(nvsz);
+ if (nvbuf == NULL) {
+ err = (zfs_error(hdl, EZFS_NOMEM, strerror(errno)));
+ goto out;
+ }
+
+ zc.zc_nvlist_dst_size = nvsz;
+ zc.zc_nvlist_dst = (uintptr_t)nvbuf;
+
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN);
+
+ if (zfs_ioctl(hdl, ZFS_IOC_GET_HOLDS, &zc) != 0) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"),
+ zc.zc_name);
+ switch (errno) {
+ case ENOMEM:
+ free(nvbuf);
+ nvsz = zc.zc_nvlist_dst_size;
+ goto tryagain;
+
+ case ENOTSUP:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "pool must be upgraded"));
+ err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
+ break;
+ case EINVAL:
+ err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
+ break;
+ case ENOENT:
+ err = zfs_error(hdl, EZFS_NOENT, errbuf);
+ break;
+ default:
+ err = zfs_standard_error_fmt(hdl, errno, errbuf);
+ break;
+ }
+ } else {
+ /* success */
+ int rc = nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0);
+ if (rc) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"),
+ zc.zc_name);
+ err = zfs_standard_error_fmt(hdl, rc, errbuf);
+ }
+ }
+
+ free(nvbuf);
+out:
+ return (err);
+}
+
uint64_t
zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props)
{