diff options
Diffstat (limited to 'lib/libzfs/libzfs_sendrecv.c')
-rw-r--r-- | lib/libzfs/libzfs_sendrecv.c | 96 |
1 files changed, 84 insertions, 12 deletions
diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c index 1d8292101..2aa0fd222 100644 --- a/lib/libzfs/libzfs_sendrecv.c +++ b/lib/libzfs/libzfs_sendrecv.c @@ -29,6 +29,7 @@ * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved. * Copyright 2016 Igor Kozhukhov <[email protected]> * Copyright (c) 2018, loli10K <[email protected]>. All rights reserved. + * Copyright (c) 2018 Datto Inc. */ #include <assert.h> @@ -616,6 +617,7 @@ typedef struct send_data { nvlist_t *parent_snaps; nvlist_t *fss; nvlist_t *snapprops; + nvlist_t *snapholds; /* user holds */ /* send-receive configuration, does not change during traversal */ const char *fsname; @@ -627,6 +629,8 @@ typedef struct send_data { boolean_t verbose; boolean_t seenfrom; boolean_t seento; + boolean_t holds; /* were holds requested with send -h */ + boolean_t props; /* * The header nvlist is of the following format: @@ -642,6 +646,7 @@ typedef struct send_data { * "props" -> { name -> value (only if set here) } * "snaps" -> { name (lastname) -> number (guid) } * "snapprops" -> { name (lastname) -> { name -> value } } + * "snapholds" -> { name (lastname) -> { holdname -> crtime } } * * "origin" -> number (guid) (if clone) * "is_encroot" -> boolean @@ -712,6 +717,15 @@ send_iterate_snap(zfs_handle_t *zhp, void *arg) send_iterate_prop(zhp, sd->backup, nv); VERIFY(0 == nvlist_add_nvlist(sd->snapprops, snapname, nv)); nvlist_free(nv); + if (sd->holds) { + nvlist_t *holds = fnvlist_alloc(); + int err = lzc_get_holds(zhp->zfs_name, &holds); + if (err == 0) { + VERIFY(0 == nvlist_add_nvlist(sd->snapholds, + snapname, holds)); + } + fnvlist_free(holds); + } zfs_close(zhp); return (0); @@ -893,9 +907,10 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg) } /* iterate over props */ - VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0)); - send_iterate_prop(zhp, sd->backup, nv); - + if (sd->props || sd->backup || sd->recursive) { + VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0)); + send_iterate_prop(zhp, sd->backup, nv); + } if (zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) != ZIO_CRYPT_OFF) { boolean_t encroot; @@ -925,17 +940,24 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg) } - VERIFY(0 == nvlist_add_nvlist(nvfs, "props", nv)); + if (nv != NULL) + VERIFY(0 == nvlist_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)); + 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)); + 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); /* add this fs to nvlist */ (void) snprintf(guidstring, sizeof (guidstring), @@ -960,7 +982,8 @@ 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, nvlist_t **nvlp, avl_tree_t **avlp) + boolean_t backup, boolean_t holds, boolean_t props, nvlist_t **nvlp, + avl_tree_t **avlp) { zfs_handle_t *zhp; send_data_t sd = { 0 }; @@ -978,6 +1001,8 @@ gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap, sd.raw = raw; sd.verbose = verbose; sd.backup = backup; + sd.holds = holds; + sd.props = props; if ((error = send_iterate_fs(zhp, &sd)) != 0) { nvlist_free(sd.fss); @@ -1008,7 +1033,7 @@ typedef struct send_dump_data { uint64_t prevsnap_obj; boolean_t seenfrom, seento, replicate, doall, fromorigin; boolean_t verbose, dryrun, parsable, progress, embed_data, std_out; - boolean_t large_block, compress, raw; + boolean_t large_block, compress, raw, holds; int outfd; boolean_t err; nvlist_t *fss; @@ -1864,6 +1889,9 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, } } + if (flags->holds) + featureflags |= DMU_BACKUP_FEATURE_HOLDS; + /* * Start the dedup thread if this is a dedup stream. We do not bother * doing this if this a raw send of an encrypted dataset with dedup off @@ -1891,7 +1919,8 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, } } - if (flags->replicate || flags->doall || flags->props || flags->backup) { + if (flags->replicate || flags->doall || flags->props || + flags->holds || flags->backup) { dmu_replay_record_t drr = { 0 }; char *packbuf = NULL; size_t buflen = 0; @@ -1899,7 +1928,8 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, ZIO_SET_CHECKSUM(&zc, 0, 0, 0, 0); - if (flags->replicate || flags->props || flags->backup) { + if (flags->replicate || flags->props || flags->backup || + flags->holds) { nvlist_t *hdrnv; VERIFY(0 == nvlist_alloc(&hdrnv, NV_UNIQUE_NAME, 0)); @@ -1918,7 +1948,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, &fss, &fsavl); + flags->verbose, flags->backup, flags->holds, + flags->props, &fss, &fsavl); if (err) goto err_out; VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss)); @@ -1988,6 +2019,7 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, sdd.embed_data = flags->embed_data; sdd.compress = flags->compress; sdd.raw = flags->raw; + sdd.holds = flags->holds; sdd.filter_cb = filter_func; sdd.filter_cb_arg = cb_arg; if (debugnvp) @@ -2020,6 +2052,7 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, sdd.cleanup_fd = -1; sdd.snapholds = NULL; } + if (flags->verbose || sdd.snapholds != NULL) { /* * Do a verbose no-op dry run to get all the verbose output @@ -2088,7 +2121,7 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, } if (!flags->dryrun && (flags->replicate || flags->doall || - flags->props || flags->backup)) { + flags->props || flags->backup || flags->holds)) { /* * write final end record. NB: want to do this even if * there was some error, because it might not be totally @@ -2820,7 +2853,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, &local_nv, &local_avl)) != 0) + recursive, B_TRUE, B_FALSE, B_FALSE, B_FALSE, B_TRUE, &local_nv, + &local_avl)) != 0) return (error); /* @@ -3655,6 +3689,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, uint64_t parent_snapguid = 0; prop_changelist_t *clp = NULL; nvlist_t *snapprops_nvlist = NULL; + nvlist_t *snapholds_nvlist = NULL; zprop_errflags_t prop_errflags; nvlist_t *prop_errors = NULL; boolean_t recursive; @@ -3683,6 +3718,9 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") == ENOENT); + /* Did the user request holds be skipped via zfs recv -k? */ + boolean_t holds = flags->holds && !flags->skipholds; + if (stream_avl != NULL) { char *keylocation = NULL; nvlist_t *lookup = NULL; @@ -3716,11 +3754,22 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, if (flags->canmountoff) { VERIFY(0 == nvlist_add_uint64(rcvprops, zfs_prop_to_name(ZFS_PROP_CANMOUNT), 0)); + } else if (newprops) { /* nothing in rcvprops, eliminate it */ + nvlist_free(rcvprops); + rcvprops = NULL; + newprops = B_FALSE; } if (0 == nvlist_lookup_nvlist(fs, "snapprops", &lookup)) { VERIFY(0 == nvlist_lookup_nvlist(lookup, snapname, &snapprops_nvlist)); } + if (holds) { + if (0 == nvlist_lookup_nvlist(fs, "snapholds", + &lookup)) { + VERIFY(0 == nvlist_lookup_nvlist(lookup, + snapname, &snapholds_nvlist)); + } + } } cp = NULL; @@ -4204,6 +4253,22 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, zcmd_free_nvlists(&zc); } } + if (err == 0 && snapholds_nvlist) { + nvpair_t *pair; + nvlist_t *holds, *errors = NULL; + int cleanup_fd = -1; + + VERIFY(0 == nvlist_alloc(&holds, 0, KM_SLEEP)); + for (pair = nvlist_next_nvpair(snapholds_nvlist, NULL); + pair != NULL; + pair = nvlist_next_nvpair(snapholds_nvlist, pair)) { + VERIFY(0 == nvlist_add_string(holds, destsnap, + nvpair_name(pair))); + } + (void) lzc_hold(holds, cleanup_fd, &errors); + nvlist_free(snapholds_nvlist); + nvlist_free(holds); + } if (err && (ioctl_errno == ENOENT || ioctl_errno == EEXIST)) { /* @@ -4222,7 +4287,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, &local_nv, &local_avl) == 0) { + 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); @@ -4544,6 +4610,12 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, return (zfs_error(hdl, EZFS_BADSTREAM, errbuf)); } + /* Holds feature is set once in the compound stream header. */ + boolean_t holds = (DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo) & + DMU_BACKUP_FEATURE_HOLDS); + if (holds) + flags->holds = B_TRUE; + if (strchr(drrb->drr_toname, '@') == NULL) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid " "stream (bad snapshot name)")); |