Skip to content

Commit

Permalink
Allow property overrides on non-compound send streams
Browse files Browse the repository at this point in the history
  • Loading branch information
loli10K committed Oct 30, 2016
1 parent ef00432 commit 93c97a1
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 88 deletions.
193 changes: 126 additions & 67 deletions lib/libzfs/libzfs_sendrecv.c
Original file line number Diff line number Diff line change
Expand Up @@ -2726,68 +2726,45 @@ recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs,
}

/*
* Update stream_avl based on override properties:
* 1. remove properties from "props" e "snapprops" nvlists if they are present
* as boolean values in override_props
* 2. add or overwrite string properties from override_props only on the
* ancestor "props" and remove them from child "props" and "snapprops" so they
* are (possibly) inherited.
* Extract from input nvlist props two separate "override" nvlists:
* 1. oprops: properties to be added to the received stream
* 2. xprops: properties to exclude from the stream
*/
static int
props_override(libzfs_handle_t *hdl, avl_tree_t *stream_avl, nvlist_t *props,
recvflags_t *flags, const char *errbuf)
parse_overrides(libzfs_handle_t *hdl, nvlist_t *props, nvlist_t *poprops,
nvlist_t *pxprops, recvflags_t *flags, const char *errbuf)
{
nvlist_t *noprops;
nvpair_t *nvp;
int ret = 0;
nvlist_t *nvl_oprops, *nvl_voprops;
zfs_handle_t *zhp = NULL; /* FIXME: do we need this? */
zpool_handle_t *zpool_hdl = NULL; /* FIXME: do we need this? */
uint64_t zoned = 0; /* FIXME: do we need this? */
const char *propname_origin = zfs_prop_to_name(ZFS_PROP_ORIGIN);
int ret = 0;

if (avl_is_empty(stream_avl) || nvlist_empty(props))
if (nvlist_empty(props))
return (0); /* No properties to override */

VERIFY(nvlist_alloc(&noprops, NV_UNIQUE_NAME, 0) == 0);
nvl_oprops = fnvlist_alloc();

/*
* first iteration: process excluded (-x) properties now and gather
* added (-o) properties to be later processed by zfs_valid_proplist().
*/
nvp = nvlist_next_nvpair(props, NULL);
while (nvp != NULL) {
nvp = NULL;
while ((nvp = nvlist_next_nvpair(props, nvp)) != NULL) {
const char *name = nvpair_name(nvp);
fsavl_node_t *fsn;

/* "origin" is processed separately, don't handle it here */
if (strcmp(name, propname_origin) == 0)
continue;

switch (nvpair_type(nvp)) {
case DATA_TYPE_BOOLEAN: /* -x property */
for (fsn = avl_first(stream_avl); fsn;
(fsn = avl_walk(stream_avl, fsn, AVL_AFTER))) {
nvlist_t *nvl = NULL;

if (0 == nvlist_lookup_nvlist(fsn->fn_nvfs,
"props", &nvl))
(void) nvlist_remove_all(nvl, name);
if (0 == nvlist_lookup_nvlist(fsn->fn_nvfs,
"snapprops", &nvl)) {
nvpair_t *pr = nvlist_next_nvpair(nvl,
NULL);
while (pr != NULL) {
nvlist_t *nv;
VERIFY(0 == nvpair_value_nvlist(
pr, &nv));
(void) nvlist_remove_all(nv,
name);
pr = nvlist_next_nvpair(nvl,
pr);
}
}
}
if (flags->verbose)
(void) printf("excluding property '%s' "
"from send stream\n", name);
fnvlist_add_nvpair(pxprops, nvp);
break;
case DATA_TYPE_STRING: /* -o property=value */
nvlist_add_nvpair(noprops, nvp);
fnvlist_add_nvpair(nvl_oprops, nvp);
break;
default:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
Expand All @@ -2797,57 +2774,123 @@ props_override(libzfs_handle_t *hdl, avl_tree_t *stream_avl, nvlist_t *props,
ret = -1;
goto error;
}
nvp = nvlist_next_nvpair(props, nvp);
}

/* convert override properties e.g. strings to native */
if ((noprops = zfs_valid_proplist(hdl, ZFS_TYPE_DATASET, noprops, zoned,
zhp, zpool_hdl, errbuf)) == NULL) {
if ((nvl_voprops = zfs_valid_proplist(hdl, ZFS_TYPE_DATASET,
nvl_oprops, zoned, zhp, zpool_hdl, errbuf)) == NULL) {
ret = -1;
goto error;
}

/* TODO: check properties that cannot be received */

/* second pass: process "-o" properties */
nvp = NULL;
while ((nvp = nvlist_next_nvpair(nvl_voprops, nvp)) != NULL) {
fnvlist_add_nvpair(poprops, nvp);
}
fnvlist_free(nvl_voprops);

error:
fnvlist_free(nvl_oprops);
return (ret);
}

/*
* Update stream_avl based on override properties:
* 1. remove properties from "props" and "snapprops" nvlists if they are present
* as boolean values in override_props
* 2. add or overwrite string properties from override_props only on the
* ancestor "props" and remove them from child "props" and "snapprops" so they
* are (possibly) inherited.
*/
static int
props_override(libzfs_handle_t *hdl, avl_tree_t *stream_avl, nvlist_t *props,
recvflags_t *flags, const char *errbuf)
{
nvlist_t *oprops, *xprops;
nvpair_t *nvp;
int ret = 0;

if (avl_is_empty(stream_avl) || nvlist_empty(props))
return (0); /* No properties to override */

oprops = fnvlist_alloc();
xprops = fnvlist_alloc();

/* parse input properties */
if ((ret = parse_overrides(hdl, props, oprops, xprops, flags,
errbuf)) != 0)
goto error;

/* first iteration: process excluded (-x) properties */
nvp = NULL;
while ((nvp = nvlist_next_nvpair(xprops, nvp)) != NULL) {
const char *name = nvpair_name(nvp);
fsavl_node_t *fsn;

for (fsn = avl_first(stream_avl); fsn;
(fsn = avl_walk(stream_avl, fsn, AVL_AFTER))) {
nvlist_t *nvl = NULL;
nvpair_t *pr = NULL;

if (0 == nvlist_lookup_nvlist(fsn->fn_nvfs, "props",
&nvl))
(void) nvlist_remove_all(nvl, name);
if (0 != nvlist_lookup_nvlist(fsn->fn_nvfs, "snapprops",
&nvl))
continue;
pr = NULL;
while ((pr = nvlist_next_nvpair(nvl, pr)) != NULL) {
nvlist_t *nv = fnvpair_value_nvlist(pr);
(void) nvlist_remove_all(nv, name);
}
}
if (flags->verbose)
(void) printf("excluding property '%s' from send "
"stream\n", name);
}

/* second iteration: process added (-o) properties */
nvp = nvlist_next_nvpair(noprops, NULL);
while (nvp != NULL) {
nvp = NULL;
while ((nvp = nvlist_next_nvpair(oprops, nvp)) != NULL) {
const char *name = nvpair_name(nvp);
fsavl_node_t *fsn;
boolean_t found = B_FALSE;

for (fsn = avl_first(stream_avl); fsn;
(fsn = avl_walk(stream_avl, fsn, AVL_AFTER))) {
nvlist_t *nvl = NULL;
uint64_t parent_fromsnap_guid;
nvpair_t *pr = NULL;
uint64_t parentid;

VERIFY(0 == nvlist_lookup_uint64(fsn->fn_nvfs,
"parentfromsnap", &parent_fromsnap_guid));
if (0 == nvlist_lookup_nvlist(fsn->fn_nvfs,
"props", &nvl)) {
parentid = fnvlist_lookup_uint64(fsn->fn_nvfs,
"parentfromsnap");
if (0 == nvlist_lookup_nvlist(fsn->fn_nvfs, "props",
&nvl)) {
if (ENOENT != nvlist_remove_all(nvl, name))
found = B_TRUE;
if (parent_fromsnap_guid == 0) /* ancestor */
nvlist_add_nvpair(nvl, nvp);
if (parentid == 0) /* ancestor */
fnvlist_add_nvpair(nvl, nvp);
}
if (0 == nvlist_lookup_nvlist(fsn->fn_nvfs,
"snapprops", &nvl)) {
nvpair_t *pr = nvlist_next_nvpair(nvl, NULL);
while (pr != NULL) {
nvlist_t *nv;
VERIFY(0 == nvpair_value_nvlist(pr,
&nv));
(void) nvlist_remove_all(nv, name);
pr = nvlist_next_nvpair(nvl, pr);
}
if (0 != nvlist_lookup_nvlist(fsn->fn_nvfs, "snapprops",
&nvl))
continue;
pr = NULL;
while ((pr = nvlist_next_nvpair(nvl, pr)) != NULL) {
nvlist_t *nv = fnvpair_value_nvlist(pr);
(void) nvlist_remove_all(nv, name);
}
}
if (flags->verbose)
(void) printf("%s property '%s' in send stream\n",
found ? "overriding" : "adding", name);
nvp = nvlist_next_nvpair(noprops, nvp);
}

error:
nvlist_free(noprops);
fnvlist_free(oprops);
fnvlist_free(xprops);
return (ret);
}

Expand Down Expand Up @@ -3208,7 +3251,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
const char *originsnap, 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, const char *finalsnap)
uint64_t *action_handlep, const char *finalsnap, nvlist_t *override_props)
{
time_t begin_time;
int ioctl_err, ioctl_errno, err;
Expand Down Expand Up @@ -3242,6 +3285,22 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
ENOENT);

if (stream_avl == NULL) {
nvlist_t *oprops, *xprops;

oprops = fnvlist_alloc();
xprops = fnvlist_alloc();
if ((err = parse_overrides(hdl, override_props, oprops, xprops,
flags, errbuf)) != 0) {
nvlist_free(oprops);
nvlist_free(xprops);
goto out;
} else {
props = oprops;
newprops = B_TRUE;
nvlist_free(xprops);
}
}
if (stream_avl != NULL) {
nvlist_t *lookup = NULL;
nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid,
Expand Down Expand Up @@ -3882,7 +3941,7 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap,
}
return (zfs_receive_one(hdl, infd, tosnap, originsnap, flags,
&drr, &drr_noswap, sendfs, stream_nv, stream_avl, top_zfs,
cleanup_fd, action_handlep, finalsnap));
cleanup_fd, action_handlep, finalsnap, override_props));
} else {
assert(DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) ==
DMU_COMPOUNDSTREAM);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
# STRATEGY:
# 1. Create a filesystem with children.
# 2. Snapshot the filesystems.
# 3. Create a full stream with properties on both the fs and snapshots.
# 4. Create also replication stream with other properties.
# 3. Create a full stream without properties.
# 4. Create also an incremental replication stream with properties.
# 5. Receive the send streams and verify we can override properties.
#

Expand Down Expand Up @@ -130,17 +130,15 @@ log_must $ZFS create $origsub
log_must $ZFS snapshot $orig@snap1
log_must $ZFS snapshot -r $orig@snap2

# 3. Create a full stream with properties on both the fs and snapshots.
log_must eval "$ZFS set '$userprop:orig1'='$userval' $orig"
log_must eval "$ZFS set '$userprop:snap1'='$userval' $orig@snap1"
log_must eval "$ZFS send -p $orig@snap1 > $streamfile_full"
# 3. Create a full stream without properties.
log_must eval "$ZFS send $orig@snap1 > $streamfile_full"

# 4. Create also replication stream with other properties.
# 4. Create also an incremental replication stream with properties.
log_must eval "$ZFS set '$userprop:orig'='$userval' $orig"
log_must eval "$ZFS set '$userprop:orig'='$userval' $origsub"
log_must eval "$ZFS set '$userprop:snap'='$userval' $orig@snap1"
log_must eval "$ZFS set '$userprop:snap'='$userval' $origsub@snap2"
log_must eval "$ZFS send -R -I $orig@snap1 $orig@snap2 > $streamfile_repl"
log_must eval "$ZFS set '$userprop:orig2'='$userval' $orig"
log_must eval "$ZFS set '$userprop:orig2'='$userval' $origsub"
log_must eval "$ZFS set '$userprop:snap2'='$userval' $orig@snap2"
log_must eval "$ZFS set '$userprop:snap2'='$userval' $origsub@snap2"

# 5. Receive the send streams and verify we can override properties.
# 5.1 Error results if the same property is specified in multiple -o or
Expand All @@ -150,28 +148,25 @@ log_mustnot eval "$ZFS recv $dest -x atime=off < $streamfile_full"
log_mustnot eval "$ZFS recv $dest -o atime=off -x atime < $streamfile_full"
log_mustnot eval "$ZFS recv $dest -o atime=off -o atime=on < $streamfile_full"
log_mustnot eval "$ZFS recv $dest -x atime -x atime < $streamfile_full"
# 5.2 Verify -o property=value and -x work on both native and user
# properties for a full send stream.

# 5.2 Verify -o property=value works on streams without properties.
log_must eval "$ZFS recv -o compression=on -o '$userprop:dest1'='$userval' "\
"-x atime -x '$userprop:orig1' -x '$userprop:snap1' "\
"$dest < $streamfile_full"
log_must eval "check_prop_source $dest compression on received"
log_must eval "check_prop_source $dest '$userprop:dest1' '$userval' received"
log_must eval "check_prop_source $dest atime on default"
log_must eval "check_prop_missing $dest '$userprop:orig1'"
log_must eval "check_prop_missing $dest@snap1 '$userprop:snap1'"

# 5.3 Verify -o property=value and -x work on both native and user
# properties for a replication send stream.
# properties for an incremental replication send stream.
log_must eval "$ZFS recv -o atime=off -o '$userprop:dest2'='$userval' "\
"-x compression -x '$userprop:orig2' -x '$userprop:snap2' "\
"-x compression -x '$userprop:orig' -x '$userprop:snap2' "\
"$dest < $streamfile_repl"
log_must eval "check_prop_source $dest atime off received"
log_must eval "check_prop_source $dest '$userprop:dest2' '$userval' received"
log_must eval "check_prop_inherit $destsub atime $dest"
log_must eval "check_prop_inherit $destsub '$userprop:dest2' $dest"
log_must eval "check_prop_inherit $destsub compression $dest"
log_must eval "check_prop_missing $dest '$userprop:orig2'"
log_must eval "check_prop_missing $destsub '$userprop:orig2'"
log_must eval "check_prop_missing $dest '$userprop:orig'"
log_must eval "check_prop_missing $destsub '$userprop:orig'"
log_must eval "check_prop_missing $dest@snap2 '$userprop:snap2'"
log_must eval "check_prop_missing $destsub@snap2 '$userprop:snap2'"

Expand Down

0 comments on commit 93c97a1

Please sign in to comment.