Skip to content

Commit

Permalink
Add 'zfs umount -u' for encrypted datasets
Browse files Browse the repository at this point in the history
This patch adds the ability for the user to unload keys for
datasets as they are being unmounted. This is analagous to
'zfs mount -l'.

Signed-off-by: Tom Caputi <tcaputi@datto.com>
  • Loading branch information
Tom Caputi committed Jun 24, 2019
1 parent df24bcf commit a3cba5e
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 5 deletions.
7 changes: 5 additions & 2 deletions cmd/zfs/zfs_main.c
Expand Up @@ -6868,13 +6868,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 ? ":au" : "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 "
Expand Down
28 changes: 27 additions & 1 deletion lib/libzfs/libzfs_mount.c
Expand Up @@ -659,6 +659,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) &&
Expand Down Expand Up @@ -687,8 +688,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);
Expand All @@ -706,7 +732,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);

Expand Down
2 changes: 1 addition & 1 deletion tests/runfiles/linux.run
Expand Up @@ -279,7 +279,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]
Expand Down
Expand Up @@ -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 \
Expand Down
@@ -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"

0 comments on commit a3cba5e

Please sign in to comment.