man: zfs-send.8: fix -X synopses and description

Also clean up the horrendously verbose -X handling in zfs_main()

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
Closes #13352
This commit is contained in:
наб 2022-03-24 21:07:07 +01:00 committed by Brian Behlendorf
parent 7eba3891e9
commit 6625262c70
2 changed files with 45 additions and 88 deletions

View File

@ -315,9 +315,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 [-DnPpRvLecwhb] " return (gettext("\tsend [-DLPbcehnpsvw] "
"[-X dataset[,dataset]...] " "[-i|-I snapshot]\n"
"[-[i|I] snapshot] <snapshot>\n" "\t [-R [-X dataset[,dataset]...]] <snapshot>\n"
"\tsend [-DnvPLecw] [-i snapshot|bookmark] " "\tsend [-DnvPLecw] [-i snapshot|bookmark] "
"<filesystem|volume|snapshot>\n" "<filesystem|volume|snapshot>\n"
"\tsend [-DnPpvLec] [-i bookmark|snapshot] " "\tsend [-DnPpvLec] [-i bookmark|snapshot] "
@ -4315,74 +4315,28 @@ usage:
return (-1); return (-1);
} }
/*
* Array of prefixes to exclude
* a linear search, even if executed for each dataset,
* is plenty good enough.
*/
typedef struct zfs_send_exclude_arg { typedef struct zfs_send_exclude_arg {
size_t count; size_t count;
char **list; const char **list;
} zfs_send_exclude_arg_t; } zfs_send_exclude_arg_t;
/*
* This function creates the zfs_send_exclude_arg_t
* object described above; it can be called multiple
* times, and the input can be comma-separated.
* This is NOT the most efficient data layout; however,
* I couldn't think of a non-pathological case where
* it should have more than a couple dozen instances
* of excludes. If that turns out to be used in
* practice, we might want to instead use a tree.
*/
static void
add_dataset_excludes(char *exclude, zfs_send_exclude_arg_t *context)
{
char *tok;
while ((tok = strsep(&exclude, ",")) != NULL) {
if (!zfs_name_valid(tok, ZFS_TYPE_DATASET) ||
strchr(tok, '/') == NULL) {
(void) fprintf(stderr, gettext("-X %s: "
"not a valid non-root dataset name.\n"), tok);
usage(B_FALSE);
}
context->list = safe_realloc(context->list,
(sizeof (char *)) * (context->count + 1));
context->list[context->count++] = tok;
}
}
static void
free_dataset_excludes(zfs_send_exclude_arg_t *exclude_list)
{
free(exclude_list->list);
}
/*
* This is the call back used by zfs_send to
* determine if a dataset should be skipped.
* As stated above, this is not the most efficient
* data structure to use, but as long as the
* number of excluded datasets is relatively
* small (a couple of dozen or so), it won't
* have a big impact on performance on modern
* processors. Since it's excluding hierarchies,
* we'd probably want to move to a more complex
* tree structure in that case.
*/
static boolean_t static boolean_t
zfs_do_send_exclude(zfs_handle_t *zhp, void *context) zfs_do_send_exclude(zfs_handle_t *zhp, void *context)
{ {
zfs_send_exclude_arg_t *exclude = context; zfs_send_exclude_arg_t *excludes = context;
const char *name = zfs_get_name(zhp); const char *name = zfs_get_name(zhp);
for (size_t indx = 0; indx < exclude->count; indx++) { for (size_t i = 0; i < excludes->count; ++i) {
char *exclude_name = exclude->list[indx]; size_t len = strlen(excludes->list[i]);
size_t len = strlen(exclude_name); if (strncmp(name, excludes->list[i], len) == 0 &&
/* If it's shorter, it can't possibly match */ memchr("/@", name[len], sizeof ("/@")))
if (strlen(name) < len)
continue;
if (strncmp(name, exclude_name, len) == 0 &&
(name[len] == '/' || name[len] == '\0' ||
name[len] == '@')) {
return (B_FALSE); return (B_FALSE);
} }
}
return (B_TRUE); return (B_TRUE);
} }
@ -4402,7 +4356,7 @@ zfs_do_send(int argc, char **argv)
int c, err; int c, err;
nvlist_t *dbgnv = NULL; nvlist_t *dbgnv = NULL;
char *redactbook = NULL; char *redactbook = NULL;
zfs_send_exclude_arg_t exclude_context = { 0 }; zfs_send_exclude_arg_t excludes = { 0 };
struct option long_options[] = { struct option long_options[] = {
{"replicate", no_argument, NULL, 'R'}, {"replicate", no_argument, NULL, 'R'},
@ -4430,7 +4384,18 @@ zfs_do_send(int argc, char **argv)
long_options, NULL)) != -1) { long_options, NULL)) != -1) {
switch (c) { switch (c) {
case 'X': case 'X':
add_dataset_excludes(optarg, &exclude_context); for (char *ds; (ds = strsep(&optarg, ",")) != NULL; ) {
if (!zfs_name_valid(ds, ZFS_TYPE_DATASET) ||
strchr(ds, '/') == NULL) {
(void) fprintf(stderr, gettext("-X %s: "
"not a valid non-root dataset name"
".\n"), ds);
usage(B_FALSE);
}
excludes.list = safe_realloc(excludes.list,
sizeof (char *) * (excludes.count + 1));
excludes.list[excludes.count++] = ds;
}
break; break;
case 'i': case 'i':
if (fromname) if (fromname)
@ -4541,7 +4506,7 @@ zfs_do_send(int argc, char **argv)
if (flags.parsable && flags.verbosity == 0) if (flags.parsable && flags.verbosity == 0)
flags.verbosity = 1; flags.verbosity = 1;
if (exclude_context.count > 0 && !flags.replicate) { if (excludes.count > 0 && !flags.replicate) {
(void) fprintf(stderr, gettext("Cannot specify " (void) fprintf(stderr, gettext("Cannot specify "
"dataset exclusion (-X) on a non-recursive " "dataset exclusion (-X) on a non-recursive "
"send.\n")); "send.\n"));
@ -4730,10 +4695,8 @@ zfs_do_send(int argc, char **argv)
flags.doall = B_TRUE; flags.doall = B_TRUE;
err = zfs_send(zhp, fromname, toname, &flags, STDOUT_FILENO, err = zfs_send(zhp, fromname, toname, &flags, STDOUT_FILENO,
exclude_context.count > 0 ? zfs_do_send_exclude : NULL, excludes.count > 0 ? zfs_do_send_exclude : NULL,
&exclude_context, flags.verbosity >= 3 ? &dbgnv : NULL); &excludes, flags.verbosity >= 3 ? &dbgnv : NULL);
free_dataset_excludes(&exclude_context);
if (flags.verbosity >= 3 && dbgnv != NULL) { if (flags.verbosity >= 3 && dbgnv != NULL) {
/* /*
@ -4745,8 +4708,9 @@ zfs_do_send(int argc, char **argv)
dump_nvlist(dbgnv, 0); dump_nvlist(dbgnv, 0);
nvlist_free(dbgnv); nvlist_free(dbgnv);
} }
zfs_close(zhp);
zfs_close(zhp);
free(excludes.list);
return (err != 0); return (err != 0);
} }

View File

@ -39,8 +39,8 @@
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm zfs .Nm zfs
.Cm send .Cm send
.Op Fl DLPRbcehnpsvw .Op Fl DLPbcehnpsvw
.Op Fl X Ar dataset Ns Oo , Ns Ar dataset Oc Ns .Op Fl R Op Fl X Ar dataset Ns Oo , Ns Ar dataset Oc Ns
.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 zfs .Nm zfs
@ -73,8 +73,8 @@
.It Xo .It Xo
.Nm zfs .Nm zfs
.Cm send .Cm send
.Op Fl DLPRbcehnpvw .Op Fl DLPbcehnpsvw
.Op Fl X Ar dataset Ns Oo , Ns Ar dataset Oc Ns .Op Fl R Op Fl X Ar dataset Ns Oo , Ns Ar dataset Oc Ns
.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
@ -143,22 +143,15 @@ flag is used to send encrypted datasets, then
.Fl w .Fl w
must also be specified. must also be specified.
.It Fl X , -exclude Ar dataset Ns Oo , Ns Ar dataset Oc Ns .It Fl X , -exclude Ar dataset Ns Oo , Ns Ar dataset Oc Ns
When the With
.Fl R .Fl R ,
flag is given,
.Fl X .Fl X
can be used to specify a list of datasets to be excluded from the specifies a set of datasets (and, hence, their descendants),
data stream. to be excluded from the send stream.
The The root dataset may not be excluded.
.Fl X .Fl X Ar a Fl X Ar b
option can be used multiple times, or the list of datasets can be is equivalent to
specified as a comma-separated list, or both. .Fl X Ar a , Ns Ar b .
.Ar dataset
must not be the pool's root dataset, and all descendant datasets of
.Ar dataset
will be excluded from the send stream.
Requires
.Fl R .
.It Fl e , -embed .It Fl e , -embed
Generate a more compact stream by using Generate a more compact stream by using
.Sy WRITE_EMBEDDED .Sy WRITE_EMBEDDED