-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
Add recursive/limited -a
mount & unmount | introduce -A
for noauto
bypass
#15264
Changes from all commits
f476a37
a1295cd
e07de81
9246667
64400c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -307,7 +307,8 @@ get_usage(zfs_help_t idx) | |
"[filesystem|volume|snapshot] ...\n")); | ||
case HELP_MOUNT: | ||
return (gettext("\tmount\n" | ||
"\tmount [-flvO] [-o opts] <-a | filesystem>\n")); | ||
"\tmount [-flvO] [-o opts] " | ||
"<-a [filesystem] | [-A] filesystem>\n")); | ||
case HELP_PROMOTE: | ||
return (gettext("\tpromote <clone-filesystem>\n")); | ||
case HELP_RECEIVE: | ||
|
@@ -346,7 +347,7 @@ get_usage(zfs_help_t idx) | |
"<filesystem|volume>@<snap> ...\n")); | ||
case HELP_UNMOUNT: | ||
return (gettext("\tunmount [-fu] " | ||
"<-a | filesystem|mountpoint>\n")); | ||
"<-a [filesystem] | [-A] filesystem|mountpoint>\n")); | ||
case HELP_UNSHARE: | ||
return (gettext("\tunshare " | ||
"<-a [nfs|smb] | filesystem|mountpoint>\n")); | ||
|
@@ -6731,6 +6732,7 @@ zfs_do_holds(int argc, char **argv) | |
typedef struct get_all_state { | ||
boolean_t ga_verbose; | ||
get_all_cb_t *ga_cbp; | ||
const char *ga_parent; | ||
} get_all_state_t; | ||
|
||
static int | ||
|
@@ -6769,18 +6771,29 @@ get_one_dataset(zfs_handle_t *zhp, void *data) | |
zfs_close(zhp); | ||
return (0); | ||
} | ||
|
||
/* | ||
* Skip any dataset that's not related to ga_parent. | ||
*/ | ||
if (state->ga_parent != NULL && | ||
!zfs_dataset_related(zhp, state->ga_parent)) { | ||
zfs_close(zhp); | ||
return (0); | ||
} | ||
|
||
libzfs_add_handle(state->ga_cbp, zhp); | ||
assert(state->ga_cbp->cb_used <= state->ga_cbp->cb_alloc); | ||
|
||
return (0); | ||
} | ||
|
||
static void | ||
get_all_datasets(get_all_cb_t *cbp, boolean_t verbose) | ||
get_all_datasets(get_all_cb_t *cbp, boolean_t verbose, const char *parent) | ||
{ | ||
get_all_state_t state = { | ||
.ga_verbose = verbose, | ||
.ga_cbp = cbp | ||
.ga_cbp = cbp, | ||
.ga_parent = parent | ||
}; | ||
|
||
if (verbose) | ||
|
@@ -6808,14 +6821,15 @@ typedef struct share_mount_state { | |
uint_t sm_total; /* number of filesystems to process */ | ||
uint_t sm_done; /* number of filesystems processed */ | ||
int sm_status; /* -1 if any of the share/mount operations failed */ | ||
boolean_t sm_mount_noauto; /* treat 'canmount=noauto' as 'on' */ | ||
} share_mount_state_t; | ||
|
||
/* | ||
* Share or mount a dataset. | ||
*/ | ||
static int | ||
share_mount_one(zfs_handle_t *zhp, int op, int flags, enum sa_protocol protocol, | ||
boolean_t explicit, const char *options) | ||
boolean_t explicit, const char *options, boolean_t mount_noauto) | ||
{ | ||
char mountpoint[ZFS_MAXPROPLEN]; | ||
char shareopts[ZFS_MAXPROPLEN]; | ||
|
@@ -6905,13 +6919,14 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, enum sa_protocol protocol, | |
} | ||
|
||
/* | ||
* canmount explicit outcome | ||
* on no pass through | ||
* on yes pass through | ||
* off no return 0 | ||
* off yes display error, return 1 | ||
* noauto no return 0 | ||
* noauto yes pass through | ||
* canmount explicit mount_noauto outcome | ||
* on no n/a pass through | ||
* on yes n/a pass through | ||
* off no n/a return 0 | ||
* off yes n/a display error, return 1 | ||
* noauto no no return 0 | ||
* noauto no yes pass through | ||
* noauto yes yes/no pass through | ||
*/ | ||
canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT); | ||
if (canmount == ZFS_CANMOUNT_OFF) { | ||
|
@@ -6922,11 +6937,13 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, enum sa_protocol protocol, | |
"'canmount' property is set to 'off'\n"), cmdname, | ||
zfs_get_name(zhp)); | ||
return (1); | ||
} else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) { | ||
} else if (canmount == ZFS_CANMOUNT_NOAUTO && | ||
!explicit && !mount_noauto) { | ||
/* | ||
* When performing a 'zfs mount -a', we skip any mounts for | ||
* datasets that have 'noauto' set. Sharing a dataset with | ||
* 'noauto' set is only allowed if it's mounted. | ||
* datasets that have 'noauto' set. However, 'zfs mount -A' | ||
* will mount datasets with 'noauto' set. Sharing a dataset | ||
* with 'noauto' set is only allowed if it's mounted. | ||
*/ | ||
if (op == OP_MOUNT) | ||
return (0); | ||
|
@@ -7082,7 +7099,7 @@ share_mount_one_cb(zfs_handle_t *zhp, void *arg) | |
int ret; | ||
|
||
ret = share_mount_one(zhp, sms->sm_op, sms->sm_flags, sms->sm_proto, | ||
B_FALSE, sms->sm_options); | ||
B_FALSE, sms->sm_options, sms->sm_mount_noauto); | ||
|
||
pthread_mutex_lock(&sms->sm_lock); | ||
if (ret != 0) | ||
|
@@ -7132,18 +7149,22 @@ sa_protocol_decode(const char *protocol) | |
static int | ||
share_mount(int op, int argc, char **argv) | ||
{ | ||
int do_all = 0; | ||
boolean_t do_all = B_FALSE; | ||
boolean_t do_noauto = B_FALSE; | ||
boolean_t verbose = B_FALSE; | ||
int c, ret = 0; | ||
char *options = NULL; | ||
int flags = 0; | ||
|
||
/* check options */ | ||
while ((c = getopt(argc, argv, op == OP_MOUNT ? ":alvo:Of" : "al")) | ||
while ((c = getopt(argc, argv, op == OP_MOUNT ? ":aAlvo:Of" : "al")) | ||
!= -1) { | ||
switch (c) { | ||
case 'a': | ||
do_all = 1; | ||
do_all = B_TRUE; | ||
break; | ||
case 'A': | ||
do_noauto = B_TRUE; | ||
break; | ||
case 'v': | ||
verbose = B_TRUE; | ||
|
@@ -7186,23 +7207,48 @@ share_mount(int op, int argc, char **argv) | |
argv += optind; | ||
|
||
/* check number of arguments */ | ||
if (do_all) { | ||
if (do_all || do_noauto) { | ||
enum sa_protocol protocol = SA_NO_PROTOCOL; | ||
|
||
if (op == OP_SHARE && argc > 0) { | ||
protocol = sa_protocol_decode(argv[0]); | ||
argc--; | ||
argv++; | ||
} | ||
|
||
if (argc != 0) { | ||
/* check number of arguments */ | ||
if (do_noauto && argc < 1) { | ||
(void) fprintf(stderr, gettext("missing " | ||
"dataset argument\n")); | ||
usage(B_FALSE); | ||
} | ||
if ((op == OP_SHARE && argc != 0) || argc > 1) { | ||
(void) fprintf(stderr, gettext("too many arguments\n")); | ||
usage(B_FALSE); | ||
} | ||
|
||
/* | ||
* Limit `-a filesystem` to mount only | ||
*/ | ||
const char *filesystem = NULL; | ||
if (op == OP_MOUNT) | ||
filesystem = argv[0]; | ||
|
||
/* | ||
* Validate filesystem is actually a valid zfs filesystem | ||
*/ | ||
if (filesystem != NULL) { | ||
zfs_handle_t *zhp = zfs_open(g_zfs, filesystem, | ||
ZFS_TYPE_FILESYSTEM); | ||
if (zhp == NULL) { | ||
free(options); | ||
return (1); | ||
} | ||
zfs_close(zhp); | ||
} | ||
Comment on lines
+7236
to
+7247
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method of validating If we only needed to validate The best alternative I've thought of so far is to modify To maintain compatibility, we can call this function Click here to see an example of the proposed code:/*
* Finds whether the dataset of the given type(s) exists.
*/
boolean_t
zfs_dataset_exists_verbose(libzfs_handle_t *hdl, const char *path, zfs_type_t types, boolean_t error)
{
zfs_handle_t *zhp;
char errbuf[ERRBUFLEN];
(void) snprintf(errbuf, sizeof (errbuf),
dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);
/*
* Validate the name before we even try to open it.
*/
if (!zfs_validate_name(hdl, path, types, B_FALSE)) {
if (error)
(void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
return (B_FALSE);
}
/*
* Try to get stats for the dataset, which will tell us if it exists.
*/
if ((zhp = make_dataset_handle(hdl, path)) != NULL) {
int ds_type = zhp->zfs_type;
zfs_close(zhp);
if (types & ds_type)
return (B_TRUE);
}
if (error)
(void) zfs_standard_error(hdl, errno, errbuf);
return (B_FALSE);
}
boolean_t
zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types)
{
return (zfs_dataset_exists_verbose(hdl, path, types, B_TRUE));
} _LIBZFS_H boolean_t zfs_dataset_exists_verbose(libzfs_handle_t *, const char *,
zfs_type_t, boolean_t); /* share_mount(): Validate filesystem */
if (filesystem != NULL && !zfs_dataset_exists_verbose(g_zfs,
filesystem, ZFS_TYPE_FILESYSTEM, B_TRUE)) {
free(options);
return (1);
}
/* unshare_unmount() Validate filesystem */
if (filesystem != NULL && !zfs_dataset_exists_verbose(g_zfs,
filesystem, ZFS_TYPE_FILESYSTEM, B_TRUE))
return (1); As seen above, the validation code looks cleaner and is easier to understand. However, I'm unsure about the general usefulness of the proposed |
||
|
||
start_progress_timer(); | ||
get_all_cb_t cb = { 0 }; | ||
get_all_datasets(&cb, verbose); | ||
get_all_datasets(&cb, verbose, filesystem); | ||
|
||
if (cb.cb_used == 0) { | ||
free(options); | ||
|
@@ -7216,6 +7262,7 @@ share_mount(int op, int argc, char **argv) | |
share_mount_state.sm_options = options; | ||
share_mount_state.sm_proto = protocol; | ||
share_mount_state.sm_total = cb.cb_used; | ||
share_mount_state.sm_mount_noauto = do_noauto; | ||
pthread_mutex_init(&share_mount_state.sm_lock, NULL); | ||
|
||
/* For a 'zfs share -a' operation start with a clean slate. */ | ||
|
@@ -7283,7 +7330,7 @@ share_mount(int op, int argc, char **argv) | |
ret = 1; | ||
} else { | ||
ret = share_mount_one(zhp, op, flags, SA_NO_PROTOCOL, | ||
B_TRUE, options); | ||
B_TRUE, options, B_FALSE); | ||
zfs_commit_shares(NULL); | ||
zfs_close(zhp); | ||
} | ||
|
@@ -7441,7 +7488,8 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) | |
static int | ||
unshare_unmount(int op, int argc, char **argv) | ||
{ | ||
int do_all = 0; | ||
boolean_t do_all = B_FALSE; | ||
boolean_t do_noauto = B_FALSE; | ||
int flags = 0; | ||
int ret = 0; | ||
int c; | ||
|
@@ -7450,10 +7498,13 @@ unshare_unmount(int op, int argc, char **argv) | |
char sharesmb[ZFS_MAXPROPLEN]; | ||
|
||
/* check options */ | ||
while ((c = getopt(argc, argv, op == OP_SHARE ? ":a" : "afu")) != -1) { | ||
while ((c = getopt(argc, argv, op == OP_SHARE ? ":a" : "aAfu")) != -1) { | ||
switch (c) { | ||
case 'a': | ||
do_all = 1; | ||
do_all = B_TRUE; | ||
break; | ||
case 'A': | ||
do_noauto = B_TRUE; | ||
break; | ||
case 'f': | ||
flags |= MS_FORCE; | ||
|
@@ -7476,7 +7527,7 @@ unshare_unmount(int op, int argc, char **argv) | |
argc -= optind; | ||
argv += optind; | ||
|
||
if (do_all) { | ||
if (do_all || do_noauto) { | ||
/* | ||
* We could make use of zfs_for_each() to walk all datasets in | ||
* the system, but this would be very inefficient, especially | ||
|
@@ -7508,11 +7559,31 @@ unshare_unmount(int op, int argc, char **argv) | |
argv++; | ||
} | ||
|
||
if (argc != 0) { | ||
/* check number of arguments */ | ||
if (do_noauto && argc < 1) { | ||
(void) fprintf(stderr, gettext("missing " | ||
"dataset argument\n")); | ||
usage(B_FALSE); | ||
} | ||
if ((op == OP_SHARE && argc != 0) || argc > 1) { | ||
(void) fprintf(stderr, gettext("too many arguments\n")); | ||
usage(B_FALSE); | ||
} | ||
|
||
/* | ||
* Limit `-a filesystem` to unmount only | ||
*/ | ||
const char *filesystem = NULL; | ||
if (op == OP_MOUNT) | ||
filesystem = argv[0]; | ||
|
||
/* | ||
* Validate filesystem is actually a valid zfs filesystem | ||
*/ | ||
if (filesystem != NULL && (zhp = zfs_open(g_zfs, | ||
filesystem, ZFS_TYPE_FILESYSTEM)) == NULL) | ||
return (1); | ||
|
||
if (((pool = uu_avl_pool_create("unmount_pool", | ||
sizeof (unshare_unmount_node_t), | ||
offsetof(unshare_unmount_node_t, un_avlnode), | ||
|
@@ -7574,15 +7645,25 @@ unshare_unmount(int op, int argc, char **argv) | |
NULL, NULL, 0, B_FALSE) == 0); | ||
if (strcmp(nfs_mnt_prop, "legacy") == 0) | ||
continue; | ||
/* Ignore canmount=noauto mounts */ | ||
/* | ||
* Ignore canmount=noauto mounts if | ||
* 'do_noauto' is set to false (default: false) | ||
*/ | ||
if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == | ||
ZFS_CANMOUNT_NOAUTO) | ||
ZFS_CANMOUNT_NOAUTO && !do_noauto) | ||
continue; | ||
break; | ||
default: | ||
break; | ||
} | ||
|
||
/* | ||
* Skip any dataset that's not related to filesystem. | ||
*/ | ||
if (filesystem != NULL && | ||
!zfs_dataset_related(zhp, filesystem)) | ||
continue; | ||
|
||
node = safe_malloc(sizeof (unshare_unmount_node_t)); | ||
node->un_zhp = zhp; | ||
node->un_mountp = safe_strdup(entry.mnt_mountp); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason this is limited only to
mount
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes/no, this pull request was originally for mount only, and
zfs share
has additional args[smb|nfs]
that I didn't see where it was pulled from.I thought it best to limit the PR to
zfs mount
untill/unless I look into how share works.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are no additional arguments documented for
zfs share
. The control flow is exactly the same except for the part that actually acts on each filesystem, so limiting this to mount-only seems arbitrary and causes an unnecessary split in the nature of the two recursive filesystem operations.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, I didn't see anything in the manpages, but usage differs?
Hence why I disabled it until I can investigate it properly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By the time you're consuming this argument, any
nfs
orsmb
argument has already been consumed zfszfs share
, so the syntax forzfs share
becomesassuming, of course, that you don't drop the
-r
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-r
is not currently enabled forzfs share
, I was usingop == OP_MOUNT
to restrict-a [filesystem]
.I believe this would be the correct syntax for the
-a
arg;My original reasoning for disabling
-a [filesystem]
zfs share
(something I should do when I have spare time)