diff --git a/cmd/zfs/zfs_iter.c b/cmd/zfs/zfs_iter.c index af4e4b4d2..9ad691938 100644 --- a/cmd/zfs/zfs_iter.c +++ b/cmd/zfs/zfs_iter.c @@ -140,7 +140,7 @@ zfs_callback(zfs_handle_t *zhp, void *data) ZFS_TYPE_BOOKMARK)) == 0) && include_snaps) (void) zfs_iter_snapshots(zhp, (cb->cb_flags & ZFS_ITER_SIMPLE) != 0, zfs_callback, - data); + data, 0, 0); if (((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT | ZFS_TYPE_BOOKMARK)) == 0) && include_bmarks) (void) zfs_iter_bookmarks(zhp, zfs_callback, data); diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index 12a5cf6cf..21c4d4334 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -27,7 +27,7 @@ * Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright 2016 Igor Kozhukhov . * Copyright 2016 Nexenta Systems, Inc. - * Copyright (c) 2018 Datto Inc. + * Copyright (c) 2019 Datto Inc. */ #include @@ -1255,7 +1255,7 @@ destroy_print_snapshots(zfs_handle_t *fs_zhp, destroy_cbdata_t *cb) int err; assert(cb->cb_firstsnap == NULL); assert(cb->cb_prevsnap == NULL); - err = zfs_iter_snapshots_sorted(fs_zhp, destroy_print_cb, cb); + err = zfs_iter_snapshots_sorted(fs_zhp, destroy_print_cb, cb, 0, 0); if (cb->cb_firstsnap != NULL) { uint64_t used = 0; if (err == 0) { @@ -3581,6 +3581,7 @@ zfs_do_promote(int argc, char **argv) */ typedef struct rollback_cbdata { uint64_t cb_create; + uint8_t cb_younger_ds_printed; boolean_t cb_first; int cb_doclones; char *cb_target; @@ -3612,15 +3613,20 @@ rollback_check_dependent(zfs_handle_t *zhp, void *data) /* - * Report any snapshots more recent than the one specified. Used when '-r' is - * not specified. We reuse this same callback for the snapshot dependents - if - * 'cb_dependent' is set, then this is a dependent and we should report it - * without checking the transaction group. + * Report some snapshots/bookmarks more recent than the one specified. + * Used when '-r' is not specified. We reuse this same callback for the + * snapshot dependents - if 'cb_dependent' is set, then this is a + * dependent and we should report it without checking the transaction group. */ static int rollback_check(zfs_handle_t *zhp, void *data) { rollback_cbdata_t *cbp = data; + /* + * Max number of younger snapshots and/or bookmarks to display before + * we stop the iteration. + */ + const uint8_t max_younger = 32; if (cbp->cb_doclones) { zfs_close(zhp); @@ -3649,9 +3655,24 @@ rollback_check(zfs_handle_t *zhp, void *data) } else { (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); + cbp->cb_younger_ds_printed++; } } zfs_close(zhp); + + if (cbp->cb_younger_ds_printed == max_younger) { + /* + * This non-recursive rollback is going to fail due to the + * presence of snapshots and/or bookmarks that are younger than + * the rollback target. + * We printed some of the offending objects, now we stop + * zfs_iter_snapshot/bookmark iteration so we can fail fast and + * avoid iterating over the rest of the younger objects + */ + (void) fprintf(stderr, gettext("Output limited to %d " + "snapshots/bookmarks\n"), max_younger); + return (-1); + } return (0); } @@ -3665,6 +3686,7 @@ zfs_do_rollback(int argc, char **argv) zfs_handle_t *zhp, *snap; char parentname[ZFS_MAX_DATASET_NAME_LEN]; char *delim; + uint64_t min_txg = 0; /* check options */ while ((c = getopt(argc, argv, "rRf")) != -1) { @@ -3720,7 +3742,12 @@ zfs_do_rollback(int argc, char **argv) cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); cb.cb_first = B_TRUE; cb.cb_error = 0; - if ((ret = zfs_iter_snapshots(zhp, B_FALSE, rollback_check, &cb)) != 0) + + if (cb.cb_create > 0) + min_txg = cb.cb_create; + + if ((ret = zfs_iter_snapshots(zhp, B_FALSE, rollback_check, &cb, + min_txg, 0)) != 0) goto out; if ((ret = zfs_iter_bookmarks(zhp, rollback_check, &cb)) != 0) goto out; diff --git a/include/libzfs.h b/include/libzfs.h index 65b06f7a8..3405bb99b 100644 --- a/include/libzfs.h +++ b/include/libzfs.h @@ -27,7 +27,7 @@ * Copyright (c) 2016, Intel Corporation. * Copyright 2016 Nexenta Systems, Inc. * Copyright (c) 2017 Open-E, Inc. All Rights Reserved. - * Copyright (c) 2018 Datto Inc. + * Copyright (c) 2019 Datto Inc. */ #ifndef _LIBZFS_H @@ -571,8 +571,10 @@ extern int zfs_iter_root(libzfs_handle_t *, zfs_iter_f, void *); extern int zfs_iter_children(zfs_handle_t *, zfs_iter_f, void *); 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 *, boolean_t, zfs_iter_f, void *); -extern int zfs_iter_snapshots_sorted(zfs_handle_t *, zfs_iter_f, void *); +extern int zfs_iter_snapshots(zfs_handle_t *, boolean_t, zfs_iter_f, void *, + uint64_t, uint64_t); +extern int zfs_iter_snapshots_sorted(zfs_handle_t *, zfs_iter_f, void *, + uint64_t, uint64_t); extern int zfs_iter_snapspec(zfs_handle_t *, const char *, zfs_iter_f, void *); extern int zfs_iter_bookmarks(zfs_handle_t *, zfs_iter_f, void *); extern int zfs_iter_mounted(zfs_handle_t *, zfs_iter_f, void *); diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h index 8a86480a6..c4d26eb87 100644 --- a/include/sys/fs/zfs.h +++ b/include/sys/fs/zfs.h @@ -25,8 +25,8 @@ * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2013, 2017 Joyent, Inc. All rights reserved. * Copyright (c) 2014 Integros [integros.com] - * Copyright (c) 2017 Datto Inc. * Copyright (c) 2017, Intel Corporation. + * Copyright (c) 2019 Datto Inc. */ /* Portions Copyright 2010 Robert Milkowski */ @@ -1076,7 +1076,7 @@ typedef enum pool_initialize_func { * is passed between kernel and userland as an nvlist uint64 array. */ typedef struct ddt_object { - uint64_t ddo_count; /* number of elements in ddt */ + uint64_t ddo_count; /* number of elements in ddt */ uint64_t ddo_dspace; /* size of ddt on disk */ uint64_t ddo_mspace; /* size of ddt in-core */ } ddt_object_t; @@ -1123,6 +1123,13 @@ typedef enum { VDEV_INITIALIZE_COMPLETE } vdev_initializing_state_t; +/* + * nvlist name constants. Facilitate restricting snapshot iteration range for + * the "list next snapshot" ioctl + */ +#define SNAP_ITER_MIN_TXG "snap_iter_min_txg" +#define SNAP_ITER_MAX_TXG "snap_iter_max_txg" + /* * /dev/zfs ioctl numbers. * diff --git a/lib/libzfs/libzfs_dataset.c b/lib/libzfs/libzfs_dataset.c index 0537b631e..de94021a6 100644 --- a/lib/libzfs/libzfs_dataset.c +++ b/lib/libzfs/libzfs_dataset.c @@ -30,7 +30,7 @@ * Copyright 2017 Nexenta Systems, Inc. * Copyright 2016 Igor Kozhukhov * Copyright 2017-2018 RackTop Systems. - * Copyright (c) 2018 Datto Inc. + * Copyright (c) 2019 Datto Inc. */ #include @@ -4367,6 +4367,7 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) boolean_t restore_resv = 0; uint64_t old_volsize = 0, new_volsize; zfs_prop_t resv_prop = { 0 }; + uint64_t min_txg = 0; assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM || zhp->zfs_type == ZFS_TYPE_VOLUME); @@ -4377,7 +4378,13 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) cb.cb_force = force; cb.cb_target = snap->zfs_name; cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); - (void) zfs_iter_snapshots(zhp, B_FALSE, rollback_destroy, &cb); + + if (cb.cb_create > 0) + min_txg = cb.cb_create; + + (void) zfs_iter_snapshots(zhp, B_FALSE, rollback_destroy, &cb, + min_txg, 0); + (void) zfs_iter_bookmarks(zhp, rollback_destroy, &cb); if (cb.cb_error) diff --git a/lib/libzfs/libzfs_iter.c b/lib/libzfs/libzfs_iter.c index b1bdc4a6d..e765a7ee7 100644 --- a/lib/libzfs/libzfs_iter.c +++ b/lib/libzfs/libzfs_iter.c @@ -23,7 +23,7 @@ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2015 by Delphix. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. - * Copyright (c) 2018 Datto Inc. + * Copyright (c) 2019 Datto Inc. */ #include @@ -141,11 +141,12 @@ zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) */ int zfs_iter_snapshots(zfs_handle_t *zhp, boolean_t simple, zfs_iter_f func, - void *data) + void *data, uint64_t min_txg, uint64_t max_txg) { zfs_cmd_t zc = {"\0"}; zfs_handle_t *nzhp; int ret; + nvlist_t *range_nvl = NULL; if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT || zhp->zfs_type == ZFS_TYPE_BOOKMARK) @@ -155,6 +156,24 @@ zfs_iter_snapshots(zfs_handle_t *zhp, boolean_t simple, zfs_iter_f func, if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) return (-1); + + if (min_txg != 0) { + range_nvl = fnvlist_alloc(); + fnvlist_add_uint64(range_nvl, SNAP_ITER_MIN_TXG, min_txg); + } + if (max_txg != 0) { + if (range_nvl == NULL) + range_nvl = fnvlist_alloc(); + fnvlist_add_uint64(range_nvl, SNAP_ITER_MAX_TXG, max_txg); + } + + if (range_nvl != NULL && + zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, range_nvl) != 0) { + zcmd_free_nvlists(&zc); + fnvlist_free(range_nvl); + return (-1); + } + while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT, &zc)) == 0) { @@ -167,10 +186,12 @@ zfs_iter_snapshots(zfs_handle_t *zhp, boolean_t simple, zfs_iter_f func, if ((ret = func(nzhp, data)) != 0) { zcmd_free_nvlists(&zc); + fnvlist_free(range_nvl); return (ret); } } zcmd_free_nvlists(&zc); + fnvlist_free(range_nvl); return ((ret < 0) ? ret : 0); } @@ -282,7 +303,8 @@ zfs_snapshot_compare(const void *larg, const void *rarg) } int -zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data) +zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data, + uint64_t min_txg, uint64_t max_txg) { int ret = 0; zfs_node_t *node; @@ -292,7 +314,8 @@ zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data) avl_create(&avl, zfs_snapshot_compare, sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode)); - ret = zfs_iter_snapshots(zhp, B_FALSE, zfs_sort_snaps, &avl); + ret = zfs_iter_snapshots(zhp, B_FALSE, zfs_sort_snaps, &avl, min_txg, + max_txg); for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node)) ret |= callback(node->zn_handle, data); @@ -395,7 +418,7 @@ zfs_iter_snapspec(zfs_handle_t *fs_zhp, const char *spec_orig, } err = zfs_iter_snapshots_sorted(fs_zhp, - snapspec_cb, &ssa); + snapspec_cb, &ssa, 0, 0); if (ret == 0) ret = err; if (ret == 0 && (!ssa.ssa_seenfirst || @@ -435,7 +458,7 @@ zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data) { int ret; - if ((ret = zfs_iter_snapshots(zhp, B_FALSE, func, data)) != 0) + if ((ret = zfs_iter_snapshots(zhp, B_FALSE, func, data, 0, 0)) != 0) return (ret); return (zfs_iter_filesystems(zhp, func, data)); @@ -501,7 +524,7 @@ iter_dependents_cb(zfs_handle_t *zhp, void *arg) err = zfs_iter_filesystems(zhp, iter_dependents_cb, ida); if (err == 0) err = zfs_iter_snapshots(zhp, B_FALSE, - iter_dependents_cb, ida); + iter_dependents_cb, ida, 0, 0); ida->stack = isf.next; } diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c index 2aa0fd222..8096089f6 100644 --- a/lib/libzfs/libzfs_sendrecv.c +++ b/lib/libzfs/libzfs_sendrecv.c @@ -29,7 +29,7 @@ * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved. * Copyright 2016 Igor Kozhukhov * Copyright (c) 2018, loli10K . All rights reserved. - * Copyright (c) 2018 Datto Inc. + * Copyright (c) 2019 Datto Inc. */ #include @@ -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,8 +978,8 @@ out: 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); @@ -981,8 +987,9 @@ out: 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 @@ again: 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); diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index ab40ae185..047193c61 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -34,9 +34,9 @@ * Copyright 2016 Toomas Soome * Copyright (c) 2016 Actifio, Inc. All rights reserved. * Copyright (c) 2018, loli10K . 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 @@ top: * 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 @@ top: 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); - - if (error == 0 && !zc->zc_simple) { - dsl_dataset_t *ds; - dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool; - - error = dsl_dataset_hold_obj(dp, zc->zc_obj, FTAG, &ds); - if (error == 0) { - objset_t *ossnap; - - error = dmu_objset_from_ds(ds, &ossnap); - if (error == 0) - error = zfs_ioc_objset_stats_impl(zc, ossnap); - dsl_dataset_rele(ds, FTAG); + while (error == 0) { + if (issig(JUSTLOOKING) && issig(FORREAL)) { + error = SET_ERROR(EINTR); + break; } - } else if (error == ENOENT) { - error = 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); + if (error == ENOENT) { + error = SET_ERROR(ESRCH); + break; + } else if (error != 0) { + break; + } + + error = dsl_dataset_hold_obj(dmu_objset_pool(os), zc->zc_obj, + FTAG, &ds); + if (error != 0) + break; + + 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; + } + + 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);