Skip to content

Commit dbb3f24

Browse files
authored
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
1 parent b9b8444 commit dbb3f24

File tree

5 files changed

+107
-30
lines changed

5 files changed

+107
-30
lines changed

cmd/zfs/zfs_main.c

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ get_usage(zfs_help_t idx)
292292
{
293293
switch (idx) {
294294
case HELP_CLONE:
295-
return (gettext("\tclone [-p] [-o property=value] ... "
295+
return (gettext("\tclone [-pu] [-o property=value] ... "
296296
"<snapshot> <filesystem|volume>\n"));
297297
case HELP_CREATE:
298298
return (gettext("\tcreate [-Pnpuv] [-o property=value] ... "
@@ -818,29 +818,32 @@ zfs_mount_and_share(libzfs_handle_t *hdl, const char *dataset, zfs_type_t type)
818818
}
819819

820820
/*
821-
* zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>
821+
* zfs clone [-pu] [-o prop=value] ... <snap> <fs | vol>
822822
*
823823
* Given an existing dataset, create a writable copy whose initial contents
824824
* are the same as the source. The newly created dataset maintains a
825825
* dependency on the original; the original cannot be destroyed so long as
826826
* the clone exists.
827827
*
828828
* The '-p' flag creates all the non-existing ancestors of the target first.
829+
*
830+
* The '-u' flag prevents the newly created file system from being mounted.
829831
*/
830832
static int
831833
zfs_do_clone(int argc, char **argv)
832834
{
833835
zfs_handle_t *zhp = NULL;
834836
boolean_t parents = B_FALSE;
837+
boolean_t nomount = B_FALSE;
835838
nvlist_t *props;
836-
int ret = 0;
839+
int ret = 1;
837840
int c;
838841

839842
if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
840843
nomem();
841844

842845
/* check options */
843-
while ((c = getopt(argc, argv, "o:p")) != -1) {
846+
while ((c = getopt(argc, argv, "o:pu")) != -1) {
844847
switch (c) {
845848
case 'o':
846849
if (!parseprop(props, optarg)) {
@@ -851,6 +854,9 @@ zfs_do_clone(int argc, char **argv)
851854
case 'p':
852855
parents = B_TRUE;
853856
break;
857+
case 'u':
858+
nomount = B_TRUE;
859+
break;
854860
case '?':
855861
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
856862
optopt);
@@ -879,8 +885,7 @@ zfs_do_clone(int argc, char **argv)
879885

880886
/* open the source dataset */
881887
if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) {
882-
nvlist_free(props);
883-
return (1);
888+
goto error_open;
884889
}
885890

886891
if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM |
@@ -892,37 +897,39 @@ zfs_do_clone(int argc, char **argv)
892897
*/
893898
if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM |
894899
ZFS_TYPE_VOLUME)) {
895-
zfs_close(zhp);
896-
nvlist_free(props);
897-
return (0);
900+
ret = 0;
901+
goto error;
898902
}
899903
if (zfs_create_ancestors(g_zfs, argv[1]) != 0) {
900-
zfs_close(zhp);
901-
nvlist_free(props);
902-
return (1);
904+
goto error;
903905
}
904906
}
905907

906908
/* pass to libzfs */
907909
ret = zfs_clone(zhp, argv[1], props);
908910

909-
/* create the mountpoint if necessary */
910-
if (ret == 0) {
911-
if (log_history) {
912-
(void) zpool_log_history(g_zfs, history_str);
913-
log_history = B_FALSE;
914-
}
911+
if (ret != 0)
912+
goto error;
915913

916-
/*
917-
* Dataset cloned successfully, mount/share failures are
918-
* non-fatal.
919-
*/
920-
(void) zfs_mount_and_share(g_zfs, argv[1], ZFS_TYPE_DATASET);
914+
/* create the mountpoint if necessary */
915+
if (log_history) {
916+
(void) zpool_log_history(g_zfs, history_str);
917+
log_history = B_FALSE;
921918
}
922919

920+
if (nomount)
921+
goto error;
922+
923+
/*
924+
* Dataset cloned successfully, mount/share failures are
925+
* non-fatal.
926+
*/
927+
(void) zfs_mount_and_share(g_zfs, argv[1], ZFS_TYPE_DATASET);
928+
929+
error:
923930
zfs_close(zhp);
931+
error_open:
924932
nvlist_free(props);
925-
926933
return (!!ret);
927934

928935
usage:
@@ -4046,7 +4053,7 @@ zfs_do_rename(int argc, char **argv)
40464053
zfs_handle_t *zhp;
40474054
renameflags_t flags = { 0 };
40484055
int c;
4049-
int ret = 0;
4056+
int ret = 1;
40504057
int types;
40514058
boolean_t parents = B_FALSE;
40524059

@@ -4118,18 +4125,19 @@ zfs_do_rename(int argc, char **argv)
41184125
types = ZFS_TYPE_DATASET;
41194126

41204127
if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL)
4121-
return (1);
4128+
goto error_open;
41224129

41234130
/* If we were asked and the name looks good, try to create ancestors. */
41244131
if (parents && zfs_name_valid(argv[1], zfs_get_type(zhp)) &&
41254132
zfs_create_ancestors(g_zfs, argv[1]) != 0) {
4126-
zfs_close(zhp);
4127-
return (1);
4133+
goto error;
41284134
}
41294135

41304136
ret = (zfs_rename(zhp, argv[1], flags) != 0);
41314137

4138+
error:
41324139
zfs_close(zhp);
4140+
error_open:
41334141
return (ret);
41344142
}
41354143

man/man8/zfs-clone.8

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
.Sh SYNOPSIS
4141
.Nm zfs
4242
.Cm clone
43-
.Op Fl p
43+
.Op Fl pu
4444
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns
4545
.Ar snapshot Ar filesystem Ns | Ns Ar volume
4646
.
@@ -64,6 +64,8 @@ Datasets created in this manner are automatically mounted according to the
6464
property inherited from their parent.
6565
If the target filesystem or volume already exists, the operation completes
6666
successfully.
67+
.It Fl u
68+
Do not mount the newly created file system.
6769
.El
6870
.
6971
.Sh EXAMPLES

tests/runfiles/common.run

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ tests = ['zfs_clone_001_neg', 'zfs_clone_002_pos', 'zfs_clone_003_pos',
196196
'zfs_clone_004_pos', 'zfs_clone_005_pos', 'zfs_clone_006_pos',
197197
'zfs_clone_007_pos', 'zfs_clone_008_neg', 'zfs_clone_009_neg',
198198
'zfs_clone_010_pos', 'zfs_clone_encrypted', 'zfs_clone_deeply_nested',
199-
'zfs_clone_rm_nested']
199+
'zfs_clone_rm_nested', 'zfs_clone_nomount']
200200
tags = ['functional', 'cli_root', 'zfs_clone']
201201

202202
[tests/functional/cli_root/zfs_copies]

tests/zfs-tests/tests/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
677677
functional/cli_root/zfs_clone/zfs_clone_010_pos.ksh \
678678
functional/cli_root/zfs_clone/zfs_clone_deeply_nested.ksh \
679679
functional/cli_root/zfs_clone/zfs_clone_encrypted.ksh \
680+
functional/cli_root/zfs_clone/zfs_clone_nomount.ksh \
680681
functional/cli_root/zfs_clone/zfs_clone_rm_nested.ksh \
681682
functional/cli_root/zfs_copies/cleanup.ksh \
682683
functional/cli_root/zfs_copies/setup.ksh \
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/bin/ksh -p
2+
# shellcheck disable=SC2066
3+
# SPDX-License-Identifier: CDDL-1.0
4+
#
5+
# This file and its contents are supplied under the terms of the
6+
# Common Development and Distribution License ("CDDL"), version 1.0.
7+
# You may only use this file in accordance with the terms of version
8+
# 1.0 of the CDDL.
9+
#
10+
# A full copy of the text of the CDDL should have accompanied this
11+
# source. A copy of the CDDL is also available via the Internet at
12+
# http://www.illumos.org/license/CDDL.
13+
#
14+
15+
#
16+
# Copyright 2025 Ivan Shapovalov <intelfx@intelfx.name>
17+
#
18+
19+
. "$STF_SUITE/include/libtest.shlib"
20+
21+
#
22+
# DESCRIPTION:
23+
# `zfs clone -u` should leave the new file system unmounted.
24+
#
25+
# STRATEGY:
26+
# 1. Prepare snapshots
27+
# 2. Clone a snapshot using `-u` and make sure the clone is not mounted.
28+
#
29+
30+
verify_runnable "both"
31+
32+
function setup_all
33+
{
34+
log_note "Creating snapshots..."
35+
36+
for snap in "$SNAPFS" ; do
37+
if ! snapexists "$snap" ; then
38+
log_must zfs snapshot "$snap"
39+
fi
40+
done
41+
42+
return 0
43+
}
44+
45+
function cleanup_all
46+
{
47+
datasetexists "$fs" && destroy_dataset "$fs"
48+
49+
for snap in "$SNAPFS" ; do
50+
snapexists "$snap" && destroy_dataset "$snap" -Rf
51+
done
52+
53+
return 0
54+
}
55+
56+
log_onexit cleanup_all
57+
log_must setup_all
58+
59+
log_assert "zfs clone -u should leave the new file system unmounted"
60+
61+
typeset fs="$TESTPOOL/clonefs$$"
62+
63+
log_must zfs clone -u "$SNAPFS" "$fs"
64+
log_mustnot ismounted "$fs"
65+
66+
log_pass "zfs clone -u leaves the new file system unmounted"

0 commit comments

Comments
 (0)