aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/zfs/zfs_main.c43
-rw-r--r--include/libzfs.h14
-rw-r--r--include/libzfs_impl.h4
-rw-r--r--include/os/freebsd/zfs/sys/Makefile.am2
-rw-r--r--include/os/freebsd/zfs/sys/zfs_vfsops_os.h (renamed from include/os/freebsd/zfs/sys/zfs_vfsops.h)1
-rw-r--r--include/os/linux/zfs/sys/Makefile.am2
-rw-r--r--include/os/linux/zfs/sys/zfs_vfsops_os.h (renamed from include/os/linux/zfs/sys/zfs_vfsops.h)0
-rw-r--r--include/sys/Makefile.am1
-rw-r--r--include/sys/zfs_vfsops.h35
-rw-r--r--lib/libzfs/libzfs_changelist.c8
-rw-r--r--lib/libzfs/libzfs_dataset.c32
-rw-r--r--lib/libzpool/kernel.c6
-rw-r--r--man/man8/zfs-rename.834
-rw-r--r--module/os/linux/zfs/zfs_vfsops.c10
-rw-r--r--module/os/linux/zfs/zpl_super.c20
-rw-r--r--module/zfs/dsl_dir.c6
-rw-r--r--module/zfs/zfs_ioctl.c3
-rw-r--r--tests/runfiles/common.run2
-rw-r--r--tests/zfs-tests/tests/functional/cli_root/zfs_rename/Makefile.am3
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zfs_rename/zfs_rename_mountpoint.ksh4
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zfs_rename/zfs_rename_nounmount.ksh93
21 files changed, 284 insertions, 39 deletions
diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c
index 7cae89fcd..1a113c5c0 100644
--- a/cmd/zfs/zfs_main.c
+++ b/cmd/zfs/zfs_main.c
@@ -311,7 +311,8 @@ get_usage(zfs_help_t idx)
case HELP_RENAME:
return (gettext("\trename [-f] <filesystem|volume|snapshot> "
"<filesystem|volume|snapshot>\n"
- "\trename [-f] -p <filesystem|volume> <filesystem|volume>\n"
+ "\trename -p [-f] <filesystem|volume> <filesystem|volume>\n"
+ "\trename -u [-f] <filesystem> <filesystem>\n"
"\trename -r <snapshot> <snapshot>\n"));
case HELP_ROLLBACK:
return (gettext("\trollback [-rRf] <snapshot>\n"));
@@ -3603,36 +3604,40 @@ zfs_do_list(int argc, char **argv)
}
/*
- * zfs rename [-f] <fs | snap | vol> <fs | snap | vol>
+ * zfs rename [-fu] <fs | snap | vol> <fs | snap | vol>
* zfs rename [-f] -p <fs | vol> <fs | vol>
- * zfs rename -r <snap> <snap>
+ * zfs rename [-u] -r <snap> <snap>
*
* Renames the given dataset to another of the same type.
*
* The '-p' flag creates all the non-existing ancestors of the target first.
+ * The '-u' flag prevents file systems from being remounted during rename.
*/
/* ARGSUSED */
static int
zfs_do_rename(int argc, char **argv)
{
zfs_handle_t *zhp;
+ renameflags_t flags = { 0 };
int c;
int ret = 0;
- boolean_t recurse = B_FALSE;
+ int types;
boolean_t parents = B_FALSE;
- boolean_t force_unmount = B_FALSE;
/* check options */
- while ((c = getopt(argc, argv, "prf")) != -1) {
+ while ((c = getopt(argc, argv, "pruf")) != -1) {
switch (c) {
case 'p':
parents = B_TRUE;
break;
case 'r':
- recurse = B_TRUE;
+ flags.recursive = B_TRUE;
+ break;
+ case 'u':
+ flags.nounmount = B_TRUE;
break;
case 'f':
- force_unmount = B_TRUE;
+ flags.forceunmount = B_TRUE;
break;
case '?':
default:
@@ -3661,20 +3666,32 @@ zfs_do_rename(int argc, char **argv)
usage(B_FALSE);
}
- if (recurse && parents) {
+ if (flags.recursive && parents) {
(void) fprintf(stderr, gettext("-p and -r options are mutually "
"exclusive\n"));
usage(B_FALSE);
}
- if (recurse && strchr(argv[0], '@') == 0) {
+ if (flags.nounmount && parents) {
+ (void) fprintf(stderr, gettext("-u and -p options are mutually "
+ "exclusive\n"));
+ usage(B_FALSE);
+ }
+
+ if (flags.recursive && strchr(argv[0], '@') == 0) {
(void) fprintf(stderr, gettext("source dataset for recursive "
"rename must be a snapshot\n"));
usage(B_FALSE);
}
- if ((zhp = zfs_open(g_zfs, argv[0], parents ? ZFS_TYPE_FILESYSTEM |
- ZFS_TYPE_VOLUME : ZFS_TYPE_DATASET)) == NULL)
+ if (flags.nounmount)
+ types = ZFS_TYPE_FILESYSTEM;
+ else if (parents)
+ types = ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;
+ else
+ types = ZFS_TYPE_DATASET;
+
+ if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL)
return (1);
/* If we were asked and the name looks good, try to create ancestors. */
@@ -3684,7 +3701,7 @@ zfs_do_rename(int argc, char **argv)
return (1);
}
- ret = (zfs_rename(zhp, argv[1], recurse, force_unmount) != 0);
+ ret = (zfs_rename(zhp, argv[1], flags) != 0);
zfs_close(zhp);
return (ret);
diff --git a/include/libzfs.h b/include/libzfs.h
index 4e6336180..6b4f518a4 100644
--- a/include/libzfs.h
+++ b/include/libzfs.h
@@ -642,7 +642,19 @@ extern int zfs_snapshot(libzfs_handle_t *, const char *, boolean_t, nvlist_t *);
extern int zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps,
nvlist_t *props);
extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, boolean_t);
-extern int zfs_rename(zfs_handle_t *, const char *, boolean_t, boolean_t);
+
+typedef struct renameflags {
+ /* recursive rename */
+ int recursive : 1;
+
+ /* don't unmount file systems */
+ int nounmount : 1;
+
+ /* force unmount file systems */
+ int forceunmount : 1;
+} renameflags_t;
+
+extern int zfs_rename(zfs_handle_t *, const char *, renameflags_t);
typedef struct sendflags {
/* Amount of extra information to print. */
diff --git a/include/libzfs_impl.h b/include/libzfs_impl.h
index 26e042964..dfb63285c 100644
--- a/include/libzfs_impl.h
+++ b/include/libzfs_impl.h
@@ -166,6 +166,10 @@ int zprop_expand_list(libzfs_handle_t *hdl, zprop_list_t **plp,
* changelist_gather() flag to force it to iterate on mounted datasets only
*/
#define CL_GATHER_ITER_MOUNTED 2
+/*
+ * Use this changelist_gather() flag to prevent unmounting of file systems.
+ */
+#define CL_GATHER_DONT_UNMOUNT 4
typedef struct prop_changelist prop_changelist_t;
diff --git a/include/os/freebsd/zfs/sys/Makefile.am b/include/os/freebsd/zfs/sys/Makefile.am
index a8cfa39f3..6a65a7326 100644
--- a/include/os/freebsd/zfs/sys/Makefile.am
+++ b/include/os/freebsd/zfs/sys/Makefile.am
@@ -6,7 +6,7 @@ KERNEL_H = \
zfs_ctldir.h \
zfs_dir.h \
zfs_ioctl_compat.h \
- zfs_vfsops.h \
+ zfs_vfsops_os.h \
zfs_vnops.h \
zfs_znode_impl.h \
zpl.h
diff --git a/include/os/freebsd/zfs/sys/zfs_vfsops.h b/include/os/freebsd/zfs/sys/zfs_vfsops_os.h
index 70ada204a..1b80ee7cb 100644
--- a/include/os/freebsd/zfs/sys/zfs_vfsops.h
+++ b/include/os/freebsd/zfs/sys/zfs_vfsops_os.h
@@ -168,7 +168,6 @@ extern boolean_t zfs_is_readonly(zfsvfs_t *zfsvfs);
extern int zfs_get_temporary_prop(struct dsl_dataset *ds, zfs_prop_t zfs_prop,
uint64_t *val, char *setpoint);
extern int zfs_busy(void);
-extern void zfsvfs_update_fromname(const char *oldname, const char *newname);
#ifdef __cplusplus
}
diff --git a/include/os/linux/zfs/sys/Makefile.am b/include/os/linux/zfs/sys/Makefile.am
index 732d94ee8..b56e6771d 100644
--- a/include/os/linux/zfs/sys/Makefile.am
+++ b/include/os/linux/zfs/sys/Makefile.am
@@ -19,7 +19,7 @@ KERNEL_H = \
zfs_context_os.h \
zfs_ctldir.h \
zfs_dir.h \
- zfs_vfsops.h \
+ zfs_vfsops_os.h \
zfs_vnops.h \
zfs_znode_impl.h \
zpl.h
diff --git a/include/os/linux/zfs/sys/zfs_vfsops.h b/include/os/linux/zfs/sys/zfs_vfsops_os.h
index 24a0a2e6a..24a0a2e6a 100644
--- a/include/os/linux/zfs/sys/zfs_vfsops.h
+++ b/include/os/linux/zfs/sys/zfs_vfsops_os.h
diff --git a/include/sys/Makefile.am b/include/sys/Makefile.am
index c2bc3be03..75727b93a 100644
--- a/include/sys/Makefile.am
+++ b/include/sys/Makefile.am
@@ -115,6 +115,7 @@ COMMON_H = \
zfs_sa.h \
zfs_stat.h \
zfs_sysfs.h \
+ zfs_vfsops.h \
zfs_znode.h \
zil.h \
zil_impl.h \
diff --git a/include/sys/zfs_vfsops.h b/include/sys/zfs_vfsops.h
new file mode 100644
index 000000000..a438c86f0
--- /dev/null
+++ b/include/sys/zfs_vfsops.h
@@ -0,0 +1,35 @@
+/*
+ * 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 http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+
+/*
+ * Portions Copyright 2020 iXsystems, Inc.
+ */
+
+#ifndef _SYS_ZFS_VFSOPS_H
+#define _SYS_ZFS_VFSOPS_H
+
+#ifdef _KERNEL
+#include <sys/zfs_vfsops_os.h>
+#endif
+
+extern void zfsvfs_update_fromname(const char *, const char *);
+
+#endif /* _SYS_ZFS_VFSOPS_H */
diff --git a/lib/libzfs/libzfs_changelist.c b/lib/libzfs/libzfs_changelist.c
index fec2fd5f2..1592b75eb 100644
--- a/lib/libzfs/libzfs_changelist.c
+++ b/lib/libzfs/libzfs_changelist.c
@@ -128,6 +128,8 @@ changelist_prefix(prop_changelist_t *clp)
*/
switch (clp->cl_prop) {
case ZFS_PROP_MOUNTPOINT:
+ if (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)
+ break;
if (zfs_unmount(cn->cn_handle, NULL,
clp->cl_mflags) != 0) {
ret = -1;
@@ -184,7 +186,8 @@ changelist_postfix(prop_changelist_t *clp)
if ((cn = uu_avl_last(clp->cl_tree)) == NULL)
return (0);
- if (clp->cl_prop == ZFS_PROP_MOUNTPOINT)
+ if (clp->cl_prop == ZFS_PROP_MOUNTPOINT &&
+ !(clp->cl_gflags & CL_GATHER_DONT_UNMOUNT))
remove_mountpoint(cn->cn_handle);
/*
@@ -235,7 +238,8 @@ changelist_postfix(prop_changelist_t *clp)
needs_key = (zfs_prop_get_int(cn->cn_handle,
ZFS_PROP_KEYSTATUS) == ZFS_KEYSTATUS_UNAVAILABLE);
- mounted = zfs_is_mounted(cn->cn_handle, NULL);
+ mounted = (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT) ||
+ zfs_is_mounted(cn->cn_handle, NULL);
if (!mounted && !needs_key && (cn->cn_mounted ||
((sharenfs || sharesmb || clp->cl_waslegacy) &&
diff --git a/lib/libzfs/libzfs_dataset.c b/lib/libzfs/libzfs_dataset.c
index d548cc0ec..2c707f23f 100644
--- a/lib/libzfs/libzfs_dataset.c
+++ b/lib/libzfs/libzfs_dataset.c
@@ -4370,14 +4370,14 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
* Renames the given dataset.
*/
int
-zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
- boolean_t force_unmount)
+zfs_rename(zfs_handle_t *zhp, const char *target, renameflags_t flags)
{
int ret = 0;
zfs_cmd_t zc = {"\0"};
char *delim;
prop_changelist_t *cl = NULL;
char parent[ZFS_MAX_DATASET_NAME_LEN];
+ char property[ZFS_MAXPROPLEN];
libzfs_handle_t *hdl = zhp->zfs_hdl;
char errbuf[1024];
@@ -4429,7 +4429,7 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
} else {
- if (recursive) {
+ if (flags.recursive) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"recursive rename must be a snapshot"));
return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
@@ -4470,8 +4470,19 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
return (zfs_error(hdl, EZFS_ZONED, errbuf));
}
- if (recursive) {
- zfs_handle_t *zhrp;
+ /*
+ * Avoid unmounting file systems with mountpoint property set to
+ * 'legacy' or 'none' even if -u option is not given.
+ */
+ if (zhp->zfs_type == ZFS_TYPE_FILESYSTEM &&
+ !flags.recursive && !flags.nounmount &&
+ zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, property,
+ sizeof (property), NULL, NULL, 0, B_FALSE) == 0 &&
+ (strcmp(property, "legacy") == 0 ||
+ strcmp(property, "none") == 0)) {
+ flags.nounmount = B_TRUE;
+ }
+ if (flags.recursive) {
char *parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name);
if (parentname == NULL) {
ret = -1;
@@ -4479,7 +4490,8 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
}
delim = strchr(parentname, '@');
*delim = '\0';
- zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET);
+ zfs_handle_t *zhrp = zfs_open(zhp->zfs_hdl, parentname,
+ ZFS_TYPE_DATASET);
free(parentname);
if (zhrp == NULL) {
ret = -1;
@@ -4488,8 +4500,9 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
zfs_close(zhrp);
} else if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT) {
if ((cl = changelist_gather(zhp, ZFS_PROP_NAME,
+ flags.nounmount ? CL_GATHER_DONT_UNMOUNT :
CL_GATHER_ITER_MOUNTED,
- force_unmount ? MS_FORCE : 0)) == NULL)
+ flags.forceunmount ? MS_FORCE : 0)) == NULL)
return (-1);
if (changelist_haszonedchild(cl)) {
@@ -4513,7 +4526,8 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
(void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value));
- zc.zc_cookie = recursive;
+ zc.zc_cookie = !!flags.recursive;
+ zc.zc_cookie |= (!!flags.nounmount) << 1;
if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) {
/*
@@ -4523,7 +4537,7 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot rename '%s'"), zc.zc_name);
- if (recursive && errno == EEXIST) {
+ if (flags.recursive && errno == EEXIST) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"a child dataset already has a snapshot "
"with the new name"));
diff --git a/lib/libzpool/kernel.c b/lib/libzpool/kernel.c
index cba1d242b..145b21d40 100644
--- a/lib/libzpool/kernel.c
+++ b/lib/libzpool/kernel.c
@@ -41,6 +41,7 @@
#include <sys/utsname.h>
#include <sys/zfs_context.h>
#include <sys/zfs_onexit.h>
+#include <sys/zfs_vfsops.h>
#include <sys/zstd/zstd.h>
#include <sys/zvol.h>
#include <zfs_fletcher.h>
@@ -1408,3 +1409,8 @@ zfs_file_put(int fd)
{
abort();
}
+
+void
+zfsvfs_update_fromname(const char *oldname, const char *newname)
+{
+}
diff --git a/man/man8/zfs-rename.8 b/man/man8/zfs-rename.8
index 9d650709a..d8d9f49d7 100644
--- a/man/man8/zfs-rename.8
+++ b/man/man8/zfs-rename.8
@@ -44,9 +44,16 @@
.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
.Nm
.Cm rename
-.Op Fl fp
+.Fl p
+.Op Fl f
.Ar filesystem Ns | Ns Ar volume
.Ar filesystem Ns | Ns Ar volume
+.Nm
+.Cm rename
+.Fl u
+.Op Fl f
+.Ar filesystem
+.Ar filesystem
.Sh DESCRIPTION
.Bl -tag -width ""
.It Xo
@@ -59,10 +66,19 @@
.It Xo
.Nm
.Cm rename
-.Op Fl fp
+.Fl p
+.Op Fl f
.Ar filesystem Ns | Ns Ar volume
.Ar filesystem Ns | Ns Ar volume
.Xc
+.It Xo
+.Nm
+.Cm rename
+.Fl u
+.Op Fl f
+.Ar filesystem
+.Ar filesystem
+.Xc
Renames the given dataset.
The new target can be located anywhere in the ZFS hierarchy, with the exception
of snapshots.
@@ -73,12 +89,24 @@ Renamed file systems can inherit new mount points, in which case they are
unmounted and remounted at the new mount point.
.Bl -tag -width "-a"
.It Fl f
-Force unmount any filesystems that need to be unmounted in the process.
+Force unmount any file systems that need to be unmounted in the process.
+This flag has no effect if used together with the
+.Fl u
+flag.
.It Fl p
Creates all the nonexistent parent datasets.
Datasets created in this manner are automatically mounted according to the
.Sy mountpoint
property inherited from their parent.
+.It Fl u
+Do not remount file systems during rename.
+If a file system's
+.Sy mountpoint
+property is set to
+.Sy legacy
+or
+.Sy none ,
+the file system is not unmounted even if this option is not given.
.El
.It Xo
.Nm
diff --git a/module/os/linux/zfs/zfs_vfsops.c b/module/os/linux/zfs/zfs_vfsops.c
index db831bf54..389200b52 100644
--- a/module/os/linux/zfs/zfs_vfsops.c
+++ b/module/os/linux/zfs/zfs_vfsops.c
@@ -2126,6 +2126,16 @@ zfs_get_vfs_flag_unmounted(objset_t *os)
return (unmounted);
}
+/*ARGSUSED*/
+void
+zfsvfs_update_fromname(const char *oldname, const char *newname)
+{
+ /*
+ * We don't need to do anything here, the devname is always current by
+ * virtue of zfsvfs->z_sb->s_op->show_devname.
+ */
+}
+
void
zfs_init(void)
{
diff --git a/module/os/linux/zfs/zpl_super.c b/module/os/linux/zfs/zpl_super.c
index 75adff517..333c64746 100644
--- a/module/os/linux/zfs/zpl_super.c
+++ b/module/os/linux/zfs/zpl_super.c
@@ -183,6 +183,25 @@ zpl_remount_fs(struct super_block *sb, int *flags, char *data)
}
static int
+__zpl_show_devname(struct seq_file *seq, zfsvfs_t *zfsvfs)
+{
+ char *fsname;
+
+ fsname = kmem_alloc(ZFS_MAX_DATASET_NAME_LEN, KM_SLEEP);
+ dmu_objset_name(zfsvfs->z_os, fsname);
+ seq_puts(seq, fsname);
+ kmem_free(fsname, ZFS_MAX_DATASET_NAME_LEN);
+
+ return (0);
+}
+
+static int
+zpl_show_devname(struct seq_file *seq, struct dentry *root)
+{
+ return (__zpl_show_devname(seq, root->d_sb->s_fs_info));
+}
+
+static int
__zpl_show_options(struct seq_file *seq, zfsvfs_t *zfsvfs)
{
seq_printf(seq, ",%s",
@@ -314,6 +333,7 @@ const struct super_operations zpl_super_operations = {
.sync_fs = zpl_sync_fs,
.statfs = zpl_statfs,
.remount_fs = zpl_remount_fs,
+ .show_devname = zpl_show_devname,
.show_options = zpl_show_options,
.show_stats = NULL,
};
diff --git a/module/zfs/dsl_dir.c b/module/zfs/dsl_dir.c
index 29672e9a6..90dd78702 100644
--- a/module/zfs/dsl_dir.c
+++ b/module/zfs/dsl_dir.c
@@ -46,14 +46,12 @@
#include <sys/sunddi.h>
#include <sys/zfeature.h>
#include <sys/policy.h>
+#include <sys/zfs_vfsops.h>
#include <sys/zfs_znode.h>
#include <sys/zvol.h>
#include <sys/zthr.h>
#include "zfs_namecheck.h"
#include "zfs_prop.h"
-#ifdef _KERNEL
-#include <sys/zfs_vfsops.h>
-#endif
/*
* Filesystem and Snapshot Limits
@@ -2124,6 +2122,8 @@ dsl_dir_rename_sync(void *arg, dmu_tx_t *tx)
VERIFY0(zap_add(mos, dsl_dir_phys(newparent)->dd_child_dir_zapobj,
dd->dd_myname, 8, 1, &dd->dd_object, tx));
+ /* TODO: A rename callback to avoid these layering violations. */
+ zfsvfs_update_fromname(ddra->ddra_oldname, ddra->ddra_newname);
zvol_rename_minors(dp->dp_spa, ddra->ddra_oldname,
ddra->ddra_newname, B_TRUE);
diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c
index 7f623bb04..495ff4707 100644
--- a/module/zfs/zfs_ioctl.c
+++ b/module/zfs/zfs_ioctl.c
@@ -4316,6 +4316,7 @@ zfs_ioc_rename(zfs_cmd_t *zc)
objset_t *os;
dmu_objset_type_t ost;
boolean_t recursive = zc->zc_cookie & 1;
+ boolean_t nounmount = !!(zc->zc_cookie & 2);
char *at;
int err;
@@ -4341,7 +4342,7 @@ zfs_ioc_rename(zfs_cmd_t *zc)
if (strncmp(zc->zc_name, zc->zc_value, at - zc->zc_name + 1))
return (SET_ERROR(EXDEV));
*at = '\0';
- if (ost == DMU_OST_ZFS) {
+ if (ost == DMU_OST_ZFS && !nounmount) {
error = dmu_objset_find(zc->zc_name,
recursive_unmount, at + 1,
recursive ? DS_FIND_CHILDREN : 0);
diff --git a/tests/runfiles/common.run b/tests/runfiles/common.run
index 851488602..fcd968460 100644
--- a/tests/runfiles/common.run
+++ b/tests/runfiles/common.run
@@ -225,7 +225,7 @@ tests = ['zfs_rename_001_pos', 'zfs_rename_002_pos', 'zfs_rename_003_pos',
'zfs_rename_007_pos', 'zfs_rename_008_pos', 'zfs_rename_009_neg',
'zfs_rename_010_neg', 'zfs_rename_011_pos', 'zfs_rename_012_neg',
'zfs_rename_013_pos', 'zfs_rename_014_neg', 'zfs_rename_encrypted_child',
- 'zfs_rename_to_encrypted', 'zfs_rename_mountpoint']
+ 'zfs_rename_to_encrypted', 'zfs_rename_mountpoint', 'zfs_rename_nounmount']
tags = ['functional', 'cli_root', 'zfs_rename']
[tests/functional/cli_root/zfs_reservation]
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_rename/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/zfs_rename/Makefile.am
index 406e27881..f8273d72c 100644
--- a/tests/zfs-tests/tests/functional/cli_root/zfs_rename/Makefile.am
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_rename/Makefile.am
@@ -18,7 +18,8 @@ dist_pkgdata_SCRIPTS = \
zfs_rename_014_neg.ksh \
zfs_rename_encrypted_child.ksh \
zfs_rename_to_encrypted.ksh \
- zfs_rename_mountpoint.ksh
+ zfs_rename_mountpoint.ksh \
+ zfs_rename_nounmount.ksh
dist_pkgdata_DATA = \
zfs_rename.cfg \
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_rename/zfs_rename_mountpoint.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_rename/zfs_rename_mountpoint.ksh
index 4d2b94dc8..7ec6b2aa4 100755
--- a/tests/zfs-tests/tests/functional/cli_root/zfs_rename/zfs_rename_mountpoint.ksh
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_rename/zfs_rename_mountpoint.ksh
@@ -34,8 +34,8 @@ verify_runnable "both"
function rename_cleanup
{
- log_note zfs destroy -fR $TESTPOOL/rename_test
- log_note zfs destroy -fR $TESTPOOL/renamed
+ zfs destroy -fR $TESTPOOL/rename_test
+ zfs destroy -fR $TESTPOOL/renamed
}
log_onexit rename_cleanup
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_rename/zfs_rename_nounmount.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_rename/zfs_rename_nounmount.ksh
new file mode 100755
index 000000000..1c707762a
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_rename/zfs_rename_nounmount.ksh
@@ -0,0 +1,93 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# 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 is of the CDDL is also available via the Internet
+# at http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 iXsystems, Inc.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# zfs rename -u should rename datasets without unmounting them
+#
+# STRATEGY:
+# 1. Create a set of nested datasets.
+# 2. Verify datasets are mounted.
+# 3. Rename with -u and verify all datasets stayed mounted.
+#
+
+verify_runnable "both"
+
+function rename_cleanup
+{
+ cd $back
+ zfs destroy -fR $TESTPOOL/rename_test
+ zfs destroy -fR $TESTPOOL/renamed
+}
+
+back=$(pwd)
+log_onexit rename_cleanup
+
+log_must zfs create $TESTPOOL/rename_test
+log_must zfs create $TESTPOOL/rename_test/child
+log_must zfs create $TESTPOOL/rename_test/child/grandchild
+
+if ! ismounted $TESTPOOL/rename_test; then
+ log_fail "$TESTPOOL/rename_test is not mounted"
+fi
+if ! ismounted $TESTPOOL/rename_test/child; then
+ log_fail "$TESTPOOL/rename_test/child is not mounted"
+fi
+if ! ismounted $TESTPOOL/rename_test/child/grandchild; then
+ log_fail "$TESTPOOL/rename_test/child/grandchild is not mounted"
+fi
+
+mntp_p=$(get_prop mountpoint $TESTPOOL/rename_test)
+mntp_c=$(get_prop mountpoint $TESTPOOL/rename_test/child)
+mntp_g=$(get_prop mountpoint $TESTPOOL/rename_test/child/grandchild)
+
+log_must cd $mntp_g
+log_mustnot zfs rename $TESTPOOL/rename_test $TESTPOOL/renamed
+log_must zfs rename -u $TESTPOOL/rename_test $TESTPOOL/renamed
+
+log_mustnot zfs list $TESTPOOL/rename_test
+log_mustnot zfs list $TESTPOOL/rename_test/child
+log_mustnot zfs list $TESTPOOL/rename_test/child/grandchild
+
+log_must zfs list $TESTPOOL/renamed
+log_must zfs list $TESTPOOL/renamed/child
+log_must zfs list $TESTPOOL/renamed/child/grandchild
+
+missing=$(zfs mount | awk -v pat=$TESTPOOL/renamed '$1 ~ pat' | awk \
+ -v mntp_p=$mntp_p \
+ -v mntp_c=$mntp_c \
+ -v mntp_g=$mntp_g '
+ BEGIN { p = c = g = 0 }
+ $2 == mntp_p { p = 1 }
+ $2 == mntp_c { c = 1 }
+ $2 == mntp_g { g = 1 }
+ END {
+ if (p != 1)
+ print mntp_p
+ if (c != 1)
+ print mntp_c
+ if (g != 1)
+ print mntp_g
+ }')
+[[ -z "$missing" ]] || log_fail "Mountpoints no longer mounted: $missing"
+
+log_pass "Verified rename -u does not unmount datasets"