"[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:
#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;
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."));
}
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;
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) {
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);
--- /dev/null
+#!/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."