/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright (c) 2012 DEY Storage Systems, Inc. All rights reserved.
+ * Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
+ * Copyright (c) 2013 Martin Matuska. All rights reserved.
+ * Copyright (c) 2013 Steven Hartland. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
*/
#include <ctype.h>
#include <grp.h>
#include <stddef.h>
#include <ucred.h>
+#ifdef HAVE_IDMAP
#include <idmap.h>
#include <aclutils.h>
#include <directory.h>
+#endif /* HAVE_IDMAP */
#include <sys/dnode.h>
#include <sys/spa.h>
return (dgettext(TEXT_DOMAIN, "snapshot"));
case ZFS_TYPE_VOLUME:
return (dgettext(TEXT_DOMAIN, "volume"));
+ default:
+ break;
}
return (NULL);
}
-/*
- * Given a path and mask of ZFS types, return a string describing this dataset.
- * This is used when we fail to open a dataset and we cannot get an exact type.
- * We guess what the type would have been based on the path and the mask of
- * acceptable types.
- */
-static const char *
-path_to_str(const char *path, int types)
-{
- /*
- * When given a single type, always report the exact type.
- */
- if (types == ZFS_TYPE_SNAPSHOT)
- return (dgettext(TEXT_DOMAIN, "snapshot"));
- if (types == ZFS_TYPE_FILESYSTEM)
- return (dgettext(TEXT_DOMAIN, "filesystem"));
- if (types == ZFS_TYPE_VOLUME)
- return (dgettext(TEXT_DOMAIN, "volume"));
-
- /*
- * The user is requesting more than one type of dataset. If this is the
- * case, consult the path itself. If we're looking for a snapshot, and
- * a '@' is found, then report it as "snapshot". Otherwise, remove the
- * snapshot attribute and try again.
- */
- if (types & ZFS_TYPE_SNAPSHOT) {
- if (strchr(path, '@') != NULL)
- return (dgettext(TEXT_DOMAIN, "snapshot"));
- return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT));
- }
-
- /*
- * The user has requested either filesystems or volumes.
- * We have no way of knowing a priori what type this would be, so always
- * report it as "filesystem" or "volume", our two primitive types.
- */
- if (types & ZFS_TYPE_FILESYSTEM)
- return (dgettext(TEXT_DOMAIN, "filesystem"));
-
- assert(types & ZFS_TYPE_VOLUME);
- return (dgettext(TEXT_DOMAIN, "volume"));
-}
-
/*
* Validate a ZFS path. This is used even before trying to open the dataset, to
* provide a more meaningful error message. We call zfs_error_aux() to
namecheck_err_t why;
char what;
+ (void) zfs_prop_get_table();
if (dataset_namecheck(path, &why, &what) != 0) {
if (hdl != NULL) {
switch (why) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"reserved disk name"));
break;
+ default:
+ break;
}
}
int len;
zpool_handle_t *zph;
- len = strcspn(zhp->zfs_name, "/@") + 1;
+ len = strcspn(zhp->zfs_name, "/@#") + 1;
pool_name = zfs_alloc(zhp->zfs_hdl, len);
(void) strlcpy(pool_name, zhp->zfs_name, len);
{
libzfs_handle_t *hdl = zhp->zfs_hdl;
nvlist_t *recvdprops;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0"};
int err;
if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
get_stats(zfs_handle_t *zhp)
{
int rc = 0;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0"};
if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
return (-1);
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);
else
abort();
zfs_handle_t *
make_dataset_handle(libzfs_handle_t *hdl, const char *path)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0"};
zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
return (zhp);
}
-static zfs_handle_t *
+zfs_handle_t *
make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
{
zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
return (zhp);
}
+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);
+}
+
+zfs_handle_t *
+zfs_handle_dup(zfs_handle_t *zhp_orig)
+{
+ zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
+
+ if (zhp == NULL)
+ return (NULL);
+
+ zhp->zfs_hdl = zhp_orig->zfs_hdl;
+ zhp->zpool_hdl = zhp_orig->zpool_hdl;
+ (void) strlcpy(zhp->zfs_name, zhp_orig->zfs_name,
+ sizeof (zhp->zfs_name));
+ zhp->zfs_type = zhp_orig->zfs_type;
+ zhp->zfs_head_type = zhp_orig->zfs_head_type;
+ zhp->zfs_dmustats = zhp_orig->zfs_dmustats;
+ if (zhp_orig->zfs_props != NULL) {
+ if (nvlist_dup(zhp_orig->zfs_props, &zhp->zfs_props, 0) != 0) {
+ (void) no_memory(zhp->zfs_hdl);
+ zfs_close(zhp);
+ return (NULL);
+ }
+ }
+ if (zhp_orig->zfs_user_props != NULL) {
+ if (nvlist_dup(zhp_orig->zfs_user_props,
+ &zhp->zfs_user_props, 0) != 0) {
+ (void) no_memory(zhp->zfs_hdl);
+ zfs_close(zhp);
+ return (NULL);
+ }
+ }
+ if (zhp_orig->zfs_recvd_props != NULL) {
+ if (nvlist_dup(zhp_orig->zfs_recvd_props,
+ &zhp->zfs_recvd_props, 0)) {
+ (void) no_memory(zhp->zfs_hdl);
+ zfs_close(zhp);
+ return (NULL);
+ }
+ }
+ zhp->zfs_mntcheck = zhp_orig->zfs_mntcheck;
+ if (zhp_orig->zfs_mntopts != NULL) {
+ zhp->zfs_mntopts = zfs_strdup(zhp_orig->zfs_hdl,
+ zhp_orig->zfs_mntopts);
+ }
+ zhp->zfs_props_table = zhp_orig->zfs_props_table;
+ return (zhp);
+}
+
+boolean_t
+zfs_bookmark_exists(const char *path)
+{
+ nvlist_t *bmarks;
+ nvlist_t *props;
+ char fsname[ZFS_MAXNAMELEN];
+ char *bmark_name;
+ char *pound;
+ int err;
+ boolean_t rv;
+
+
+ (void) strlcpy(fsname, path, sizeof (fsname));
+ pound = strchr(fsname, '#');
+ if (pound == NULL)
+ return (B_FALSE);
+
+ *pound = '\0';
+ bmark_name = pound + 1;
+ props = fnvlist_alloc();
+ err = lzc_get_bookmarks(fsname, props, &bmarks);
+ nvlist_free(props);
+ if (err != 0) {
+ nvlist_free(bmarks);
+ return (B_FALSE);
+ }
+
+ rv = nvlist_exists(bmarks, bmark_name);
+ nvlist_free(bmarks);
+ return (rv);
+}
+
+zfs_handle_t *
+make_bookmark_handle(zfs_handle_t *parent, const char *path,
+ nvlist_t *bmark_props)
+{
+ zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
+
+ if (zhp == NULL)
+ return (NULL);
+
+ /* Fill in the name. */
+ zhp->zfs_hdl = parent->zfs_hdl;
+ (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
+
+ /* Set the property lists. */
+ if (nvlist_dup(bmark_props, &zhp->zfs_props, 0) != 0) {
+ free(zhp);
+ return (NULL);
+ }
+
+ /* Set the types. */
+ zhp->zfs_head_type = parent->zfs_head_type;
+ zhp->zfs_type = ZFS_TYPE_BOOKMARK;
+
+ if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL) {
+ nvlist_free(zhp->zfs_props);
+ free(zhp);
+ return (NULL);
+ }
+
+ return (zhp);
+}
+
/*
* Opens the given snapshot, filesystem, or volume. The 'types'
* argument is a mask of acceptable types. The function will print an
sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node));
}
-void
+int
libzfs_mnttab_update(libzfs_handle_t *hdl)
{
struct mnttab entry;
- rewind(hdl->libzfs_mnttab);
+ /* Reopen MNTTAB to prevent reading stale data from open file */
+ if (freopen(MNTTAB, "r", hdl->libzfs_mnttab) == NULL)
+ return (ENOENT);
+
while (getmntent(hdl->libzfs_mnttab, &entry) == 0) {
mnttab_node_t *mtn;
+ avl_index_t where;
if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
continue;
+
mtn = zfs_alloc(hdl, sizeof (mnttab_node_t));
mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special);
mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp);
mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype);
mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts);
+
+ /* Exclude duplicate mounts */
+ if (avl_find(&hdl->libzfs_mnttab_cache, mtn, &where) != NULL) {
+ free(mtn->mtn_mt.mnt_special);
+ free(mtn->mtn_mt.mnt_mountp);
+ free(mtn->mtn_mt.mnt_fstype);
+ free(mtn->mtn_mt.mnt_mntopts);
+ free(mtn);
+ continue;
+ }
+
avl_add(&hdl->libzfs_mnttab_cache, mtn);
}
+
+ return (0);
}
void
void *cookie = NULL;
mnttab_node_t *mtn;
- while (mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) {
+ while ((mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie))) {
free(mtn->mtn_mt.mnt_special);
free(mtn->mtn_mt.mnt_mountp);
free(mtn->mtn_mt.mnt_fstype);
{
mnttab_node_t find;
mnttab_node_t *mtn;
+ int error;
if (!hdl->libzfs_mnttab_enable) {
struct mnttab srch = { 0 };
if (avl_numnodes(&hdl->libzfs_mnttab_cache))
libzfs_mnttab_fini(hdl);
- rewind(hdl->libzfs_mnttab);
+
+ /* Reopen MNTTAB to prevent reading stale data from open file */
+ if (freopen(MNTTAB, "r", hdl->libzfs_mnttab) == NULL)
+ return (ENOENT);
+
srch.mnt_special = (char *)fsname;
srch.mnt_fstype = MNTTYPE_ZFS;
if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0)
}
if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0)
- libzfs_mnttab_update(hdl);
+ if ((error = libzfs_mnttab_update(hdl)) != 0)
+ return (error);
find.mtn_mt.mnt_special = (char *)fsname;
mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL);
mnttab_node_t *ret;
find.mtn_mt.mnt_special = (char *)fsname;
- if (ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) {
+ if ((ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL))) {
avl_remove(&hdl->libzfs_mnttab_cache, ret);
free(ret->mtn_mt.mnt_special);
free(ret->mtn_mt.mnt_mountp);
goto error;
}
continue;
+ } else if (prop == ZPROP_INVAL && zfs_prop_written(propname)) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "'%s' is readonly"),
+ propname);
+ (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
+ goto error;
}
if (prop == ZPROP_INVAL) {
goto error;
}
- if (!zfs_prop_valid_for_type(prop, type)) {
+ if (!zfs_prop_valid_for_type(prop, type, B_FALSE)) {
zfs_error_aux(hdl,
dgettext(TEXT_DOMAIN, "'%s' does not "
"apply to datasets of this type"), propname);
break;
}
- case ZFS_PROP_RECORDSIZE:
case ZFS_PROP_VOLBLOCKSIZE:
- /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */
+ case ZFS_PROP_RECORDSIZE:
+ {
+ int maxbs = SPA_MAXBLOCKSIZE;
+ char buf[64];
+
+ if (zhp != NULL) {
+ maxbs = zpool_get_prop_int(zhp->zpool_hdl,
+ ZPOOL_PROP_MAXBLOCKSIZE, NULL);
+ }
+ /*
+ * The value must be a power of two between
+ * SPA_MINBLOCKSIZE and maxbs.
+ */
if (intval < SPA_MINBLOCKSIZE ||
- intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) {
+ intval > maxbs || !ISP2(intval)) {
+ zfs_nicenum(maxbs, buf, sizeof (buf));
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be power of 2 from %u "
- "to %uk"), propname,
- (uint_t)SPA_MINBLOCKSIZE,
- (uint_t)SPA_MAXBLOCKSIZE >> 10);
+ "'%s' must be power of 2 from 512B "
+ "to %s"), propname, buf);
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
goto error;
}
break;
-
+ }
case ZFS_PROP_MLSLABEL:
{
+#ifdef HAVE_MLSLABEL
/*
* Verify the mlslabel string and convert to
* internal hex label string.
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
m_label_free(new_sl); /* OK if null */
goto error;
-
+#else
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "mlslabels are unsupported"));
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+#endif /* HAVE_MLSLABEL */
}
case ZFS_PROP_MOUNTPOINT:
"component of '%s' is too long"),
propname);
break;
+ default:
+ break;
}
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
goto error;
case ZFS_PROP_NORMALIZE:
chosen_normal = (int)intval;
break;
+ default:
+ break;
}
/*
goto error;
}
break;
+ default:
+ break;
}
}
}
(void) zfs_error(hdl, EZFS_DSREADONLY, errbuf);
break;
+ case E2BIG:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "property value too long"));
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ break;
+
case ENOTSUP:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"pool and or dataset must be upgraded to set this "
break;
case ERANGE:
- if (prop == ZFS_PROP_COMPRESSION) {
+ if (prop == ZFS_PROP_COMPRESSION ||
+ prop == ZFS_PROP_RECORDSIZE) {
(void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"property setting is not allowed on "
"bootable datasets"));
}
}
+static boolean_t
+zfs_is_namespace_prop(zfs_prop_t prop)
+{
+ switch (prop) {
+
+ case ZFS_PROP_ATIME:
+ case ZFS_PROP_RELATIME:
+ 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.
*/
int
zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0"};
int ret = -1;
prop_changelist_t *cl = NULL;
char errbuf[1024];
libzfs_handle_t *hdl = zhp->zfs_hdl;
nvlist_t *nvl = NULL, *realprops;
zfs_prop_t prop;
- boolean_t do_prefix;
- uint64_t idx;
- int added_resv;
+ boolean_t do_prefix = B_TRUE;
+ int added_resv = 0;
(void) snprintf(errbuf, sizeof (errbuf),
dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
}
/*
- * If the dataset's canmount property is being set to noauto,
- * then we want to prevent unmounting & remounting it.
+ * We don't want to unmount & remount the dataset when changing
+ * its canmount property to 'on' or 'noauto'. We only use
+ * the changelist logic to unmount when setting canmount=off.
*/
- do_prefix = !((prop == ZFS_PROP_CANMOUNT) &&
- (zprop_string_to_index(prop, propval, &idx,
- ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO));
+ if (prop == ZFS_PROP_CANMOUNT) {
+ uint64_t idx;
+ int err = zprop_string_to_index(prop, propval, &idx,
+ ZFS_TYPE_DATASET);
+ if (err == 0 && idx != ZFS_CANMOUNT_OFF)
+ do_prefix = B_FALSE;
+ }
if (do_prefix && (ret = changelist_prefix(cl)) != 0)
goto error;
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:
int
zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0"};
int ret;
prop_changelist_t *cl;
libzfs_handle_t *hdl = zhp->zfs_hdl;
/*
* Check to see if the value applies to this type
*/
- if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
+ if (!zfs_prop_valid_for_type(prop, zhp->zfs_type, B_FALSE))
return (zfs_error(hdl, EZFS_PROPTYPE, errbuf));
/*
* Refresh the statistics so the new property 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".
*/
get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
char **source, uint64_t *val)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0"};
nvlist_t *zplprops = NULL;
struct mnttab mnt;
char *mntopt_on = NULL;
*source = NULL;
+ /*
+ * If the property is being fetched for a snapshot, check whether
+ * the property is valid for the snapshot's head dataset type.
+ */
+ if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT &&
+ !zfs_prop_valid_for_type(prop, zhp->zfs_head_type, B_TRUE)) {
+ *val = zfs_prop_default_numeric(prop);
+ return (-1);
+ }
+
switch (prop) {
case ZFS_PROP_ATIME:
mntopt_on = MNTOPT_ATIME;
mntopt_off = MNTOPT_NOATIME;
break;
+ case ZFS_PROP_RELATIME:
+ mntopt_on = MNTOPT_RELATIME;
+ mntopt_off = MNTOPT_NORELATIME;
+ break;
+
case ZFS_PROP_DEVICES:
mntopt_on = MNTOPT_DEVICES;
mntopt_off = MNTOPT_NODEVICES;
mntopt_on = MNTOPT_NBMAND;
mntopt_off = MNTOPT_NONBMAND;
break;
+ default:
+ break;
}
/*
* 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 &&
switch (prop) {
case ZFS_PROP_ATIME:
+ case ZFS_PROP_RELATIME:
case ZFS_PROP_DEVICES:
case ZFS_PROP_EXEC:
case ZFS_PROP_READONLY:
case ZFS_PROP_REFQUOTA:
case ZFS_PROP_RESERVATION:
case ZFS_PROP_REFRESERVATION:
+ case ZFS_PROP_FILESYSTEM_LIMIT:
+ case ZFS_PROP_SNAPSHOT_LIMIT:
+ case ZFS_PROP_FILESYSTEM_COUNT:
+ case ZFS_PROP_SNAPSHOT_COUNT:
*val = getprop_uint64(zhp, prop, source);
if (*source == NULL) {
case ZFS_PROP_NORMALIZE:
case ZFS_PROP_UTF8ONLY:
case ZFS_PROP_CASE:
- if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) ||
- zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
+ if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
return (-1);
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) {
zcmd_free_nvlists(&zc);
+ if (prop == ZFS_PROP_VERSION &&
+ zhp->zfs_type == ZFS_TYPE_VOLUME)
+ *val = zfs_prop_default_numeric(prop);
return (-1);
}
if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 ||
zcmd_free_nvlists(&zc);
break;
+ case ZFS_PROP_INCONSISTENT:
+ *val = zhp->zfs_dmustats.dds_inconsistent;
+ break;
+
default:
switch (zfs_prop_get_type(prop)) {
case PROP_TYPE_NUMBER:
err = zfs_prop_get(zhp, prop, propbuf, proplen,
NULL, NULL, 0, literal);
zfs_unset_recvd_props_mode(zhp, &cookie);
- } else if (zfs_prop_userquota(propname)) {
- return (-1);
} else {
nvlist_t *propval;
char *recvdval;
return (err == 0 ? 0 : -1);
}
+static int
+get_clones_string(zfs_handle_t *zhp, char *propbuf, size_t proplen)
+{
+ nvlist_t *value;
+ nvpair_t *pair;
+
+ value = zfs_get_clones_nvl(zhp);
+ if (value == NULL)
+ return (-1);
+
+ propbuf[0] = '\0';
+ for (pair = nvlist_next_nvpair(value, NULL); pair != NULL;
+ pair = nvlist_next_nvpair(value, pair)) {
+ if (propbuf[0] != '\0')
+ (void) strlcat(propbuf, ",", proplen);
+ (void) strlcat(propbuf, nvpair_name(pair), proplen);
+ }
+
+ return (0);
+}
+
+struct get_clones_arg {
+ uint64_t numclones;
+ nvlist_t *value;
+ const char *origin;
+ char buf[ZFS_MAXNAMELEN];
+};
+
+int
+get_clones_cb(zfs_handle_t *zhp, void *arg)
+{
+ struct get_clones_arg *gca = arg;
+
+ if (gca->numclones == 0) {
+ zfs_close(zhp);
+ return (0);
+ }
+
+ if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, gca->buf, sizeof (gca->buf),
+ NULL, NULL, 0, B_TRUE) != 0)
+ goto out;
+ if (strcmp(gca->buf, gca->origin) == 0) {
+ fnvlist_add_boolean(gca->value, zfs_get_name(zhp));
+ gca->numclones--;
+ }
+
+out:
+ (void) zfs_iter_children(zhp, get_clones_cb, gca);
+ zfs_close(zhp);
+ return (0);
+}
+
+nvlist_t *
+zfs_get_clones_nvl(zfs_handle_t *zhp)
+{
+ nvlist_t *nv, *value;
+
+ if (nvlist_lookup_nvlist(zhp->zfs_props,
+ zfs_prop_to_name(ZFS_PROP_CLONES), &nv) != 0) {
+ struct get_clones_arg gca;
+
+ /*
+ * if this is a snapshot, then the kernel wasn't able
+ * to get the clones. Do it by slowly iterating.
+ */
+ if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT)
+ return (NULL);
+ if (nvlist_alloc(&nv, NV_UNIQUE_NAME, 0) != 0)
+ return (NULL);
+ if (nvlist_alloc(&value, NV_UNIQUE_NAME, 0) != 0) {
+ nvlist_free(nv);
+ return (NULL);
+ }
+
+ gca.numclones = zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES);
+ gca.value = value;
+ gca.origin = zhp->zfs_name;
+
+ if (gca.numclones != 0) {
+ zfs_handle_t *root;
+ char pool[ZFS_MAXNAMELEN];
+ char *cp = pool;
+
+ /* get the pool name */
+ (void) strlcpy(pool, zhp->zfs_name, sizeof (pool));
+ (void) strsep(&cp, "/@");
+ root = zfs_open(zhp->zfs_hdl, pool,
+ ZFS_TYPE_FILESYSTEM);
+
+ (void) get_clones_cb(root, &gca);
+ }
+
+ if (gca.numclones != 0 ||
+ nvlist_add_nvlist(nv, ZPROP_VALUE, value) != 0 ||
+ nvlist_add_nvlist(zhp->zfs_props,
+ zfs_prop_to_name(ZFS_PROP_CLONES), nv) != 0) {
+ nvlist_free(nv);
+ nvlist_free(value);
+ return (NULL);
+ }
+ nvlist_free(nv);
+ nvlist_free(value);
+ verify(0 == nvlist_lookup_nvlist(zhp->zfs_props,
+ zfs_prop_to_name(ZFS_PROP_CLONES), &nv));
+ }
+
+ verify(nvlist_lookup_nvlist(nv, ZPROP_VALUE, &value) == 0);
+
+ return (value);
+}
+
/*
* Retrieve a property from the given object. If 'literal' is specified, then
* numbers are left as exact values. Otherwise, numbers are converted to a
/*
* Check to see if this property applies to our object
*/
- if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
+ if (!zfs_prop_valid_for_type(prop, zhp->zfs_type, B_FALSE))
return (-1);
if (received && zfs_prop_readonly(prop))
localtime_r(&time, &t) == NULL ||
strftime(propbuf, proplen, "%a %b %e %k:%M %Y",
&t) == 0)
- (void) snprintf(propbuf, proplen, "%llu", (u_longlong_t) val);
+ (void) snprintf(propbuf, proplen, "%llu",
+ (u_longlong_t) val);
}
break;
return (-1);
break;
+ case ZFS_PROP_CLONES:
+ if (get_clones_string(zhp, propbuf, proplen) != 0)
+ return (-1);
+ break;
+
case ZFS_PROP_QUOTA:
case ZFS_PROP_REFQUOTA:
case ZFS_PROP_RESERVATION:
}
break;
+ case ZFS_PROP_FILESYSTEM_LIMIT:
+ case ZFS_PROP_SNAPSHOT_LIMIT:
+ case ZFS_PROP_FILESYSTEM_COUNT:
+ case ZFS_PROP_SNAPSHOT_COUNT:
+
+ if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
+ return (-1);
+
+ /*
+ * If limit is UINT64_MAX, we translate this into 'none' (unless
+ * literal is set), and indicate that it's the default value.
+ * Otherwise, we print the number nicely and indicate that it's
+ * set locally.
+ */
+ if (literal) {
+ (void) snprintf(propbuf, proplen, "%llu",
+ (u_longlong_t)val);
+ } else if (val == UINT64_MAX) {
+ (void) strlcpy(propbuf, "none", proplen);
+ } else {
+ zfs_nicenum(val, propbuf, proplen);
+ }
+ break;
+
+ case ZFS_PROP_REFRATIO:
case ZFS_PROP_COMPRESSRATIO:
if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
return (-1);
case ZFS_TYPE_SNAPSHOT:
str = "snapshot";
break;
+ case ZFS_TYPE_BOOKMARK:
+ str = "bookmark";
+ break;
default:
abort();
}
case ZFS_PROP_MLSLABEL:
{
+#ifdef HAVE_MLSLABEL
m_label_t *new_sl = NULL;
char *ascii = NULL; /* human readable label */
(void) strlcpy(propbuf, ascii, proplen);
free(ascii);
+#else
+ (void) strlcpy(propbuf,
+ getprop_string(zhp, prop, &source), proplen);
+#endif /* HAVE_MLSLABEL */
}
break;
+ case ZFS_PROP_GUID:
+ /*
+ * GUIDs are stored as numbers, but they are identifiers.
+ * We don't want them to be pretty printed, because pretty
+ * printing mangles the ID into a truncated and useless value.
+ */
+ if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
+ return (-1);
+ (void) snprintf(propbuf, proplen, "%llu", (u_longlong_t)val);
+ break;
+
default:
switch (zfs_prop_get_type(prop)) {
case PROP_TYPE_NUMBER:
/*
* Check to see if this property applies to our object
*/
- if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) {
+ if (!zfs_prop_valid_for_type(prop, zhp->zfs_type, B_FALSE)) {
return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE,
dgettext(TEXT_DOMAIN, "cannot get property '%s'"),
zfs_prop_to_name(prop)));
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_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
+ * 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 */
- }
-
- 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 {
/* It's a user/group ID (eg "12345"). */
- uid_t id = strtoul(cp, &end, 10);
- idmap_rid_t rid;
- char *mapdomain;
-
+ uid_t id;
+ char *end;
+ id = strtoul(cp, &end, 10);
if (*end != '\0')
return (EINVAL);
if (id > MAXUID) {
+#ifdef HAVE_IDMAP
/* It's an ephemeral ID. */
+ idmap_rid_t rid;
+ char *mapdomain;
+
if (idmap_id_to_numeric_domain_rid(id, isuser,
&mapdomain, &rid) != 0)
return (ENOENT);
(void) strlcpy(domain, mapdomain, domainlen);
*ridp = rid;
+#else
+ return (ENOSYS);
+#endif /* HAVE_IDMAP */
} else {
*ridp = id;
}
}
- ASSERT3P(numericsid, ==, NULL);
return (0);
}
uint64_t *propvalue, zfs_userquota_prop_t *typep)
{
int err;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0"};
- (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
err = userquota_propname_decode(propname,
zfs_prop_get_int(zhp, ZFS_PROP_ZONED),
if (literal) {
(void) snprintf(propbuf, proplen, "%llu",
- (u_longlong_t)propvalue);
+ (u_longlong_t)propvalue);
} else if (propvalue == 0 &&
(type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) {
(void) strlcpy(propbuf, "none", proplen);
return (0);
}
-/*
- * Returns the name of the given zfs handle.
- */
-const char *
-zfs_get_name(const zfs_handle_t *zhp)
+int
+zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
+ uint64_t *propvalue)
{
- return (zhp->zfs_name);
-}
+ int err;
+ zfs_cmd_t zc = {"\0"};
+ const char *snapname;
-/*
- * Returns the type of the given zfs handle.
- */
-zfs_type_t
-zfs_get_type(const zfs_handle_t *zhp)
-{
- return (zhp->zfs_type);
-}
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-static int
-zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc)
-{
- int rc;
- uint64_t orig_cookie;
+ snapname = strchr(propname, '@') + 1;
+ if (strchr(snapname, '@')) {
+ (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
+ } else {
+ /* snapname is the short name, append it to zhp's fsname */
+ char *cp;
+
+ (void) strlcpy(zc.zc_value, zhp->zfs_name,
+ sizeof (zc.zc_value));
+ cp = strchr(zc.zc_value, '@');
+ if (cp != NULL)
+ *cp = '\0';
+ (void) strlcat(zc.zc_value, "@", sizeof (zc.zc_value));
+ (void) strlcat(zc.zc_value, snapname, sizeof (zc.zc_value));
+ }
- orig_cookie = zc->zc_cookie;
-top:
- (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
- rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc);
+ err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_WRITTEN, &zc);
+ if (err)
+ return (err);
- if (rc == -1) {
- switch (errno) {
- case ENOMEM:
- /* expand nvlist memory and try again */
- if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) {
- zcmd_free_nvlists(zc);
- return (-1);
- }
- zc->zc_cookie = orig_cookie;
- goto top;
- /*
- * An errno value of ESRCH indicates normal completion.
- * If ENOENT is returned, then the underlying dataset
- * has been removed since we obtained the handle.
- */
- case ESRCH:
- case ENOENT:
- rc = 1;
- break;
- default:
- rc = zfs_standard_error(zhp->zfs_hdl, errno,
- dgettext(TEXT_DOMAIN,
- "cannot iterate filesystems"));
- break;
- }
- }
- return (rc);
+ *propvalue = zc.zc_cookie;
+ return (0);
}
-/*
- * Iterate over all child filesystems
- */
int
-zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
+ char *propbuf, int proplen, boolean_t literal)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
- zfs_handle_t *nzhp;
- int ret;
-
- if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
- return (0);
+ int err;
+ uint64_t propvalue;
- if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
- return (-1);
+ err = zfs_prop_get_written_int(zhp, propname, &propvalue);
- while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT,
- &zc)) == 0) {
- /*
- * Silently ignore errors, as the only plausible explanation is
- * that the pool has since been removed.
- */
- if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
- &zc)) == NULL) {
- continue;
- }
+ if (err)
+ return (err);
- if ((ret = func(nzhp, data)) != 0) {
- zcmd_free_nvlists(&zc);
- return (ret);
- }
+ if (literal) {
+ (void) snprintf(propbuf, proplen, "%llu",
+ (u_longlong_t)propvalue);
+ } else {
+ zfs_nicenum(propvalue, propbuf, proplen);
}
- zcmd_free_nvlists(&zc);
- return ((ret < 0) ? ret : 0);
+
+ return (0);
}
/*
- * Iterate over all snapshots
+ * Returns the name of the given zfs handle.
*/
-int
-zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+const char *
+zfs_get_name(const zfs_handle_t *zhp)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
- zfs_handle_t *nzhp;
- int ret;
-
- if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
- return (0);
-
- 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) {
- continue;
- }
-
- if ((ret = func(nzhp, data)) != 0) {
- zcmd_free_nvlists(&zc);
- return (ret);
- }
- }
- zcmd_free_nvlists(&zc);
- return ((ret < 0) ? ret : 0);
+ return (zhp->zfs_name);
}
/*
- * Iterate over all children, snapshots and filesystems
+ * Returns the type of the given zfs handle.
*/
-int
-zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+zfs_type_t
+zfs_get_type(const zfs_handle_t *zhp)
{
- int ret;
-
- if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
- return (ret);
-
- return (zfs_iter_snapshots(zhp, func, data));
+ return (zhp->zfs_type);
}
/*
/*
* Given a complete name, return just the portion that refers to the parent.
- * Can return NULL if this is a pool.
+ * Will return -1 if there is no parent (path is just the name of the
+ * pool).
*/
static int
parent_name(const char *path, char *buf, size_t buflen)
{
- char *loc;
+ char *slashp;
- if ((loc = strrchr(path, '/')) == NULL)
- return (-1);
+ (void) strlcpy(buf, path, buflen);
- (void) strncpy(buf, path, MIN(buflen, loc - path));
- buf[loc - path] = '\0';
+ if ((slashp = strrchr(buf, '/')) == NULL)
+ return (-1);
+ *slashp = '\0';
return (0);
}
check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned,
boolean_t accept_ancestor, int *prefixlen)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0"};
char parent[ZFS_MAXNAMELEN];
char *slash;
zfs_handle_t *zhp;
* up to the prefixlen-long one.
*/
for (cp = target + prefixlen + 1;
- cp = strchr(cp, '/'); *cp = '/', cp++) {
- char *logstr;
+ (cp = strchr(cp, '/')); *cp = '/', cp++) {
*cp = '\0';
continue;
}
- logstr = hdl->libzfs_log_str;
- hdl->libzfs_log_str = NULL;
if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,
NULL) != 0) {
- hdl->libzfs_log_str = logstr;
opname = dgettext(TEXT_DOMAIN, "create");
goto ancestorerr;
}
- hdl->libzfs_log_str = logstr;
h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
if (h == NULL) {
opname = dgettext(TEXT_DOMAIN, "open");
{
int prefix;
char *path_copy;
- int rc;
+ int rc = 0;
if (check_parents(hdl, path, NULL, B_TRUE, &prefix) != 0)
return (-1);
zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
nvlist_t *props)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
int ret;
uint64_t size = 0;
uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
char errbuf[1024];
uint64_t zoned;
+ dmu_objset_type_t ost;
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot create '%s'"), path);
* will return ENOENT, not EEXIST. To prevent this from happening, we
* first try to see if the dataset exists.
*/
- (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name));
- if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
+ if (zfs_dataset_exists(hdl, path, ZFS_TYPE_DATASET)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"dataset already exists"));
return (zfs_error(hdl, EZFS_EXISTS, errbuf));
}
if (type == ZFS_TYPE_VOLUME)
- zc.zc_objset_type = DMU_OST_ZVOL;
+ ost = DMU_OST_ZVOL;
else
- zc.zc_objset_type = DMU_OST_ZFS;
+ ost = DMU_OST_ZFS;
if (props && (props = zfs_valid_proplist(hdl, type, props,
zoned, NULL, errbuf)) == 0)
}
}
- if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0)
- return (-1);
- nvlist_free(props);
-
/* create the dataset */
- ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc);
-
- zcmd_free_nvlists(&zc);
+ ret = lzc_create(path, ost, props);
+ nvlist_free(props);
/* check for failure */
if (ret != 0) {
char parent[ZFS_MAXNAMELEN];
+ char buf[64];
+
(void) parent_name(path, parent, sizeof (parent));
switch (errno) {
return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
case EDOM:
+ zfs_nicenum(SPA_MAXBLOCKSIZE, buf, sizeof (buf));
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"volume block size must be power of 2 from "
- "%u to %uk"),
- (uint_t)SPA_MINBLOCKSIZE,
- (uint_t)SPA_MAXBLOCKSIZE >> 10);
+ "512B to %s"), buf);
return (zfs_error(hdl, EZFS_BADPROP, errbuf));
/*
* Destroys the given dataset. The caller must make sure that the filesystem
- * isn't mounted, and that there are no active dependents.
+ * isn't mounted, and that there are no active dependents. If the file system
+ * does not exist this function does nothing.
*/
int
zfs_destroy(zfs_handle_t *zhp, boolean_t defer)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0"};
+
+ if (zhp->zfs_type == ZFS_TYPE_BOOKMARK) {
+ nvlist_t *nv = fnvlist_alloc();
+ fnvlist_add_boolean(nv, zhp->zfs_name);
+ int error = lzc_destroy_bookmarks(nv, NULL);
+ fnvlist_free(nv);
+ if (error != 0) {
+ return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,
+ dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
+ zhp->zfs_name));
+ }
+ return (0);
+ }
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
}
zc.zc_defer_destroy = defer;
- if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) {
+ if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0 &&
+ errno != ENOENT) {
return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,
dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
zhp->zfs_name));
}
struct destroydata {
- char *snapname;
- boolean_t gotone;
- boolean_t closezhp;
+ nvlist_t *nvl;
+ const char *snapname;
};
static int
zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
{
struct destroydata *dd = arg;
- zfs_handle_t *szhp;
char name[ZFS_MAXNAMELEN];
- boolean_t closezhp = dd->closezhp;
int rv = 0;
- (void) strlcpy(name, zhp->zfs_name, sizeof (name));
- (void) strlcat(name, "@", sizeof (name));
- (void) strlcat(name, dd->snapname, sizeof (name));
+ (void) snprintf(name, sizeof (name),
+ "%s@%s", zhp->zfs_name, dd->snapname);
- szhp = make_dataset_handle(zhp->zfs_hdl, name);
- if (szhp) {
- dd->gotone = B_TRUE;
- zfs_close(szhp);
- }
+ if (lzc_exists(name))
+ verify(nvlist_add_boolean(dd->nvl, name) == 0);
- dd->closezhp = B_TRUE;
- if (!dd->gotone)
- rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, arg);
- if (closezhp)
- zfs_close(zhp);
+ rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, dd);
+ zfs_close(zhp);
return (rv);
}
int
zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
int ret;
struct destroydata dd = { 0 };
dd.snapname = snapname;
- (void) zfs_check_snap_cb(zhp, &dd);
+ verify(nvlist_alloc(&dd.nvl, NV_UNIQUE_NAME, 0) == 0);
+ (void) zfs_check_snap_cb(zfs_handle_dup(zhp), &dd);
- if (!dd.gotone) {
- return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
+ if (nvlist_empty(dd.nvl)) {
+ ret = zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),
- zhp->zfs_name, snapname));
+ zhp->zfs_name, snapname);
+ } else {
+ ret = zfs_destroy_snaps_nvl(zhp->zfs_hdl, dd.nvl, defer);
}
+ nvlist_free(dd.nvl);
+ return (ret);
+}
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
- zc.zc_defer_destroy = defer;
+/*
+ * Destroys all the snapshots named in the nvlist.
+ */
+int
+zfs_destroy_snaps_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, boolean_t defer)
+{
+ int ret;
+ nvlist_t *errlist;
+ nvpair_t *pair;
- ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc);
- if (ret != 0) {
+ ret = lzc_destroy_snaps(snaps, defer, &errlist);
+
+ if (ret == 0)
+ return (0);
+
+ if (nvlist_empty(errlist)) {
char errbuf[1024];
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot destroy snapshots"));
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot destroy '%s@%s'"), zc.zc_name, snapname);
+ ret = zfs_standard_error(hdl, ret, errbuf);
+ }
+ for (pair = nvlist_next_nvpair(errlist, NULL);
+ pair != NULL; pair = nvlist_next_nvpair(errlist, pair)) {
+ char errbuf[1024];
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot destroy snapshot %s"),
+ nvpair_name(pair));
- switch (errno) {
+ switch (fnvpair_value_int32(pair)) {
case EEXIST:
- zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
- "snapshot is cloned"));
- return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf));
-
+ zfs_error_aux(hdl,
+ dgettext(TEXT_DOMAIN, "snapshot is cloned"));
+ ret = zfs_error(hdl, EZFS_EXISTS, errbuf);
+ break;
default:
- return (zfs_standard_error(zhp->zfs_hdl, errno,
- errbuf));
+ ret = zfs_standard_error(hdl, errno, errbuf);
+ break;
}
}
- return (0);
+ return (ret);
}
/*
int
zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
char parent[ZFS_MAXNAMELEN];
int ret;
char errbuf[1024];
libzfs_handle_t *hdl = zhp->zfs_hdl;
- zfs_type_t type;
uint64_t zoned;
assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot create '%s'"), target);
- /* validate the target name */
+ /* validate the target/clone name */
if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE))
return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
(void) parent_name(target, parent, sizeof (parent));
/* do the clone */
- if (ZFS_IS_VOLUME(zhp)) {
- zc.zc_objset_type = DMU_OST_ZVOL;
- type = ZFS_TYPE_VOLUME;
- } else {
- zc.zc_objset_type = DMU_OST_ZFS;
- type = ZFS_TYPE_FILESYSTEM;
- }
if (props) {
+ zfs_type_t type;
+ if (ZFS_IS_VOLUME(zhp)) {
+ type = ZFS_TYPE_VOLUME;
+ } else {
+ type = ZFS_TYPE_FILESYSTEM;
+ }
if ((props = zfs_valid_proplist(hdl, type, props, zoned,
zhp, errbuf)) == NULL)
return (-1);
-
- if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
- nvlist_free(props);
- return (-1);
- }
-
- nvlist_free(props);
}
- (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value));
- ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc);
-
- zcmd_free_nvlists(&zc);
+ ret = lzc_clone(target, zhp->zfs_name, props);
+ nvlist_free(props);
if (ret != 0) {
switch (errno) {
zfs_promote(zfs_handle_t *zhp)
{
libzfs_handle_t *hdl = zhp->zfs_hdl;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0"};
char parent[MAXPATHLEN];
int ret;
char errbuf[1024];
return (ret);
}
+typedef struct snapdata {
+ nvlist_t *sd_nvl;
+ const char *sd_snapname;
+} snapdata_t;
+
+static int
+zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
+{
+ snapdata_t *sd = arg;
+ char name[ZFS_MAXNAMELEN];
+ int rv = 0;
+
+ if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) == 0) {
+ (void) snprintf(name, sizeof (name),
+ "%s@%s", zfs_get_name(zhp), sd->sd_snapname);
+
+ fnvlist_add_boolean(sd->sd_nvl, name);
+
+ rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd);
+ }
+ zfs_close(zhp);
+
+ return (rv);
+}
+
/*
- * Takes a snapshot of the given dataset.
+ * Creates snapshots. The keys in the snaps nvlist are the snapshots to be
+ * created.
*/
int
-zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
- nvlist_t *props)
+zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props)
{
- const char *delim;
- char parent[ZFS_MAXNAMELEN];
- zfs_handle_t *zhp;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
int ret;
char errbuf[1024];
+ nvpair_t *elem;
+ nvlist_t *errors;
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot snapshot '%s'"), path);
+ "cannot create snapshots "));
- /* validate the target name */
- if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
+ elem = NULL;
+ while ((elem = nvlist_next_nvpair(snaps, elem)) != NULL) {
+ const char *snapname = nvpair_name(elem);
- if (props) {
- if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
- props, B_FALSE, NULL, errbuf)) == NULL)
- return (-1);
+ /* validate the target name */
+ if (!zfs_validate_name(hdl, snapname, ZFS_TYPE_SNAPSHOT,
+ B_TRUE)) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN,
+ "cannot create snapshot '%s'"), snapname);
+ return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
+ }
+ }
- if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
- nvlist_free(props);
- return (-1);
+ if (props != NULL &&
+ (props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
+ props, B_FALSE, NULL, errbuf)) == NULL) {
+ return (-1);
+ }
+
+ ret = lzc_snapshot(snaps, props, &errors);
+
+ if (ret != 0) {
+ boolean_t printed = B_FALSE;
+ for (elem = nvlist_next_nvpair(errors, NULL);
+ elem != NULL;
+ elem = nvlist_next_nvpair(errors, elem)) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN,
+ "cannot create snapshot '%s'"), nvpair_name(elem));
+ (void) zfs_standard_error(hdl,
+ fnvpair_value_int32(elem), errbuf);
+ printed = B_TRUE;
}
+ if (!printed) {
+ switch (ret) {
+ case EXDEV:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "multiple snapshots of same "
+ "fs not allowed"));
+ (void) zfs_error(hdl, EZFS_EXISTS, errbuf);
- nvlist_free(props);
+ break;
+ default:
+ (void) zfs_standard_error(hdl, ret, errbuf);
+ }
+ }
}
- /* make sure the parent exists and is of the appropriate type */
- delim = strchr(path, '@');
- (void) strncpy(parent, path, delim - path);
- parent[delim - path] = '\0';
+ nvlist_free(props);
+ nvlist_free(errors);
+ return (ret);
+}
+
+int
+zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
+ nvlist_t *props)
+{
+ int ret;
+ snapdata_t sd = { 0 };
+ char fsname[ZFS_MAXNAMELEN];
+ char *cp;
+ zfs_handle_t *zhp;
+ char errbuf[1024];
+
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+ "cannot snapshot %s"), path);
+
+ if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
+ return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
+
+ (void) strlcpy(fsname, path, sizeof (fsname));
+ cp = strchr(fsname, '@');
+ *cp = '\0';
+ sd.sd_snapname = cp + 1;
- if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM |
+ if ((zhp = zfs_open(hdl, fsname, ZFS_TYPE_FILESYSTEM |
ZFS_TYPE_VOLUME)) == NULL) {
- zcmd_free_nvlists(&zc);
return (-1);
}
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value));
- if (ZFS_IS_VOLUME(zhp))
- zc.zc_objset_type = DMU_OST_ZVOL;
- else
- zc.zc_objset_type = DMU_OST_ZFS;
- zc.zc_cookie = recursive;
- ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc);
-
- zcmd_free_nvlists(&zc);
-
- /*
- * if it was recursive, the one that actually failed will be in
- * zc.zc_name.
- */
- if (ret != 0) {
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
- (void) zfs_standard_error(hdl, errno, errbuf);
+ verify(nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) == 0);
+ if (recursive) {
+ (void) zfs_snapshot_cb(zfs_handle_dup(zhp), &sd);
+ } else {
+ fnvlist_add_boolean(sd.sd_nvl, path);
}
+ ret = zfs_snapshot_nvl(hdl, sd.sd_nvl, props);
+ nvlist_free(sd.sd_nvl);
zfs_close(zhp);
-
return (ret);
}
const char *cb_target; /* the snapshot */
uint64_t cb_create; /* creation time reference */
boolean_t cb_error;
- boolean_t cb_dependent;
boolean_t cb_force;
} rollback_data_t;
static int
-rollback_destroy(zfs_handle_t *zhp, void *data)
+rollback_destroy_dependent(zfs_handle_t *zhp, void *data)
{
rollback_data_t *cbp = data;
+ prop_changelist_t *clp;
- if (!cbp->cb_dependent) {
- if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 &&
- zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
- zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
- cbp->cb_create) {
- char *logstr;
-
- cbp->cb_dependent = B_TRUE;
- cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE,
- rollback_destroy, cbp);
- cbp->cb_dependent = B_FALSE;
+ /* We must destroy this clone; first unmount it */
+ clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
+ cbp->cb_force ? MS_FORCE: 0);
+ if (clp == NULL || changelist_prefix(clp) != 0) {
+ cbp->cb_error = B_TRUE;
+ zfs_close(zhp);
+ return (0);
+ }
+ if (zfs_destroy(zhp, B_FALSE) != 0)
+ cbp->cb_error = B_TRUE;
+ else
+ changelist_remove(clp, zhp->zfs_name);
+ (void) changelist_postfix(clp);
+ changelist_free(clp);
- logstr = zhp->zfs_hdl->libzfs_log_str;
- zhp->zfs_hdl->libzfs_log_str = NULL;
- cbp->cb_error |= zfs_destroy(zhp, B_FALSE);
- zhp->zfs_hdl->libzfs_log_str = logstr;
- }
- } else {
- /* We must destroy this clone; first unmount it */
- prop_changelist_t *clp;
+ zfs_close(zhp);
+ return (0);
+}
- clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
- cbp->cb_force ? MS_FORCE: 0);
- if (clp == NULL || changelist_prefix(clp) != 0) {
- cbp->cb_error = B_TRUE;
- zfs_close(zhp);
- return (0);
- }
- if (zfs_destroy(zhp, B_FALSE) != 0)
- cbp->cb_error = B_TRUE;
- else
- changelist_remove(clp, zhp->zfs_name);
- (void) changelist_postfix(clp);
- changelist_free(clp);
+static int
+rollback_destroy(zfs_handle_t *zhp, void *data)
+{
+ rollback_data_t *cbp = data;
+
+ if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) {
+ cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE,
+ rollback_destroy_dependent, cbp);
+
+ cbp->cb_error |= zfs_destroy(zhp, B_FALSE);
}
zfs_close(zhp);
* Given a dataset, rollback to a specific snapshot, discarding any
* data changes since then and making it the active dataset.
*
- * Any snapshots more recent than the target are destroyed, along with
- * their dependents.
+ * Any snapshots and bookmarks more recent than the target are
+ * destroyed, along with their dependents (i.e. clones).
*/
int
zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
{
rollback_data_t cb = { 0 };
int err;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
boolean_t restore_resv = 0;
- uint64_t old_volsize, new_volsize;
- zfs_prop_t resv_prop;
+ uint64_t old_volsize = 0, new_volsize;
+ zfs_prop_t resv_prop = { 0 };
assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM ||
zhp->zfs_type == ZFS_TYPE_VOLUME);
/*
- * Destroy all recent snapshots and its dependends.
+ * Destroy all recent snapshots and their dependents.
*/
cb.cb_force = force;
cb.cb_target = snap->zfs_name;
cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
- (void) zfs_iter_children(zhp, rollback_destroy, &cb);
+ (void) zfs_iter_snapshots(zhp, B_FALSE, rollback_destroy, &cb);
+ (void) zfs_iter_bookmarks(zhp, rollback_destroy, &cb);
if (cb.cb_error)
return (-1);
(old_volsize == zfs_prop_get_int(zhp, resv_prop));
}
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-
- if (ZFS_IS_VOLUME(zhp))
- zc.zc_objset_type = DMU_OST_ZVOL;
- else
- zc.zc_objset_type = DMU_OST_ZFS;
-
/*
* We rely on zfs_iter_children() to verify that there are no
* newer snapshots for the given dataset. Therefore, we can
* simply pass the name on to the ioctl() call. There is still
* an unlikely race condition where the user has taken a
* snapshot since we verified that this was the most recent.
- *
*/
- if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) {
+ err = lzc_rollback(zhp->zfs_name, NULL, 0);
+ if (err != 0) {
(void) zfs_standard_error_fmt(zhp->zfs_hdl, errno,
dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
zhp->zfs_name);
return (err);
}
-/*
- * Iterate over all dependents for a given dataset. This includes both
- * hierarchical dependents (children) and data dependents (snapshots and
- * clones). The bulk of the processing occurs in get_dependents() in
- * libzfs_graph.c.
- */
-int
-zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
- zfs_iter_f func, void *data)
-{
- char **dependents;
- size_t count;
- int i;
- zfs_handle_t *child;
- int ret = 0;
-
- if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name,
- &dependents, &count) != 0)
- return (-1);
-
- for (i = 0; i < count; i++) {
- if ((child = make_dataset_handle(zhp->zfs_hdl,
- dependents[i])) == NULL)
- continue;
-
- if ((ret = func(child, data)) != 0)
- break;
- }
-
- for (i = 0; i < count; i++)
- free(dependents[i]);
- free(dependents);
-
- return (ret);
-}
-
/*
* Renames the given dataset.
*/
int
-zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive)
+zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
+ boolean_t force_unmount)
{
int ret;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0"};
char *delim;
prop_changelist_t *cl = NULL;
zfs_handle_t *zhrp = NULL;
}
} else {
- if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL)
+ if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0,
+ force_unmount ? MS_FORCE : 0)) == NULL)
return (-1);
if (changelist_haszonedchild(cl)) {
"child dataset with inherited mountpoint is used "
"in a non-global zone"));
(void) zfs_error(hdl, EZFS_ZONED, errbuf);
+ ret = -1;
goto error;
}
return (zhp->zfs_user_props);
}
-nvlist_t *
-zfs_get_recvd_props(zfs_handle_t *zhp)
-{
- if (zhp->zfs_recvd_props == NULL)
- if (get_recvd_props_ioctl(zhp) != 0)
- return (NULL);
- return (zhp->zfs_recvd_props);
-}
-
/*
* This function is used by 'zfs list' to determine the exact set of columns to
* display, and their maximum widths. This does two main things:
* of the RECEIVED column.
*/
int
-zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received)
+zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received,
+ boolean_t literal)
{
libzfs_handle_t *hdl = zhp->zfs_hdl;
zprop_list_t *entry;
* Now go through and check the width of any non-fixed columns
*/
for (entry = *plp; entry != NULL; entry = entry->pl_next) {
- if (entry->pl_fixed)
+ if (entry->pl_fixed && !literal)
continue;
if (entry->pl_prop != ZPROP_INVAL) {
if (zfs_prop_get(zhp, entry->pl_prop,
- buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) {
+ buf, sizeof (buf), NULL, NULL, 0, literal) == 0) {
if (strlen(buf) > entry->pl_width)
entry->pl_width = strlen(buf);
}
if (received && zfs_prop_get_recvd(zhp,
zfs_prop_to_name(entry->pl_prop),
- buf, sizeof (buf), B_FALSE) == 0)
+ buf, sizeof (buf), literal) == 0)
if (strlen(buf) > entry->pl_recvd_width)
entry->pl_recvd_width = strlen(buf);
} else {
}
if (received && zfs_prop_get_recvd(zhp,
entry->pl_user_prop,
- buf, sizeof (buf), B_FALSE) == 0)
+ buf, sizeof (buf), literal) == 0)
if (strlen(buf) > entry->pl_recvd_width)
entry->pl_recvd_width = strlen(buf);
}
return (0);
}
-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);
-}
-
void
zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props)
{
zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path,
zfs_smb_acl_op_t cmd, char *resource1, char *resource2)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0"};
nvlist_t *nvlist = NULL;
int error;
if (cmd == ZFS_SMB_ACL_RENAME) {
if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) {
(void) no_memory(hdl);
- return (NULL);
+ return (-1);
}
}
zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
zfs_userspace_cb_t func, void *arg)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
- int error;
+ zfs_cmd_t zc = {"\0"};
zfs_useracct_t buf[100];
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
+ int ret;
- (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+ (void) strlcpy(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) {
+ for (;;) {
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)
+ if (zfs_ioctl(hdl, ZFS_IOC_USERSPACE_MANY, &zc) != 0) {
+ char errbuf[ZFS_MAXNAMELEN + 32];
+
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN,
+ "cannot get used/quota for %s"), zc.zc_name);
+ return (zfs_standard_error_fmt(hdl, errno, errbuf));
+ }
+ if (zc.zc_nvlist_dst_size == 0)
break;
while (zc.zc_nvlist_dst_size > 0) {
- error = func(arg, zua->zu_domain, zua->zu_rid,
- zua->zu_space);
- if (error != 0)
- return (error);
+ if ((ret = func(arg, zua->zu_domain, zua->zu_rid,
+ zua->zu_space)) != 0)
+ return (ret);
zua++;
zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t);
}
}
- return (error);
+ return (0);
+}
+
+struct holdarg {
+ nvlist_t *nvl;
+ const char *snapname;
+ const char *tag;
+ boolean_t recursive;
+ int error;
+};
+
+static int
+zfs_hold_one(zfs_handle_t *zhp, void *arg)
+{
+ struct holdarg *ha = arg;
+ char name[ZFS_MAXNAMELEN];
+ int rv = 0;
+
+ (void) snprintf(name, sizeof (name),
+ "%s@%s", zhp->zfs_name, ha->snapname);
+
+ if (lzc_exists(name))
+ fnvlist_add_string(ha->nvl, name, ha->tag);
+
+ if (ha->recursive)
+ rv = zfs_iter_filesystems(zhp, zfs_hold_one, ha);
+ zfs_close(zhp);
+ return (rv);
}
int
zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
- boolean_t recursive, boolean_t temphold, boolean_t enoent_ok,
- int cleanup_fd, uint64_t dsobj, uint64_t createtxg)
+ boolean_t recursive, int cleanup_fd)
+{
+ int ret;
+ struct holdarg ha;
+
+ ha.nvl = fnvlist_alloc();
+ ha.snapname = snapname;
+ ha.tag = tag;
+ ha.recursive = recursive;
+ (void) zfs_hold_one(zfs_handle_dup(zhp), &ha);
+
+ if (nvlist_empty(ha.nvl)) {
+ char errbuf[1024];
+
+ fnvlist_free(ha.nvl);
+ ret = ENOENT;
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN,
+ "cannot hold snapshot '%s@%s'"),
+ zhp->zfs_name, snapname);
+ (void) zfs_standard_error(zhp->zfs_hdl, ret, errbuf);
+ return (ret);
+ }
+
+ ret = zfs_hold_nvl(zhp, cleanup_fd, ha.nvl);
+ fnvlist_free(ha.nvl);
+
+ return (ret);
+}
+
+int
+zfs_hold_nvl(zfs_handle_t *zhp, int cleanup_fd, nvlist_t *holds)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ int ret;
+ nvlist_t *errors;
libzfs_handle_t *hdl = zhp->zfs_hdl;
+ char errbuf[1024];
+ nvpair_t *elem;
- ASSERT(!recursive || dsobj == 0);
+ errors = NULL;
+ ret = lzc_hold(holds, cleanup_fd, &errors);
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
- if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string))
- >= sizeof (zc.zc_string))
- return (zfs_error(hdl, EZFS_TAGTOOLONG, tag));
- zc.zc_cookie = recursive;
- zc.zc_temphold = temphold;
- zc.zc_cleanup_fd = cleanup_fd;
- zc.zc_sendobj = dsobj;
- zc.zc_createtxg = createtxg;
+ if (ret == 0) {
+ /* There may be errors even in the success case. */
+ fnvlist_free(errors);
+ return (0);
+ }
- if (zfs_ioctl(hdl, ZFS_IOC_HOLD, &zc) != 0) {
- char errbuf[ZFS_MAXNAMELEN+32];
+ if (nvlist_empty(errors)) {
+ /* no hold-specific errors */
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot hold"));
+ switch (ret) {
+ case ENOTSUP:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "pool must be upgraded"));
+ (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
+ break;
+ case EINVAL:
+ (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
+ break;
+ default:
+ (void) zfs_standard_error(hdl, ret, errbuf);
+ }
+ }
- /*
- * if it was recursive, the one that actually failed will be in
- * zc.zc_name.
- */
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot hold '%s@%s'"), zc.zc_name, snapname);
- switch (errno) {
+ for (elem = nvlist_next_nvpair(errors, NULL);
+ elem != NULL;
+ elem = nvlist_next_nvpair(errors, elem)) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN,
+ "cannot hold snapshot '%s'"), nvpair_name(elem));
+ switch (fnvpair_value_int32(elem)) {
case E2BIG:
/*
* Temporary tags wind up having the ds object id
* above, it's still possible for the tag to wind
* up being slightly too long.
*/
- return (zfs_error(hdl, EZFS_TAGTOOLONG, errbuf));
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded"));
- return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
+ (void) zfs_error(hdl, EZFS_TAGTOOLONG, errbuf);
+ break;
case EINVAL:
- return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
+ (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
+ break;
case EEXIST:
- return (zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf));
- case ENOENT:
- if (enoent_ok)
- return (ENOENT);
- /* FALLTHROUGH */
+ (void) zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf);
+ break;
default:
- return (zfs_standard_error_fmt(hdl, errno, errbuf));
+ (void) zfs_standard_error(hdl,
+ fnvpair_value_int32(elem), errbuf);
}
}
- return (0);
+ fnvlist_free(errors);
+ return (ret);
+}
+
+static int
+zfs_release_one(zfs_handle_t *zhp, void *arg)
+{
+ struct holdarg *ha = arg;
+ char name[ZFS_MAXNAMELEN];
+ int rv = 0;
+ nvlist_t *existing_holds;
+
+ (void) snprintf(name, sizeof (name),
+ "%s@%s", zhp->zfs_name, ha->snapname);
+
+ if (lzc_get_holds(name, &existing_holds) != 0) {
+ ha->error = ENOENT;
+ } else if (!nvlist_exists(existing_holds, ha->tag)) {
+ ha->error = ESRCH;
+ } else {
+ nvlist_t *torelease = fnvlist_alloc();
+ fnvlist_add_boolean(torelease, ha->tag);
+ fnvlist_add_nvlist(ha->nvl, name, torelease);
+ fnvlist_free(torelease);
+ }
+
+ if (ha->recursive)
+ rv = zfs_iter_filesystems(zhp, zfs_release_one, ha);
+ zfs_close(zhp);
+ return (rv);
}
int
zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
boolean_t recursive)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ int ret;
+ struct holdarg ha;
+ nvlist_t *errors = NULL;
+ nvpair_t *elem;
libzfs_handle_t *hdl = zhp->zfs_hdl;
+ char errbuf[1024];
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
- if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string))
- >= sizeof (zc.zc_string))
- return (zfs_error(hdl, EZFS_TAGTOOLONG, tag));
- zc.zc_cookie = recursive;
+ ha.nvl = fnvlist_alloc();
+ ha.snapname = snapname;
+ ha.tag = tag;
+ ha.recursive = recursive;
+ ha.error = 0;
+ (void) zfs_release_one(zfs_handle_dup(zhp), &ha);
+
+ if (nvlist_empty(ha.nvl)) {
+ fnvlist_free(ha.nvl);
+ ret = ha.error;
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN,
+ "cannot release hold from snapshot '%s@%s'"),
+ zhp->zfs_name, snapname);
+ if (ret == ESRCH) {
+ (void) zfs_error(hdl, EZFS_REFTAG_RELE, errbuf);
+ } else {
+ (void) zfs_standard_error(hdl, ret, errbuf);
+ }
+ return (ret);
+ }
- if (zfs_ioctl(hdl, ZFS_IOC_RELEASE, &zc) != 0) {
- char errbuf[ZFS_MAXNAMELEN+32];
+ ret = lzc_release(ha.nvl, &errors);
+ fnvlist_free(ha.nvl);
- /*
- * if it was recursive, the one that actually failed will be in
- * zc.zc_name.
- */
+ if (ret == 0) {
+ /* There may be errors even in the success case. */
+ fnvlist_free(errors);
+ return (0);
+ }
+
+ if (nvlist_empty(errors)) {
+ /* no hold-specific errors */
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot release '%s' from '%s@%s'"), tag, zc.zc_name,
- snapname);
+ "cannot release"));
switch (errno) {
+ case ENOTSUP:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "pool must be upgraded"));
+ (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
+ break;
+ default:
+ (void) zfs_standard_error_fmt(hdl, errno, errbuf);
+ }
+ }
+
+ for (elem = nvlist_next_nvpair(errors, NULL);
+ elem != NULL;
+ elem = nvlist_next_nvpair(errors, elem)) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN,
+ "cannot release hold from snapshot '%s'"),
+ nvpair_name(elem));
+ switch (fnvpair_value_int32(elem)) {
case ESRCH:
- return (zfs_error(hdl, EZFS_REFTAG_RELE, errbuf));
+ (void) zfs_error(hdl, EZFS_REFTAG_RELE, errbuf);
+ break;
+ case EINVAL:
+ (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
+ break;
+ default:
+ (void) zfs_standard_error_fmt(hdl,
+ fnvpair_value_int32(elem), errbuf);
+ }
+ }
+
+ fnvlist_free(errors);
+ return (ret);
+}
+
+int
+zfs_get_fsacl(zfs_handle_t *zhp, nvlist_t **nvl)
+{
+ zfs_cmd_t zc = {"\0"};
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
+ int nvsz = 2048;
+ void *nvbuf;
+ int err = 0;
+ char errbuf[1024];
+
+ 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"));
- return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
+ err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
+ break;
case EINVAL:
- return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
+ err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
+ break;
+ case ENOENT:
+ err = zfs_error(hdl, EZFS_NOENT, errbuf);
+ break;
default:
- return (zfs_standard_error_fmt(hdl, errno, errbuf));
+ 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);
}
}
- return (0);
+ free(nvbuf);
+out:
+ return (err);
+}
+
+int
+zfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl)
+{
+ zfs_cmd_t zc = {"\0"};
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
+ char *nvbuf;
+ char errbuf[1024];
+ 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)
+{
+ int err;
+ char errbuf[1024];
+
+ err = lzc_get_holds(zhp->zfs_name, nvl);
+
+ if (err != 0) {
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
+
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"),
+ zhp->zfs_name);
+ switch (err) {
+ 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;
+ }
+ }
+
+ return (err);
+}
+
+/*
+ * Convert the zvol's volume size to an appropriate reservation.
+ * Note: If this routine is updated, it is necessary to update the ZFS test
+ * suite's shell version in reservation.kshlib.
+ */
uint64_t
zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props)
{