summaryrefslogtreecommitdiff
path: root/usr/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib')
-rw-r--r--usr/src/lib/libzfs/Makefile.com3
-rw-r--r--usr/src/lib/libzfs/common/libzfs.h18
-rw-r--r--usr/src/lib/libzfs/common/libzfs_dataset.c447
-rw-r--r--usr/src/lib/libzfs/common/libzfs_graph.c653
-rw-r--r--usr/src/lib/libzfs/common/libzfs_impl.h4
-rw-r--r--usr/src/lib/libzfs/common/libzfs_iter.c462
-rw-r--r--usr/src/lib/libzfs/common/libzfs_sendrecv.c369
-rw-r--r--usr/src/lib/libzfs/common/libzfs_util.c4
-rw-r--r--usr/src/lib/libzfs/common/mapfile-vers8
9 files changed, 980 insertions, 988 deletions
diff --git a/usr/src/lib/libzfs/Makefile.com b/usr/src/lib/libzfs/Makefile.com
index 44327f934b..77e2aa4467 100644
--- a/usr/src/lib/libzfs/Makefile.com
+++ b/usr/src/lib/libzfs/Makefile.com
@@ -20,7 +20,6 @@
#
#
# Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright (c) 2011 by Delphix. All rights reserved.
#
LIBRARY= libzfs.a
@@ -41,8 +40,8 @@ OBJS_COMMON= \
libzfs_dataset.o \
libzfs_diff.o \
libzfs_fru.o \
+ libzfs_graph.o \
libzfs_import.o \
- libzfs_iter.o \
libzfs_mount.o \
libzfs_pool.o \
libzfs_sendrecv.o \
diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h
index f7adf7a8f8..1b5329c3e1 100644
--- a/usr/src/lib/libzfs/common/libzfs.h
+++ b/usr/src/lib/libzfs/common/libzfs.h
@@ -22,7 +22,6 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2010 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2011 by Delphix. All rights reserved.
*/
#ifndef _LIBZFS_H
@@ -382,7 +381,6 @@ extern void zpool_explain_recover(libzfs_handle_t *, const char *, int,
* underlying datasets, only the references to them.
*/
extern zfs_handle_t *zfs_open(libzfs_handle_t *, const char *, int);
-extern zfs_handle_t *zfs_handle_dup(zfs_handle_t *);
extern void zfs_close(zfs_handle_t *);
extern zfs_type_t zfs_get_type(const zfs_handle_t *);
extern const char *zfs_get_name(const zfs_handle_t *);
@@ -416,20 +414,12 @@ extern int zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname,
uint64_t *propvalue);
extern int zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
char *propbuf, int proplen, boolean_t literal);
-extern int zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
- uint64_t *propvalue);
-extern int zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
- char *propbuf, int proplen, boolean_t literal);
-extern int zfs_get_snapused_int(zfs_handle_t *firstsnap, zfs_handle_t *lastsnap,
- uint64_t *usedp);
extern uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t);
extern int zfs_prop_inherit(zfs_handle_t *, const char *, boolean_t);
extern const char *zfs_prop_values(zfs_prop_t);
extern int zfs_prop_is_string(zfs_prop_t prop);
extern nvlist_t *zfs_get_user_props(zfs_handle_t *);
extern nvlist_t *zfs_get_recvd_props(zfs_handle_t *);
-extern nvlist_t *zfs_get_clones_nvl(zfs_handle_t *);
-
typedef struct zprop_list {
int pl_prop;
@@ -504,7 +494,6 @@ extern int zfs_iter_dependents(zfs_handle_t *, boolean_t, zfs_iter_f, void *);
extern int zfs_iter_filesystems(zfs_handle_t *, zfs_iter_f, void *);
extern int zfs_iter_snapshots(zfs_handle_t *, zfs_iter_f, void *);
extern int zfs_iter_snapshots_sorted(zfs_handle_t *, zfs_iter_f, void *);
-extern int zfs_iter_snapspec(zfs_handle_t *, const char *, zfs_iter_f, void *);
typedef struct get_all_cb {
zfs_handle_t **cb_handles;
@@ -525,7 +514,6 @@ extern int zfs_create(libzfs_handle_t *, const char *, zfs_type_t,
extern int zfs_create_ancestors(libzfs_handle_t *, const char *);
extern int zfs_destroy(zfs_handle_t *, boolean_t);
extern int zfs_destroy_snaps(zfs_handle_t *, char *, boolean_t);
-extern int zfs_destroy_snaps_nvl(zfs_handle_t *, nvlist_t *, boolean_t);
extern int zfs_clone(zfs_handle_t *, const char *, nvlist_t *);
extern int zfs_snapshot(libzfs_handle_t *, const char *, boolean_t, nvlist_t *);
extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, boolean_t);
@@ -549,12 +537,6 @@ typedef struct sendflags {
/* send properties (ie, -p) */
int props : 1;
-
- /* do not send (no-op, ie. -n) */
- int noop : 1;
-
- /* parsable verbose output (ie. -P) */
- int parsable : 1;
} sendflags_t;
typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *);
diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c
index 0682b10d29..041f6f91d0 100644
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c
@@ -496,7 +496,7 @@ make_dataset_handle(libzfs_handle_t *hdl, const char *path)
return (zhp);
}
-zfs_handle_t *
+static 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);
@@ -513,53 +513,6 @@ make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
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);
-}
-
/*
* Opens the given snapshot, filesystem, or volume. The 'types'
* argument is a mask of acceptable types. The function will print an
@@ -923,12 +876,6 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
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) {
@@ -1902,6 +1849,8 @@ zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf,
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;
@@ -1916,120 +1865,6 @@ zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf,
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) {
- if (nvlist_add_boolean(gca->value, zfs_get_name(zhp)) != 0) {
- zfs_close(zhp);
- return (no_memory(zhp->zfs_hdl));
- }
- 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
@@ -2158,11 +1993,6 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
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:
@@ -2523,7 +2353,7 @@ zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname,
int err;
zfs_cmd_t zc = { 0 };
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+ (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),
@@ -2575,95 +2405,144 @@ zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
return (0);
}
-int
-zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
- uint64_t *propvalue)
+/*
+ * Returns the name of the given zfs handle.
+ */
+const char *
+zfs_get_name(const zfs_handle_t *zhp)
{
- int err;
- zfs_cmd_t zc = { 0 };
- const char *snapname;
+ return (zhp->zfs_name);
+}
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+/*
+ * Returns the type of the given zfs handle.
+ */
+zfs_type_t
+zfs_get_type(const zfs_handle_t *zhp)
+{
+ return (zhp->zfs_type);
+}
- 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));
- }
+static int
+zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc)
+{
+ int rc;
+ uint64_t orig_cookie;
- err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_WRITTEN, &zc);
- if (err)
- return (err);
+ 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);
- *propvalue = zc.zc_cookie;
- return (0);
+ 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);
}
+/*
+ * Iterate over all child filesystems
+ */
int
-zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
- char *propbuf, int proplen, boolean_t literal)
+zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
{
- int err;
- uint64_t propvalue;
+ zfs_cmd_t zc = { 0 };
+ zfs_handle_t *nzhp;
+ int ret;
- err = zfs_prop_get_written_int(zhp, propname, &propvalue);
+ if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
+ return (0);
- if (err)
- return (err);
+ if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
+ return (-1);
- if (literal) {
- (void) snprintf(propbuf, proplen, "%llu", propvalue);
- } else {
- zfs_nicenum(propvalue, propbuf, proplen);
+ 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 ((ret = func(nzhp, data)) != 0) {
+ zcmd_free_nvlists(&zc);
+ return (ret);
+ }
}
- return (0);
+ zcmd_free_nvlists(&zc);
+ return ((ret < 0) ? ret : 0);
}
+/*
+ * Iterate over all snapshots
+ */
int
-zfs_get_snapused_int(zfs_handle_t *firstsnap, zfs_handle_t *lastsnap,
- uint64_t *usedp)
+zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
{
- int err;
zfs_cmd_t zc = { 0 };
+ zfs_handle_t *nzhp;
+ int ret;
- (void) strlcpy(zc.zc_name, lastsnap->zfs_name, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, firstsnap->zfs_name, sizeof (zc.zc_value));
+ if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
+ return (0);
- err = ioctl(lastsnap->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_SNAPS, &zc);
- if (err)
- return (err);
+ 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) {
- *usedp = zc.zc_cookie;
+ if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
+ &zc)) == NULL) {
+ continue;
+ }
- return (0);
+ if ((ret = func(nzhp, data)) != 0) {
+ zcmd_free_nvlists(&zc);
+ return (ret);
+ }
+ }
+ zcmd_free_nvlists(&zc);
+ return ((ret < 0) ? ret : 0);
}
/*
- * Returns the name of the given zfs handle.
+ * Iterate over all children, snapshots and filesystems
*/
-const char *
-zfs_get_name(const zfs_handle_t *zhp)
+int
+zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
{
- return (zhp->zfs_name);
-}
+ int ret;
-/*
- * Returns the type of the given zfs handle.
- */
-zfs_type_t
-zfs_get_type(const zfs_handle_t *zhp)
-{
- return (zhp->zfs_type);
+ if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
+ return (ret);
+
+ return (zfs_iter_snapshots(zhp, func, data));
}
/*
@@ -2699,7 +2578,7 @@ parent_name(const char *path, char *buf, size_t buflen)
if ((loc = strrchr(path, '/')) == NULL)
return (-1);
- (void) strlcpy(buf, path, MIN(buflen, loc - path));
+ (void) strncpy(buf, path, MIN(buflen, loc - path));
buf[loc - path] = '\0';
return (0);
@@ -2737,7 +2616,7 @@ check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned,
/* check to see if the pool exists */
if ((slash = strchr(parent, '/')) == NULL)
slash = parent + strlen(parent);
- (void) strlcpy(zc.zc_name, parent, slash - parent);
+ (void) strncpy(zc.zc_name, parent, slash - parent);
zc.zc_name[slash - parent] = '\0';
if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 &&
errno == ENOENT) {
@@ -3099,8 +2978,9 @@ zfs_destroy(zfs_handle_t *zhp, boolean_t defer)
}
struct destroydata {
- nvlist_t *nvl;
- const char *snapname;
+ char *snapname;
+ boolean_t gotone;
+ boolean_t closezhp;
};
static int
@@ -3109,19 +2989,24 @@ 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) snprintf(name, sizeof (name),
- "%s@%s", zhp->zfs_name, dd->snapname);
+ (void) strlcpy(name, zhp->zfs_name, sizeof (name));
+ (void) strlcat(name, "@", sizeof (name));
+ (void) strlcat(name, dd->snapname, sizeof (name));
szhp = make_dataset_handle(zhp->zfs_hdl, name);
if (szhp) {
- verify(nvlist_add_boolean(dd->nvl, name) == 0);
+ dd->gotone = B_TRUE;
zfs_close(szhp);
}
- rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, dd);
- zfs_close(zhp);
+ dd->closezhp = B_TRUE;
+ if (!dd->gotone)
+ rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, arg);
+ if (closezhp)
+ zfs_close(zhp);
return (rv);
}
@@ -3131,45 +3016,29 @@ zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
int
zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer)
{
+ zfs_cmd_t zc = { 0 };
int ret;
struct destroydata dd = { 0 };
dd.snapname = snapname;
- verify(nvlist_alloc(&dd.nvl, NV_UNIQUE_NAME, 0) == 0);
- (void) zfs_check_snap_cb(zfs_handle_dup(zhp), &dd);
+ (void) zfs_check_snap_cb(zhp, &dd);
- if (nvlist_next_nvpair(dd.nvl, NULL) == NULL) {
- ret = zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
+ if (!dd.gotone) {
+ return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),
- zhp->zfs_name, snapname);
- } else {
- ret = zfs_destroy_snaps_nvl(zhp, dd.nvl, defer);
+ zhp->zfs_name, snapname));
}
- nvlist_free(dd.nvl);
- return (ret);
-}
-
-/*
- * Destroys all the snapshots named in the nvlist. They must be underneath
- * the zhp (either snapshots of it, or snapshots of its descendants).
- */
-int
-zfs_destroy_snaps_nvl(zfs_handle_t *zhp, nvlist_t *snaps, boolean_t defer)
-{
- int ret;
- zfs_cmd_t zc = { 0 };
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- if (zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, snaps) != 0)
- return (-1);
+ (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
zc.zc_defer_destroy = defer;
- ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS_NVL, &zc);
+ ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc);
if (ret != 0) {
char errbuf[1024];
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot destroy snapshots in %s"), zc.zc_name);
+ "cannot destroy '%s@%s'"), zc.zc_name, snapname);
switch (errno) {
case EEXIST:
@@ -3205,7 +3074,7 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot create '%s'"), target);
- /* validate the target/clone name */
+ /* validate the target name */
if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE))
return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
@@ -3362,7 +3231,7 @@ zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
/* make sure the parent exists and is of the appropriate type */
delim = strchr(path, '@');
- (void) strlcpy(parent, path, delim - path);
+ (void) strncpy(parent, path, delim - path);
parent[delim - path] = '\0';
if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM |
@@ -3542,6 +3411,42 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
}
/*
+ * 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
@@ -3990,7 +3895,7 @@ zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
int error;
zfs_useracct_t buf[100];
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+ (void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
zc.zc_objset_type = type;
zc.zc_nvlist_dst = (uintptr_t)buf;
diff --git a/usr/src/lib/libzfs/common/libzfs_graph.c b/usr/src/lib/libzfs/common/libzfs_graph.c
new file mode 100644
index 0000000000..bc21c51ae2
--- /dev/null
+++ b/usr/src/lib/libzfs/common/libzfs_graph.c
@@ -0,0 +1,653 @@
+/*
+ * 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.
+ */
+
+/*
+ * Iterate over all children of the current object. This includes the normal
+ * dataset hierarchy, but also arbitrary hierarchies due to clones. We want to
+ * walk all datasets in the pool, and construct a directed graph of the form:
+ *
+ * home
+ * |
+ * +----+----+
+ * | |
+ * v v ws
+ * bar baz |
+ * | |
+ * v v
+ * @yesterday ----> foo
+ *
+ * In order to construct this graph, we have to walk every dataset in the pool,
+ * because the clone parent is stored as a property of the child, not the
+ * parent. The parent only keeps track of the number of clones.
+ *
+ * In the normal case (without clones) this would be rather expensive. To avoid
+ * unnecessary computation, we first try a walk of the subtree hierarchy
+ * starting from the initial node. At each dataset, we construct a node in the
+ * graph and an edge leading from its parent. If we don't see any snapshots
+ * with a non-zero clone count, then we are finished.
+ *
+ * If we do find a cloned snapshot, then we finish the walk of the current
+ * subtree, but indicate that we need to do a complete walk. We then perform a
+ * global walk of all datasets, avoiding the subtree we already processed.
+ *
+ * At the end of this, we'll end up with a directed graph of all relevant (and
+ * possible some irrelevant) datasets in the system. We need to both find our
+ * limiting subgraph and determine a safe ordering in which to destroy the
+ * datasets. We do a topological ordering of our graph starting at our target
+ * dataset, and then walk the results in reverse.
+ *
+ * It's possible for the graph to have cycles if, for example, the user renames
+ * a clone to be the parent of its origin snapshot. The user can request to
+ * generate an error in this case, or ignore the cycle and continue.
+ *
+ * When removing datasets, we want to destroy the snapshots in chronological
+ * order (because this is the most efficient method). In order to accomplish
+ * this, we store the creation transaction group with each vertex and keep each
+ * vertex's edges sorted according to this value. The topological sort will
+ * automatically walk the snapshots in the correct order.
+ */
+
+#include <assert.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <libzfs.h>
+
+#include "libzfs_impl.h"
+#include "zfs_namecheck.h"
+
+#define MIN_EDGECOUNT 4
+
+/*
+ * Vertex structure. Indexed by dataset name, this structure maintains a list
+ * of edges to other vertices.
+ */
+struct zfs_edge;
+typedef struct zfs_vertex {
+ char zv_dataset[ZFS_MAXNAMELEN];
+ struct zfs_vertex *zv_next;
+ int zv_visited;
+ uint64_t zv_txg;
+ struct zfs_edge **zv_edges;
+ int zv_edgecount;
+ int zv_edgealloc;
+} zfs_vertex_t;
+
+enum {
+ VISIT_SEEN = 1,
+ VISIT_SORT_PRE,
+ VISIT_SORT_POST
+};
+
+/*
+ * Edge structure. Simply maintains a pointer to the destination vertex. There
+ * is no need to store the source vertex, since we only use edges in the context
+ * of the source vertex.
+ */
+typedef struct zfs_edge {
+ zfs_vertex_t *ze_dest;
+ struct zfs_edge *ze_next;
+} zfs_edge_t;
+
+#define ZFS_GRAPH_SIZE 1027 /* this could be dynamic some day */
+
+/*
+ * Graph structure. Vertices are maintained in a hash indexed by dataset name.
+ */
+typedef struct zfs_graph {
+ zfs_vertex_t **zg_hash;
+ size_t zg_size;
+ size_t zg_nvertex;
+ const char *zg_root;
+ int zg_clone_count;
+} zfs_graph_t;
+
+/*
+ * Allocate a new edge pointing to the target vertex.
+ */
+static zfs_edge_t *
+zfs_edge_create(libzfs_handle_t *hdl, zfs_vertex_t *dest)
+{
+ zfs_edge_t *zep = zfs_alloc(hdl, sizeof (zfs_edge_t));
+
+ if (zep == NULL)
+ return (NULL);
+
+ zep->ze_dest = dest;
+
+ return (zep);
+}
+
+/*
+ * Destroy an edge.
+ */
+static void
+zfs_edge_destroy(zfs_edge_t *zep)
+{
+ free(zep);
+}
+
+/*
+ * Allocate a new vertex with the given name.
+ */
+static zfs_vertex_t *
+zfs_vertex_create(libzfs_handle_t *hdl, const char *dataset)
+{
+ zfs_vertex_t *zvp = zfs_alloc(hdl, sizeof (zfs_vertex_t));
+
+ if (zvp == NULL)
+ return (NULL);
+
+ assert(strlen(dataset) < ZFS_MAXNAMELEN);
+
+ (void) strlcpy(zvp->zv_dataset, dataset, sizeof (zvp->zv_dataset));
+
+ if ((zvp->zv_edges = zfs_alloc(hdl,
+ MIN_EDGECOUNT * sizeof (void *))) == NULL) {
+ free(zvp);
+ return (NULL);
+ }
+
+ zvp->zv_edgealloc = MIN_EDGECOUNT;
+
+ return (zvp);
+}
+
+/*
+ * Destroy a vertex. Frees up any associated edges.
+ */
+static void
+zfs_vertex_destroy(zfs_vertex_t *zvp)
+{
+ int i;
+
+ for (i = 0; i < zvp->zv_edgecount; i++)
+ zfs_edge_destroy(zvp->zv_edges[i]);
+
+ free(zvp->zv_edges);
+ free(zvp);
+}
+
+/*
+ * Given a vertex, add an edge to the destination vertex.
+ */
+static int
+zfs_vertex_add_edge(libzfs_handle_t *hdl, zfs_vertex_t *zvp,
+ zfs_vertex_t *dest)
+{
+ zfs_edge_t *zep = zfs_edge_create(hdl, dest);
+
+ if (zep == NULL)
+ return (-1);
+
+ if (zvp->zv_edgecount == zvp->zv_edgealloc) {
+ void *ptr;
+
+ if ((ptr = zfs_realloc(hdl, zvp->zv_edges,
+ zvp->zv_edgealloc * sizeof (void *),
+ zvp->zv_edgealloc * 2 * sizeof (void *))) == NULL)
+ return (-1);
+
+ zvp->zv_edges = ptr;
+ zvp->zv_edgealloc *= 2;
+ }
+
+ zvp->zv_edges[zvp->zv_edgecount++] = zep;
+
+ return (0);
+}
+
+static int
+zfs_edge_compare(const void *a, const void *b)
+{
+ const zfs_edge_t *ea = *((zfs_edge_t **)a);
+ const zfs_edge_t *eb = *((zfs_edge_t **)b);
+
+ if (ea->ze_dest->zv_txg < eb->ze_dest->zv_txg)
+ return (-1);
+ if (ea->ze_dest->zv_txg > eb->ze_dest->zv_txg)
+ return (1);
+ return (0);
+}
+
+/*
+ * Sort the given vertex edges according to the creation txg of each vertex.
+ */
+static void
+zfs_vertex_sort_edges(zfs_vertex_t *zvp)
+{
+ if (zvp->zv_edgecount == 0)
+ return;
+
+ qsort(zvp->zv_edges, zvp->zv_edgecount, sizeof (void *),
+ zfs_edge_compare);
+}
+
+/*
+ * Construct a new graph object. We allow the size to be specified as a
+ * parameter so in the future we can size the hash according to the number of
+ * datasets in the pool.
+ */
+static zfs_graph_t *
+zfs_graph_create(libzfs_handle_t *hdl, const char *dataset, size_t size)
+{
+ zfs_graph_t *zgp = zfs_alloc(hdl, sizeof (zfs_graph_t));
+
+ if (zgp == NULL)
+ return (NULL);
+
+ zgp->zg_size = size;
+ if ((zgp->zg_hash = zfs_alloc(hdl,
+ size * sizeof (zfs_vertex_t *))) == NULL) {
+ free(zgp);
+ return (NULL);
+ }
+
+ zgp->zg_root = dataset;
+ zgp->zg_clone_count = 0;
+
+ return (zgp);
+}
+
+/*
+ * Destroy a graph object. We have to iterate over all the hash chains,
+ * destroying each vertex in the process.
+ */
+static void
+zfs_graph_destroy(zfs_graph_t *zgp)
+{
+ int i;
+ zfs_vertex_t *current, *next;
+
+ for (i = 0; i < zgp->zg_size; i++) {
+ current = zgp->zg_hash[i];
+ while (current != NULL) {
+ next = current->zv_next;
+ zfs_vertex_destroy(current);
+ current = next;
+ }
+ }
+
+ free(zgp->zg_hash);
+ free(zgp);
+}
+
+/*
+ * Graph hash function. Classic bernstein k=33 hash function, taken from
+ * usr/src/cmd/sgs/tools/common/strhash.c
+ */
+static size_t
+zfs_graph_hash(zfs_graph_t *zgp, const char *str)
+{
+ size_t hash = 5381;
+ int c;
+
+ while ((c = *str++) != 0)
+ hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+
+ return (hash % zgp->zg_size);
+}
+
+/*
+ * Given a dataset name, finds the associated vertex, creating it if necessary.
+ */
+static zfs_vertex_t *
+zfs_graph_lookup(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset,
+ uint64_t txg)
+{
+ size_t idx = zfs_graph_hash(zgp, dataset);
+ zfs_vertex_t *zvp;
+
+ for (zvp = zgp->zg_hash[idx]; zvp != NULL; zvp = zvp->zv_next) {
+ if (strcmp(zvp->zv_dataset, dataset) == 0) {
+ if (zvp->zv_txg == 0)
+ zvp->zv_txg = txg;
+ return (zvp);
+ }
+ }
+
+ if ((zvp = zfs_vertex_create(hdl, dataset)) == NULL)
+ return (NULL);
+
+ zvp->zv_next = zgp->zg_hash[idx];
+ zvp->zv_txg = txg;
+ zgp->zg_hash[idx] = zvp;
+ zgp->zg_nvertex++;
+
+ return (zvp);
+}
+
+/*
+ * Given two dataset names, create an edge between them. For the source vertex,
+ * mark 'zv_visited' to indicate that we have seen this vertex, and not simply
+ * created it as a destination of another edge. If 'dest' is NULL, then this
+ * is an individual vertex (i.e. the starting vertex), so don't add an edge.
+ */
+static int
+zfs_graph_add(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *source,
+ const char *dest, uint64_t txg)
+{
+ zfs_vertex_t *svp, *dvp;
+
+ if ((svp = zfs_graph_lookup(hdl, zgp, source, 0)) == NULL)
+ return (-1);
+ svp->zv_visited = VISIT_SEEN;
+ if (dest != NULL) {
+ dvp = zfs_graph_lookup(hdl, zgp, dest, txg);
+ if (dvp == NULL)
+ return (-1);
+ if (zfs_vertex_add_edge(hdl, svp, dvp) != 0)
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Iterate over all children of the given dataset, adding any vertices
+ * as necessary. Returns -1 if there was an error, or 0 otherwise.
+ * This is a simple recursive algorithm - the ZFS namespace typically
+ * is very flat. We manually invoke the necessary ioctl() calls to
+ * avoid the overhead and additional semantics of zfs_open().
+ */
+static int
+iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
+{
+ zfs_cmd_t zc = { 0 };
+ zfs_vertex_t *zvp;
+
+ /*
+ * Look up the source vertex, and avoid it if we've seen it before.
+ */
+ zvp = zfs_graph_lookup(hdl, zgp, dataset, 0);
+ if (zvp == NULL)
+ return (-1);
+ if (zvp->zv_visited == VISIT_SEEN)
+ return (0);
+
+ /*
+ * Iterate over all children
+ */
+ 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))) {
+ /*
+ * Get statistics for this dataset, to determine the type of the
+ * dataset and clone statistics. If this fails, the dataset has
+ * since been removed, and we're pretty much screwed anyway.
+ */
+ zc.zc_objset_stats.dds_origin[0] = '\0';
+ if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
+ continue;
+
+ if (zc.zc_objset_stats.dds_origin[0] != '\0') {
+ if (zfs_graph_add(hdl, zgp,
+ zc.zc_objset_stats.dds_origin, zc.zc_name,
+ zc.zc_objset_stats.dds_creation_txg) != 0)
+ return (-1);
+ /*
+ * Count origins only if they are contained in the graph
+ */
+ if (isa_child_of(zc.zc_objset_stats.dds_origin,
+ zgp->zg_root))
+ zgp->zg_clone_count--;
+ }
+
+ /*
+ * Add an edge between the parent and the child.
+ */
+ if (zfs_graph_add(hdl, zgp, dataset, zc.zc_name,
+ zc.zc_objset_stats.dds_creation_txg) != 0)
+ return (-1);
+
+ /*
+ * Recursively visit child
+ */
+ if (iterate_children(hdl, zgp, zc.zc_name))
+ return (-1);
+ }
+
+ /*
+ * Now iterate over all snapshots.
+ */
+ bzero(&zc, sizeof (zc));
+
+ for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
+ ioctl(hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT, &zc) == 0;
+ (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {
+
+ /*
+ * Get statistics for this dataset, to determine the type of the
+ * dataset and clone statistics. If this fails, the dataset has
+ * since been removed, and we're pretty much screwed anyway.
+ */
+ if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
+ continue;
+
+ /*
+ * Add an edge between the parent and the child.
+ */
+ if (zfs_graph_add(hdl, zgp, dataset, zc.zc_name,
+ zc.zc_objset_stats.dds_creation_txg) != 0)
+ return (-1);
+
+ zgp->zg_clone_count += zc.zc_objset_stats.dds_num_clones;
+ }
+
+ zvp->zv_visited = VISIT_SEEN;
+
+ return (0);
+}
+
+/*
+ * Returns false if there are no snapshots with dependent clones in this
+ * subtree or if all of those clones are also in this subtree. Returns
+ * true if there is an error or there are external dependents.
+ */
+static boolean_t
+external_dependents(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
+{
+ zfs_cmd_t zc = { 0 };
+
+ /*
+ * Check whether this dataset is a clone or has clones since
+ * iterate_children() only checks the children.
+ */
+ (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
+ if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
+ return (B_TRUE);
+
+ if (zc.zc_objset_stats.dds_origin[0] != '\0') {
+ if (zfs_graph_add(hdl, zgp,
+ zc.zc_objset_stats.dds_origin, zc.zc_name,
+ zc.zc_objset_stats.dds_creation_txg) != 0)
+ return (B_TRUE);
+ if (isa_child_of(zc.zc_objset_stats.dds_origin, dataset))
+ zgp->zg_clone_count--;
+ }
+
+ if ((zc.zc_objset_stats.dds_num_clones) ||
+ iterate_children(hdl, zgp, dataset))
+ return (B_TRUE);
+
+ return (zgp->zg_clone_count != 0);
+}
+
+/*
+ * Construct a complete graph of all necessary vertices. First, iterate over
+ * only our object's children. If no cloned snapshots are found, or all of
+ * the cloned snapshots are in this subtree then return a graph of the subtree.
+ * Otherwise, start at the root of the pool and iterate over all datasets.
+ */
+static zfs_graph_t *
+construct_graph(libzfs_handle_t *hdl, const char *dataset)
+{
+ zfs_graph_t *zgp = zfs_graph_create(hdl, dataset, ZFS_GRAPH_SIZE);
+ int ret = 0;
+
+ if (zgp == NULL)
+ return (zgp);
+
+ if ((strchr(dataset, '/') == NULL) ||
+ (external_dependents(hdl, zgp, dataset))) {
+ /*
+ * Determine pool name and try again.
+ */
+ int len = strcspn(dataset, "/@") + 1;
+ char *pool = zfs_alloc(hdl, len);
+
+ if (pool == NULL) {
+ zfs_graph_destroy(zgp);
+ return (NULL);
+ }
+ (void) strlcpy(pool, dataset, len);
+
+ if (iterate_children(hdl, zgp, pool) == -1 ||
+ zfs_graph_add(hdl, zgp, pool, NULL, 0) != 0) {
+ free(pool);
+ zfs_graph_destroy(zgp);
+ return (NULL);
+ }
+ free(pool);
+ }
+
+ if (ret == -1 || zfs_graph_add(hdl, zgp, dataset, NULL, 0) != 0) {
+ zfs_graph_destroy(zgp);
+ return (NULL);
+ }
+
+ return (zgp);
+}
+
+/*
+ * Given a graph, do a recursive topological sort into the given array. This is
+ * really just a depth first search, so that the deepest nodes appear first.
+ * hijack the 'zv_visited' marker to avoid visiting the same vertex twice.
+ */
+static int
+topo_sort(libzfs_handle_t *hdl, boolean_t allowrecursion, char **result,
+ size_t *idx, zfs_vertex_t *zgv)
+{
+ int i;
+
+ if (zgv->zv_visited == VISIT_SORT_PRE && !allowrecursion) {
+ /*
+ * If we've already seen this vertex as part of our depth-first
+ * search, then we have a cyclic dependency, and we must return
+ * an error.
+ */
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "recursive dependency at '%s'"),
+ zgv->zv_dataset);
+ return (zfs_error(hdl, EZFS_RECURSIVE,
+ dgettext(TEXT_DOMAIN,
+ "cannot determine dependent datasets")));
+ } else if (zgv->zv_visited >= VISIT_SORT_PRE) {
+ /*
+ * If we've already processed this as part of the topological
+ * sort, then don't bother doing so again.
+ */
+ return (0);
+ }
+
+ zgv->zv_visited = VISIT_SORT_PRE;
+
+ /* avoid doing a search if we don't have to */
+ zfs_vertex_sort_edges(zgv);
+ for (i = 0; i < zgv->zv_edgecount; i++) {
+ if (topo_sort(hdl, allowrecursion, result, idx,
+ zgv->zv_edges[i]->ze_dest) != 0)
+ return (-1);
+ }
+
+ /* we may have visited this in the course of the above */
+ if (zgv->zv_visited == VISIT_SORT_POST)
+ return (0);
+
+ if ((result[*idx] = zfs_alloc(hdl,
+ strlen(zgv->zv_dataset) + 1)) == NULL)
+ return (-1);
+
+ (void) strcpy(result[*idx], zgv->zv_dataset);
+ *idx += 1;
+ zgv->zv_visited = VISIT_SORT_POST;
+ return (0);
+}
+
+/*
+ * The only public interface for this file. Do the dirty work of constructing a
+ * child list for the given object. Construct the graph, do the toplogical
+ * sort, and then return the array of strings to the caller.
+ *
+ * The 'allowrecursion' parameter controls behavior when cycles are found. If
+ * it is set, the the cycle is ignored and the results returned as if the cycle
+ * did not exist. If it is not set, then the routine will generate an error if
+ * a cycle is found.
+ */
+int
+get_dependents(libzfs_handle_t *hdl, boolean_t allowrecursion,
+ const char *dataset, char ***result, size_t *count)
+{
+ zfs_graph_t *zgp;
+ zfs_vertex_t *zvp;
+
+ if ((zgp = construct_graph(hdl, dataset)) == NULL)
+ return (-1);
+
+ if ((*result = zfs_alloc(hdl,
+ zgp->zg_nvertex * sizeof (char *))) == NULL) {
+ zfs_graph_destroy(zgp);
+ return (-1);
+ }
+
+ if ((zvp = zfs_graph_lookup(hdl, zgp, dataset, 0)) == NULL) {
+ free(*result);
+ zfs_graph_destroy(zgp);
+ return (-1);
+ }
+
+ *count = 0;
+ if (topo_sort(hdl, allowrecursion, *result, count, zvp) != 0) {
+ free(*result);
+ zfs_graph_destroy(zgp);
+ return (-1);
+ }
+
+ /*
+ * Get rid of the last entry, which is our starting vertex and not
+ * strictly a dependent.
+ */
+ assert(*count > 0);
+ free((*result)[*count - 1]);
+ (*count)--;
+
+ zfs_graph_destroy(zgp);
+
+ return (0);
+}
diff --git a/usr/src/lib/libzfs/common/libzfs_impl.h b/usr/src/lib/libzfs/common/libzfs_impl.h
index b1eae47ed2..c9b09a2050 100644
--- a/usr/src/lib/libzfs/common/libzfs_impl.h
+++ b/usr/src/lib/libzfs/common/libzfs_impl.h
@@ -21,7 +21,6 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011 by Delphix. All rights reserved.
*/
#ifndef _LIBFS_IMPL_H
@@ -116,7 +115,7 @@ struct zpool_handle {
diskaddr_t zpool_start_block;
};
-typedef enum {
+typedef enum {
PROTO_NFS = 0,
PROTO_SMB = 1,
PROTO_END = 2
@@ -148,7 +147,6 @@ int zpool_standard_error_fmt(libzfs_handle_t *, int, const char *, ...);
int get_dependents(libzfs_handle_t *, boolean_t, const char *, char ***,
size_t *);
-zfs_handle_t *make_dataset_handle_zc(libzfs_handle_t *, zfs_cmd_t *);
int zprop_parse_value(libzfs_handle_t *, nvpair_t *, int, zfs_type_t,
diff --git a/usr/src/lib/libzfs/common/libzfs_iter.c b/usr/src/lib/libzfs/common/libzfs_iter.c
deleted file mode 100644
index 212383d0e6..0000000000
--- a/usr/src/lib/libzfs/common/libzfs_iter.c
+++ /dev/null
@@ -1,462 +0,0 @@
-/*
- * 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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2011 by Delphix. All rights reserved.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
-#include <unistd.h>
-#include <stddef.h>
-#include <libintl.h>
-#include <libzfs.h>
-
-#include "libzfs_impl.h"
-
-int
-zfs_iter_clones(zfs_handle_t *zhp, zfs_iter_f func, void *data)
-{
- nvlist_t *nvl = zfs_get_clones_nvl(zhp);
- nvpair_t *pair;
-
- if (nvl == NULL)
- return (0);
-
- for (pair = nvlist_next_nvpair(nvl, NULL); pair != NULL;
- pair = nvlist_next_nvpair(nvl, pair)) {
- zfs_handle_t *clone = zfs_open(zhp->zfs_hdl, nvpair_name(pair),
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
- if (clone != NULL) {
- int err = func(clone, data);
- if (err != 0)
- return (err);
- }
- }
- return (0);
-}
-
-static int
-zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc)
-{
- int rc;
- uint64_t orig_cookie;
-
- 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);
-
- 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);
-}
-
-/*
- * Iterate over all child filesystems
- */
-int
-zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
-{
- zfs_cmd_t zc = { 0 };
- zfs_handle_t *nzhp;
- int ret;
-
- if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
- return (0);
-
- if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
- return (-1);
-
- 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 ((ret = func(nzhp, data)) != 0) {
- zcmd_free_nvlists(&zc);
- return (ret);
- }
- }
- zcmd_free_nvlists(&zc);
- return ((ret < 0) ? ret : 0);
-}
-
-/*
- * Iterate over all snapshots
- */
-int
-zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
-{
- zfs_cmd_t zc = { 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);
-}
-
-/*
- * Routines for dealing with the sorted snapshot functionality
- */
-typedef struct zfs_node {
- zfs_handle_t *zn_handle;
- avl_node_t zn_avlnode;
-} zfs_node_t;
-
-static int
-zfs_sort_snaps(zfs_handle_t *zhp, void *data)
-{
- avl_tree_t *avl = data;
- zfs_node_t *node;
- zfs_node_t search;
-
- search.zn_handle = zhp;
- node = avl_find(avl, &search, NULL);
- if (node) {
- /*
- * If this snapshot was renamed while we were creating the
- * AVL tree, it's possible that we already inserted it under
- * its old name. Remove the old handle before adding the new
- * one.
- */
- zfs_close(node->zn_handle);
- avl_remove(avl, node);
- free(node);
- }
-
- node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t));
- node->zn_handle = zhp;
- avl_add(avl, node);
-
- return (0);
-}
-
-static int
-zfs_snapshot_compare(const void *larg, const void *rarg)
-{
- zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
- zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
- uint64_t lcreate, rcreate;
-
- /*
- * Sort them according to creation time. We use the hidden
- * CREATETXG property to get an absolute ordering of snapshots.
- */
- lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);
- rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);
-
- if (lcreate < rcreate)
- return (-1);
- else if (lcreate > rcreate)
- return (+1);
- else
- return (0);
-}
-
-int
-zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data)
-{
- int ret = 0;
- zfs_node_t *node;
- avl_tree_t avl;
- void *cookie = NULL;
-
- avl_create(&avl, zfs_snapshot_compare,
- sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode));
-
- ret = zfs_iter_snapshots(zhp, zfs_sort_snaps, &avl);
-
- for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node))
- ret |= callback(node->zn_handle, data);
-
- while ((node = avl_destroy_nodes(&avl, &cookie)) != NULL)
- free(node);
-
- avl_destroy(&avl);
-
- return (ret);
-}
-
-typedef struct {
- char *ssa_first;
- char *ssa_last;
- boolean_t ssa_seenfirst;
- boolean_t ssa_seenlast;
- zfs_iter_f ssa_func;
- void *ssa_arg;
-} snapspec_arg_t;
-
-static int
-snapspec_cb(zfs_handle_t *zhp, void *arg) {
- snapspec_arg_t *ssa = arg;
- char *shortsnapname;
- int err = 0;
-
- if (ssa->ssa_seenlast)
- return (0);
- shortsnapname = zfs_strdup(zhp->zfs_hdl,
- strchr(zfs_get_name(zhp), '@') + 1);
-
- if (!ssa->ssa_seenfirst && strcmp(shortsnapname, ssa->ssa_first) == 0)
- ssa->ssa_seenfirst = B_TRUE;
-
- if (ssa->ssa_seenfirst) {
- err = ssa->ssa_func(zhp, ssa->ssa_arg);
- } else {
- zfs_close(zhp);
- }
-
- if (strcmp(shortsnapname, ssa->ssa_last) == 0)
- ssa->ssa_seenlast = B_TRUE;
- free(shortsnapname);
-
- return (err);
-}
-
-/*
- * spec is a string like "A,B%C,D"
- *
- * <snaps>, where <snaps> can be:
- * <snap> (single snapshot)
- * <snap>%<snap> (range of snapshots, inclusive)
- * %<snap> (range of snapshots, starting with earliest)
- * <snap>% (range of snapshots, ending with last)
- * % (all snapshots)
- * <snaps>[,...] (comma separated list of the above)
- *
- * If a snapshot can not be opened, continue trying to open the others, but
- * return ENOENT at the end.
- */
-int
-zfs_iter_snapspec(zfs_handle_t *fs_zhp, const char *spec_orig,
- zfs_iter_f func, void *arg)
-{
- char buf[ZFS_MAXNAMELEN];
- char *comma_separated, *cp;
- int err = 0;
- int ret = 0;
-
- (void) strlcpy(buf, spec_orig, sizeof (buf));
- cp = buf;
-
- while ((comma_separated = strsep(&cp, ",")) != NULL) {
- char *pct = strchr(comma_separated, '%');
- if (pct != NULL) {
- snapspec_arg_t ssa = { 0 };
- ssa.ssa_func = func;
- ssa.ssa_arg = arg;
-
- if (pct == comma_separated)
- ssa.ssa_seenfirst = B_TRUE;
- else
- ssa.ssa_first = comma_separated;
- *pct = '\0';
- ssa.ssa_last = pct + 1;
-
- /*
- * If there is a lastname specified, make sure it
- * exists.
- */
- if (ssa.ssa_last[0] != '\0') {
- char snapname[ZFS_MAXNAMELEN];
- (void) snprintf(snapname, sizeof (snapname),
- "%s@%s", zfs_get_name(fs_zhp),
- ssa.ssa_last);
- if (!zfs_dataset_exists(fs_zhp->zfs_hdl,
- snapname, ZFS_TYPE_SNAPSHOT)) {
- ret = ENOENT;
- continue;
- }
- }
-
- err = zfs_iter_snapshots_sorted(fs_zhp,
- snapspec_cb, &ssa);
- if (ret == 0)
- ret = err;
- if (ret == 0 && (!ssa.ssa_seenfirst ||
- (ssa.ssa_last[0] != '\0' && !ssa.ssa_seenlast))) {
- ret = ENOENT;
- }
- } else {
- char snapname[ZFS_MAXNAMELEN];
- zfs_handle_t *snap_zhp;
- (void) snprintf(snapname, sizeof (snapname), "%s@%s",
- zfs_get_name(fs_zhp), comma_separated);
- snap_zhp = make_dataset_handle(fs_zhp->zfs_hdl,
- snapname);
- if (snap_zhp == NULL) {
- ret = ENOENT;
- continue;
- }
- err = func(snap_zhp, arg);
- if (ret == 0)
- ret = err;
- }
- }
-
- return (ret);
-}
-
-/*
- * Iterate over all children, snapshots and filesystems
- */
-int
-zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
-{
- int ret;
-
- if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
- return (ret);
-
- return (zfs_iter_snapshots(zhp, func, data));
-}
-
-
-typedef struct iter_stack_frame {
- struct iter_stack_frame *next;
- zfs_handle_t *zhp;
-} iter_stack_frame_t;
-
-typedef struct iter_dependents_arg {
- boolean_t first;
- boolean_t allowrecursion;
- iter_stack_frame_t *stack;
- zfs_iter_f func;
- void *data;
-} iter_dependents_arg_t;
-
-static int
-iter_dependents_cb(zfs_handle_t *zhp, void *arg)
-{
- iter_dependents_arg_t *ida = arg;
- int err;
- boolean_t first = ida->first;
- ida->first = B_FALSE;
-
- if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
- err = zfs_iter_clones(zhp, iter_dependents_cb, ida);
- } else {
- iter_stack_frame_t isf;
- iter_stack_frame_t *f;
-
- /*
- * check if there is a cycle by seeing if this fs is already
- * on the stack.
- */
- for (f = ida->stack; f != NULL; f = f->next) {
- if (f->zhp->zfs_dmustats.dds_guid ==
- zhp->zfs_dmustats.dds_guid) {
- if (ida->allowrecursion) {
- zfs_close(zhp);
- return (0);
- } else {
- zfs_error_aux(zhp->zfs_hdl,
- dgettext(TEXT_DOMAIN,
- "recursive dependency at '%s'"),
- zfs_get_name(zhp));
- err = zfs_error(zhp->zfs_hdl,
- EZFS_RECURSIVE,
- dgettext(TEXT_DOMAIN,
- "cannot determine dependent "
- "datasets"));
- zfs_close(zhp);
- return (err);
- }
- }
- }
-
- isf.zhp = zhp;
- isf.next = ida->stack;
- ida->stack = &isf;
- err = zfs_iter_filesystems(zhp, iter_dependents_cb, ida);
- if (err == 0)
- err = zfs_iter_snapshots(zhp, iter_dependents_cb, ida);
- ida->stack = isf.next;
- }
- if (!first && err == 0)
- err = ida->func(zhp, ida->data);
- return (err);
-}
-
-int
-zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
- zfs_iter_f func, void *data)
-{
- iter_dependents_arg_t ida;
- ida.allowrecursion = allowrecursion;
- ida.stack = NULL;
- ida.func = func;
- ida.data = data;
- ida.first = B_TRUE;
- return (iter_dependents_cb(zfs_handle_dup(zhp), &ida));
-}
diff --git a/usr/src/lib/libzfs/common/libzfs_sendrecv.c b/usr/src/lib/libzfs/common/libzfs_sendrecv.c
index 598709ff89..3093ab974d 100644
--- a/usr/src/lib/libzfs/common/libzfs_sendrecv.c
+++ b/usr/src/lib/libzfs/common/libzfs_sendrecv.c
@@ -21,7 +21,6 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011 by Delphix. All rights reserved.
*/
#include <assert.h>
@@ -772,6 +771,88 @@ gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
}
/*
+ * Routines for dealing with the sorted snapshot functionality
+ */
+typedef struct zfs_node {
+ zfs_handle_t *zn_handle;
+ avl_node_t zn_avlnode;
+} zfs_node_t;
+
+static int
+zfs_sort_snaps(zfs_handle_t *zhp, void *data)
+{
+ avl_tree_t *avl = data;
+ zfs_node_t *node;
+ zfs_node_t search;
+
+ search.zn_handle = zhp;
+ node = avl_find(avl, &search, NULL);
+ if (node) {
+ /*
+ * If this snapshot was renamed while we were creating the
+ * AVL tree, it's possible that we already inserted it under
+ * its old name. Remove the old handle before adding the new
+ * one.
+ */
+ zfs_close(node->zn_handle);
+ avl_remove(avl, node);
+ free(node);
+ }
+
+ node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t));
+ node->zn_handle = zhp;
+ avl_add(avl, node);
+
+ return (0);
+}
+
+static int
+zfs_snapshot_compare(const void *larg, const void *rarg)
+{
+ zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
+ zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
+ uint64_t lcreate, rcreate;
+
+ /*
+ * Sort them according to creation time. We use the hidden
+ * CREATETXG property to get an absolute ordering of snapshots.
+ */
+ lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);
+ rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);
+
+ if (lcreate < rcreate)
+ return (-1);
+ else if (lcreate > rcreate)
+ return (+1);
+ else
+ return (0);
+}
+
+int
+zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data)
+{
+ int ret = 0;
+ zfs_node_t *node;
+ avl_tree_t avl;
+ void *cookie = NULL;
+
+ avl_create(&avl, zfs_snapshot_compare,
+ sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode));
+
+ ret = zfs_iter_snapshots(zhp, zfs_sort_snaps, &avl);
+
+ for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node))
+ ret |= callback(node->zn_handle, data);
+
+ while ((node = avl_destroy_nodes(&avl, &cookie)) != NULL)
+ free(node);
+
+ avl_destroy(&avl);
+
+ return (ret);
+}
+
+/*
* Routines specific to "zfs send"
*/
typedef struct send_dump_data {
@@ -781,7 +862,7 @@ typedef struct send_dump_data {
char prevsnap[ZFS_MAXNAMELEN];
uint64_t prevsnap_obj;
boolean_t seenfrom, seento, replicate, doall, fromorigin;
- boolean_t verbose, noop, parsable;
+ boolean_t verbose;
int outfd;
boolean_t err;
nvlist_t *fss;
@@ -791,69 +872,8 @@ typedef struct send_dump_data {
nvlist_t *debugnv;
char holdtag[ZFS_MAXNAMELEN];
int cleanup_fd;
- uint64_t size;
} send_dump_data_t;
-static int
-estimate_ioctl(zfs_handle_t *zhp, uint64_t fromsnap_obj,
- boolean_t fromorigin, uint64_t *sizep)
-{
- zfs_cmd_t zc = { 0 };
- libzfs_handle_t *hdl = zhp->zfs_hdl;
-
- assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
- assert(fromsnap_obj == 0 || !fromorigin);
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- zc.zc_obj = fromorigin;
- zc.zc_sendobj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
- zc.zc_fromobj = fromsnap_obj;
- zc.zc_guid = 1; /* estimate flag */
-
- if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SEND, &zc) != 0) {
- char errbuf[1024];
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "warning: cannot estimate space for '%s'"), zhp->zfs_name);
-
- switch (errno) {
- case EXDEV:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "not an earlier snapshot from the same fs"));
- return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
-
- case ENOENT:
- if (zfs_dataset_exists(hdl, zc.zc_name,
- ZFS_TYPE_SNAPSHOT)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "incremental source (@%s) does not exist"),
- zc.zc_value);
- }
- return (zfs_error(hdl, EZFS_NOENT, errbuf));
-
- case EDQUOT:
- case EFBIG:
- case EIO:
- case ENOLINK:
- case ENOSPC:
- case ENOSTR:
- case ENXIO:
- case EPIPE:
- case ERANGE:
- case EFAULT:
- case EROFS:
- zfs_error_aux(hdl, strerror(errno));
- return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
-
- default:
- return (zfs_standard_error(hdl, errno, errbuf));
- }
- }
-
- *sizep = zc.zc_objset_type;
-
- return (0);
-}
-
/*
* Dumps a backup of the given snapshot (incremental from fromsnap if it's not
* NULL) to the file descriptor specified by outfd.
@@ -881,7 +901,7 @@ dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj,
"fromsnap", fromsnap));
}
- if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SEND, &zc) != 0) {
+ if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SEND, &zc) != 0) {
char errbuf[1024];
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"warning: cannot send '%s'"), zhp->zfs_name);
@@ -894,6 +914,7 @@ dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj,
nvlist_free(thisdbg);
switch (errno) {
+
case EXDEV:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"not an earlier snapshot from the same fs"));
@@ -943,9 +964,6 @@ hold_for_send(zfs_handle_t *zhp, send_dump_data_t *sdd)
assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
- if (sdd->noop)
- return (0);
-
/*
* zfs_send() only opens a cleanup_fd for sends that need it,
* e.g. replication and doall.
@@ -979,7 +997,7 @@ dump_snapshot(zfs_handle_t *zhp, void *arg)
send_dump_data_t *sdd = arg;
char *thissnap;
int err;
- boolean_t isfromsnap, istosnap, fromorigin;
+ boolean_t isfromsnap, istosnap;
boolean_t exclude = B_FALSE;
thissnap = strchr(zhp->zfs_name, '@') + 1;
@@ -1056,47 +1074,15 @@ dump_snapshot(zfs_handle_t *zhp, void *arg)
return (err);
}
- fromorigin = sdd->prevsnap[0] == '\0' &&
- (sdd->fromorigin || sdd->replicate);
-
+ /* send it */
if (sdd->verbose) {
- uint64_t size;
- err = estimate_ioctl(zhp, sdd->prevsnap_obj,
- fromorigin, &size);
-
- if (sdd->parsable) {
- if (sdd->prevsnap[0] != '\0') {
- (void) fprintf(stderr, "incremental\t%s\t%s",
- sdd->prevsnap, zhp->zfs_name);
- } else {
- (void) fprintf(stderr, "full\t%s",
- zhp->zfs_name);
- }
- } else {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "send from @%s to %s"),
- sdd->prevsnap, zhp->zfs_name);
- }
- if (err == 0) {
- if (sdd->parsable) {
- (void) fprintf(stderr, "\t%llu\n",
- (longlong_t)size);
- } else {
- char buf[16];
- zfs_nicenum(size, buf, sizeof (buf));
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- " estimated size is %s\n"), buf);
- }
- sdd->size += size;
- } else {
- (void) fprintf(stderr, "\n");
- }
+ (void) fprintf(stderr, "sending from @%s to %s\n",
+ sdd->prevsnap, zhp->zfs_name);
}
- if (!sdd->noop) {
- err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
- fromorigin, sdd->outfd, sdd->debugnv);
- }
+ err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
+ sdd->prevsnap[0] == '\0' && (sdd->fromorigin || sdd->replicate),
+ sdd->outfd, sdd->debugnv);
(void) strcpy(sdd->prevsnap, thissnap);
sdd->prevsnap_obj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
@@ -1115,8 +1101,8 @@ dump_filesystem(zfs_handle_t *zhp, void *arg)
(void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s",
zhp->zfs_name, sdd->tosnap);
if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "WARNING: could not send %s@%s: does not exist\n"),
+ (void) fprintf(stderr, "WARNING: "
+ "could not send %s@%s: does not exist\n",
zhp->zfs_name, sdd->tosnap);
sdd->err = B_TRUE;
return (0);
@@ -1145,24 +1131,23 @@ dump_filesystem(zfs_handle_t *zhp, void *arg)
rv = zfs_iter_snapshots_sorted(zhp, dump_snapshot, arg);
if (!sdd->seenfrom) {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ (void) fprintf(stderr,
"WARNING: could not send %s@%s:\n"
- "incremental source (%s@%s) does not exist\n"),
+ "incremental source (%s@%s) does not exist\n",
zhp->zfs_name, sdd->tosnap,
zhp->zfs_name, sdd->fromsnap);
sdd->err = B_TRUE;
} else if (!sdd->seento) {
if (sdd->fromsnap) {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ (void) fprintf(stderr,
"WARNING: could not send %s@%s:\n"
"incremental source (%s@%s) "
- "is not earlier than it\n"),
+ "is not earlier than it\n",
zhp->zfs_name, sdd->tosnap,
zhp->zfs_name, sdd->fromsnap);
} else {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "WARNING: "
- "could not send %s@%s: does not exist\n"),
+ (void) fprintf(stderr, "WARNING: "
+ "could not send %s@%s: does not exist\n",
zhp->zfs_name, sdd->tosnap);
}
sdd->err = B_TRUE;
@@ -1208,12 +1193,11 @@ again:
needagain = progress = B_FALSE;
for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
- nvlist_t *fslist, *parent_nv;
+ nvlist_t *fslist;
char *fsname;
zfs_handle_t *zhp;
int err;
uint64_t origin_guid = 0;
- uint64_t parent_guid = 0;
VERIFY(nvpair_value_nvlist(fspair, &fslist) == 0);
if (nvlist_lookup_boolean(fslist, "sent") == 0)
@@ -1221,23 +1205,13 @@ again:
VERIFY(nvlist_lookup_string(fslist, "name", &fsname) == 0);
(void) nvlist_lookup_uint64(fslist, "origin", &origin_guid);
- (void) nvlist_lookup_uint64(fslist, "parentfromsnap",
- &parent_guid);
-
- if (parent_guid != 0) {
- parent_nv = fsavl_find(sdd->fsavl, parent_guid, NULL);
- if (!nvlist_exists(parent_nv, "sent")) {
- /* parent has not been sent; skip this one */
- needagain = B_TRUE;
- continue;
- }
- }
if (origin_guid != 0) {
nvlist_t *origin_nv = fsavl_find(sdd->fsavl,
origin_guid, NULL);
if (origin_nv != NULL &&
- !nvlist_exists(origin_nv, "sent")) {
+ nvlist_lookup_boolean(origin_nv,
+ "sent") == ENOENT) {
/*
* origin has not been sent yet;
* skip this clone.
@@ -1261,16 +1235,6 @@ again:
assert(progress);
goto again;
}
-
- /* clean out the sent flags in case we reuse this fss */
- for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
- fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
- nvlist_t *fslist;
-
- VERIFY(nvpair_value_nvlist(fspair, &fslist) == 0);
- (void) nvlist_remove_all(fslist, "sent");
- }
-
return (0);
}
@@ -1297,7 +1261,7 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
{
char errbuf[1024];
send_dump_data_t sdd = { 0 };
- int err = 0;
+ int err;
nvlist_t *fss = NULL;
avl_tree_t *fsavl = NULL;
static uint64_t holdseq;
@@ -1325,12 +1289,12 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
}
}
- if (!flags.noop && zfs_spa_version(zhp, &spa_version) == 0 &&
+ if (zfs_spa_version(zhp, &spa_version) == 0 &&
spa_version >= SPA_VERSION_USERREFS &&
(flags.doall || flags.replicate))
holdsnaps = B_TRUE;
- if (flags.dedup && !flags.noop) {
+ if (flags.dedup) {
featureflags |= (DMU_BACKUP_FEATURE_DEDUP |
DMU_BACKUP_FEATURE_DEDUPPROPS);
if (err = pipe(pipefd)) {
@@ -1388,48 +1352,43 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
}
}
- if (!flags.noop) {
- /* write first begin record */
- drr.drr_type = DRR_BEGIN;
- drr.drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC;
- DMU_SET_STREAM_HDRTYPE(drr.drr_u.drr_begin.
- drr_versioninfo, DMU_COMPOUNDSTREAM);
- DMU_SET_FEATUREFLAGS(drr.drr_u.drr_begin.
- drr_versioninfo, featureflags);
- (void) snprintf(drr.drr_u.drr_begin.drr_toname,
- sizeof (drr.drr_u.drr_begin.drr_toname),
- "%s@%s", zhp->zfs_name, tosnap);
- drr.drr_payloadlen = buflen;
- err = cksum_and_write(&drr, sizeof (drr), &zc, outfd);
-
- /* write header nvlist */
- if (err != -1 && packbuf != NULL) {
- err = cksum_and_write(packbuf, buflen, &zc,
- outfd);
- }
- free(packbuf);
+ /* write first begin record */
+ drr.drr_type = DRR_BEGIN;
+ drr.drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC;
+ DMU_SET_STREAM_HDRTYPE(drr.drr_u.drr_begin.drr_versioninfo,
+ DMU_COMPOUNDSTREAM);
+ DMU_SET_FEATUREFLAGS(drr.drr_u.drr_begin.drr_versioninfo,
+ featureflags);
+ (void) snprintf(drr.drr_u.drr_begin.drr_toname,
+ sizeof (drr.drr_u.drr_begin.drr_toname),
+ "%s@%s", zhp->zfs_name, tosnap);
+ drr.drr_payloadlen = buflen;
+ err = cksum_and_write(&drr, sizeof (drr), &zc, outfd);
+
+ /* write header nvlist */
+ if (err != -1 && packbuf != NULL) {
+ err = cksum_and_write(packbuf, buflen, &zc, outfd);
+ }
+ free(packbuf);
+ if (err == -1) {
+ fsavl_destroy(fsavl);
+ nvlist_free(fss);
+ err = errno;
+ goto stderr_out;
+ }
+
+ /* write end record */
+ if (err != -1) {
+ bzero(&drr, sizeof (drr));
+ drr.drr_type = DRR_END;
+ drr.drr_u.drr_end.drr_checksum = zc;
+ err = write(outfd, &drr, sizeof (drr));
if (err == -1) {
fsavl_destroy(fsavl);
nvlist_free(fss);
err = errno;
goto stderr_out;
}
-
- /* write end record */
- if (err != -1) {
- bzero(&drr, sizeof (drr));
- drr.drr_type = DRR_END;
- drr.drr_u.drr_end.drr_checksum = zc;
- err = write(outfd, &drr, sizeof (drr));
- if (err == -1) {
- fsavl_destroy(fsavl);
- nvlist_free(fss);
- err = errno;
- goto stderr_out;
- }
- }
- if (err != -1)
- err = 0;
}
}
@@ -1446,8 +1405,6 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
sdd.fss = fss;
sdd.fsavl = fsavl;
sdd.verbose = flags.verbose;
- sdd.parsable = flags.parsable;
- sdd.noop = flags.noop;
sdd.filter_cb = filter_func;
sdd.filter_cb_arg = cb_arg;
if (debugnvp)
@@ -1464,26 +1421,6 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
} else {
sdd.cleanup_fd = -1;
}
- if (flags.verbose) {
- /*
- * Do a verbose no-op dry run to get all the verbose output
- * before generating any data. Then do a non-verbose real
- * run to generate the streams.
- */
- sdd.noop = B_TRUE;
- err = dump_filesystems(zhp, &sdd);
- sdd.noop = flags.noop;
- sdd.verbose = B_FALSE;
- if (flags.parsable) {
- (void) fprintf(stderr, "size\t%llu\n",
- (longlong_t)sdd.size);
- } else {
- char buf[16];
- zfs_nicenum(sdd.size, buf, sizeof (buf));
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "total estimated size is %s\n"), buf);
- }
- }
err = dump_filesystems(zhp, &sdd);
fsavl_destroy(fsavl);
nvlist_free(fss);
@@ -1498,7 +1435,7 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
sdd.cleanup_fd = -1;
}
- if (!flags.noop && (flags.replicate || flags.doall || flags.props)) {
+ if (flags.replicate || flags.doall || flags.props) {
/*
* write final end record. NB: want to do this even if
* there was some error, because it might not be totally
@@ -1778,8 +1715,7 @@ guid_to_name(libzfs_handle_t *hdl, const char *parent, uint64_t guid,
}
/*
- * Return +1 if guid1 is before guid2, 0 if they are the same, and -1 if
- * guid1 is after guid2.
+ * Return true if dataset guid1 is created before guid2.
*/
static int
created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
@@ -1789,8 +1725,7 @@ created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
char *fsname, *snapname;
char buf[ZFS_MAXNAMELEN];
int rv;
- zfs_handle_t *guid1hdl, *guid2hdl;
- uint64_t create1, create2;
+ zfs_node_t zn1, zn2;
if (guid2 == 0)
return (0);
@@ -1800,31 +1735,23 @@ created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
nvfs = fsavl_find(avl, guid1, &snapname);
VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
(void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname);
- guid1hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
- if (guid1hdl == NULL)
+ zn1.zn_handle = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
+ if (zn1.zn_handle == NULL)
return (-1);
nvfs = fsavl_find(avl, guid2, &snapname);
VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
(void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname);
- guid2hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
- if (guid2hdl == NULL) {
- zfs_close(guid1hdl);
+ zn2.zn_handle = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
+ if (zn2.zn_handle == NULL) {
+ zfs_close(zn2.zn_handle);
return (-1);
}
- create1 = zfs_prop_get_int(guid1hdl, ZFS_PROP_CREATETXG);
- create2 = zfs_prop_get_int(guid2hdl, ZFS_PROP_CREATETXG);
-
- if (create1 < create2)
- rv = -1;
- else if (create1 > create2)
- rv = +1;
- else
- rv = 0;
+ rv = (zfs_snapshot_compare(&zn1, &zn2) == -1);
- zfs_close(guid1hdl);
- zfs_close(guid2hdl);
+ zfs_close(zn1.zn_handle);
+ zfs_close(zn2.zn_handle);
return (rv);
}
diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c
index 2b1a65edd6..01b7c8732e 100644
--- a/usr/src/lib/libzfs/common/libzfs_util.c
+++ b/usr/src/lib/libzfs/common/libzfs_util.c
@@ -20,7 +20,6 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011 by Delphix. All rights reserved.
*/
/*
@@ -1282,8 +1281,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_userquota(propname) &&
- !zfs_prop_written(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 b4032662e4..b435b58e05 100644
--- a/usr/src/lib/libzfs/common/mapfile-vers
+++ b/usr/src/lib/libzfs/common/mapfile-vers
@@ -20,7 +20,6 @@
#
# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2010 Nexenta Systems, Inc. All rights reserved.
-# Copyright (c) 2011 by Delphix. All rights reserved.
#
# MAPFILE HEADER START
#
@@ -69,16 +68,13 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zfs_deleg_share_nfs;
zfs_destroy;
zfs_destroy_snaps;
- zfs_destroy_snaps_nvl;
zfs_expand_proplist;
zfs_get_handle;
zfs_get_holds;
zfs_get_name;
zfs_get_pool_handle;
- zfs_get_snapused_int;
zfs_get_user_props;
zfs_get_type;
- zfs_handle_dup;
zfs_history_event_names;
zfs_hold;
zfs_is_mounted;
@@ -91,7 +87,6 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zfs_iter_root;
zfs_iter_snapshots;
zfs_iter_snapshots_sorted;
- zfs_iter_snapspec;
zfs_mount;
zfs_name_to_prop;
zfs_name_valid;
@@ -111,8 +106,6 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zfs_prop_get_table;
zfs_prop_get_userquota_int;
zfs_prop_get_userquota;
- zfs_prop_get_written_int;
- zfs_prop_get_written;
zfs_prop_inherit;
zfs_prop_inheritable;
zfs_prop_init;
@@ -125,7 +118,6 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zfs_prop_userquota;
zfs_prop_valid_for_type;
zfs_prop_values;
- zfs_prop_written;
zfs_prune_proplist;
zfs_receive;
zfs_refresh_properties;