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:
parent
88d19d7cc2
commit
7b4e27232d
|
@ -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);
|
||||||
|
|
|
@ -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. */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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 */
|
|
@ -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) &&
|
||||||
|
|
|
@ -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"));
|
||||||
|
|
|
@ -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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
Loading…
Reference in New Issue