Add 'zfs rename -u' to rename without remounting

Allow to rename file systems without remounting if it is possible.
It is possible for file systems with 'mountpoint' property set to
'legacy' or 'none' - we don't have to change mount directory for them.
Currently such file systems are unmounted on rename and not even
mounted back.

This introduces layering violation, as we need to update
'f_mntfromname' field in statfs structure related to mountpoint (for
the dataset we are renaming and all its children).

In my opinion it is worth it, as it allow to update FreeBSD in even
cleaner way - in ZFS-only configuration root file system is ZFS file
system with 'mountpoint' property set to 'legacy'. If root dataset is
named system/rootfs, we can snapshot it (system/rootfs@upgrade), clone
it (system/oldrootfs), update FreeBSD and if it doesn't boot we can
boot back from system/oldrootfs and rename it back to system/rootfs
while it is mounted as /. Before it was not possible, because
unmounting / was not possible.

Authored by: Pawel Jakub Dawidek <pjd@FreeBSD.org>
Reviewed-by: Allan Jude <allan@klarasystems.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Ported by: Matt Macy <mmacy@freebsd.org>
Signed-off-by: Ryan Moeller <ryan@iXsystems.com>
Closes #10839
This commit is contained in:
Ryan Moeller 2020-09-01 19:14:16 -04:00 committed by GitHub
parent 88d19d7cc2
commit 7b4e27232d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 284 additions and 39 deletions

View File

@ -311,7 +311,8 @@ get_usage(zfs_help_t idx)
case HELP_RENAME: case HELP_RENAME:
return (gettext("\trename [-f] <filesystem|volume|snapshot> " return (gettext("\trename [-f] <filesystem|volume|snapshot> "
"<filesystem|volume|snapshot>\n" "<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")); "\trename -r <snapshot> <snapshot>\n"));
case HELP_ROLLBACK: case HELP_ROLLBACK:
return (gettext("\trollback [-rRf] <snapshot>\n")); 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 [-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. * Renames the given dataset to another of the same type.
* *
* The '-p' flag creates all the non-existing ancestors of the target first. * 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 */ /* ARGSUSED */
static int static int
zfs_do_rename(int argc, char **argv) zfs_do_rename(int argc, char **argv)
{ {
zfs_handle_t *zhp; zfs_handle_t *zhp;
renameflags_t flags = { 0 };
int c; int c;
int ret = 0; int ret = 0;
boolean_t recurse = B_FALSE; int types;
boolean_t parents = B_FALSE; boolean_t parents = B_FALSE;
boolean_t force_unmount = B_FALSE;
/* check options */ /* check options */
while ((c = getopt(argc, argv, "prf")) != -1) { while ((c = getopt(argc, argv, "pruf")) != -1) {
switch (c) { switch (c) {
case 'p': case 'p':
parents = B_TRUE; parents = B_TRUE;
break; break;
case 'r': case 'r':
recurse = B_TRUE; flags.recursive = B_TRUE;
break;
case 'u':
flags.nounmount = B_TRUE;
break; break;
case 'f': case 'f':
force_unmount = B_TRUE; flags.forceunmount = B_TRUE;
break; break;
case '?': case '?':
default: default:
@ -3661,20 +3666,32 @@ zfs_do_rename(int argc, char **argv)
usage(B_FALSE); usage(B_FALSE);
} }
if (recurse && parents) { if (flags.recursive && parents) {
(void) fprintf(stderr, gettext("-p and -r options are mutually " (void) fprintf(stderr, gettext("-p and -r options are mutually "
"exclusive\n")); "exclusive\n"));
usage(B_FALSE); 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 " (void) fprintf(stderr, gettext("source dataset for recursive "
"rename must be a snapshot\n")); "rename must be a snapshot\n"));
usage(B_FALSE); usage(B_FALSE);
} }
if ((zhp = zfs_open(g_zfs, argv[0], parents ? ZFS_TYPE_FILESYSTEM | if (flags.nounmount)
ZFS_TYPE_VOLUME : ZFS_TYPE_DATASET)) == NULL) 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); return (1);
/* If we were asked and the name looks good, try to create ancestors. */ /* 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); return (1);
} }
ret = (zfs_rename(zhp, argv[1], recurse, force_unmount) != 0); ret = (zfs_rename(zhp, argv[1], flags) != 0);
zfs_close(zhp); zfs_close(zhp);
return (ret); return (ret);

View File

@ -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, extern int zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps,
nvlist_t *props); nvlist_t *props);
extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, boolean_t); 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 { typedef struct sendflags {
/* Amount of extra information to print. */ /* Amount of extra information to print. */

View File

@ -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 * changelist_gather() flag to force it to iterate on mounted datasets only
*/ */
#define CL_GATHER_ITER_MOUNTED 2 #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; typedef struct prop_changelist prop_changelist_t;

View File

@ -6,7 +6,7 @@ KERNEL_H = \
zfs_ctldir.h \ zfs_ctldir.h \
zfs_dir.h \ zfs_dir.h \
zfs_ioctl_compat.h \ zfs_ioctl_compat.h \
zfs_vfsops.h \ zfs_vfsops_os.h \
zfs_vnops.h \ zfs_vnops.h \
zfs_znode_impl.h \ zfs_znode_impl.h \
zpl.h zpl.h

View File

@ -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, extern int zfs_get_temporary_prop(struct dsl_dataset *ds, zfs_prop_t zfs_prop,
uint64_t *val, char *setpoint); uint64_t *val, char *setpoint);
extern int zfs_busy(void); extern int zfs_busy(void);
extern void zfsvfs_update_fromname(const char *oldname, const char *newname);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -19,7 +19,7 @@ KERNEL_H = \
zfs_context_os.h \ zfs_context_os.h \
zfs_ctldir.h \ zfs_ctldir.h \
zfs_dir.h \ zfs_dir.h \
zfs_vfsops.h \ zfs_vfsops_os.h \
zfs_vnops.h \ zfs_vnops.h \
zfs_znode_impl.h \ zfs_znode_impl.h \
zpl.h zpl.h

View File

@ -115,6 +115,7 @@ COMMON_H = \
zfs_sa.h \ zfs_sa.h \
zfs_stat.h \ zfs_stat.h \
zfs_sysfs.h \ zfs_sysfs.h \
zfs_vfsops.h \
zfs_znode.h \ zfs_znode.h \
zil.h \ zil.h \
zil_impl.h \ zil_impl.h \

35
include/sys/zfs_vfsops.h Normal file
View File

@ -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 */

View File

@ -128,6 +128,8 @@ changelist_prefix(prop_changelist_t *clp)
*/ */
switch (clp->cl_prop) { switch (clp->cl_prop) {
case ZFS_PROP_MOUNTPOINT: case ZFS_PROP_MOUNTPOINT:
if (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)
break;
if (zfs_unmount(cn->cn_handle, NULL, if (zfs_unmount(cn->cn_handle, NULL,
clp->cl_mflags) != 0) { clp->cl_mflags) != 0) {
ret = -1; ret = -1;
@ -184,7 +186,8 @@ changelist_postfix(prop_changelist_t *clp)
if ((cn = uu_avl_last(clp->cl_tree)) == NULL) if ((cn = uu_avl_last(clp->cl_tree)) == NULL)
return (0); 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); remove_mountpoint(cn->cn_handle);
/* /*
@ -235,7 +238,8 @@ changelist_postfix(prop_changelist_t *clp)
needs_key = (zfs_prop_get_int(cn->cn_handle, needs_key = (zfs_prop_get_int(cn->cn_handle,
ZFS_PROP_KEYSTATUS) == ZFS_KEYSTATUS_UNAVAILABLE); 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 || if (!mounted && !needs_key && (cn->cn_mounted ||
((sharenfs || sharesmb || clp->cl_waslegacy) && ((sharenfs || sharesmb || clp->cl_waslegacy) &&

View File

@ -4370,14 +4370,14 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
* Renames the given dataset. * Renames the given dataset.
*/ */
int int
zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive, zfs_rename(zfs_handle_t *zhp, const char *target, renameflags_t flags)
boolean_t force_unmount)
{ {
int ret = 0; int ret = 0;
zfs_cmd_t zc = {"\0"}; zfs_cmd_t zc = {"\0"};
char *delim; char *delim;
prop_changelist_t *cl = NULL; prop_changelist_t *cl = NULL;
char parent[ZFS_MAX_DATASET_NAME_LEN]; char parent[ZFS_MAX_DATASET_NAME_LEN];
char property[ZFS_MAXPROPLEN];
libzfs_handle_t *hdl = zhp->zfs_hdl; libzfs_handle_t *hdl = zhp->zfs_hdl;
char errbuf[1024]; 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)) if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
} else { } else {
if (recursive) { if (flags.recursive) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"recursive rename must be a snapshot")); "recursive rename must be a snapshot"));
return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); 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)); 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); char *parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name);
if (parentname == NULL) { if (parentname == NULL) {
ret = -1; ret = -1;
@ -4479,7 +4490,8 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
} }
delim = strchr(parentname, '@'); delim = strchr(parentname, '@');
*delim = '\0'; *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); free(parentname);
if (zhrp == NULL) { if (zhrp == NULL) {
ret = -1; ret = -1;
@ -4488,8 +4500,9 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
zfs_close(zhrp); zfs_close(zhrp);
} else if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT) { } else if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT) {
if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, if ((cl = changelist_gather(zhp, ZFS_PROP_NAME,
flags.nounmount ? CL_GATHER_DONT_UNMOUNT :
CL_GATHER_ITER_MOUNTED, CL_GATHER_ITER_MOUNTED,
force_unmount ? MS_FORCE : 0)) == NULL) flags.forceunmount ? MS_FORCE : 0)) == NULL)
return (-1); return (-1);
if (changelist_haszonedchild(cl)) { 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_name, zhp->zfs_name, sizeof (zc.zc_name));
(void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value)); (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) { 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, (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot rename '%s'"), zc.zc_name); "cannot rename '%s'"), zc.zc_name);
if (recursive && errno == EEXIST) { if (flags.recursive && errno == EEXIST) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"a child dataset already has a snapshot " "a child dataset already has a snapshot "
"with the new name")); "with the new name"));

View File

@ -41,6 +41,7 @@
#include <sys/utsname.h> #include <sys/utsname.h>
#include <sys/zfs_context.h> #include <sys/zfs_context.h>
#include <sys/zfs_onexit.h> #include <sys/zfs_onexit.h>
#include <sys/zfs_vfsops.h>
#include <sys/zstd/zstd.h> #include <sys/zstd/zstd.h>
#include <sys/zvol.h> #include <sys/zvol.h>
#include <zfs_fletcher.h> #include <zfs_fletcher.h>
@ -1408,3 +1409,8 @@ zfs_file_put(int fd)
{ {
abort(); abort();
} }
void
zfsvfs_update_fromname(const char *oldname, const char *newname)
{
}

View File

@ -44,9 +44,16 @@
.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot .Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
.Nm .Nm
.Cm rename .Cm rename
.Op Fl fp .Fl p
.Op Fl f
.Ar filesystem Ns | Ns Ar volume .Ar filesystem Ns | Ns Ar volume
.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 .Sh DESCRIPTION
.Bl -tag -width "" .Bl -tag -width ""
.It Xo .It Xo
@ -59,10 +66,19 @@
.It Xo .It Xo
.Nm .Nm
.Cm rename .Cm rename
.Op Fl fp .Fl p
.Op Fl f
.Ar filesystem Ns | Ns Ar volume .Ar filesystem Ns | Ns Ar volume
.Ar filesystem Ns | Ns Ar volume .Ar filesystem Ns | Ns Ar volume
.Xc .Xc
.It Xo
.Nm
.Cm rename
.Fl u
.Op Fl f
.Ar filesystem
.Ar filesystem
.Xc
Renames the given dataset. Renames the given dataset.
The new target can be located anywhere in the ZFS hierarchy, with the exception The new target can be located anywhere in the ZFS hierarchy, with the exception
of snapshots. of snapshots.
@ -74,11 +90,23 @@ unmounted and remounted at the new mount point.
.Bl -tag -width "-a" .Bl -tag -width "-a"
.It Fl f .It Fl f
Force unmount any file systems 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 .It Fl p
Creates all the nonexistent parent datasets. Creates all the nonexistent parent datasets.
Datasets created in this manner are automatically mounted according to the Datasets created in this manner are automatically mounted according to the
.Sy mountpoint .Sy mountpoint
property inherited from their parent. 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 .El
.It Xo .It Xo
.Nm .Nm

View File

@ -2126,6 +2126,16 @@ zfs_get_vfs_flag_unmounted(objset_t *os)
return (unmounted); 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 void
zfs_init(void) zfs_init(void)
{ {

View File

@ -182,6 +182,25 @@ zpl_remount_fs(struct super_block *sb, int *flags, char *data)
return (error); return (error);
} }
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 static int
__zpl_show_options(struct seq_file *seq, zfsvfs_t *zfsvfs) __zpl_show_options(struct seq_file *seq, zfsvfs_t *zfsvfs)
{ {
@ -314,6 +333,7 @@ const struct super_operations zpl_super_operations = {
.sync_fs = zpl_sync_fs, .sync_fs = zpl_sync_fs,
.statfs = zpl_statfs, .statfs = zpl_statfs,
.remount_fs = zpl_remount_fs, .remount_fs = zpl_remount_fs,
.show_devname = zpl_show_devname,
.show_options = zpl_show_options, .show_options = zpl_show_options,
.show_stats = NULL, .show_stats = NULL,
}; };

View File

@ -46,14 +46,12 @@
#include <sys/sunddi.h> #include <sys/sunddi.h>
#include <sys/zfeature.h> #include <sys/zfeature.h>
#include <sys/policy.h> #include <sys/policy.h>
#include <sys/zfs_vfsops.h>
#include <sys/zfs_znode.h> #include <sys/zfs_znode.h>
#include <sys/zvol.h> #include <sys/zvol.h>
#include <sys/zthr.h> #include <sys/zthr.h>
#include "zfs_namecheck.h" #include "zfs_namecheck.h"
#include "zfs_prop.h" #include "zfs_prop.h"
#ifdef _KERNEL
#include <sys/zfs_vfsops.h>
#endif
/* /*
* Filesystem and Snapshot Limits * 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, VERIFY0(zap_add(mos, dsl_dir_phys(newparent)->dd_child_dir_zapobj,
dd->dd_myname, 8, 1, &dd->dd_object, tx)); 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, zvol_rename_minors(dp->dp_spa, ddra->ddra_oldname,
ddra->ddra_newname, B_TRUE); ddra->ddra_newname, B_TRUE);

View File

@ -4316,6 +4316,7 @@ zfs_ioc_rename(zfs_cmd_t *zc)
objset_t *os; objset_t *os;
dmu_objset_type_t ost; dmu_objset_type_t ost;
boolean_t recursive = zc->zc_cookie & 1; boolean_t recursive = zc->zc_cookie & 1;
boolean_t nounmount = !!(zc->zc_cookie & 2);
char *at; char *at;
int err; 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)) if (strncmp(zc->zc_name, zc->zc_value, at - zc->zc_name + 1))
return (SET_ERROR(EXDEV)); return (SET_ERROR(EXDEV));
*at = '\0'; *at = '\0';
if (ost == DMU_OST_ZFS) { if (ost == DMU_OST_ZFS && !nounmount) {
error = dmu_objset_find(zc->zc_name, error = dmu_objset_find(zc->zc_name,
recursive_unmount, at + 1, recursive_unmount, at + 1,
recursive ? DS_FIND_CHILDREN : 0); recursive ? DS_FIND_CHILDREN : 0);

View File

@ -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_007_pos', 'zfs_rename_008_pos', 'zfs_rename_009_neg',
'zfs_rename_010_neg', 'zfs_rename_011_pos', 'zfs_rename_012_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_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'] tags = ['functional', 'cli_root', 'zfs_rename']
[tests/functional/cli_root/zfs_reservation] [tests/functional/cli_root/zfs_reservation]

View File

@ -18,7 +18,8 @@ dist_pkgdata_SCRIPTS = \
zfs_rename_014_neg.ksh \ zfs_rename_014_neg.ksh \
zfs_rename_encrypted_child.ksh \ zfs_rename_encrypted_child.ksh \
zfs_rename_to_encrypted.ksh \ zfs_rename_to_encrypted.ksh \
zfs_rename_mountpoint.ksh zfs_rename_mountpoint.ksh \
zfs_rename_nounmount.ksh
dist_pkgdata_DATA = \ dist_pkgdata_DATA = \
zfs_rename.cfg \ zfs_rename.cfg \

View File

@ -34,8 +34,8 @@ verify_runnable "both"
function rename_cleanup function rename_cleanup
{ {
log_note zfs destroy -fR $TESTPOOL/rename_test zfs destroy -fR $TESTPOOL/rename_test
log_note zfs destroy -fR $TESTPOOL/renamed zfs destroy -fR $TESTPOOL/renamed
} }
log_onexit rename_cleanup log_onexit rename_cleanup

View File

@ -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"