Skip to content

Commit

Permalink
Add 'zfs rename -u' to rename without remounting
Browse files Browse the repository at this point in the history
Allow to rename file systems without remounting if it is possible.
It is possible for file systems with 'mountpoint' preperty 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>
Ported by: Matt Macy <mmacy@freebsd.org>
Signed-off-by: Ryan Moeller <ryan@iXsystems.com>
  • Loading branch information
Ryan Moeller authored and Ryan Moeller committed Aug 29, 2020
1 parent 2b07c5a commit c1c41ec
Show file tree
Hide file tree
Showing 21 changed files with 284 additions and 39 deletions.
43 changes: 30 additions & 13 deletions cmd/zfs/zfs_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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. */
Expand All @@ -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);
Expand Down
14 changes: 13 additions & 1 deletion include/libzfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
4 changes: 4 additions & 0 deletions include/libzfs_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion include/os/freebsd/zfs/sys/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion include/os/linux/zfs/sys/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
File renamed without changes.
1 change: 1 addition & 0 deletions include/sys/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
35 changes: 35 additions & 0 deletions include/sys/zfs_vfsops.h
Original file line number Diff line number Diff line change
@@ -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 */
8 changes: 6 additions & 2 deletions lib/libzfs/libzfs_changelist.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

/*
Expand Down Expand Up @@ -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) &&
Expand Down
32 changes: 23 additions & 9 deletions lib/libzfs/libzfs_dataset.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];

Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -4470,16 +4470,28 @@ 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;
goto error;
}
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;
Expand All @@ -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)) {
Expand All @@ -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) {
/*
Expand All @@ -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"));
Expand Down
6 changes: 6 additions & 0 deletions lib/libzpool/kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -1408,3 +1409,8 @@ zfs_file_put(int fd)
{
abort();
}

void
zfsvfs_update_fromname(const char *oldname, const char *newname)
{
}
Loading

0 comments on commit c1c41ec

Please sign in to comment.