summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/zfs/zfs_main.c13
-rw-r--r--lib/libzfs/libzfs_mount.c28
-rw-r--r--man/man8/zfs.813
-rw-r--r--tests/runfiles/linux.run2
-rw-r--r--tests/zfs-tests/tests/functional/cli_root/zfs_unmount/Makefile.am3
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zfs_unmount/zfs_unmount_unload_keys.ksh79
6 files changed, 125 insertions, 13 deletions
diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c
index 30942e1f0..bddf25c2d 100644
--- a/cmd/zfs/zfs_main.c
+++ b/cmd/zfs/zfs_main.c
@@ -315,7 +315,7 @@ get_usage(zfs_help_t idx)
return (gettext("\tsnapshot [-r] [-o property=value] ... "
"<filesystem|volume>@<snap> ...\n"));
case HELP_UNMOUNT:
- return (gettext("\tunmount [-f] "
+ return (gettext("\tunmount [-fu] "
"<-a | filesystem|mountpoint>\n"));
case HELP_UNSHARE:
return (gettext("\tunshare "
@@ -7015,13 +7015,16 @@ unshare_unmount(int op, int argc, char **argv)
char sharesmb[ZFS_MAXPROPLEN];
/* check options */
- while ((c = getopt(argc, argv, op == OP_SHARE ? ":a" : "af")) != -1) {
+ while ((c = getopt(argc, argv, op == OP_SHARE ? ":a" : "afu")) != -1) {
switch (c) {
case 'a':
do_all = 1;
break;
case 'f':
- flags = MS_FORCE;
+ flags |= MS_FORCE;
+ break;
+ case 'u':
+ flags |= MS_CRYPT;
break;
case ':':
(void) fprintf(stderr, gettext("missing argument for "
@@ -7281,8 +7284,8 @@ unshare_unmount(int op, int argc, char **argv)
}
/*
- * zfs unmount -a
- * zfs unmount filesystem
+ * zfs unmount [-fu] -a
+ * zfs unmount [-fu] filesystem
*
* Unmount all filesystems, or a specific ZFS filesystem.
*/
diff --git a/lib/libzfs/libzfs_mount.c b/lib/libzfs/libzfs_mount.c
index 39ca2be05..7497a7223 100644
--- a/lib/libzfs/libzfs_mount.c
+++ b/lib/libzfs/libzfs_mount.c
@@ -668,6 +668,7 @@ zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags)
libzfs_handle_t *hdl = zhp->zfs_hdl;
struct mnttab entry;
char *mntpt = NULL;
+ boolean_t encroot, unmounted = B_FALSE;
/* check to see if we need to unmount the filesystem */
if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
@@ -696,8 +697,33 @@ zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags)
(void) zfs_shareall(zhp);
return (-1);
}
+
libzfs_mnttab_remove(hdl, zhp->zfs_name);
free(mntpt);
+ unmounted = B_TRUE;
+ }
+
+ /*
+ * If the MS_CRYPT flag is provided we must ensure we attempt to
+ * unload the dataset's key regardless of whether we did any work
+ * to unmount it. We only do this for encryption roots.
+ */
+ if ((flags & MS_CRYPT) != 0 &&
+ zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) != ZIO_CRYPT_OFF) {
+ zfs_refresh_properties(zhp);
+
+ if (zfs_crypto_get_encryption_root(zhp, &encroot, NULL) != 0 &&
+ unmounted) {
+ (void) zfs_mount(zhp, NULL, 0);
+ return (-1);
+ }
+
+ if (encroot && zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS) ==
+ ZFS_KEYSTATUS_AVAILABLE &&
+ zfs_crypto_unload_key(zhp) != 0) {
+ (void) zfs_mount(zhp, NULL, 0);
+ return (-1);
+ }
}
return (0);
@@ -715,7 +741,7 @@ zfs_unmountall(zfs_handle_t *zhp, int flags)
int ret;
clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT,
- CL_GATHER_ITER_MOUNTED, 0);
+ CL_GATHER_ITER_MOUNTED, flags);
if (clp == NULL)
return (-1);
diff --git a/man/man8/zfs.8 b/man/man8/zfs.8
index 0a23466b7..7058ec503 100644
--- a/man/man8/zfs.8
+++ b/man/man8/zfs.8
@@ -182,7 +182,7 @@
.Fl a | Ar filesystem
.Nm
.Cm unmount
-.Op Fl f
+.Op Fl fu
.Fl a | Ar filesystem Ns | Ns Ar mountpoint
.Nm
.Cm share
@@ -3462,7 +3462,7 @@ Attempt to force mounting of all filesystems, even those that couldn't normally
.It Xo
.Nm
.Cm unmount
-.Op Fl f
+.Op Fl fu
.Fl a | Ar filesystem Ns | Ns Ar mountpoint
.Xc
Unmounts currently mounted ZFS file systems.
@@ -3470,13 +3470,16 @@ Unmounts currently mounted ZFS file systems.
.It Fl a
Unmount all available ZFS file systems.
Invoked automatically as part of the shutdown process.
+.It Fl f
+Forcefully unmount the file system, even if it is currently in use.
+.El
+.It Fl u
+Unload keys for any encryption roots unmounted by this command.
+.El
.It Ar filesystem Ns | Ns Ar mountpoint
Unmount the specified filesystem.
The command can also be given a path to a ZFS file system mount point on the
system.
-.It Fl f
-Forcefully unmount the file system, even if it is currently in use.
-.El
.It Xo
.Nm
.Cm share
diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run
index 4a9c06065..e5dd044ac 100644
--- a/tests/runfiles/linux.run
+++ b/tests/runfiles/linux.run
@@ -275,7 +275,7 @@ tags = ['functional', 'cli_root', 'zfs_unload-key']
tests = ['zfs_unmount_001_pos', 'zfs_unmount_002_pos', 'zfs_unmount_003_pos',
'zfs_unmount_004_pos', 'zfs_unmount_005_pos', 'zfs_unmount_006_pos',
'zfs_unmount_007_neg', 'zfs_unmount_008_neg', 'zfs_unmount_009_pos',
- 'zfs_unmount_all_001_pos', 'zfs_unmount_nested']
+ 'zfs_unmount_all_001_pos', 'zfs_unmount_nested', 'zfs_unmount_unload_keys']
tags = ['functional', 'cli_root', 'zfs_unmount']
[tests/functional/cli_root/zfs_unshare]
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_unmount/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/zfs_unmount/Makefile.am
index 34cbb17ae..6507b094d 100644
--- a/tests/zfs-tests/tests/functional/cli_root/zfs_unmount/Makefile.am
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_unmount/Makefile.am
@@ -12,7 +12,8 @@ dist_pkgdata_SCRIPTS = \
zfs_unmount_008_neg.ksh \
zfs_unmount_009_pos.ksh \
zfs_unmount_all_001_pos.ksh \
- zfs_unmount_nested.ksh
+ zfs_unmount_nested.ksh \
+ zfs_unmount_unload_keys.ksh
dist_pkgdata_DATA = \
zfs_unmount.cfg \
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_unmount/zfs_unmount_unload_keys.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_unmount/zfs_unmount_unload_keys.ksh
new file mode 100755
index 000000000..d6d0a7e9a
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_unmount/zfs_unmount_unload_keys.ksh
@@ -0,0 +1,79 @@
+#!/bin/ksh -p
+#
+# 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
+#
+
+#
+# Copyright (c) 2017 Datto, Inc. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zfs_unmount/zfs_unmount.kshlib
+. $STF_SUITE/tests/functional/cli_root/zfs_load-key/zfs_load-key_common.kshlib
+
+#
+# DESCRIPTION:
+# "zfs unmount -u" should allow the user to unload their encryption
+# keys while unmounting one or more datasets
+#
+# STRATEGY:
+# 1. Create a hierarchy of encrypted datasets
+# 2. Test that 'zfs unmount -u' unloads keys as it unmounts a dataset
+# 3. Test that 'zfs unmount -u' unloads keys as it unmounts multiple datasets
+# 4. Test that 'zfs unmount -u' returns an error if the key is still in
+# use by a clone.
+#
+
+verify_runnable "both"
+
+function cleanup
+{
+ datasetexists $TESTPOOL/$TESTFS2 && \
+ log_must zfs destroy -r $TESTPOOL/$TESTFS2
+ datasetexists $TESTPOOL/$TESTFS2/newroot && \
+ log_must zfs destroy -r $TESTPOOL/$TESTFS2/newroot
+ datasetexists $TESTPOOL/$TESTFS2/child && \
+ log_must zfs destroy -r $TESTPOOL/$TESTFS2/child
+
+}
+log_onexit cleanup
+
+log_assert "'zfs unmount -u' should unload keys for datasets as they are unmounted"
+log_must eval "echo 'password' | zfs create -o encryption=on -o keyformat=passphrase $TESTPOOL/$TESTFS2"
+log_must eval "echo 'password' | zfs create -o encryption=on -o keyformat=passphrase $TESTPOOL/$TESTFS2/newroot"
+log_must zfs create $TESTPOOL/$TESTFS2/child
+
+log_must zfs umount -u $TESTPOOL/$TESTFS2/newroot
+log_must key_unavailable $TESTPOOL/$TESTFS2/newroot
+log_must eval "echo 'password' | zfs mount -l $TESTPOOL/$TESTFS2/newroot"
+
+log_must zfs umount -u $TESTPOOL/$TESTFS2
+log_must key_unavailable $TESTPOOL/$TESTFS2
+log_must key_unavailable $TESTPOOL/$TESTFS2/newroot
+log_must key_unavailable $TESTPOOL/$TESTFS2/child
+log_must eval "echo 'password' | zfs mount -l $TESTPOOL/$TESTFS2/newroot"
+
+log_must zfs snap $TESTPOOL/$TESTFS2/newroot@1
+log_must zfs clone $TESTPOOL/$TESTFS2/newroot@1 $TESTPOOL/$TESTFS2/clone
+log_mustnot zfs umount -u $TESTPOOL/$TESTFS2/newroot
+log_must key_available $TESTPOOL/$TESTFS2/newroot
+log_must mounted $TESTPOOL/$TESTFS2/newroot
+
+log_pass "'zfs unmount -u' unloads keys for datasets as they are unmounted"