@@ -29,7 +29,7 @@
* Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
* Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
* Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
* Copyright (c) 2018 Datto Inc.
* Copyright (c) 2019 Datto Inc.
*/

#include <assert.h>
@@ -623,10 +623,11 @@ typedef struct send_data {
const char *fsname;
const char *fromsnap;
const char *tosnap;
boolean_t raw;
boolean_t backup;
boolean_t recursive;
boolean_t raw;
boolean_t replicate;
boolean_t verbose;
boolean_t backup;
boolean_t seenfrom;
boolean_t seento;
boolean_t holds; /* were holds requested with send -h */
@@ -845,6 +846,7 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg)
send_data_t *sd = arg;
nvlist_t *nvfs = NULL, *nv = NULL;
int rv = 0;
uint64_t min_txg = 0, max_txg = 0;
uint64_t parent_fromsnap_guid_save = sd->parent_fromsnap_guid;
uint64_t fromsnap_txg_save = sd->fromsnap_txg;
uint64_t tosnap_txg_save = sd->tosnap_txg;
@@ -888,10 +890,10 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg)
goto out;
}

VERIFY(0 == nvlist_alloc(&nvfs, NV_UNIQUE_NAME, 0));
VERIFY(0 == nvlist_add_string(nvfs, "name", zhp->zfs_name));
VERIFY(0 == nvlist_add_uint64(nvfs, "parentfromsnap",
sd->parent_fromsnap_guid));
nvfs = fnvlist_alloc();
fnvlist_add_string(nvfs, "name", zhp->zfs_name);
fnvlist_add_uint64(nvfs, "parentfromsnap",
sd->parent_fromsnap_guid);

if (zhp->zfs_dmustats.dds_origin[0]) {
zfs_handle_t *origin = zfs_open(zhp->zfs_hdl,
@@ -900,15 +902,15 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg)
rv = -1;
goto out;
}
VERIFY(0 == nvlist_add_uint64(nvfs, "origin",
origin->zfs_dmustats.dds_guid));
fnvlist_add_uint64(nvfs, "origin",
origin->zfs_dmustats.dds_guid);

zfs_close(origin);
}

/* iterate over props */
if (sd->props || sd->backup || sd->recursive) {
VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0));
nv = fnvlist_alloc();
send_iterate_prop(zhp, sd->backup, nv);
}
if (zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) != ZIO_CRYPT_OFF) {
@@ -921,7 +923,7 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg)
}

if (encroot)
VERIFY(0 == nvlist_add_boolean(nvfs, "is_encroot"));
fnvlist_add_boolean(nvfs, "is_encroot");

/*
* Encrypted datasets can only be sent with properties if
@@ -941,28 +943,32 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg)
}

if (nv != NULL)
VERIFY(0 == nvlist_add_nvlist(nvfs, "props", nv));
fnvlist_add_nvlist(nvfs, "props", nv);

/* iterate over snaps, and set sd->parent_fromsnap_guid */
sd->parent_fromsnap_guid = 0;
VERIFY(0 == nvlist_alloc(&sd->parent_snaps, NV_UNIQUE_NAME, 0));
VERIFY(0 == nvlist_alloc(&sd->snapprops, NV_UNIQUE_NAME, 0));
sd->parent_snaps = fnvlist_alloc();
sd->snapprops = fnvlist_alloc();
if (!sd->replicate && fromsnap_txg != 0)
min_txg = fromsnap_txg;
if (!sd->replicate && tosnap_txg != 0)
max_txg = tosnap_txg;
if (sd->holds)
VERIFY(0 == nvlist_alloc(&sd->snapholds, NV_UNIQUE_NAME, 0));
(void) zfs_iter_snapshots_sorted(zhp, send_iterate_snap, sd);
VERIFY(0 == nvlist_add_nvlist(nvfs, "snaps", sd->parent_snaps));
VERIFY(0 == nvlist_add_nvlist(nvfs, "snapprops", sd->snapprops));
(void) zfs_iter_snapshots_sorted(zhp, send_iterate_snap, sd,
min_txg, max_txg);
fnvlist_add_nvlist(nvfs, "snaps", sd->parent_snaps);
fnvlist_add_nvlist(nvfs, "snapprops", sd->snapprops);
if (sd->holds)
VERIFY(0 == nvlist_add_nvlist(nvfs, "snapholds",
sd->snapholds));
nvlist_free(sd->parent_snaps);
nvlist_free(sd->snapprops);
nvlist_free(sd->snapholds);
fnvlist_add_nvlist(nvfs, "snapholds", sd->snapholds);
fnvlist_free(sd->parent_snaps);
fnvlist_free(sd->snapprops);
fnvlist_free(sd->snapholds);

/* add this fs to nvlist */
(void) snprintf(guidstring, sizeof (guidstring),
"0x%llx", (longlong_t)guid);
VERIFY(0 == nvlist_add_nvlist(sd->fss, guidstring, nvfs));
fnvlist_add_nvlist(sd->fss, guidstring, nvfs);

/* iterate over children */
if (sd->recursive)
@@ -972,17 +978,18 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg)
sd->parent_fromsnap_guid = parent_fromsnap_guid_save;
sd->fromsnap_txg = fromsnap_txg_save;
sd->tosnap_txg = tosnap_txg_save;
nvlist_free(nv);
nvlist_free(nvfs);
fnvlist_free(nv);
fnvlist_free(nvfs);

zfs_close(zhp);
return (rv);
}

static int
gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
const char *tosnap, boolean_t recursive, boolean_t raw, boolean_t verbose,
boolean_t backup, boolean_t holds, boolean_t props, nvlist_t **nvlp,
const char *tosnap, boolean_t recursive, boolean_t raw, boolean_t replicate,
boolean_t verbose, boolean_t backup, boolean_t holds, boolean_t props,
nvlist_t **nvlp,
avl_tree_t **avlp)
{
zfs_handle_t *zhp;
@@ -999,6 +1006,7 @@ gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
sd.tosnap = tosnap;
sd.recursive = recursive;
sd.raw = raw;
sd.replicate = replicate;
sd.verbose = verbose;
sd.backup = backup;
sd.holds = holds;
@@ -1437,6 +1445,7 @@ dump_filesystem(zfs_handle_t *zhp, void *arg)
send_dump_data_t *sdd = arg;
boolean_t missingfrom = B_FALSE;
zfs_cmd_t zc = {"\0"};
uint64_t min_txg = 0, max_txg = 0;

(void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s",
zhp->zfs_name, sdd->tosnap);
@@ -1469,7 +1478,15 @@ dump_filesystem(zfs_handle_t *zhp, void *arg)
if (sdd->fromsnap == NULL || missingfrom)
sdd->seenfrom = B_TRUE;

rv = zfs_iter_snapshots_sorted(zhp, dump_snapshot, arg);
if (!sdd->replicate && sdd->fromsnap != NULL)
min_txg = get_snap_txg(zhp->zfs_hdl, zhp->zfs_name,
sdd->fromsnap);
if (!sdd->replicate && sdd->tosnap != NULL)
max_txg = get_snap_txg(zhp->zfs_hdl, zhp->zfs_name,
sdd->tosnap);

rv = zfs_iter_snapshots_sorted(zhp, dump_snapshot, arg,
min_txg, max_txg);
if (!sdd->seenfrom) {
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
"WARNING: could not send %s@%s:\n"
@@ -1948,8 +1965,8 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,

err = gather_nvlist(zhp->zfs_hdl, zhp->zfs_name,
fromsnap, tosnap, flags->replicate, flags->raw,
flags->verbose, flags->backup, flags->holds,
flags->props, &fss, &fsavl);
flags->replicate, flags->verbose, flags->backup,
flags->holds, flags->props, &fss, &fsavl);
if (err)
goto err_out;
VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss));
@@ -2853,8 +2870,8 @@ recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs,
VERIFY(0 == nvlist_alloc(&deleted, NV_UNIQUE_NAME, 0));

if ((error = gather_nvlist(hdl, tofs, fromsnap, NULL,
recursive, B_TRUE, B_FALSE, B_FALSE, B_FALSE, B_TRUE, &local_nv,
&local_avl)) != 0)
recursive, B_TRUE, recursive, B_FALSE, B_FALSE, B_FALSE, B_TRUE,
&local_nv, &local_avl)) != 0)
return (error);

/*
@@ -4287,8 +4304,8 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
*/
*cp = '\0';
if (gather_nvlist(hdl, destsnap, NULL, NULL, B_FALSE, B_TRUE,
B_FALSE, B_FALSE, B_FALSE, B_TRUE, &local_nv, &local_avl)
== 0) {
B_FALSE, B_FALSE, B_FALSE, B_FALSE, B_TRUE, &local_nv,
&local_avl) == 0) {
*cp = '@';
fs = fsavl_find(local_avl, drrb->drr_toguid, NULL);
fsavl_destroy(local_avl);
@@ -34,9 +34,9 @@
* Copyright 2016 Toomas Soome <tsoome@me.com>
* Copyright (c) 2016 Actifio, Inc. All rights reserved.
* Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
* Copyright (c) 2017 Datto Inc. All rights reserved.
* Copyright 2017 RackTop Systems.
* Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
* Copyright (c) 2019 Datto Inc.
*/

/*
@@ -2315,7 +2315,8 @@ zfs_ioc_dataset_list_next(zfs_cmd_t *zc)
* inputs:
* zc_name name of filesystem
* zc_cookie zap cursor
* zc_nvlist_dst_size size of buffer for property nvlist
* zc_nvlist_src iteration range nvlist
* zc_nvlist_src_size size of iteration range nvlist
*
* outputs:
* zc_name name of next snapshot
@@ -2326,8 +2327,23 @@ zfs_ioc_dataset_list_next(zfs_cmd_t *zc)
static int
zfs_ioc_snapshot_list_next(zfs_cmd_t *zc)
{
objset_t *os;
int error;
objset_t *os, *ossnap;
dsl_dataset_t *ds;
uint64_t min_txg = 0, max_txg = 0;

if (zc->zc_nvlist_src_size != 0) {
nvlist_t *props = NULL;
error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
zc->zc_iflags, &props);
if (error != 0)
return (error);
(void) nvlist_lookup_uint64(props, SNAP_ITER_MIN_TXG,
&min_txg);
(void) nvlist_lookup_uint64(props, SNAP_ITER_MAX_TXG,
&max_txg);
nvlist_free(props);
}

error = dmu_objset_hold(zc->zc_name, FTAG, &os);
if (error != 0) {
@@ -2344,26 +2360,52 @@ zfs_ioc_snapshot_list_next(zfs_cmd_t *zc)
return (SET_ERROR(ESRCH));
}

error = dmu_snapshot_list_next(os,
sizeof (zc->zc_name) - strlen(zc->zc_name),
zc->zc_name + strlen(zc->zc_name), &zc->zc_obj, &zc->zc_cookie,
NULL);
while (error == 0) {
if (issig(JUSTLOOKING) && issig(FORREAL)) {
error = SET_ERROR(EINTR);
break;
}

if (error == 0 && !zc->zc_simple) {
dsl_dataset_t *ds;
dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool;
error = dmu_snapshot_list_next(os,
sizeof (zc->zc_name) - strlen(zc->zc_name),
zc->zc_name + strlen(zc->zc_name), &zc->zc_obj,
&zc->zc_cookie, NULL);
if (error == ENOENT) {
error = SET_ERROR(ESRCH);
break;
} else if (error != 0) {
break;
}

error = dsl_dataset_hold_obj(dp, zc->zc_obj, FTAG, &ds);
if (error == 0) {
objset_t *ossnap;
error = dsl_dataset_hold_obj(dmu_objset_pool(os), zc->zc_obj,
FTAG, &ds);
if (error != 0)
break;

error = dmu_objset_from_ds(ds, &ossnap);
if (error == 0)
error = zfs_ioc_objset_stats_impl(zc, ossnap);
if ((min_txg != 0 && dsl_get_creationtxg(ds) < min_txg) ||
(max_txg != 0 && dsl_get_creationtxg(ds) > max_txg)) {
dsl_dataset_rele(ds, FTAG);
/* undo snapshot name append */
*(strchr(zc->zc_name, '@') + 1) = '\0';
/* skip snapshot */
continue;
}
} else if (error == ENOENT) {
error = SET_ERROR(ESRCH);

if (zc->zc_simple) {
dsl_dataset_rele(ds, FTAG);
break;
}

if ((error = dmu_objset_from_ds(ds, &ossnap)) != 0) {
dsl_dataset_rele(ds, FTAG);
break;
}
if ((error = zfs_ioc_objset_stats_impl(zc, ossnap)) != 0) {
dsl_dataset_rele(ds, FTAG);
break;
}
dsl_dataset_rele(ds, FTAG);
break;
}

dmu_objset_rele(os, FTAG);