Skip to content

Commit

Permalink
Add property overriding (-o) and filtering (-x) to 'zfs receive'
Browse files Browse the repository at this point in the history
This allows a user to specify "-o property=value" to override and "-x property"
to exclude properties when receiving a stream. Both native and user properties
can be specified.

This is useful when using zfs send/receive for periodic backup/replication
because it allows a user to change properties such as canmount, mountpoint,
or compression without modifying the source dataset.

References:
  https://www.illumos.org/issues/2745
  https://www.illumos.org/issues/3753

Signed-off-by: loli10K <ezomori.nozomu@gmail.com>
  • Loading branch information
loli10K committed Mar 22, 2017
1 parent 64fc776 commit 3b011b5
Show file tree
Hide file tree
Showing 9 changed files with 689 additions and 82 deletions.
56 changes: 39 additions & 17 deletions cmd/zfs/zfs_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -249,10 +249,13 @@ get_usage(zfs_help_t idx)
case HELP_PROMOTE:
return (gettext("\tpromote <clone-filesystem>\n"));
case HELP_RECEIVE:
return (gettext("\treceive [-vnsFu] <filesystem|volume|"
"snapshot>\n"
"\treceive [-vnsFu] [-o origin=<snapshot>] [-d | -e] "
"<filesystem>\n"
return (gettext("\treceive [-vnsFu] [[-o property=<value>] | "
"[-x property]] ...\n"
"\t [-o origin=<snapshot>] "
"<filesystem|volume|snapshot>\n"
"\treceive [-vnsFu] [[-o property=<value>] | "
"[-x property]] ... \n"
"\t [-d | -e] [-o origin=<snapshot>] <filesystem>\n"
"\treceive -A <filesystem|volume>\n"));
case HELP_RENAME:
return (gettext("\trename [-f] <filesystem|volume|snapshot> "
Expand Down Expand Up @@ -490,7 +493,7 @@ usage(boolean_t requested)
static int
parseprop(nvlist_t *props, char *propname)
{
char *propval, *strval;
char *propval;

if ((propval = strchr(propname, '=')) == NULL) {
(void) fprintf(stderr, gettext("missing "
Expand All @@ -499,7 +502,7 @@ parseprop(nvlist_t *props, char *propname)
}
*propval = '\0';
propval++;
if (nvlist_lookup_string(props, propname, &strval) == 0) {
if (nvlist_exists(props, propname)) {
(void) fprintf(stderr, gettext("property '%s' "
"specified multiple times\n"), propname);
return (-1);
Expand All @@ -509,6 +512,28 @@ parseprop(nvlist_t *props, char *propname)
return (0);
}

/*
* Take a property name argument and add it to the given nvlist.
* Modifies the argument inplace.
*/
static int
parsepropname(nvlist_t *props, char *propname)
{
if (strchr(propname, '=') != NULL) {
(void) fprintf(stderr, gettext("invalid character "
"'=' in property argument\n"));
return (-1);
}
if (nvlist_exists(props, propname)) {
(void) fprintf(stderr, gettext("property '%s' "
"specified multiple times\n"), propname);
return (-1);
}
if (nvlist_add_boolean(props, propname) != 0)
nomem();
return (0);
}

static int
parse_depth(char *opt, int *flags)
{
Expand Down Expand Up @@ -4014,20 +4039,24 @@ zfs_do_receive(int argc, char **argv)
int c, err = 0;
recvflags_t flags = { 0 };
boolean_t abort_resumable = B_FALSE;

nvlist_t *props;
nvpair_t *nvp = NULL;

if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
nomem();

/* check options */
while ((c = getopt(argc, argv, ":o:denuvFsA")) != -1) {
while ((c = getopt(argc, argv, ":o:x:denuvFsA")) != -1) {
switch (c) {
case 'o':
if (parseprop(props, optarg) != 0) {
nvlist_free(props);
return (1);
usage(B_FALSE);
}
break;
case 'x':
if (parsepropname(props, optarg) != 0) {
nvlist_free(props);
usage(B_FALSE);
}
break;
case 'd':
Expand Down Expand Up @@ -4080,13 +4109,6 @@ zfs_do_receive(int argc, char **argv)
usage(B_FALSE);
}

while ((nvp = nvlist_next_nvpair(props, nvp))) {
if (strcmp(nvpair_name(nvp), "origin") != 0) {
(void) fprintf(stderr, gettext("invalid option"));
usage(B_FALSE);
}
}

if (abort_resumable) {
if (flags.isprefix || flags.istail || flags.dryrun ||
flags.resumable || flags.nomount) {
Expand Down
6 changes: 3 additions & 3 deletions include/libzfs_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ int lzc_receive_resumable(const char *, nvlist_t *, const char *,
boolean_t, int);
int lzc_receive_with_header(const char *, nvlist_t *, const char *, boolean_t,
boolean_t, int, const struct dmu_replay_record *);
int lzc_receive_one(const char *, nvlist_t *, const char *, boolean_t,
boolean_t, int, const struct dmu_replay_record *, int, uint64_t *,
uint64_t *, uint64_t *, nvlist_t **);
int lzc_receive_one(const char *, nvlist_t *, nvlist_t *, const char *,
boolean_t, boolean_t, int, const struct dmu_replay_record *, int,
uint64_t *, uint64_t *, uint64_t *, nvlist_t **);

boolean_t lzc_exists(const char *);

Expand Down
Loading

0 comments on commit 3b011b5

Please sign in to comment.