mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-12-26 11:19:32 +03:00
Want 'zfs send -b'
This change implements 'zfs send -b' which can be used to send only received property values whether or not they are overridden by local settings. This can be very useful during "restore" operations from a backup pool because it allows to send only the property values originally sent from the backup source, even though they were later modified on the destination either by a 'zfs set' operation, explicit 'zfs inherit' or overridden during the receive process via 'zfs receive -o|-x'. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: loli10K <ezomori.nozomu@gmail.com> Closes #7156
This commit is contained in:
parent
b0918402dc
commit
faa97c1619
@ -288,9 +288,9 @@ get_usage(zfs_help_t idx)
|
|||||||
case HELP_ROLLBACK:
|
case HELP_ROLLBACK:
|
||||||
return (gettext("\trollback [-rRf] <snapshot>\n"));
|
return (gettext("\trollback [-rRf] <snapshot>\n"));
|
||||||
case HELP_SEND:
|
case HELP_SEND:
|
||||||
return (gettext("\tsend [-DnPpRvLecr] [-[i|I] snapshot] "
|
return (gettext("\tsend [-DnPpRvLecwb] [-[i|I] snapshot] "
|
||||||
"<snapshot>\n"
|
"<snapshot>\n"
|
||||||
"\tsend [-Lecr] [-i snapshot|bookmark] "
|
"\tsend [-nvPLecw] [-i snapshot|bookmark] "
|
||||||
"<filesystem|volume|snapshot>\n"
|
"<filesystem|volume|snapshot>\n"
|
||||||
"\tsend [-nvPe] -t <receive_resume_token>\n"));
|
"\tsend [-nvPe] -t <receive_resume_token>\n"));
|
||||||
case HELP_SET:
|
case HELP_SET:
|
||||||
@ -3944,11 +3944,12 @@ zfs_do_send(int argc, char **argv)
|
|||||||
{"resume", required_argument, NULL, 't'},
|
{"resume", required_argument, NULL, 't'},
|
||||||
{"compressed", no_argument, NULL, 'c'},
|
{"compressed", no_argument, NULL, 'c'},
|
||||||
{"raw", no_argument, NULL, 'w'},
|
{"raw", no_argument, NULL, 'w'},
|
||||||
|
{"backup", no_argument, NULL, 'b'},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* check options */
|
/* check options */
|
||||||
while ((c = getopt_long(argc, argv, ":i:I:RDpvnPLet:cw", long_options,
|
while ((c = getopt_long(argc, argv, ":i:I:RDpvnPLet:cwb", long_options,
|
||||||
NULL)) != -1) {
|
NULL)) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'i':
|
case 'i':
|
||||||
@ -3968,6 +3969,9 @@ zfs_do_send(int argc, char **argv)
|
|||||||
case 'p':
|
case 'p':
|
||||||
flags.props = B_TRUE;
|
flags.props = B_TRUE;
|
||||||
break;
|
break;
|
||||||
|
case 'b':
|
||||||
|
flags.backup = B_TRUE;
|
||||||
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
flags.parsable = B_TRUE;
|
flags.parsable = B_TRUE;
|
||||||
flags.verbose = B_TRUE;
|
flags.verbose = B_TRUE;
|
||||||
@ -4048,7 +4052,7 @@ zfs_do_send(int argc, char **argv)
|
|||||||
|
|
||||||
if (resume_token != NULL) {
|
if (resume_token != NULL) {
|
||||||
if (fromname != NULL || flags.replicate || flags.props ||
|
if (fromname != NULL || flags.replicate || flags.props ||
|
||||||
flags.dedup) {
|
flags.backup || flags.dedup) {
|
||||||
(void) fprintf(stderr,
|
(void) fprintf(stderr,
|
||||||
gettext("invalid flags combined with -t\n"));
|
gettext("invalid flags combined with -t\n"));
|
||||||
usage(B_FALSE);
|
usage(B_FALSE);
|
||||||
@ -4090,7 +4094,8 @@ zfs_do_send(int argc, char **argv)
|
|||||||
char frombuf[ZFS_MAX_DATASET_NAME_LEN];
|
char frombuf[ZFS_MAX_DATASET_NAME_LEN];
|
||||||
|
|
||||||
if (flags.replicate || flags.doall || flags.props ||
|
if (flags.replicate || flags.doall || flags.props ||
|
||||||
flags.dedup || (strchr(argv[0], '@') == NULL &&
|
flags.backup || flags.dedup ||
|
||||||
|
(strchr(argv[0], '@') == NULL &&
|
||||||
(flags.dryrun || flags.verbose || flags.progress))) {
|
(flags.dryrun || flags.verbose || flags.progress))) {
|
||||||
(void) fprintf(stderr, gettext("Error: "
|
(void) fprintf(stderr, gettext("Error: "
|
||||||
"Unsupported flag with filesystem or bookmark.\n"));
|
"Unsupported flag with filesystem or bookmark.\n"));
|
||||||
|
@ -673,6 +673,9 @@ typedef struct sendflags {
|
|||||||
|
|
||||||
/* raw encrypted records are permitted */
|
/* raw encrypted records are permitted */
|
||||||
boolean_t raw;
|
boolean_t raw;
|
||||||
|
|
||||||
|
/* only send received properties (ie. -b) */
|
||||||
|
boolean_t backup;
|
||||||
} sendflags_t;
|
} sendflags_t;
|
||||||
|
|
||||||
typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *);
|
typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *);
|
||||||
|
@ -621,6 +621,7 @@ typedef struct send_data {
|
|||||||
const char *fromsnap;
|
const char *fromsnap;
|
||||||
const char *tosnap;
|
const char *tosnap;
|
||||||
boolean_t raw;
|
boolean_t raw;
|
||||||
|
boolean_t backup;
|
||||||
boolean_t recursive;
|
boolean_t recursive;
|
||||||
boolean_t verbose;
|
boolean_t verbose;
|
||||||
boolean_t seenfrom;
|
boolean_t seenfrom;
|
||||||
@ -651,7 +652,8 @@ typedef struct send_data {
|
|||||||
*/
|
*/
|
||||||
} send_data_t;
|
} send_data_t;
|
||||||
|
|
||||||
static void send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv);
|
static void
|
||||||
|
send_iterate_prop(zfs_handle_t *zhp, boolean_t received_only, nvlist_t *nv);
|
||||||
|
|
||||||
static int
|
static int
|
||||||
send_iterate_snap(zfs_handle_t *zhp, void *arg)
|
send_iterate_snap(zfs_handle_t *zhp, void *arg)
|
||||||
@ -706,7 +708,7 @@ send_iterate_snap(zfs_handle_t *zhp, void *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0));
|
VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0));
|
||||||
send_iterate_prop(zhp, nv);
|
send_iterate_prop(zhp, sd->backup, nv);
|
||||||
VERIFY(0 == nvlist_add_nvlist(sd->snapprops, snapname, nv));
|
VERIFY(0 == nvlist_add_nvlist(sd->snapprops, snapname, nv));
|
||||||
nvlist_free(nv);
|
nvlist_free(nv);
|
||||||
|
|
||||||
@ -715,11 +717,17 @@ send_iterate_snap(zfs_handle_t *zhp, void *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv)
|
send_iterate_prop(zfs_handle_t *zhp, boolean_t received_only, nvlist_t *nv)
|
||||||
{
|
{
|
||||||
|
nvlist_t *props = NULL;
|
||||||
nvpair_t *elem = NULL;
|
nvpair_t *elem = NULL;
|
||||||
|
|
||||||
while ((elem = nvlist_next_nvpair(zhp->zfs_props, elem)) != NULL) {
|
if (received_only)
|
||||||
|
props = zfs_get_recvd_props(zhp);
|
||||||
|
else
|
||||||
|
props = zhp->zfs_props;
|
||||||
|
|
||||||
|
while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
|
||||||
char *propname = nvpair_name(elem);
|
char *propname = nvpair_name(elem);
|
||||||
zfs_prop_t prop = zfs_name_to_prop(propname);
|
zfs_prop_t prop = zfs_name_to_prop(propname);
|
||||||
nvlist_t *propnv;
|
nvlist_t *propnv;
|
||||||
@ -885,7 +893,7 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg)
|
|||||||
|
|
||||||
/* iterate over props */
|
/* iterate over props */
|
||||||
VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0));
|
VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0));
|
||||||
send_iterate_prop(zhp, nv);
|
send_iterate_prop(zhp, sd->backup, nv);
|
||||||
|
|
||||||
if (zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) != ZIO_CRYPT_OFF) {
|
if (zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) != ZIO_CRYPT_OFF) {
|
||||||
boolean_t encroot;
|
boolean_t encroot;
|
||||||
@ -951,7 +959,7 @@ out:
|
|||||||
static int
|
static int
|
||||||
gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
|
gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
|
||||||
const char *tosnap, boolean_t recursive, boolean_t raw, boolean_t verbose,
|
const char *tosnap, boolean_t recursive, boolean_t raw, boolean_t verbose,
|
||||||
nvlist_t **nvlp, avl_tree_t **avlp)
|
boolean_t backup, nvlist_t **nvlp, avl_tree_t **avlp)
|
||||||
{
|
{
|
||||||
zfs_handle_t *zhp;
|
zfs_handle_t *zhp;
|
||||||
send_data_t sd = { 0 };
|
send_data_t sd = { 0 };
|
||||||
@ -968,6 +976,7 @@ gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
|
|||||||
sd.recursive = recursive;
|
sd.recursive = recursive;
|
||||||
sd.raw = raw;
|
sd.raw = raw;
|
||||||
sd.verbose = verbose;
|
sd.verbose = verbose;
|
||||||
|
sd.backup = backup;
|
||||||
|
|
||||||
if ((error = send_iterate_fs(zhp, &sd)) != 0) {
|
if ((error = send_iterate_fs(zhp, &sd)) != 0) {
|
||||||
nvlist_free(sd.fss);
|
nvlist_free(sd.fss);
|
||||||
@ -1881,7 +1890,7 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags->replicate || flags->doall || flags->props) {
|
if (flags->replicate || flags->doall || flags->props || flags->backup) {
|
||||||
dmu_replay_record_t drr = { 0 };
|
dmu_replay_record_t drr = { 0 };
|
||||||
char *packbuf = NULL;
|
char *packbuf = NULL;
|
||||||
size_t buflen = 0;
|
size_t buflen = 0;
|
||||||
@ -1889,7 +1898,7 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
|
|||||||
|
|
||||||
ZIO_SET_CHECKSUM(&zc, 0, 0, 0, 0);
|
ZIO_SET_CHECKSUM(&zc, 0, 0, 0, 0);
|
||||||
|
|
||||||
if (flags->replicate || flags->props) {
|
if (flags->replicate || flags->props || flags->backup) {
|
||||||
nvlist_t *hdrnv;
|
nvlist_t *hdrnv;
|
||||||
|
|
||||||
VERIFY(0 == nvlist_alloc(&hdrnv, NV_UNIQUE_NAME, 0));
|
VERIFY(0 == nvlist_alloc(&hdrnv, NV_UNIQUE_NAME, 0));
|
||||||
@ -1908,7 +1917,7 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
|
|||||||
|
|
||||||
err = gather_nvlist(zhp->zfs_hdl, zhp->zfs_name,
|
err = gather_nvlist(zhp->zfs_hdl, zhp->zfs_name,
|
||||||
fromsnap, tosnap, flags->replicate, flags->raw,
|
fromsnap, tosnap, flags->replicate, flags->raw,
|
||||||
flags->verbose, &fss, &fsavl);
|
flags->verbose, flags->backup, &fss, &fsavl);
|
||||||
if (err)
|
if (err)
|
||||||
goto err_out;
|
goto err_out;
|
||||||
VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss));
|
VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss));
|
||||||
@ -2078,7 +2087,7 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!flags->dryrun && (flags->replicate || flags->doall ||
|
if (!flags->dryrun && (flags->replicate || flags->doall ||
|
||||||
flags->props)) {
|
flags->props || flags->backup)) {
|
||||||
/*
|
/*
|
||||||
* write final end record. NB: want to do this even if
|
* write final end record. NB: want to do this even if
|
||||||
* there was some error, because it might not be totally
|
* there was some error, because it might not be totally
|
||||||
@ -2816,7 +2825,7 @@ again:
|
|||||||
VERIFY(0 == nvlist_alloc(&deleted, NV_UNIQUE_NAME, 0));
|
VERIFY(0 == nvlist_alloc(&deleted, NV_UNIQUE_NAME, 0));
|
||||||
|
|
||||||
if ((error = gather_nvlist(hdl, tofs, fromsnap, NULL,
|
if ((error = gather_nvlist(hdl, tofs, fromsnap, NULL,
|
||||||
recursive, B_TRUE, B_FALSE, &local_nv, &local_avl)) != 0)
|
recursive, B_TRUE, B_FALSE, B_FALSE, &local_nv, &local_avl)) != 0)
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -4121,7 +4130,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
|||||||
*/
|
*/
|
||||||
*cp = '\0';
|
*cp = '\0';
|
||||||
if (gather_nvlist(hdl, destsnap, NULL, NULL, B_FALSE, B_TRUE,
|
if (gather_nvlist(hdl, destsnap, NULL, NULL, B_FALSE, B_TRUE,
|
||||||
B_FALSE, &local_nv, &local_avl) == 0) {
|
B_FALSE, B_FALSE, &local_nv, &local_avl) == 0) {
|
||||||
*cp = '@';
|
*cp = '@';
|
||||||
fs = fsavl_find(local_avl, drrb->drr_toguid, NULL);
|
fs = fsavl_find(local_avl, drrb->drr_toguid, NULL);
|
||||||
fsavl_destroy(local_avl);
|
fsavl_destroy(local_avl);
|
||||||
|
@ -194,7 +194,7 @@
|
|||||||
.Ar snapshot bookmark
|
.Ar snapshot bookmark
|
||||||
.Nm
|
.Nm
|
||||||
.Cm send
|
.Cm send
|
||||||
.Op Fl DLPRcenpvw
|
.Op Fl DLPRbcenpvw
|
||||||
.Op Oo Fl I Ns | Ns Fl i Oc Ar snapshot
|
.Op Oo Fl I Ns | Ns Fl i Oc Ar snapshot
|
||||||
.Ar snapshot
|
.Ar snapshot
|
||||||
.Nm
|
.Nm
|
||||||
@ -3321,7 +3321,7 @@ feature.
|
|||||||
.It Xo
|
.It Xo
|
||||||
.Nm
|
.Nm
|
||||||
.Cm send
|
.Cm send
|
||||||
.Op Fl DLPRcenpvw
|
.Op Fl DLPRbcenpvw
|
||||||
.Op Oo Fl I Ns | Ns Fl i Oc Ar snapshot
|
.Op Oo Fl I Ns | Ns Fl i Oc Ar snapshot
|
||||||
.Ar snapshot
|
.Ar snapshot
|
||||||
.Xc
|
.Xc
|
||||||
@ -3417,6 +3417,14 @@ See
|
|||||||
for details on ZFS feature flags and the
|
for details on ZFS feature flags and the
|
||||||
.Sy embedded_data
|
.Sy embedded_data
|
||||||
feature.
|
feature.
|
||||||
|
.It Fl b, -backup
|
||||||
|
Sends only received property values whether or not they are overridden by local
|
||||||
|
settings, but only if the dataset has ever been received. Use this option when
|
||||||
|
you want
|
||||||
|
.Nm zfs Cm receive
|
||||||
|
to restore received properties backed up on the sent dataset and to avoid
|
||||||
|
sending local settings that may have nothing to do with the source dataset,
|
||||||
|
but only with how the data is backed up.
|
||||||
.It Fl c, -compressed
|
.It Fl c, -compressed
|
||||||
Generate a more compact stream by using compressed WRITE records for blocks
|
Generate a more compact stream by using compressed WRITE records for blocks
|
||||||
which are compressed on disk and in memory
|
which are compressed on disk and in memory
|
||||||
|
@ -223,7 +223,7 @@ tags = ['functional', 'cli_root', 'zfs_rollback']
|
|||||||
tests = ['zfs_send_001_pos', 'zfs_send_002_pos', 'zfs_send_003_pos',
|
tests = ['zfs_send_001_pos', 'zfs_send_002_pos', 'zfs_send_003_pos',
|
||||||
'zfs_send_004_neg', 'zfs_send_005_pos', 'zfs_send_006_pos',
|
'zfs_send_004_neg', 'zfs_send_005_pos', 'zfs_send_006_pos',
|
||||||
'zfs_send_007_pos', 'zfs_send_encrypted', 'zfs_send_raw',
|
'zfs_send_007_pos', 'zfs_send_encrypted', 'zfs_send_raw',
|
||||||
'zfs_send_sparse']
|
'zfs_send_sparse', 'zfs_send-b']
|
||||||
tags = ['functional', 'cli_root', 'zfs_send']
|
tags = ['functional', 'cli_root', 'zfs_send']
|
||||||
|
|
||||||
[tests/functional/cli_root/zfs_set]
|
[tests/functional/cli_root/zfs_set]
|
||||||
|
@ -45,105 +45,6 @@ function cleanup
|
|||||||
log_must zfs destroy -r -f $dest
|
log_must zfs destroy -r -f $dest
|
||||||
}
|
}
|
||||||
|
|
||||||
#
|
|
||||||
# Verify property $2 is set from source $4 on dataset $1 and has value $3.
|
|
||||||
#
|
|
||||||
# $1 checked dataset
|
|
||||||
# $2 user property
|
|
||||||
# $3 property value
|
|
||||||
# $4 source
|
|
||||||
#
|
|
||||||
function check_prop_source
|
|
||||||
{
|
|
||||||
typeset dataset="$1"
|
|
||||||
typeset prop="$2"
|
|
||||||
typeset value="$3"
|
|
||||||
typeset source="$4"
|
|
||||||
typeset chk_value=$(get_prop "$prop" "$dataset")
|
|
||||||
typeset chk_source=$(get_source "$prop" "$dataset")
|
|
||||||
|
|
||||||
if [[ "$chk_value" != "$value" || "$chk_source" != "$4" ]]
|
|
||||||
then
|
|
||||||
return 1
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Verify target dataset $1 inherit property $2 from dataset $3.
|
|
||||||
#
|
|
||||||
# $1 checked dataset
|
|
||||||
# $2 property
|
|
||||||
# $3 inherited dataset
|
|
||||||
#
|
|
||||||
function check_prop_inherit
|
|
||||||
{
|
|
||||||
typeset checked_dtst="$1"
|
|
||||||
typeset prop="$2"
|
|
||||||
typeset inherited_dtst="$3"
|
|
||||||
typeset inherited_value=$(get_prop "$prop" "$inherited_dtst")
|
|
||||||
typeset value=$(get_prop "$prop" "$checked_dtst")
|
|
||||||
typeset source=$(get_source "$prop" "$checked_dtst")
|
|
||||||
|
|
||||||
if [[ "$value" != "$inherited_value" || \
|
|
||||||
"$source" != "inherited from $inherited_dtst" ]]
|
|
||||||
then
|
|
||||||
return 1
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Verify property $2 received value on dataset $1 has value $3
|
|
||||||
#
|
|
||||||
# $1 checked dataset
|
|
||||||
# $2 property name
|
|
||||||
# $3 checked value
|
|
||||||
#
|
|
||||||
function check_prop_received
|
|
||||||
{
|
|
||||||
typeset dataset="$1"
|
|
||||||
typeset prop="$2"
|
|
||||||
typeset value="$3"
|
|
||||||
|
|
||||||
received=$(zfs get -H -o received "$prop" "$dataset")
|
|
||||||
if (($? != 0)); then
|
|
||||||
log_fail "Unable to get $prop received value for dataset " \
|
|
||||||
"$dataset"
|
|
||||||
fi
|
|
||||||
if [[ "$received" == "$value" ]]
|
|
||||||
then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Verify user property $2 is not set on dataset $1
|
|
||||||
#
|
|
||||||
# $1 checked dataset
|
|
||||||
# $2 property name
|
|
||||||
#
|
|
||||||
function check_prop_missing
|
|
||||||
{
|
|
||||||
typeset dataset="$1"
|
|
||||||
typeset prop="$2"
|
|
||||||
|
|
||||||
value=$(zfs get -H -o value "$prop" "$dataset")
|
|
||||||
if (($? != 0)); then
|
|
||||||
log_fail "Unable to get $prop value for dataset $dataset"
|
|
||||||
fi
|
|
||||||
if [[ "-" == "$value" ]]
|
|
||||||
then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
log_assert "ZFS receive property override and exclude options work as expected."
|
log_assert "ZFS receive property override and exclude options work as expected."
|
||||||
log_onexit cleanup
|
log_onexit cleanup
|
||||||
|
|
||||||
|
@ -12,4 +12,5 @@ dist_pkgdata_SCRIPTS = \
|
|||||||
zfs_send_007_pos.ksh \
|
zfs_send_007_pos.ksh \
|
||||||
zfs_send_encrypted.ksh \
|
zfs_send_encrypted.ksh \
|
||||||
zfs_send_raw.ksh \
|
zfs_send_raw.ksh \
|
||||||
zfs_send_sparse.ksh
|
zfs_send_sparse.ksh \
|
||||||
|
zfs_send-b.ksh
|
||||||
|
103
tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send-b.ksh
Executable file
103
tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send-b.ksh
Executable file
@ -0,0 +1,103 @@
|
|||||||
|
#!/bin/ksh -p
|
||||||
|
#
|
||||||
|
# This file and its contents are supplied under the terms of the
|
||||||
|
# Common Development and Distribution License ("CDDL"), version 1.0.
|
||||||
|
# You may only use this file in accordance with the terms of version
|
||||||
|
# 1.0 of the CDDL.
|
||||||
|
#
|
||||||
|
# A full copy of the text of the CDDL should have accompanied this
|
||||||
|
# source. A copy of the CDDL is also available via the Internet at
|
||||||
|
# http://www.illumos.org/license/CDDL.
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2018, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
. $STF_SUITE/include/libtest.shlib
|
||||||
|
. $STF_SUITE/tests/functional/cli_root/zfs_set/zfs_set_common.kshlib
|
||||||
|
|
||||||
|
#
|
||||||
|
# DESCRIPTION:
|
||||||
|
# 'zfs send -b' should works as expected.
|
||||||
|
#
|
||||||
|
# STRATEGY:
|
||||||
|
# 1. Create a source dataset and set some properties
|
||||||
|
# 2. Verify command line options interact with '-b' correctly
|
||||||
|
# 3. Send the dataset and its properties to a new "backup" destination
|
||||||
|
# 4. Set some properties on the new "backup" dataset
|
||||||
|
# 5. Restore the "backup" dataset to a new destination
|
||||||
|
# 6. Verify only original (received) properties are sent from "backup"
|
||||||
|
#
|
||||||
|
|
||||||
|
verify_runnable "both"
|
||||||
|
|
||||||
|
function cleanup
|
||||||
|
{
|
||||||
|
for ds in "$SENDFS" "$BACKUP" "$RESTORE"; do
|
||||||
|
datasetexists $ds && log_must zfs destroy -r $ds
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
log_assert "'zfs send -b' should work as expected."
|
||||||
|
log_onexit cleanup
|
||||||
|
|
||||||
|
SENDFS="$TESTPOOL/sendfs"
|
||||||
|
BACKUP="$TESTPOOL/backup"
|
||||||
|
RESTORE="$TESTPOOL/restore"
|
||||||
|
|
||||||
|
# 1. Create a source dataset and set some properties
|
||||||
|
log_must zfs create $SENDFS
|
||||||
|
log_must zfs snapshot "$SENDFS@s1"
|
||||||
|
log_must zfs bookmark "$SENDFS@s1" "$SENDFS#bm"
|
||||||
|
log_must zfs snapshot "$SENDFS@s2"
|
||||||
|
log_must zfs set "compression=gzip" $SENDFS
|
||||||
|
log_must zfs set "org.zfsonlinux:prop=val" $SENDFS
|
||||||
|
log_must zfs set "org.zfsonlinux:snapprop=val" "$SENDFS@s1"
|
||||||
|
|
||||||
|
# 2. Verify command line options interact with '-b' correctly
|
||||||
|
typeset opts=("" "p" "Rp" "cew" "nv" "D" "DLPRcenpvw")
|
||||||
|
for opt in ${opts[@]}; do
|
||||||
|
log_must eval "zfs send -b$opt $SENDFS@s1 > /dev/null"
|
||||||
|
log_must eval "zfs send -b$opt -i $SENDFS@s1 $SENDFS@s2 > /dev/null"
|
||||||
|
log_must eval "zfs send -b$opt -I $SENDFS@s1 $SENDFS@s2 > /dev/null"
|
||||||
|
done
|
||||||
|
for opt in ${opts[@]}; do
|
||||||
|
log_mustnot eval "zfs send -b$opt $SENDFS > /dev/null"
|
||||||
|
log_mustnot eval "zfs send -b$opt $SENDFS#bm > /dev/null"
|
||||||
|
log_mustnot eval "zfs send -b$opt -i $SENDFS#bm $SENDFS@s2 > /dev/null"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Do 3..6 in a loop to verify various combination of "zfs send" options
|
||||||
|
typeset opts=("" "p" "R" "pR" "cew")
|
||||||
|
for opt in ${opts[@]}; do
|
||||||
|
# 3. Send the dataset and its properties to a new "backup" destination
|
||||||
|
# NOTE: only need to send properties (-p) here
|
||||||
|
log_must eval "zfs send -p $SENDFS@s1 | zfs recv $BACKUP"
|
||||||
|
|
||||||
|
# 4. Set some properties on the new "backup" dataset
|
||||||
|
# NOTE: override "received" values and set some new properties as well
|
||||||
|
log_must zfs set "compression=lz4" $BACKUP
|
||||||
|
log_must zfs set "exec=off" $BACKUP
|
||||||
|
log_must zfs set "org.zfsonlinux:prop=newval" $BACKUP
|
||||||
|
log_must zfs set "org.zfsonlinux:newprop=newval" $BACKUP
|
||||||
|
log_must zfs set "org.zfsonlinux:snapprop=newval" "$BACKUP@s1"
|
||||||
|
log_must zfs set "org.zfsonlinux:newsnapprop=newval" "$BACKUP@s1"
|
||||||
|
|
||||||
|
# 5. Restore the "backup" dataset to a new destination
|
||||||
|
log_must eval "zfs send -b$opt $BACKUP@s1 | zfs recv $RESTORE"
|
||||||
|
|
||||||
|
# 6. Verify only original (received) properties are sent from "backup"
|
||||||
|
log_must eval "check_prop_source $RESTORE compression gzip received"
|
||||||
|
log_must eval "check_prop_source $RESTORE org.zfsonlinux:prop val received"
|
||||||
|
log_must eval "check_prop_source $RESTORE@s1 org.zfsonlinux:snapprop val received"
|
||||||
|
log_must eval "check_prop_source $RESTORE exec on default"
|
||||||
|
log_must eval "check_prop_missing $RESTORE org.zfsonlinux:newprop"
|
||||||
|
log_must eval "check_prop_missing $RESTORE@s1 org.zfsonlinux:newsnapprop"
|
||||||
|
|
||||||
|
# cleanup
|
||||||
|
log_must zfs destroy -r $BACKUP
|
||||||
|
log_must zfs destroy -r $RESTORE
|
||||||
|
done
|
||||||
|
|
||||||
|
log_pass "'zfs send -b' works as expected."
|
@ -267,3 +267,110 @@ function get_source
|
|||||||
|
|
||||||
echo "$source"
|
echo "$source"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Verify property $2 is set from source $4 on dataset $1 and has value $3.
|
||||||
|
#
|
||||||
|
# $1 checked dataset
|
||||||
|
# $2 user property
|
||||||
|
# $3 property value
|
||||||
|
# $4 source
|
||||||
|
#
|
||||||
|
# Returns: 0 if both expected source and value match, 1 otherwise
|
||||||
|
#
|
||||||
|
function check_prop_source
|
||||||
|
{
|
||||||
|
typeset dataset="$1"
|
||||||
|
typeset prop="$2"
|
||||||
|
typeset value="$3"
|
||||||
|
typeset source="$4"
|
||||||
|
typeset chk_value=$(get_prop "$prop" "$dataset")
|
||||||
|
typeset chk_source=$(get_source "$prop" "$dataset")
|
||||||
|
|
||||||
|
if [[ "$chk_value" != "$value" || "$chk_source" != "$4" ]]
|
||||||
|
then
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Verify target dataset $1 inherit property $2 from dataset $3.
|
||||||
|
#
|
||||||
|
# $1 checked dataset
|
||||||
|
# $2 property
|
||||||
|
# $3 inherited dataset
|
||||||
|
#
|
||||||
|
# Returns: 0 if property has expected value and is inherited, 1 otherwise
|
||||||
|
#
|
||||||
|
function check_prop_inherit
|
||||||
|
{
|
||||||
|
typeset checked_dtst="$1"
|
||||||
|
typeset prop="$2"
|
||||||
|
typeset inherited_dtst="$3"
|
||||||
|
typeset inherited_value=$(get_prop "$prop" "$inherited_dtst")
|
||||||
|
typeset value=$(get_prop "$prop" "$checked_dtst")
|
||||||
|
typeset source=$(get_source "$prop" "$checked_dtst")
|
||||||
|
|
||||||
|
if [[ "$value" != "$inherited_value" || \
|
||||||
|
"$source" != "inherited from $inherited_dtst" ]]
|
||||||
|
then
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Verify property $2 received value on dataset $1 has value $3
|
||||||
|
#
|
||||||
|
# $1 checked dataset
|
||||||
|
# $2 property name
|
||||||
|
# $3 checked value
|
||||||
|
#
|
||||||
|
# Returns: 0 if property has expected value and is received, 1 otherwise
|
||||||
|
#
|
||||||
|
function check_prop_received
|
||||||
|
{
|
||||||
|
typeset dataset="$1"
|
||||||
|
typeset prop="$2"
|
||||||
|
typeset value="$3"
|
||||||
|
|
||||||
|
received=$(zfs get -H -o received "$prop" "$dataset")
|
||||||
|
if (($? != 0)); then
|
||||||
|
log_fail "Unable to get $prop received value for dataset " \
|
||||||
|
"$dataset"
|
||||||
|
fi
|
||||||
|
if [[ "$received" == "$value" ]]
|
||||||
|
then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Verify user property $2 is not set on dataset $1
|
||||||
|
#
|
||||||
|
# $1 checked dataset
|
||||||
|
# $2 property name
|
||||||
|
#
|
||||||
|
# Returns: 0 if property is missing (not set), 1 otherwise
|
||||||
|
#
|
||||||
|
function check_prop_missing
|
||||||
|
{
|
||||||
|
typeset dataset="$1"
|
||||||
|
typeset prop="$2"
|
||||||
|
|
||||||
|
value=$(zfs get -H -o value "$prop" "$dataset")
|
||||||
|
if (($? != 0)); then
|
||||||
|
log_fail "Unable to get $prop value for dataset $dataset"
|
||||||
|
fi
|
||||||
|
if [[ "-" == "$value" ]]
|
||||||
|
then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user