summaryrefslogtreecommitdiffstats
path: root/lib/libzfs/libzfs_sendrecv.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libzfs/libzfs_sendrecv.c')
-rw-r--r--lib/libzfs/libzfs_sendrecv.c56
1 files changed, 46 insertions, 10 deletions
diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c
index 612a09914..1ffb6294c 100644
--- a/lib/libzfs/libzfs_sendrecv.c
+++ b/lib/libzfs/libzfs_sendrecv.c
@@ -113,6 +113,9 @@ fsavl_destroy(avl_tree_t *avl)
free(avl);
}
+/*
+ * Given an nvlist, produce an avl tree of snapshots, ordered by guid
+ */
static avl_tree_t *
fsavl_create(nvlist_t *fss)
{
@@ -243,7 +246,9 @@ send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv)
continue;
verify(nvpair_value_nvlist(elem, &propnv) == 0);
- if (prop == ZFS_PROP_QUOTA || prop == ZFS_PROP_RESERVATION) {
+ if (prop == ZFS_PROP_QUOTA || prop == ZFS_PROP_RESERVATION ||
+ prop == ZFS_PROP_REFQUOTA ||
+ prop == ZFS_PROP_REFRESERVATION) {
/* these guys are modifyable, but have no source */
uint64_t value;
verify(nvlist_lookup_uint64(propnv,
@@ -274,6 +279,11 @@ send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv)
}
}
+/*
+ * recursively generate nvlists describing datasets. See comment
+ * for the data structure send_data_t above for description of contents
+ * of the nvlist.
+ */
static int
send_iterate_fs(zfs_handle_t *zhp, void *arg)
{
@@ -689,9 +699,20 @@ again:
}
/*
- * Dumps a backup of tosnap, incremental from fromsnap if it isn't NULL.
- * If 'doall', dump all intermediate snaps.
- * If 'replicate', dump special header and do recursively.
+ * Generate a send stream for the dataset identified by the argument zhp.
+ *
+ * The content of the send stream is the snapshot identified by
+ * 'tosnap'. Incremental streams are requested in two ways:
+ * - from the snapshot identified by "fromsnap" (if non-null) or
+ * - from the origin of the dataset identified by zhp, which must
+ * be a clone. In this case, "fromsnap" is null and "fromorigin"
+ * is TRUE.
+ *
+ * The send stream is recursive (i.e. dumps a hierarchy of snapshots) and
+ * uses a special header (with a version field of DMU_BACKUP_HEADER_VERSION)
+ * if "replicate" is set. If "doall" is set, dump all the intermediate
+ * snapshots. The DMU_BACKUP_HEADER_VERSION header is used in the "doall"
+ * case too.
*/
int
zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
@@ -900,11 +921,12 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
if (err)
return (err);
+ zc.zc_objset_type = DMU_OST_ZFS;
+ (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
+
if (tryname) {
(void) strcpy(newname, tryname);
- zc.zc_objset_type = DMU_OST_ZFS;
- (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
(void) strlcpy(zc.zc_value, tryname, sizeof (zc.zc_value));
if (flags.verbose) {
@@ -959,12 +981,18 @@ recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
int err = 0;
prop_changelist_t *clp;
zfs_handle_t *zhp;
+ boolean_t defer = B_FALSE;
+ int spa_version;
zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
if (zhp == NULL)
return (-1);
clp = changelist_gather(zhp, ZFS_PROP_NAME, 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)
+ defer = B_TRUE;
zfs_close(zhp);
if (clp == NULL)
return (-1);
@@ -973,12 +1001,12 @@ recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
return (err);
zc.zc_objset_type = DMU_OST_ZFS;
+ zc.zc_defer_destroy = defer;
(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
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)
(void) printf("success\n");
@@ -988,7 +1016,12 @@ recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
(void) changelist_postfix(clp);
changelist_free(clp);
- if (err != 0)
+ /*
+ * Deferred destroy should always succeed. Since we can't tell
+ * if it destroyed the dataset or just marked it for deferred
+ * destroy, always do the rename just in case.
+ */
+ if (err != 0 || defer)
err = recv_rename(hdl, name, NULL, baselen, newname, flags);
return (err);
@@ -1775,11 +1808,13 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
/* We can't do online recv in this case */
clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0);
if (clp == NULL) {
+ zfs_close(zhp);
zcmd_free_nvlists(&zc);
return (-1);
}
if (changelist_prefix(clp) != 0) {
changelist_free(clp);
+ zfs_close(zhp);
zcmd_free_nvlists(&zc);
return (-1);
}
@@ -1936,7 +1971,8 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
* (if created, or if we tore them down to do an incremental
* restore), and the /dev links for the new snapshot (if
* created). Also mount any children of the target filesystem
- * if we did an incremental receive.
+ * if we did a replication receive (indicated by stream_avl
+ * being non-NULL).
*/
cp = strchr(zc.zc_value, '@');
if (cp && (ioctl_err == 0 || !newfs)) {
@@ -1952,7 +1988,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
if (err == 0 && ioctl_err == 0)
err = zvol_create_link(hdl,
zc.zc_value);
- } else if (newfs) {
+ } else if (newfs || stream_avl) {
/*
* Track the first/top of hierarchy fs,
* for mounting and sharing later.