Skip to content

Commit

Permalink
Add support for zfs mount -R <filesystem>
Browse files Browse the repository at this point in the history
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.

Signed-off-by: Umer Saleem <[email protected]>
  • Loading branch information
usaleem-ix committed Apr 6, 2024
1 parent 8f2f6cd commit dcf3ade
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 18 deletions.
75 changes: 61 additions & 14 deletions cmd/zfs/zfs_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,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|-R filesystem|"
"filesystem>\n"));
case HELP_PROMOTE:
return (gettext("\tpromote <clone-filesystem>\n"));
case HELP_RECEIVE:
Expand Down Expand Up @@ -6754,6 +6755,8 @@ zfs_do_holds(int argc, char **argv)
#define MOUNT_TIME 1 /* seconds */

typedef struct get_all_state {
char **ga_datasets;
int ga_count;
boolean_t ga_verbose;
get_all_cb_t *ga_cbp;
} get_all_state_t;
Expand Down Expand Up @@ -6800,19 +6803,35 @@ get_one_dataset(zfs_handle_t *zhp, void *data)
return (0);
}

static void
get_all_datasets(get_all_cb_t *cbp, boolean_t verbose)
static int
get_recursive_datasets(zfs_handle_t *zhp, void *data)
{
get_all_state_t state = {
.ga_verbose = verbose,
.ga_cbp = cbp
};
get_all_state_t *state = data;
int len = strlen(zfs_get_name(zhp));
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"));
(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."));
}

Expand Down Expand Up @@ -7158,18 +7177,22 @@ static int
share_mount(int op, int argc, char **argv)
{
int do_all = 0;
int recursive = 0;
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 ? ":aRlvo:Of" : "al"))
!= -1) {
switch (c) {
case 'a':
do_all = 1;
break;
case 'R':
recursive = 1;
break;
case 'v':
verbose = B_TRUE;
break;
Expand Down Expand Up @@ -7211,7 +7234,7 @@ share_mount(int op, int argc, char **argv)
argv += optind;

/* check number of arguments */
if (do_all) {
if (do_all || recursive) {
enum sa_protocol protocol = SA_NO_PROTOCOL;

if (op == OP_SHARE && argc > 0) {
Expand All @@ -7220,14 +7243,38 @@ share_mount(int op, int argc, char **argv)
argv++;
}

if (argc != 0) {
if (argc != 0 && do_all) {
(void) fprintf(stderr, gettext("too many arguments\n"));
usage(B_FALSE);
}

if (argc == 0 && recursive) {
(void) fprintf(stderr,
gettext("no dataset provided\n"));
usage(B_FALSE);
}

start_progress_timer();
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) {
free(options);
Expand Down
6 changes: 4 additions & 2 deletions man/man8/zfs-mount.8
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
.Cm mount
.Op Fl Oflv
.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
.Cm unmount
.Op Fl fu
Expand All @@ -61,7 +61,7 @@ Displays all ZFS file systems currently mounted.
.Cm mount
.Op Fl Oflv
.Op Fl o Ar options
.Fl a Ns | Ns Ar filesystem
.Fl a Ns | Ns Fl R Ar filesystem Ns | Ns Ar filesystem
.Xc
Mount ZFS filesystem on a path described by its
.Sy mountpoint
Expand All @@ -83,6 +83,8 @@ for more information.
.It Fl a
Mount all available ZFS file systems.
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
Mount the specified filesystem.
.It Fl o Ar options
Expand Down
2 changes: 1 addition & 1 deletion tests/runfiles/common.run
Original file line number Diff line number Diff line change
Expand Up @@ -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_012_pos', 'zfs_mount_all_001_pos', 'zfs_mount_encrypted',
'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']

[tests/functional/cli_root/zfs_program]
Expand Down
3 changes: 2 additions & 1 deletion tests/runfiles/sanity.run
Original file line number Diff line number Diff line change
Expand Up @@ -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_009_neg', 'zfs_mount_010_neg', 'zfs_mount_011_neg',
'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']

[tests/functional/cli_root/zfs_program]
Expand Down
1 change: 1 addition & 0 deletions tests/zfs-tests/tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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_mountpoints.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_test_race.ksh \
functional/cli_root/zfs_mount/zfs_multi_mount.ksh \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
export mountcmd=mount
export mountforce="$mountcmd -f"
export mountall="$mountcmd -a"
export mountrecursive="$mountcmd -R"

export unmountcmd=unmount
export unmountforce="$unmountcmd -f"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#!/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. Recusrive mount root datasets, this should mount all datasets
# present in a pool
# 4. Unmount all datasets
# 5. Recusrsive 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 ddataset recursively again and confirm all child
# datasets are mounted.
#

verify_runnable "both"

function cleanup
{
log_must poolexists $TESTPOOL && destroy_pool $TESTPOOL
}

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 it's 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."

0 comments on commit dcf3ade

Please sign in to comment.