]> granicus.if.org Git - zfs/commitdiff
Add 'zfs umount -u' for encrypted datasets
authorTom Caputi <tcaputi@datto.com>
Fri, 28 Jun 2019 19:38:37 +0000 (15:38 -0400)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Fri, 28 Jun 2019 19:38:37 +0000 (12:38 -0700)
This patch adds the ability for the user to unload keys for
datasets as they are being unmounted. This is analogous to
'zfs mount -l'.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alek Pinchuk <apinchuk@datto.com>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes: #8917
Closes: #8952
cmd/zfs/zfs_main.c
lib/libzfs/libzfs_mount.c
man/man8/zfs.8
tests/runfiles/linux.run
tests/zfs-tests/tests/functional/cli_root/zfs_unmount/Makefile.am
tests/zfs-tests/tests/functional/cli_root/zfs_unmount/zfs_unmount_unload_keys.ksh [new file with mode: 0755]

index 30942e1f0e83fc441b4c5fa618e1c2109d45c1b5..bddf25c2d40e8f14e7deec12dc2188b283163e7b 100644 (file)
@@ -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.
  */
index 39ca2be05019664d78ef6edb5c593209e835df56..7497a72233ad5ceaf3f8a2cd4a68f1955ba74203 100644 (file)
@@ -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);
 
index 0a23466b7f99e97eeb889dc1feff553645287a7e..7058ec503fad4ed4681ed602060ed7d9bbe946d4 100644 (file)
 .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
index 4a9c060654dd3cd95a0bbfc392e083fad21c5a33..e5dd044ac01f268e77d102aa7a83e8380e74bfc0 100644 (file)
@@ -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]
index 34cbb17ae4cecd65234268eccd316aaa1bbe51d2..6507b094df4711a378a8d3372b3bd9ab027ef679 100644 (file)
@@ -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 (executable)
index 0000000..d6d0a7e
--- /dev/null
@@ -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"