summaryrefslogtreecommitdiffstats
path: root/cddl/contrib/opensolaris/lib/libzfs/common
diff options
context:
space:
mode:
Diffstat (limited to 'cddl/contrib/opensolaris/lib/libzfs/common')
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h57
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c454
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_graph.c653
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h4
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c18
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_iter.c462
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c45
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c549
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c5
9 files changed, 1164 insertions, 1083 deletions
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h
index 6b25879..e1d9c93 100644
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h
@@ -21,8 +21,8 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>.
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
* All rights reserved.
*/
@@ -233,6 +233,7 @@ typedef struct splitflags {
*/
extern int zpool_scan(zpool_handle_t *, pool_scan_func_t);
extern int zpool_clear(zpool_handle_t *, const char *, nvlist_t *);
+extern int zpool_reguid(zpool_handle_t *);
extern int zpool_vdev_online(zpool_handle_t *, const char *, int,
vdev_state_t *);
@@ -384,6 +385,7 @@ 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 *);
@@ -417,12 +419,20 @@ 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;
@@ -497,6 +507,7 @@ 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;
@@ -517,6 +528,7 @@ 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);
@@ -533,29 +545,34 @@ extern int zfs_rename(zfs_handle_t *, const char *, renameflags_t flags);
typedef struct sendflags {
/* print informational messages (ie, -v was specified) */
- int verbose : 1;
+ boolean_t verbose;
/* recursive send (ie, -R) */
- int replicate : 1;
+ boolean_t replicate;
/* for incrementals, do all intermediate snapshots */
- int doall : 1; /* (ie, -I) */
+ boolean_t doall;
/* if dataset is a clone, do incremental from its origin */
- int fromorigin : 1;
+ boolean_t fromorigin;
/* do deduplication */
- int dedup : 1;
+ boolean_t dedup;
/* send properties (ie, -p) */
- int props : 1;
+ boolean_t props;
+
+ /* do not send (no-op, ie. -n) */
+ boolean_t dryrun;
+
+ /* parsable verbose output (ie. -P) */
+ boolean_t parsable;
} sendflags_t;
typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *);
-extern int zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
- sendflags_t flags, int outfd, snapfilter_cb_t filter_func,
- void *cb_arg, nvlist_t **debugnvp);
+extern int zfs_send(zfs_handle_t *, const char *, const char *,
+ sendflags_t *, int, snapfilter_cb_t, void *, nvlist_t **);
extern int zfs_promote(zfs_handle_t *);
extern int zfs_hold(zfs_handle_t *, const char *, const char *, boolean_t,
@@ -575,34 +592,34 @@ extern int zfs_set_fsacl(zfs_handle_t *, boolean_t, nvlist_t *);
typedef struct recvflags {
/* print informational messages (ie, -v was specified) */
- int verbose : 1;
+ boolean_t verbose;
/* the destination is a prefix, not the exact fs (ie, -d) */
- int isprefix : 1;
+ boolean_t isprefix;
/*
* Only the tail of the sent snapshot path is appended to the
* destination to determine the received snapshot name (ie, -e).
*/
- int istail : 1;
+ boolean_t istail;
/* do not actually do the recv, just check if it would work (ie, -n) */
- int dryrun : 1;
+ boolean_t dryrun;
/* rollback/destroy filesystems as necessary (eg, -F) */
- int force : 1;
+ boolean_t force;
/* set "canmount=off" on all modified filesystems */
- int canmountoff : 1;
+ boolean_t canmountoff;
/* byteswap flag is used internally; callers need not specify */
- int byteswap : 1;
+ boolean_t byteswap;
/* do not mount file systems as they are extracted (private) */
- int nomount : 1;
+ boolean_t nomount;
} recvflags_t;
-extern int zfs_receive(libzfs_handle_t *, const char *, recvflags_t,
+extern int zfs_receive(libzfs_handle_t *, const char *, recvflags_t *,
int, avl_tree_t *);
typedef enum diff_flags {
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
index a65b079..bff0586 100644
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
@@ -496,7 +496,7 @@ make_dataset_handle(libzfs_handle_t *hdl, const char *path)
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);
@@ -513,6 +513,53 @@ 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
@@ -876,6 +923,12 @@ 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) {
@@ -1869,8 +1922,6 @@ 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;
@@ -1885,6 +1936,120 @@ 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
@@ -2013,6 +2178,11 @@ 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:
@@ -2385,7 +2555,7 @@ zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname,
int err;
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),
@@ -2437,144 +2607,95 @@ zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
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, unsigned long 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 };
- 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", propvalue);
+ } else {
+ zfs_nicenum(propvalue, propbuf, proplen);
}
- zcmd_free_nvlists(&zc);
- return ((ret < 0) ? ret : 0);
+ return (0);
}
-/*
- * Iterate over all snapshots
- */
int
-zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+zfs_get_snapused_int(zfs_handle_t *firstsnap, zfs_handle_t *lastsnap,
+ uint64_t *usedp)
{
+ int err;
zfs_cmd_t zc = { 0 };
- zfs_handle_t *nzhp;
- int ret;
- if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
- return (0);
+ (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 (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) {
+ err = ioctl(lastsnap->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_SNAPS, &zc);
+ if (err)
+ return (err);
- if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
- &zc)) == NULL) {
- continue;
- }
+ *usedp = zc.zc_cookie;
- if ((ret = func(nzhp, data)) != 0) {
- zcmd_free_nvlists(&zc);
- return (ret);
- }
- }
- zcmd_free_nvlists(&zc);
- return ((ret < 0) ? ret : 0);
+ return (0);
}
/*
- * Iterate over all children, snapshots and filesystems
+ * Returns the name of the given zfs handle.
*/
-int
-zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+const char *
+zfs_get_name(const zfs_handle_t *zhp)
{
- int ret;
-
- if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
- return (ret);
+ return (zhp->zfs_name);
+}
- return (zfs_iter_snapshots(zhp, func, data));
+/*
+ * Returns the type of the given zfs handle.
+ */
+zfs_type_t
+zfs_get_type(const zfs_handle_t *zhp)
+{
+ return (zhp->zfs_type);
}
/*
@@ -2600,18 +2721,19 @@ is_descendant(const char *ds1, const char *ds2)
/*
* 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);
}
@@ -3010,9 +3132,8 @@ zfs_destroy(zfs_handle_t *zhp, boolean_t defer)
}
struct destroydata {
- char *snapname;
- boolean_t gotone;
- boolean_t closezhp;
+ nvlist_t *nvl;
+ const char *snapname;
};
static int
@@ -3021,24 +3142,19 @@ 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;
+ verify(nvlist_add_boolean(dd->nvl, name) == 0);
zfs_close(szhp);
}
- 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);
}
@@ -3048,29 +3164,45 @@ 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;
- (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_next_nvpair(dd.nvl, NULL) == NULL) {
+ 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, dd.nvl, defer);
}
+ 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));
- (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
+ if (zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, snaps) != 0)
+ return (-1);
zc.zc_defer_destroy = defer;
- ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc);
+ ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS_NVL, &zc);
if (ret != 0) {
char errbuf[1024];
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot destroy '%s@%s'"), zc.zc_name, snapname);
+ "cannot destroy snapshots in %s"), zc.zc_name);
switch (errno) {
case EEXIST:
@@ -3106,7 +3238,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 name */
+ /* validate the target/clone name */
if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE))
return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
@@ -3443,42 +3575,6 @@ 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
@@ -3947,7 +4043,7 @@ zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
int error;
zfs_useracct_t buf[100];
- (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;
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_graph.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_graph.c
deleted file mode 100644
index bc21c51..0000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_graph.c
+++ /dev/null
@@ -1,653 +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 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/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h
index 9cd1ed3..48a8edb 100644
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h
@@ -23,6 +23,7 @@
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>.
* All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
#ifndef _LIBFS_IMPL_H
@@ -116,7 +117,7 @@ struct zpool_handle {
diskaddr_t zpool_start_block;
};
-typedef enum {
+typedef enum {
PROTO_NFS = 0,
PROTO_SMB = 1,
PROTO_END = 2
@@ -148,6 +149,7 @@ 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/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c
index cafb981..6f579c5 100644
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c
@@ -20,6 +20,8 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
/*
@@ -435,7 +437,7 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok)
uint_t i, nspares, nl2cache;
boolean_t config_seen;
uint64_t best_txg;
- char *name, *hostname;
+ char *name, *hostname, *comment;
uint64_t version, guid;
uint_t children = 0;
nvlist_t **child = NULL;
@@ -524,6 +526,7 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok)
* version
* pool guid
* name
+ * comment (if available)
* pool state
* hostid (if available)
* hostname (if available)
@@ -545,11 +548,24 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok)
if (nvlist_add_string(config,
ZPOOL_CONFIG_POOL_NAME, name) != 0)
goto nomem;
+
+ /*
+ * COMMENT is optional, don't bail if it's not
+ * there, instead, set it to NULL.
+ */
+ if (nvlist_lookup_string(tmp,
+ ZPOOL_CONFIG_COMMENT, &comment) != 0)
+ comment = NULL;
+ else if (nvlist_add_string(config,
+ ZPOOL_CONFIG_COMMENT, comment) != 0)
+ goto nomem;
+
verify(nvlist_lookup_uint64(tmp,
ZPOOL_CONFIG_POOL_STATE, &state) == 0);
if (nvlist_add_uint64(config,
ZPOOL_CONFIG_POOL_STATE, state) != 0)
goto nomem;
+
hostid = 0;
if (nvlist_lookup_uint64(tmp,
ZPOOL_CONFIG_HOSTID, &hostid) == 0) {
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_iter.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_iter.c
new file mode 100644
index 0000000..a3e1b9f
--- /dev/null
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_iter.c
@@ -0,0 +1,462 @@
+/*
+ * 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, unsigned long 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/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
index 061daab..2605768 100644
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
@@ -21,6 +21,8 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
#include <sys/types.h>
@@ -261,6 +263,7 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len,
case ZPOOL_PROP_ALTROOT:
case ZPOOL_PROP_CACHEFILE:
+ case ZPOOL_PROP_COMMENT:
if (zhp->zpool_props != NULL ||
zpool_get_all_props(zhp) == 0) {
(void) strlcpy(buf,
@@ -412,7 +415,7 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
zpool_prop_t prop;
char *strval;
uint64_t intval;
- char *slash;
+ char *slash, *check;
struct stat64 statbuf;
zpool_handle_t *zhp;
nvlist_t *nvroot;
@@ -573,6 +576,26 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
*slash = '/';
break;
+ case ZPOOL_PROP_COMMENT:
+ for (check = strval; *check != '\0'; check++) {
+ if (!isprint(*check)) {
+ zfs_error_aux(hdl,
+ dgettext(TEXT_DOMAIN,
+ "comment may only have printable "
+ "characters"));
+ (void) zfs_error(hdl, EZFS_BADPROP,
+ errbuf);
+ goto error;
+ }
+ }
+ if (strlen(strval) > ZPROP_MAX_COMMENT) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "comment must not exceed %d characters"),
+ ZPROP_MAX_COMMENT);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+ break;
case ZPOOL_PROP_READONLY:
if (!flags.import) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
@@ -3011,6 +3034,26 @@ zpool_vdev_clear(zpool_handle_t *zhp, uint64_t guid)
}
/*
+ * Change the GUID for a pool.
+ */
+int
+zpool_reguid(zpool_handle_t *zhp)
+{
+ char msg[1024];
+ libzfs_handle_t *hdl = zhp->zpool_hdl;
+ zfs_cmd_t zc = { 0 };
+
+ (void) snprintf(msg, sizeof (msg),
+ dgettext(TEXT_DOMAIN, "cannot reguid '%s'"), zhp->zpool_name);
+
+ (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+ if (zfs_ioctl(hdl, ZFS_IOC_POOL_REGUID, &zc) == 0)
+ return (0);
+
+ return (zpool_standard_error(hdl, errno, msg));
+}
+
+/*
* Convert from a devid string to a path.
*/
static char *
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c
index 9d3c984..b0f034b 100644
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
#include <assert.h>
@@ -53,7 +54,7 @@ extern void zfs_setprop_error(libzfs_handle_t *, zfs_prop_t, int, char *);
/* We need to use something for ENODATA. */
#define ENODATA EIDRM
-static int zfs_receive_impl(libzfs_handle_t *, const char *, recvflags_t,
+static int zfs_receive_impl(libzfs_handle_t *, const char *, recvflags_t *,
int, const char *, nvlist_t *, avl_tree_t *, char **, int, uint64_t *);
static const zio_cksum_t zero_cksum = { 0 };
@@ -774,88 +775,6 @@ 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 {
@@ -865,7 +784,7 @@ typedef struct send_dump_data {
char prevsnap[ZFS_MAXNAMELEN];
uint64_t prevsnap_obj;
boolean_t seenfrom, seento, replicate, doall, fromorigin;
- boolean_t verbose;
+ boolean_t verbose, dryrun, parsable;
int outfd;
boolean_t err;
nvlist_t *fss;
@@ -875,8 +794,68 @@ 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 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.
@@ -904,7 +883,7 @@ dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj,
"fromsnap", fromsnap));
}
- if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SEND, &zc) != 0) {
+ if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SEND, &zc) != 0) {
char errbuf[1024];
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"warning: cannot send '%s'"), zhp->zfs_name);
@@ -917,7 +896,6 @@ 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"));
@@ -937,6 +915,9 @@ dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj,
case EIO:
case ENOLINK:
case ENOSPC:
+#ifdef sun
+ case ENOSTR:
+#endif
case ENXIO:
case EPIPE:
case ERANGE:
@@ -966,6 +947,9 @@ hold_for_send(zfs_handle_t *zhp, send_dump_data_t *sdd)
assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
+ if (sdd->dryrun)
+ return (0);
+
/*
* zfs_send() only opens a cleanup_fd for sends that need it,
* e.g. replication and doall.
@@ -999,7 +983,7 @@ dump_snapshot(zfs_handle_t *zhp, void *arg)
send_dump_data_t *sdd = arg;
char *thissnap;
int err;
- boolean_t isfromsnap, istosnap;
+ boolean_t isfromsnap, istosnap, fromorigin;
boolean_t exclude = B_FALSE;
thissnap = strchr(zhp->zfs_name, '@') + 1;
@@ -1076,15 +1060,47 @@ dump_snapshot(zfs_handle_t *zhp, void *arg)
return (err);
}
- /* send it */
+ fromorigin = sdd->prevsnap[0] == '\0' &&
+ (sdd->fromorigin || sdd->replicate);
+
if (sdd->verbose) {
- (void) fprintf(stderr, "sending from @%s to %s\n",
- sdd->prevsnap, zhp->zfs_name);
+ 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");
+ }
}
- err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
- sdd->prevsnap[0] == '\0' && (sdd->fromorigin || sdd->replicate),
- sdd->outfd, sdd->debugnv);
+ if (!sdd->dryrun) {
+ err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
+ fromorigin, sdd->outfd, sdd->debugnv);
+ }
(void) strcpy(sdd->prevsnap, thissnap);
sdd->prevsnap_obj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
@@ -1103,8 +1119,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, "WARNING: "
- "could not send %s@%s: does not exist\n",
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "WARNING: could not send %s@%s: does not exist\n"),
zhp->zfs_name, sdd->tosnap);
sdd->err = B_TRUE;
return (0);
@@ -1133,23 +1149,24 @@ dump_filesystem(zfs_handle_t *zhp, void *arg)
rv = zfs_iter_snapshots_sorted(zhp, dump_snapshot, arg);
if (!sdd->seenfrom) {
- (void) fprintf(stderr,
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
"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,
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
"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, "WARNING: "
- "could not send %s@%s: does not exist\n",
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "WARNING: "
+ "could not send %s@%s: does not exist\n"),
zhp->zfs_name, sdd->tosnap);
}
sdd->err = B_TRUE;
@@ -1195,11 +1212,12 @@ again:
needagain = progress = B_FALSE;
for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
- nvlist_t *fslist;
+ nvlist_t *fslist, *parent_nv;
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)
@@ -1207,13 +1225,23 @@ 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_lookup_boolean(origin_nv,
- "sent") == ENOENT) {
+ !nvlist_exists(origin_nv, "sent")) {
/*
* origin has not been sent yet;
* skip this clone.
@@ -1237,6 +1265,16 @@ 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);
}
@@ -1258,12 +1296,12 @@ again:
*/
int
zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
- sendflags_t flags, int outfd, snapfilter_cb_t filter_func,
+ sendflags_t *flags, int outfd, snapfilter_cb_t filter_func,
void *cb_arg, nvlist_t **debugnvp)
{
char errbuf[1024];
send_dump_data_t sdd = { 0 };
- int err;
+ int err = 0;
nvlist_t *fss = NULL;
avl_tree_t *fsavl = NULL;
static uint64_t holdseq;
@@ -1291,12 +1329,12 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
}
}
- if (zfs_spa_version(zhp, &spa_version) == 0 &&
+ if (!flags->dryrun && zfs_spa_version(zhp, &spa_version) == 0 &&
spa_version >= SPA_VERSION_USERREFS &&
- (flags.doall || flags.replicate))
+ (flags->doall || flags->replicate))
holdsnaps = B_TRUE;
- if (flags.dedup) {
+ if (flags->dedup && !flags->dryrun) {
featureflags |= (DMU_BACKUP_FEATURE_DEDUP |
DMU_BACKUP_FEATURE_DEDUPPROPS);
if (err = pipe(pipefd)) {
@@ -1316,13 +1354,13 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
}
}
- if (flags.replicate || flags.doall || flags.props) {
+ if (flags->replicate || flags->doall || flags->props) {
dmu_replay_record_t drr = { 0 };
char *packbuf = NULL;
size_t buflen = 0;
zio_cksum_t zc = { 0 };
- if (flags.replicate || flags.props) {
+ if (flags->replicate || flags->props) {
nvlist_t *hdrnv;
VERIFY(0 == nvlist_alloc(&hdrnv, NV_UNIQUE_NAME, 0));
@@ -1331,13 +1369,13 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
"fromsnap", fromsnap));
}
VERIFY(0 == nvlist_add_string(hdrnv, "tosnap", tosnap));
- if (!flags.replicate) {
+ if (!flags->replicate) {
VERIFY(0 == nvlist_add_boolean(hdrnv,
"not_recursive"));
}
err = gather_nvlist(zhp->zfs_hdl, zhp->zfs_name,
- fromsnap, tosnap, flags.replicate, &fss, &fsavl);
+ fromsnap, tosnap, flags->replicate, &fss, &fsavl);
if (err)
goto err_out;
VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss));
@@ -1354,33 +1392,34 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
}
}
- /* 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;
- }
+ if (!flags->dryrun) {
+ /* 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) {
+ /* write end record */
bzero(&drr, sizeof (drr));
drr.drr_type = DRR_END;
drr.drr_u.drr_end.drr_checksum = zc;
@@ -1391,22 +1430,26 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
err = errno;
goto stderr_out;
}
+
+ err = 0;
}
}
/* dump each stream */
sdd.fromsnap = fromsnap;
sdd.tosnap = tosnap;
- if (flags.dedup)
+ if (flags->dedup)
sdd.outfd = pipefd[0];
else
sdd.outfd = outfd;
- sdd.replicate = flags.replicate;
- sdd.doall = flags.doall;
- sdd.fromorigin = flags.fromorigin;
+ sdd.replicate = flags->replicate;
+ sdd.doall = flags->doall;
+ sdd.fromorigin = flags->fromorigin;
sdd.fss = fss;
sdd.fsavl = fsavl;
- sdd.verbose = flags.verbose;
+ sdd.verbose = flags->verbose;
+ sdd.parsable = flags->parsable;
+ sdd.dryrun = flags->dryrun;
sdd.filter_cb = filter_func;
sdd.filter_cb_arg = cb_arg;
if (debugnvp)
@@ -1423,11 +1466,31 @@ 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.dryrun = B_TRUE;
+ err = dump_filesystems(zhp, &sdd);
+ sdd.dryrun = flags->dryrun;
+ 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);
- if (flags.dedup) {
+ if (flags->dedup) {
(void) close(pipefd[0]);
(void) pthread_join(tid, NULL);
}
@@ -1437,7 +1500,8 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
sdd.cleanup_fd = -1;
}
- if (flags.replicate || flags.doall || flags.props) {
+ if (!flags->dryrun && (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
@@ -1458,7 +1522,7 @@ stderr_out:
err_out:
if (sdd.cleanup_fd != -1)
VERIFY(0 == close(sdd.cleanup_fd));
- if (flags.dedup) {
+ if (flags->dedup) {
(void) pthread_cancel(tid);
(void) pthread_join(tid, NULL);
(void) close(pipefd[0]);
@@ -1529,7 +1593,7 @@ recv_read_nvlist(libzfs_handle_t *hdl, int fd, int len, nvlist_t **nvp,
static int
recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
- int baselen, char *newname, recvflags_t flags)
+ int baselen, char *newname, recvflags_t *flags)
{
static int seq;
zfs_cmd_t zc = { 0 };
@@ -1541,7 +1605,7 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
if (zhp == NULL)
return (-1);
clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
- flags.force ? MS_FORCE : 0);
+ flags->force ? MS_FORCE : 0);
zfs_close(zhp);
if (clp == NULL)
return (-1);
@@ -1557,7 +1621,7 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
(void) strlcpy(zc.zc_value, tryname, sizeof (zc.zc_value));
- if (flags.verbose) {
+ if (flags->verbose) {
(void) printf("attempting rename %s to %s\n",
zc.zc_name, zc.zc_value);
}
@@ -1576,19 +1640,19 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
"recv-%u-%u", getpid(), seq);
(void) strlcpy(zc.zc_value, newname, sizeof (zc.zc_value));
- if (flags.verbose) {
+ if (flags->verbose) {
(void) printf("failed - trying rename %s to %s\n",
zc.zc_name, zc.zc_value);
}
err = ioctl(hdl->libzfs_fd, ZFS_IOC_RENAME, &zc);
if (err == 0)
changelist_rename(clp, name, newname);
- if (err && flags.verbose) {
+ if (err && flags->verbose) {
(void) printf("failed (%u) - "
"will try again on next pass\n", errno);
}
err = EAGAIN;
- } else if (flags.verbose) {
+ } else if (flags->verbose) {
if (err == 0)
(void) printf("success\n");
else
@@ -1603,7 +1667,7 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
static int
recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
- char *newname, recvflags_t flags)
+ char *newname, recvflags_t *flags)
{
zfs_cmd_t zc = { 0 };
int err = 0;
@@ -1616,7 +1680,7 @@ recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
if (zhp == NULL)
return (-1);
clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
- flags.force ? MS_FORCE : 0);
+ flags->force ? MS_FORCE : 0);
if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
zfs_spa_version(zhp, &spa_version) == 0 &&
spa_version >= SPA_VERSION_USERREFS)
@@ -1632,11 +1696,11 @@ recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
zc.zc_defer_destroy = defer;
(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
- if (flags.verbose)
+ if (flags->verbose)
(void) printf("attempting destroy %s\n", zc.zc_name);
err = ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc);
if (err == 0) {
- if (flags.verbose)
+ if (flags->verbose)
(void) printf("success\n");
changelist_remove(clp, zc.zc_name);
}
@@ -1659,6 +1723,7 @@ recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
typedef struct guid_to_name_data {
uint64_t guid;
char *name;
+ char *skip;
} guid_to_name_data_t;
static int
@@ -1667,21 +1732,35 @@ guid_to_name_cb(zfs_handle_t *zhp, void *arg)
guid_to_name_data_t *gtnd = arg;
int err;
+ if (gtnd->skip != NULL &&
+ strcmp(zhp->zfs_name, gtnd->skip) == 0) {
+ return (0);
+ }
+
if (zhp->zfs_dmustats.dds_guid == gtnd->guid) {
(void) strcpy(gtnd->name, zhp->zfs_name);
zfs_close(zhp);
return (EEXIST);
}
+
err = zfs_iter_children(zhp, guid_to_name_cb, gtnd);
zfs_close(zhp);
return (err);
}
+/*
+ * Attempt to find the local dataset associated with this guid. In the case of
+ * multiple matches, we attempt to find the "best" match by searching
+ * progressively larger portions of the hierarchy. This allows one to send a
+ * tree of datasets individually and guarantee that we will find the source
+ * guid within that hierarchy, even if there are multiple matches elsewhere.
+ */
static int
guid_to_name(libzfs_handle_t *hdl, const char *parent, uint64_t guid,
char *name)
{
/* exhaustive search all local snapshots */
+ char pname[ZFS_MAXNAMELEN];
guid_to_name_data_t gtnd;
int err = 0;
zfs_handle_t *zhp;
@@ -1689,35 +1768,42 @@ guid_to_name(libzfs_handle_t *hdl, const char *parent, uint64_t guid,
gtnd.guid = guid;
gtnd.name = name;
+ gtnd.skip = NULL;
- if (strchr(parent, '@') == NULL) {
- zhp = make_dataset_handle(hdl, parent);
- if (zhp != NULL) {
- err = zfs_iter_children(zhp, guid_to_name_cb, &gtnd);
- zfs_close(zhp);
- if (err == EEXIST)
- return (0);
- }
- }
+ (void) strlcpy(pname, parent, sizeof (pname));
- cp = strchr(parent, '/');
- if (cp)
+ /*
+ * Search progressively larger portions of the hierarchy. This will
+ * select the "most local" version of the origin snapshot in the case
+ * that there are multiple matching snapshots in the system.
+ */
+ while ((cp = strrchr(pname, '/')) != NULL) {
+
+ /* Chop off the last component and open the parent */
*cp = '\0';
- zhp = make_dataset_handle(hdl, parent);
- if (cp)
- *cp = '/';
+ zhp = make_dataset_handle(hdl, pname);
+
+ if (zhp == NULL)
+ continue;
- if (zhp) {
err = zfs_iter_children(zhp, guid_to_name_cb, &gtnd);
zfs_close(zhp);
- }
+ if (err == EEXIST)
+ return (0);
- return (err == EEXIST ? 0 : ENOENT);
+ /*
+ * Remember the dataset that we already searched, so we
+ * skip it next time through.
+ */
+ gtnd.skip = pname;
+ }
+ return (ENOENT);
}
/*
- * Return true if dataset guid1 is created before guid2.
+ * Return +1 if guid1 is before guid2, 0 if they are the same, and -1 if
+ * guid1 is after guid2.
*/
static int
created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
@@ -1727,7 +1813,8 @@ created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
char *fsname, *snapname;
char buf[ZFS_MAXNAMELEN];
int rv;
- zfs_node_t zn1, zn2;
+ zfs_handle_t *guid1hdl, *guid2hdl;
+ uint64_t create1, create2;
if (guid2 == 0)
return (0);
@@ -1737,30 +1824,38 @@ 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);
- zn1.zn_handle = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
- if (zn1.zn_handle == NULL)
+ guid1hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
+ if (guid1hdl == 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);
- zn2.zn_handle = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
- if (zn2.zn_handle == NULL) {
- zfs_close(zn2.zn_handle);
+ guid2hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
+ if (guid2hdl == NULL) {
+ zfs_close(guid1hdl);
return (-1);
}
- rv = (zfs_snapshot_compare(&zn1, &zn2) == -1);
+ create1 = zfs_prop_get_int(guid1hdl, ZFS_PROP_CREATETXG);
+ create2 = zfs_prop_get_int(guid2hdl, ZFS_PROP_CREATETXG);
- zfs_close(zn1.zn_handle);
- zfs_close(zn2.zn_handle);
+ if (create1 < create2)
+ rv = -1;
+ else if (create1 > create2)
+ rv = +1;
+ else
+ rv = 0;
+
+ zfs_close(guid1hdl);
+ zfs_close(guid2hdl);
return (rv);
}
static int
recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs,
- recvflags_t flags, nvlist_t *stream_nv, avl_tree_t *stream_avl,
+ recvflags_t *flags, nvlist_t *stream_nv, avl_tree_t *stream_avl,
nvlist_t *renamed)
{
nvlist_t *local_nv;
@@ -1777,7 +1872,7 @@ recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs,
recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
ENOENT);
- if (flags.dryrun)
+ if (flags->dryrun)
return (0);
again:
@@ -1837,7 +1932,7 @@ again:
nvlist_t *origin_nvfs;
char *origin_fsname;
- if (flags.verbose)
+ if (flags->verbose)
(void) printf("promoting %s\n", fsname);
origin_nvfs = fsavl_find(local_avl, originguid,
@@ -1885,7 +1980,7 @@ again:
if (found == NULL) {
char name[ZFS_MAXNAMELEN];
- if (!flags.force)
+ if (!flags->force)
continue;
(void) snprintf(name, sizeof (name), "%s@%s",
@@ -1943,7 +2038,7 @@ again:
/* check for delete */
if (stream_nvfs == NULL) {
- if (!flags.force)
+ if (!flags->force)
continue;
error = recv_destroy(hdl, fsname, strlen(tofs)+1,
@@ -1956,7 +2051,7 @@ again:
}
if (fromguid == 0) {
- if (flags.verbose) {
+ if (flags->verbose) {
(void) printf("local fs %s does not have "
"fromsnap (%s in stream); must have "
"been deleted locally; ignoring\n",
@@ -1981,7 +2076,7 @@ again:
if ((stream_parent_fromsnap_guid != 0 &&
parent_fromsnap_guid != 0 &&
stream_parent_fromsnap_guid != parent_fromsnap_guid) ||
- ((flags.isprefix || strcmp(tofs, fsname) != 0) &&
+ ((flags->isprefix || strcmp(tofs, fsname) != 0) &&
(s1 != NULL) && (s2 != NULL) && strcmp(s1, s2) != 0)) {
nvlist_t *parent;
char tryname[ZFS_MAXNAMELEN];
@@ -2004,7 +2099,7 @@ again:
"%s%s", pname, strrchr(stream_fsname, '/'));
} else {
tryname[0] = '\0';
- if (flags.verbose) {
+ if (flags->verbose) {
(void) printf("local fs %s new parent "
"not found\n", fsname);
}
@@ -2032,7 +2127,7 @@ again:
if (needagain && progress) {
/* do another pass to fix up temporary names */
- if (flags.verbose)
+ if (flags->verbose)
(void) printf("another pass:\n");
goto again;
}
@@ -2042,7 +2137,7 @@ again:
static int
zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
- recvflags_t flags, dmu_replay_record_t *drr, zio_cksum_t *zc,
+ recvflags_t *flags, dmu_replay_record_t *drr, zio_cksum_t *zc,
char **top_zfs, int cleanup_fd, uint64_t *action_handlep)
{
nvlist_t *stream_nv = NULL;
@@ -2071,7 +2166,7 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
*/
if (drr->drr_payloadlen != 0) {
error = recv_read_nvlist(hdl, fd, drr->drr_payloadlen,
- &stream_nv, flags.byteswap, zc);
+ &stream_nv, flags->byteswap, zc);
if (error) {
error = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
goto out;
@@ -2092,9 +2187,9 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
* Read in the end record and verify checksum.
*/
if (0 != (error = recv_read(hdl, fd, &drre, sizeof (drre),
- flags.byteswap, NULL)))
+ flags->byteswap, NULL)))
goto out;
- if (flags.byteswap) {
+ if (flags->byteswap) {
drre.drr_type = BSWAP_32(drre.drr_type);
drre.drr_u.drr_end.drr_checksum.zc_word[0] =
BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[0]);
@@ -2135,11 +2230,11 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
nvpair_t *pair = NULL;
(void) strlcpy(tofs, destname, ZFS_MAXNAMELEN);
- if (flags.isprefix) {
+ if (flags->isprefix) {
struct drr_begin *drrb = &drr->drr_u.drr_begin;
int i;
- if (flags.istail) {
+ if (flags->istail) {
cp = strrchr(drrb->drr_toname, '/');
if (cp == NULL) {
(void) strlcat(tofs, "/",
@@ -2157,7 +2252,7 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
*strchr(tofs, '@') = '\0';
}
- if (recursive && !flags.dryrun && !flags.nomount) {
+ if (recursive && !flags->dryrun && !flags->nomount) {
VERIFY(0 == nvlist_alloc(&renamed,
NV_UNIQUE_NAME, 0));
}
@@ -2331,7 +2426,7 @@ recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap)
*/
static int
zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
- recvflags_t flags, dmu_replay_record_t *drr,
+ recvflags_t *flags, dmu_replay_record_t *drr,
dmu_replay_record_t *drr_noswap, const char *sendfs,
nvlist_t *stream_nv, avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
uint64_t *action_handlep)
@@ -2373,7 +2468,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
if (err)
VERIFY(0 == nvlist_alloc(&props, NV_UNIQUE_NAME, 0));
- if (flags.canmountoff) {
+ if (flags->canmountoff) {
VERIFY(0 == nvlist_add_uint64(props,
zfs_prop_to_name(ZFS_PROP_CANMOUNT), 0));
}
@@ -2400,7 +2495,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
* If they specified a snapshot, chop the entire name stored in
* the stream.
*/
- if (flags.istail) {
+ if (flags->istail) {
/*
* A filesystem was specified with -e. We want to tack on only
* the tail of the sent snapshot path.
@@ -2426,7 +2521,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
} else {
chopprefix = drrb->drr_toname + (chopprefix - sendfs);
}
- } else if (flags.isprefix) {
+ } else if (flags->isprefix) {
/*
* A filesystem was specified with -d. We want to tack on
* everything but the first element of the sent snapshot path
@@ -2480,7 +2575,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
* Determine the name of the origin snapshot, store in zc_string.
*/
if (drrb->drr_flags & DRR_FLAG_CLONE) {
- if (guid_to_name(hdl, tosnap,
+ if (guid_to_name(hdl, zc.zc_value,
drrb->drr_fromguid, zc.zc_string) != 0) {
zcmd_free_nvlists(&zc);
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
@@ -2488,7 +2583,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
zc.zc_value);
return (zfs_error(hdl, EZFS_NOENT, errbuf));
}
- if (flags.verbose)
+ if (flags->verbose)
(void) printf("found clone origin %s\n", zc.zc_string);
}
@@ -2511,7 +2606,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
!zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
char suffix[ZFS_MAXNAMELEN];
(void) strcpy(suffix, strrchr(zc.zc_value, '/'));
- if (guid_to_name(hdl, tosnap, parent_snapguid,
+ if (guid_to_name(hdl, zc.zc_name, parent_snapguid,
zc.zc_value) == 0) {
*strchr(zc.zc_value, '@') = '\0';
(void) strcat(zc.zc_value, suffix);
@@ -2533,12 +2628,12 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
* topmost path in the stream, then if the fs does not exist we
* should look no further.
*/
- if ((flags.isprefix || (*(chopprefix = drrb->drr_toname +
+ if ((flags->isprefix || (*(chopprefix = drrb->drr_toname +
strlen(sendfs)) != '\0' && *chopprefix != '@')) &&
!zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
char snap[ZFS_MAXNAMELEN];
(void) strcpy(snap, strchr(zc.zc_value, '@'));
- if (guid_to_name(hdl, tosnap, drrb->drr_fromguid,
+ if (guid_to_name(hdl, zc.zc_name, drrb->drr_fromguid,
zc.zc_value) == 0) {
*strchr(zc.zc_value, '@') = '\0';
(void) strcat(zc.zc_value, snap);
@@ -2560,7 +2655,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
* snapshots).
*/
if (stream_wantsnewfs) {
- if (!flags.force) {
+ if (!flags->force) {
zcmd_free_nvlists(&zc);
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"destination '%s' exists\n"
@@ -2596,7 +2691,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
return (zfs_error(hdl, EZFS_EXISTS, errbuf));
}
- if (!flags.dryrun && zhp->zfs_type == ZFS_TYPE_FILESYSTEM &&
+ if (!flags->dryrun && zhp->zfs_type == ZFS_TYPE_FILESYSTEM &&
stream_wantsnewfs) {
/* We can't do online recv in this case */
clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0);
@@ -2635,7 +2730,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
*/
*cp = '\0';
- if (flags.isprefix && !flags.istail && !flags.dryrun &&
+ if (flags->isprefix && !flags->istail && !flags->dryrun &&
create_parents(hdl, zc.zc_value, strlen(tosnap)) != 0) {
zcmd_free_nvlists(&zc);
return (zfs_error(hdl, EZFS_BADRESTORE, errbuf));
@@ -2646,18 +2741,18 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
zc.zc_begin_record = drr_noswap->drr_u.drr_begin;
zc.zc_cookie = infd;
- zc.zc_guid = flags.force;
- if (flags.verbose) {
+ zc.zc_guid = flags->force;
+ if (flags->verbose) {
(void) printf("%s %s stream of %s into %s\n",
- flags.dryrun ? "would receive" : "receiving",
+ flags->dryrun ? "would receive" : "receiving",
drrb->drr_fromguid ? "incremental" : "full",
drrb->drr_toname, zc.zc_value);
(void) fflush(stdout);
}
- if (flags.dryrun) {
+ if (flags->dryrun) {
zcmd_free_nvlists(&zc);
- return (recv_skip(hdl, infd, flags.byteswap));
+ return (recv_skip(hdl, infd, flags->byteswap));
}
zc.zc_nvlist_dst = (uint64_t)(uintptr_t)prop_errbuf;
@@ -2738,12 +2833,12 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
nvlist_free(local_nv);
if (fs != NULL) {
- if (flags.verbose) {
+ if (flags->verbose) {
(void) printf("snap %s already exists; "
"ignoring\n", zc.zc_value);
}
err = ioctl_err = recv_skip(hdl, infd,
- flags.byteswap);
+ flags->byteswap);
}
}
*cp = '@';
@@ -2795,7 +2890,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
case EDQUOT:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"destination %s space quota exceeded"), zc.zc_name);
- (void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
+ (void) zfs_error(hdl, EZFS_NOSPC, errbuf);
break;
default:
(void) zfs_standard_error(hdl, ioctl_errno, errbuf);
@@ -2853,7 +2948,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
*action_handlep = zc.zc_action_handle;
- if (flags.verbose) {
+ if (flags->verbose) {
char buf1[64];
char buf2[64];
uint64_t bytes = zc.zc_cookie;
@@ -2871,7 +2966,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
}
static int
-zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
+zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t *flags,
int infd, const char *sendfs, nvlist_t *stream_nv, avl_tree_t *stream_avl,
char **top_zfs, int cleanup_fd, uint64_t *action_handlep)
{
@@ -2886,7 +2981,7 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot receive"));
- if (flags.isprefix &&
+ if (flags->isprefix &&
!zfs_dataset_exists(hdl, tosnap, ZFS_TYPE_DATASET)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "specified fs "
"(%s) does not exist"), tosnap);
@@ -2906,7 +3001,7 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
/* the kernel needs the non-byteswapped begin record */
drr_noswap = drr;
- flags.byteswap = B_FALSE;
+ flags->byteswap = B_FALSE;
if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) {
/*
* We computed the checksum in the wrong byteorder in
@@ -2914,7 +3009,7 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
*/
bzero(&zcksum, sizeof (zio_cksum_t));
fletcher_4_incremental_byteswap(&drr, sizeof (drr), &zcksum);
- flags.byteswap = B_TRUE;
+ flags->byteswap = B_TRUE;
drr.drr_type = BSWAP_32(drr.drr_type);
drr.drr_payloadlen = BSWAP_32(drr.drr_payloadlen);
@@ -2982,7 +3077,7 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
* (-1 will override -2).
*/
int
-zfs_receive(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
+zfs_receive(libzfs_handle_t *hdl, const char *tosnap, recvflags_t *flags,
int infd, avl_tree_t *stream_avl)
{
char *top_zfs = NULL;
@@ -2998,7 +3093,7 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
VERIFY(0 == close(cleanup_fd));
- if (err == 0 && !flags.nomount && top_zfs) {
+ if (err == 0 && !flags->nomount && top_zfs) {
zfs_handle_t *zhp;
prop_changelist_t *clp;
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c
index e457a23..c903696 100644
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
*/
/*
@@ -351,6 +352,7 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
switch (error) {
case ENXIO:
case ENODEV:
+ case EPIPE:
zfs_verror(hdl, EZFS_IO, fmt, ap);
break;
@@ -1324,7 +1326,8 @@ 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_user(propname) && !zfs_prop_userquota(propname) &&
+ !zfs_prop_written(propname)))) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"invalid property '%s'"), propname);
return (zfs_error(hdl, EZFS_BADPROP,
OpenPOWER on IntegriCloud