aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Shapovalov <intelfx@intelfx.name>2026-01-05 17:21:56 +0000
committerTony Hutter <hutter2@llnl.gov>2026-02-05 21:48:31 +0000
commit6ab8f46c6c4702fe021726c75442140027a387b3 (patch)
tree7d78e0ef4f773574e4492fbefd782632201af0f2
parentcb1833023f35ca226d6adb5feced65fc5e853aab (diff)
cmd/zfs: clone: accept `-u` to not mount newly created datasets
Signed-off-by: Ivan Shapovalov <intelfx@intelfx.name> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Tony Hutter <hutter2@llnl.gov> Reviewed-by: Alexander Motin <alexander.motin@TrueNAS.com> Closes #18080
-rw-r--r--cmd/zfs/zfs_main.c64
-rw-r--r--man/man8/zfs-clone.84
-rw-r--r--tests/runfiles/common.run2
-rw-r--r--tests/zfs-tests/tests/Makefile.am1
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zfs_clone/zfs_clone_nomount.ksh66
5 files changed, 107 insertions, 30 deletions
diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c
index ccdd5ffef8e6..4ffea27f1c16 100644
--- a/cmd/zfs/zfs_main.c
+++ b/cmd/zfs/zfs_main.c
@@ -293,7 +293,7 @@ get_usage(zfs_help_t idx)
{
switch (idx) {
case HELP_CLONE:
- return (gettext("\tclone [-p] [-o property=value] ... "
+ return (gettext("\tclone [-pu] [-o property=value] ... "
"<snapshot> <filesystem|volume>\n"));
case HELP_CREATE:
return (gettext("\tcreate [-Pnpuv] [-o property=value] ... "
@@ -819,7 +819,7 @@ zfs_mount_and_share(libzfs_handle_t *hdl, const char *dataset, zfs_type_t type)
}
/*
- * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>
+ * zfs clone [-pu] [-o prop=value] ... <snap> <fs | vol>
*
* Given an existing dataset, create a writable copy whose initial contents
* are the same as the source. The newly created dataset maintains a
@@ -827,21 +827,24 @@ zfs_mount_and_share(libzfs_handle_t *hdl, const char *dataset, zfs_type_t type)
* the clone exists.
*
* The '-p' flag creates all the non-existing ancestors of the target first.
+ *
+ * The '-u' flag prevents the newly created file system from being mounted.
*/
static int
zfs_do_clone(int argc, char **argv)
{
zfs_handle_t *zhp = NULL;
boolean_t parents = B_FALSE;
+ boolean_t nomount = B_FALSE;
nvlist_t *props;
- int ret = 0;
+ int ret = 1;
int c;
if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
nomem();
/* check options */
- while ((c = getopt(argc, argv, "o:p")) != -1) {
+ while ((c = getopt(argc, argv, "o:pu")) != -1) {
switch (c) {
case 'o':
if (!parseprop(props, optarg)) {
@@ -852,6 +855,9 @@ zfs_do_clone(int argc, char **argv)
case 'p':
parents = B_TRUE;
break;
+ case 'u':
+ nomount = B_TRUE;
+ break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
@@ -880,8 +886,7 @@ zfs_do_clone(int argc, char **argv)
/* open the source dataset */
if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) {
- nvlist_free(props);
- return (1);
+ goto error_open;
}
if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM |
@@ -893,37 +898,39 @@ zfs_do_clone(int argc, char **argv)
*/
if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM |
ZFS_TYPE_VOLUME)) {
- zfs_close(zhp);
- nvlist_free(props);
- return (0);
+ ret = 0;
+ goto error;
}
if (zfs_create_ancestors(g_zfs, argv[1]) != 0) {
- zfs_close(zhp);
- nvlist_free(props);
- return (1);
+ goto error;
}
}
/* pass to libzfs */
ret = zfs_clone(zhp, argv[1], props);
- /* create the mountpoint if necessary */
- if (ret == 0) {
- if (log_history) {
- (void) zpool_log_history(g_zfs, history_str);
- log_history = B_FALSE;
- }
+ if (ret != 0)
+ goto error;
- /*
- * Dataset cloned successfully, mount/share failures are
- * non-fatal.
- */
- (void) zfs_mount_and_share(g_zfs, argv[1], ZFS_TYPE_DATASET);
+ /* create the mountpoint if necessary */
+ if (log_history) {
+ (void) zpool_log_history(g_zfs, history_str);
+ log_history = B_FALSE;
}
+ if (nomount)
+ goto error;
+
+ /*
+ * Dataset cloned successfully, mount/share failures are
+ * non-fatal.
+ */
+ (void) zfs_mount_and_share(g_zfs, argv[1], ZFS_TYPE_DATASET);
+
+error:
zfs_close(zhp);
+error_open:
nvlist_free(props);
-
return (!!ret);
usage:
@@ -4071,7 +4078,7 @@ zfs_do_rename(int argc, char **argv)
zfs_handle_t *zhp;
renameflags_t flags = { 0 };
int c;
- int ret = 0;
+ int ret = 1;
int types;
boolean_t parents = B_FALSE;
@@ -4143,18 +4150,19 @@ zfs_do_rename(int argc, char **argv)
types = ZFS_TYPE_DATASET;
if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL)
- return (1);
+ goto error_open;
/* If we were asked and the name looks good, try to create ancestors. */
if (parents && zfs_name_valid(argv[1], zfs_get_type(zhp)) &&
zfs_create_ancestors(g_zfs, argv[1]) != 0) {
- zfs_close(zhp);
- return (1);
+ goto error;
}
ret = (zfs_rename(zhp, argv[1], flags) != 0);
+error:
zfs_close(zhp);
+error_open:
return (ret);
}
diff --git a/man/man8/zfs-clone.8 b/man/man8/zfs-clone.8
index 9609cf2ce36a..786b17521688 100644
--- a/man/man8/zfs-clone.8
+++ b/man/man8/zfs-clone.8
@@ -40,7 +40,7 @@
.Sh SYNOPSIS
.Nm zfs
.Cm clone
-.Op Fl p
+.Op Fl pu
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns …
.Ar snapshot Ar filesystem Ns | Ns Ar volume
.
@@ -64,6 +64,8 @@ Datasets created in this manner are automatically mounted according to the
property inherited from their parent.
If the target filesystem or volume already exists, the operation completes
successfully.
+.It Fl u
+Do not mount the newly created file system.
.El
.
.Sh EXAMPLES
diff --git a/tests/runfiles/common.run b/tests/runfiles/common.run
index 9b4c4154c056..d0b35933cf7b 100644
--- a/tests/runfiles/common.run
+++ b/tests/runfiles/common.run
@@ -196,7 +196,7 @@ tests = ['zfs_clone_001_neg', 'zfs_clone_002_pos', 'zfs_clone_003_pos',
'zfs_clone_004_pos', 'zfs_clone_005_pos', 'zfs_clone_006_pos',
'zfs_clone_007_pos', 'zfs_clone_008_neg', 'zfs_clone_009_neg',
'zfs_clone_010_pos', 'zfs_clone_encrypted', 'zfs_clone_deeply_nested',
- 'zfs_clone_rm_nested']
+ 'zfs_clone_rm_nested', 'zfs_clone_nomount']
tags = ['functional', 'cli_root', 'zfs_clone']
[tests/functional/cli_root/zfs_copies]
diff --git a/tests/zfs-tests/tests/Makefile.am b/tests/zfs-tests/tests/Makefile.am
index a4b3e7376093..5c486754d89e 100644
--- a/tests/zfs-tests/tests/Makefile.am
+++ b/tests/zfs-tests/tests/Makefile.am
@@ -677,6 +677,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
functional/cli_root/zfs_clone/zfs_clone_010_pos.ksh \
functional/cli_root/zfs_clone/zfs_clone_deeply_nested.ksh \
functional/cli_root/zfs_clone/zfs_clone_encrypted.ksh \
+ functional/cli_root/zfs_clone/zfs_clone_nomount.ksh \
functional/cli_root/zfs_clone/zfs_clone_rm_nested.ksh \
functional/cli_root/zfs_copies/cleanup.ksh \
functional/cli_root/zfs_copies/setup.ksh \
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_clone/zfs_clone_nomount.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_clone/zfs_clone_nomount.ksh
new file mode 100755
index 000000000000..b549f4571757
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_clone/zfs_clone_nomount.ksh
@@ -0,0 +1,66 @@
+#!/bin/ksh -p
+# shellcheck disable=SC2066
+# SPDX-License-Identifier: CDDL-1.0
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2025 Ivan Shapovalov <intelfx@intelfx.name>
+#
+
+. "$STF_SUITE/include/libtest.shlib"
+
+#
+# DESCRIPTION:
+# `zfs clone -u` should leave the new file system unmounted.
+#
+# STRATEGY:
+# 1. Prepare snapshots
+# 2. Clone a snapshot using `-u` and make sure the clone is not mounted.
+#
+
+verify_runnable "both"
+
+function setup_all
+{
+ log_note "Creating snapshots..."
+
+ for snap in "$SNAPFS" ; do
+ if ! snapexists "$snap" ; then
+ log_must zfs snapshot "$snap"
+ fi
+ done
+
+ return 0
+}
+
+function cleanup_all
+{
+ datasetexists "$fs" && destroy_dataset "$fs"
+
+ for snap in "$SNAPFS" ; do
+ snapexists "$snap" && destroy_dataset "$snap" -Rf
+ done
+
+ return 0
+}
+
+log_onexit cleanup_all
+log_must setup_all
+
+log_assert "zfs clone -u should leave the new file system unmounted"
+
+typeset fs="$TESTPOOL/clonefs$$"
+
+log_must zfs clone -u "$SNAPFS" "$fs"
+log_mustnot ismounted "$fs"
+
+log_pass "zfs clone -u leaves the new file system unmounted"