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] " "\n" - "\trename [-f] -p \n" + "\trename -p [-f] \n" + "\trename -u [-f] \n" "\trename -r \n")); case HELP_ROLLBACK: return (gettext("\trollback [-rRf] \n")); @@ -3603,36 +3604,40 @@ zfs_do_list(int argc, char **argv) } /* - * zfs rename [-f] + * zfs rename [-fu] * zfs rename [-f] -p - * zfs rename -r + * zfs rename [-u] -r * * 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 similarity index 98% rename from include/os/freebsd/zfs/sys/zfs_vfsops.h rename to 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 similarity index 100% rename from include/os/linux/zfs/sys/zfs_vfsops.h rename to 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 +#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 #include #include +#include #include #include #include @@ -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 @@ -182,6 +182,25 @@ zpl_remount_fs(struct super_block *sb, int *flags, char *data) 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 __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, .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 #include #include +#include #include #include #include #include "zfs_namecheck.h" #include "zfs_prop.h" -#ifdef _KERNEL -#include -#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"