Add support for zfs mount -R <filesystem>

This commit adds support for mounting a dataset along with all of
it's children with '-R' flag for zfs mount. There can be scenarios
where we want to mount all datasets under one hierarchy instead of
mounting all datasets present on system with '-a' flag.

'-R' flag should work on all root and non-root datasets. Usage
information and man page has been updated for zfs mount. A test
for verifying the behavior for '-R' flag is also added.

Reviewed-by: Ameer Hamza <ahamza@ixsystems.com>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Umer Saleem <usaleem@ixsystems.com>
Closes #16015
This commit is contained in:
Umer Saleem 2024-04-12 03:10:24 +05:00 committed by Tony Hutter
parent 9a7ef02f4d
commit 8a56047135
7 changed files with 216 additions and 18 deletions

View File

@ -309,7 +309,8 @@ get_usage(zfs_help_t idx)
"[filesystem|volume|snapshot] ...\n")); "[filesystem|volume|snapshot] ...\n"));
case HELP_MOUNT: case HELP_MOUNT:
return (gettext("\tmount\n" return (gettext("\tmount\n"
"\tmount [-flvO] [-o opts] <-a | filesystem>\n")); "\tmount [-flvO] [-o opts] <-a|-R filesystem|"
"filesystem>\n"));
case HELP_PROMOTE: case HELP_PROMOTE:
return (gettext("\tpromote <clone-filesystem>\n")); return (gettext("\tpromote <clone-filesystem>\n"));
case HELP_RECEIVE: case HELP_RECEIVE:
@ -6750,6 +6751,8 @@ zfs_do_holds(int argc, char **argv)
#define MOUNT_TIME 1 /* seconds */ #define MOUNT_TIME 1 /* seconds */
typedef struct get_all_state { typedef struct get_all_state {
char **ga_datasets;
int ga_count;
boolean_t ga_verbose; boolean_t ga_verbose;
get_all_cb_t *ga_cbp; get_all_cb_t *ga_cbp;
} get_all_state_t; } get_all_state_t;
@ -6796,19 +6799,35 @@ get_one_dataset(zfs_handle_t *zhp, void *data)
return (0); return (0);
} }
static void static int
get_all_datasets(get_all_cb_t *cbp, boolean_t verbose) get_recursive_datasets(zfs_handle_t *zhp, void *data)
{ {
get_all_state_t state = { get_all_state_t *state = data;
.ga_verbose = verbose, int len = strlen(zfs_get_name(zhp));
.ga_cbp = cbp for (int i = 0; i < state->ga_count; ++i) {
}; if (strcmp(state->ga_datasets[i], zfs_get_name(zhp)) == 0)
return (get_one_dataset(zhp, data));
else if ((strncmp(state->ga_datasets[i], zfs_get_name(zhp),
len) == 0) && state->ga_datasets[i][len] == '/') {
(void) zfs_iter_filesystems_v2(zhp, 0,
get_recursive_datasets, data);
}
}
zfs_close(zhp);
return (0);
}
if (verbose) static void
get_all_datasets(get_all_state_t *state)
{
if (state->ga_verbose)
set_progress_header(gettext("Reading ZFS config")); set_progress_header(gettext("Reading ZFS config"));
(void) zfs_iter_root(g_zfs, get_one_dataset, &state); if (state->ga_datasets == NULL)
(void) zfs_iter_root(g_zfs, get_one_dataset, state);
else
(void) zfs_iter_root(g_zfs, get_recursive_datasets, state);
if (verbose) if (state->ga_verbose)
finish_progress(gettext("done.")); finish_progress(gettext("done."));
} }
@ -7154,18 +7173,22 @@ static int
share_mount(int op, int argc, char **argv) share_mount(int op, int argc, char **argv)
{ {
int do_all = 0; int do_all = 0;
int recursive = 0;
boolean_t verbose = B_FALSE; boolean_t verbose = B_FALSE;
int c, ret = 0; int c, ret = 0;
char *options = NULL; char *options = NULL;
int flags = 0; int flags = 0;
/* check options */ /* check options */
while ((c = getopt(argc, argv, op == OP_MOUNT ? ":alvo:Of" : "al")) while ((c = getopt(argc, argv, op == OP_MOUNT ? ":aRlvo:Of" : "al"))
!= -1) { != -1) {
switch (c) { switch (c) {
case 'a': case 'a':
do_all = 1; do_all = 1;
break; break;
case 'R':
recursive = 1;
break;
case 'v': case 'v':
verbose = B_TRUE; verbose = B_TRUE;
break; break;
@ -7207,7 +7230,7 @@ share_mount(int op, int argc, char **argv)
argv += optind; argv += optind;
/* check number of arguments */ /* check number of arguments */
if (do_all) { if (do_all || recursive) {
enum sa_protocol protocol = SA_NO_PROTOCOL; enum sa_protocol protocol = SA_NO_PROTOCOL;
if (op == OP_SHARE && argc > 0) { if (op == OP_SHARE && argc > 0) {
@ -7216,14 +7239,38 @@ share_mount(int op, int argc, char **argv)
argv++; argv++;
} }
if (argc != 0) { if (argc != 0 && do_all) {
(void) fprintf(stderr, gettext("too many arguments\n")); (void) fprintf(stderr, gettext("too many arguments\n"));
usage(B_FALSE); usage(B_FALSE);
} }
if (argc == 0 && recursive) {
(void) fprintf(stderr,
gettext("no dataset provided\n"));
usage(B_FALSE);
}
start_progress_timer(); start_progress_timer();
get_all_cb_t cb = { 0 }; get_all_cb_t cb = { 0 };
get_all_datasets(&cb, verbose); get_all_state_t state = { 0 };
if (argc == 0) {
state.ga_datasets = NULL;
state.ga_count = -1;
} else {
zfs_handle_t *zhp;
for (int i = 0; i < argc; i++) {
zhp = zfs_open(g_zfs, argv[i],
ZFS_TYPE_FILESYSTEM);
if (zhp == NULL)
usage(B_FALSE);
zfs_close(zhp);
}
state.ga_datasets = argv;
state.ga_count = argc;
}
state.ga_verbose = verbose;
state.ga_cbp = &cb;
get_all_datasets(&state);
if (cb.cb_used == 0) { if (cb.cb_used == 0) {
free(options); free(options);

View File

@ -43,7 +43,7 @@
.Cm mount .Cm mount
.Op Fl Oflv .Op Fl Oflv
.Op Fl o Ar options .Op Fl o Ar options
.Fl a Ns | Ns Ar filesystem .Fl a Ns | Ns Fl R Ar filesystem Ns | Ns Ar filesystem
.Nm zfs .Nm zfs
.Cm unmount .Cm unmount
.Op Fl fu .Op Fl fu
@ -61,7 +61,7 @@ Displays all ZFS file systems currently mounted.
.Cm mount .Cm mount
.Op Fl Oflv .Op Fl Oflv
.Op Fl o Ar options .Op Fl o Ar options
.Fl a Ns | Ns Ar filesystem .Fl a Ns | Ns Fl R Ar filesystem Ns | Ns Ar filesystem
.Xc .Xc
Mount ZFS filesystem on a path described by its Mount ZFS filesystem on a path described by its
.Sy mountpoint .Sy mountpoint
@ -83,6 +83,8 @@ for more information.
.It Fl a .It Fl a
Mount all available ZFS file systems. Mount all available ZFS file systems.
Invoked automatically as part of the boot process if configured. Invoked automatically as part of the boot process if configured.
.It Fl R
Mount the specified filesystems along with all their children.
.It Ar filesystem .It Ar filesystem
Mount the specified filesystem. Mount the specified filesystem.
.It Fl o Ar options .It Fl o Ar options

View File

@ -246,7 +246,7 @@ tests = ['zfs_mount_001_pos', 'zfs_mount_002_pos', 'zfs_mount_003_pos',
'zfs_mount_009_neg', 'zfs_mount_010_neg', 'zfs_mount_011_neg', 'zfs_mount_009_neg', 'zfs_mount_010_neg', 'zfs_mount_011_neg',
'zfs_mount_012_pos', 'zfs_mount_all_001_pos', 'zfs_mount_encrypted', 'zfs_mount_012_pos', 'zfs_mount_all_001_pos', 'zfs_mount_encrypted',
'zfs_mount_remount', 'zfs_mount_all_fail', 'zfs_mount_all_mountpoints', 'zfs_mount_remount', 'zfs_mount_all_fail', 'zfs_mount_all_mountpoints',
'zfs_mount_test_race'] 'zfs_mount_test_race', 'zfs_mount_recursive']
tags = ['functional', 'cli_root', 'zfs_mount'] tags = ['functional', 'cli_root', 'zfs_mount']
[tests/functional/cli_root/zfs_program] [tests/functional/cli_root/zfs_program]

View File

@ -155,7 +155,8 @@ tests = ['zfs_mount_001_pos', 'zfs_mount_002_pos', 'zfs_mount_003_pos',
'zfs_mount_004_pos', 'zfs_mount_005_pos', 'zfs_mount_007_pos', 'zfs_mount_004_pos', 'zfs_mount_005_pos', 'zfs_mount_007_pos',
'zfs_mount_009_neg', 'zfs_mount_010_neg', 'zfs_mount_011_neg', 'zfs_mount_009_neg', 'zfs_mount_010_neg', 'zfs_mount_011_neg',
'zfs_mount_012_pos', 'zfs_mount_encrypted', 'zfs_mount_remount', 'zfs_mount_012_pos', 'zfs_mount_encrypted', 'zfs_mount_remount',
'zfs_mount_all_fail', 'zfs_mount_all_mountpoints', 'zfs_mount_test_race'] 'zfs_mount_all_fail', 'zfs_mount_all_mountpoints',
'zfs_mount_test_race', 'zfs_mount_recursive']
tags = ['functional', 'cli_root', 'zfs_mount'] tags = ['functional', 'cli_root', 'zfs_mount']
[tests/functional/cli_root/zfs_program] [tests/functional/cli_root/zfs_program]

View File

@ -769,6 +769,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
functional/cli_root/zfs_mount/zfs_mount_all_fail.ksh \ functional/cli_root/zfs_mount/zfs_mount_all_fail.ksh \
functional/cli_root/zfs_mount/zfs_mount_all_mountpoints.ksh \ functional/cli_root/zfs_mount/zfs_mount_all_mountpoints.ksh \
functional/cli_root/zfs_mount/zfs_mount_encrypted.ksh \ functional/cli_root/zfs_mount/zfs_mount_encrypted.ksh \
functional/cli_root/zfs_mount/zfs_mount_recursive.ksh \
functional/cli_root/zfs_mount/zfs_mount_remount.ksh \ functional/cli_root/zfs_mount/zfs_mount_remount.ksh \
functional/cli_root/zfs_mount/zfs_mount_test_race.ksh \ functional/cli_root/zfs_mount/zfs_mount_test_race.ksh \
functional/cli_root/zfs_mount/zfs_multi_mount.ksh \ functional/cli_root/zfs_mount/zfs_multi_mount.ksh \

View File

@ -31,6 +31,7 @@
export mountcmd=mount export mountcmd=mount
export mountforce="$mountcmd -f" export mountforce="$mountcmd -f"
export mountall="$mountcmd -a" export mountall="$mountcmd -a"
export mountrecursive="$mountcmd -R"
export unmountcmd=unmount export unmountcmd=unmount
export unmountforce="$unmountcmd -f" export unmountforce="$unmountcmd -f"

View File

@ -0,0 +1,146 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or https://opensource.org/licenses/CDDL-1.0.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright 2024, iXsystems Inc. All rights reserved.
#
. $STF_SUITE/include/libtest.shlib
. $STF_SUITE/tests/functional/cli_root/zfs_mount/zfs_mount.kshlib
#
# DESCRIPTION:
# Verify zfs mount -R <filesystems/s> functionality.
#
# STRATEGY:
# 1. Create nested datasets
# 2. Unmount all datasets
# 3. Recusrively mount root datasets, this should mount all datasets
# present in a pool
# 4. Unmount all datasets
# 5. Recusrsively mount child datasets with children. This should mount
# child datasets, but not the root dataset or parent datasets
# 6. Unmount all datasets
# 7. Mount root dataset recursively again and confirm all child
# datasets are mounted.
#
verify_runnable "both"
function cleanup
{
log_must datasetexists $TESTPOOL/$TESTFS1 && \
destroy_dataset $TESTPOOL/$TESTFS1 -R
log_must datasetexists $TESTPOOL/$TESTFS2 && \
destroy_dataset $TESTPOOL/$TESTFS2 -R
log_must datasetexists $TESTPOOL/$TESTFS3 && \
destroy_dataset $TESTPOOL/$TESTFS3 -R
}
function setup_all
{
log_must datasetexists $TESTPOOL/$TESTFS || zfs create $TESTPOOL/$TESTFS
log_must zfs create $TESTPOOL/$TESTFS1
log_must zfs create $TESTPOOL/$TESTFS2
log_must zfs create $TESTPOOL/$TESTFS3
log_must zfs create $TESTPOOL/$TESTFS2/child1
log_must zfs create $TESTPOOL/$TESTFS2/child2
log_must zfs create $TESTPOOL/$TESTFS2/child3
log_must zfs create $TESTPOOL/$TESTFS2/child2/subchild
log_must zfs create $TESTPOOL/$TESTFS3/child
}
log_assert "Verify that 'zfs $mountrecursive' successfully, " \
"mounts the dataset along with all its children."
log_onexit cleanup
log_must setup_all
log_must zfs $unmountall
log_must zfs $mountrecursive $TESTPOOL
log_must mounted $TESTPOOL
log_must mounted $TESTPOOL/$TESTFS
log_must mounted $TESTPOOL/$TESTFS1
log_must mounted $TESTPOOL/$TESTFS2
log_must mounted $TESTPOOL/$TESTFS3
log_must mounted $TESTPOOL/$TESTFS2/child1
log_must mounted $TESTPOOL/$TESTFS2/child2
log_must mounted $TESTPOOL/$TESTFS2/child3
log_must mounted $TESTPOOL/$TESTFS2/child2/subchild
log_must mounted $TESTPOOL/$TESTFS3/child
log_must zfs $unmountall
log_mustnot mounted $TESTPOOL
log_mustnot mounted $TESTPOOL/$TESTFS
log_mustnot mounted $TESTPOOL/$TESTFS1
log_mustnot mounted $TESTPOOL/$TESTFS2
log_mustnot mounted $TESTPOOL/$TESTFS3
log_mustnot mounted $TESTPOOL/$TESTFS2/child1
log_mustnot mounted $TESTPOOL/$TESTFS2/child2
log_mustnot mounted $TESTPOOL/$TESTFS2/child3
log_mustnot mounted $TESTPOOL/$TESTFS2/child2/subchild
log_mustnot mounted $TESTPOOL/$TESTFS3/child
log_must zfs $mountrecursive $TESTPOOL/$TESTFS2 $TESTPOOL/$TESTFS3
log_mustnot mounted $TESTPOOL
log_mustnot mounted $TESTPOOL/$TESTFS
log_mustnot mounted $TESTPOOL/$TESTFS1
log_must mounted $TESTPOOL/$TESTFS2
log_must mounted $TESTPOOL/$TESTFS3
log_must mounted $TESTPOOL/$TESTFS2/child1
log_must mounted $TESTPOOL/$TESTFS2/child2
log_must mounted $TESTPOOL/$TESTFS2/child3
log_must mounted $TESTPOOL/$TESTFS2/child2/subchild
log_must mounted $TESTPOOL/$TESTFS3/child
log_must zfs $unmountall
log_mustnot mounted $TESTPOOL
log_mustnot mounted $TESTPOOL/$TESTFS
log_mustnot mounted $TESTPOOL/$TESTFS1
log_mustnot mounted $TESTPOOL/$TESTFS2
log_mustnot mounted $TESTPOOL/$TESTFS3
log_mustnot mounted $TESTPOOL/$TESTFS2/child1
log_mustnot mounted $TESTPOOL/$TESTFS2/child2
log_mustnot mounted $TESTPOOL/$TESTFS2/child3
log_mustnot mounted $TESTPOOL/$TESTFS2/child2/subchild
log_mustnot mounted $TESTPOOL/$TESTFS3/child
log_must zfs $mountrecursive $TESTPOOL/$TESTFS2/child2
log_must mounted $TESTPOOL/$TESTFS2/child2
log_must mounted $TESTPOOL/$TESTFS2/child2/subchild
log_mustnot mounted $TESTPOOL
log_mustnot mounted $TESTPOOL/$TESTFS
log_mustnot mounted $TESTPOOL/$TESTFS1
log_mustnot mounted $TESTPOOL/$TESTFS2
log_mustnot mounted $TESTPOOL/$TESTFS3
log_mustnot mounted $TESTPOOL/$TESTFS2/child1
log_mustnot mounted $TESTPOOL/$TESTFS2/child3
log_mustnot mounted $TESTPOOL/$TESTFS3/child
log_pass "'zfs $mountrecursive' behaves as expected."