summaryrefslogtreecommitdiffstats
path: root/lib/libzfs/libzfs_dataset.c
diff options
context:
space:
mode:
authorRyan Moeller <[email protected]>2020-09-01 19:14:16 -0400
committerGitHub <[email protected]>2020-09-01 16:14:16 -0700
commit7b4e27232dec4595ef842ce7c0e31aafd255a20a (patch)
treee96bac7203b11fa82a0c2dad1177dfc78540ddcc /lib/libzfs/libzfs_dataset.c
parent88d19d7cc2586fbb4fe917b16763e88bcdf62d98 (diff)
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 <[email protected]> Reviewed-by: Allan Jude <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Ported by: Matt Macy <[email protected]> Signed-off-by: Ryan Moeller <[email protected]> Closes #10839
Diffstat (limited to 'lib/libzfs/libzfs_dataset.c')
-rw-r--r--lib/libzfs/libzfs_dataset.c32
1 files changed, 23 insertions, 9 deletions
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"));