Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

man: fix ellipses, -X in zfs-send.8 part 2 #13352

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 33 additions & 69 deletions cmd/zfs/zfs_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -315,9 +315,9 @@ get_usage(zfs_help_t idx)
case HELP_ROLLBACK:
return (gettext("\trollback [-rRf] <snapshot>\n"));
case HELP_SEND:
return (gettext("\tsend [-DnPpRvLecwhb] "
"[-X dataset[,dataset]...] "
"[-[i|I] snapshot] <snapshot>\n"
return (gettext("\tsend [-DLPbcehnpsvw] "
"[-i|-I snapshot]\n"
"\t [-R [-X dataset[,dataset]...]] <snapshot>\n"
"\tsend [-DnvPLecw] [-i snapshot|bookmark] "
"<filesystem|volume|snapshot>\n"
"\tsend [-DnPpvLec] [-i bookmark|snapshot] "
Expand Down Expand Up @@ -4315,73 +4315,27 @@ zfs_do_snapshot(int argc, char **argv)
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 {
size_t count;
char **list;
const char **list;
} 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
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);

for (size_t indx = 0; indx < exclude->count; indx++) {
char *exclude_name = exclude->list[indx];
size_t len = strlen(exclude_name);
/* If it's shorter, it can't possibly match */
if (strlen(name) < len)
continue;
if (strncmp(name, exclude_name, len) == 0 &&
(name[len] == '/' || name[len] == '\0' ||
name[len] == '@')) {
for (size_t i = 0; i < excludes->count; ++i) {
size_t len = strlen(excludes->list[i]);
if (strncmp(name, excludes->list[i], len) == 0 &&
memchr("/@", name[len], sizeof ("/@")))
return (B_FALSE);
}
}

return (B_TRUE);
Expand All @@ -4402,11 +4356,11 @@ zfs_do_send(int argc, char **argv)
int c, err;
nvlist_t *dbgnv = NULL;
char *redactbook = NULL;
zfs_send_exclude_arg_t exclude_context = { 0 };
zfs_send_exclude_arg_t excludes = { 0 };

struct option long_options[] = {
{"replicate", no_argument, NULL, 'R'},
{"skip-missing", no_argument, NULL, 's'},
{"skip-missing", no_argument, NULL, 's'},
{"redact", required_argument, NULL, 'd'},
{"props", no_argument, NULL, 'p'},
{"parsable", no_argument, NULL, 'P'},
Expand All @@ -4430,7 +4384,18 @@ zfs_do_send(int argc, char **argv)
long_options, NULL)) != -1) {
switch (c) {
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;
case 'i':
if (fromname)
Expand Down Expand Up @@ -4541,7 +4506,7 @@ zfs_do_send(int argc, char **argv)
if (flags.parsable && flags.verbosity == 0)
flags.verbosity = 1;

if (exclude_context.count > 0 && !flags.replicate) {
if (excludes.count > 0 && !flags.replicate) {
(void) fprintf(stderr, gettext("Cannot specify "
"dataset exclusion (-X) on a non-recursive "
"send.\n"));
Expand Down Expand Up @@ -4730,10 +4695,8 @@ zfs_do_send(int argc, char **argv)
flags.doall = B_TRUE;

err = zfs_send(zhp, fromname, toname, &flags, STDOUT_FILENO,
exclude_context.count > 0 ? zfs_do_send_exclude : NULL,
&exclude_context, flags.verbosity >= 3 ? &dbgnv : NULL);

free_dataset_excludes(&exclude_context);
excludes.count > 0 ? zfs_do_send_exclude : NULL,
&excludes, flags.verbosity >= 3 ? &dbgnv : NULL);

if (flags.verbosity >= 3 && dbgnv != NULL) {
/*
Expand All @@ -4745,8 +4708,9 @@ zfs_do_send(int argc, char **argv)
dump_nvlist(dbgnv, 0);
nvlist_free(dbgnv);
}
zfs_close(zhp);

zfs_close(zhp);
free(excludes.list);
return (err != 0);
}

Expand Down
31 changes: 12 additions & 19 deletions man/man8/zfs-send.8
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@
.Sh SYNOPSIS
.Nm zfs
.Cm send
.Op Fl DLPRbcehnpsvw
.Op Fl X Ar dataset Ns Oo , Ns Ar dataset Oc Ns
.Op Fl DLPbcehnpsvw
.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
.Ar snapshot
.Nm zfs
Expand Down Expand Up @@ -73,8 +73,8 @@
.It Xo
.Nm zfs
.Cm send
.Op Fl DLPRbcehnpvw
.Op Fl X Ar dataset Ns Oo , Ns Ar dataset Oc Ns
.Op Fl DLPbcehnpsvw
.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
.Ar snapshot
.Xc
Expand Down Expand Up @@ -143,22 +143,15 @@ flag is used to send encrypted datasets, then
.Fl w
must also be specified.
.It Fl X , -exclude Ar dataset Ns Oo , Ns Ar dataset Oc Ns
When the
.Fl R
flag is given,
.Fl X
can be used to specify a list of datasets to be excluded from the
data stream.
The
With
.Fl R ,
.Fl X
option can be used multiple times, or the list of datasets can be
specified as a comma-separated list, or both.
.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 .
specifies a set of datasets (and, hence, their descendants),
to be excluded from the send stream.
The root dataset may not be excluded.
.Fl X Ar a Fl X Ar b
is equivalent to
.Fl X Ar a , Ns Ar b .
.It Fl e , -embed
Generate a more compact stream by using
.Sy WRITE_EMBEDDED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,6 @@ TEMPFILE="$TEST_BASE_DIR/zfs_001_neg.$$.txt"
zfs > $TEMPFILE 2>&1
log_must grep "usage: zfs command args" "$TEMPFILE"

log_must awk '{if (length($0) > 80) exit 1}' $TEMPFILE
log_must awk 'length($0) > 80 {print; ++err} END {exit err}' $TEMPFILE

log_pass "zfs shows a usage message when run as a user"