]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Add support for zfs mount -R <filesystem>
authorUmer Saleem <usaleem@ixsystems.com>
Thu, 11 Apr 2024 22:10:24 +0000 (03:10 +0500)
committerGitHub <noreply@github.com>
Thu, 11 Apr 2024 22:10:24 +0000 (15:10 -0700)
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

cmd/zfs/zfs_main.c
man/man8/zfs-mount.8
tests/runfiles/common.run
tests/runfiles/sanity.run
tests/zfs-tests/tests/Makefile.am
tests/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount.cfg
tests/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_recursive.ksh [new file with mode: 0755]

index c2147c8f4acd45107f926c51d88a4b216e1ae2b7..ec52c563b447771be51a00a730b101cb02bbdce8 100644 (file)
@@ -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:
@@ -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;
@@ -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."));
 }
 
@@ -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;
@@ -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) {
@@ -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);
index 35aa187cf063370a53315a2bdaae5fc4a20fcab6..20dbe4d0e64805a0729ab66401c6369774ab65c4 100644 (file)
@@ -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
@@ -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
@@ -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
index 4295ca1b6f31b7f104b2fbafc67f1e9ed4909e37..558cd425afd81ad35b90829de6395b86e5e606af 100644 (file)
@@ -252,7 +252,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]
index 598123bcd27716839016e47495268a93f9dc861a..d6a791e3375dec73d9cb26511fdc0f2f64049bd2 100644 (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_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]
index db6b4c0146a72d272869e1a21e6e93c2f7d2fcdc..f182a2825cd6c76cf386383caee6122106c4100d 100644 (file)
@@ -770,6 +770,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 \
index 06d25faf0356db964485eb2f1d86e8c57af45583..739baf16086ab680baa07a0a2afcb1766cfe2871 100644 (file)
@@ -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"
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_recursive.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_recursive.ksh
new file mode 100755 (executable)
index 0000000..0e5cc5d
--- /dev/null
@@ -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."