Add 'zfs umount -u' for encrypted datasets

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
This commit is contained in:
Tom Caputi 2019-06-28 15:38:37 -04:00 committed by Brian Behlendorf
parent 679b0f2abf
commit 765d1f0644
6 changed files with 125 additions and 13 deletions

View File

@ -315,7 +315,7 @@ get_usage(zfs_help_t idx)
return (gettext("\tsnapshot [-r] [-o property=value] ... " return (gettext("\tsnapshot [-r] [-o property=value] ... "
"<filesystem|volume>@<snap> ...\n")); "<filesystem|volume>@<snap> ...\n"));
case HELP_UNMOUNT: case HELP_UNMOUNT:
return (gettext("\tunmount [-f] " return (gettext("\tunmount [-fu] "
"<-a | filesystem|mountpoint>\n")); "<-a | filesystem|mountpoint>\n"));
case HELP_UNSHARE: case HELP_UNSHARE:
return (gettext("\tunshare " return (gettext("\tunshare "
@ -7015,13 +7015,16 @@ unshare_unmount(int op, int argc, char **argv)
char sharesmb[ZFS_MAXPROPLEN]; char sharesmb[ZFS_MAXPROPLEN];
/* check options */ /* 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) { switch (c) {
case 'a': case 'a':
do_all = 1; do_all = 1;
break; break;
case 'f': case 'f':
flags = MS_FORCE; flags |= MS_FORCE;
break;
case 'u':
flags |= MS_CRYPT;
break; break;
case ':': case ':':
(void) fprintf(stderr, gettext("missing argument for " (void) fprintf(stderr, gettext("missing argument for "
@ -7281,8 +7284,8 @@ unshare_unmount(int op, int argc, char **argv)
} }
/* /*
* zfs unmount -a * zfs unmount [-fu] -a
* zfs unmount filesystem * zfs unmount [-fu] filesystem
* *
* Unmount all filesystems, or a specific ZFS filesystem. * Unmount all filesystems, or a specific ZFS filesystem.
*/ */

View File

@ -668,6 +668,7 @@ zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags)
libzfs_handle_t *hdl = zhp->zfs_hdl; libzfs_handle_t *hdl = zhp->zfs_hdl;
struct mnttab entry; struct mnttab entry;
char *mntpt = NULL; char *mntpt = NULL;
boolean_t encroot, unmounted = B_FALSE;
/* check to see if we need to unmount the filesystem */ /* check to see if we need to unmount the filesystem */
if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_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); (void) zfs_shareall(zhp);
return (-1); return (-1);
} }
libzfs_mnttab_remove(hdl, zhp->zfs_name); libzfs_mnttab_remove(hdl, zhp->zfs_name);
free(mntpt); 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); return (0);
@ -715,7 +741,7 @@ zfs_unmountall(zfs_handle_t *zhp, int flags)
int ret; int ret;
clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT,
CL_GATHER_ITER_MOUNTED, 0); CL_GATHER_ITER_MOUNTED, flags);
if (clp == NULL) if (clp == NULL)
return (-1); return (-1);

View File

@ -182,7 +182,7 @@
.Fl a | Ar filesystem .Fl a | Ar filesystem
.Nm .Nm
.Cm unmount .Cm unmount
.Op Fl f .Op Fl fu
.Fl a | Ar filesystem Ns | Ns Ar mountpoint .Fl a | Ar filesystem Ns | Ns Ar mountpoint
.Nm .Nm
.Cm share .Cm share
@ -3462,7 +3462,7 @@ Attempt to force mounting of all filesystems, even those that couldn't normally
.It Xo .It Xo
.Nm .Nm
.Cm unmount .Cm unmount
.Op Fl f .Op Fl fu
.Fl a | Ar filesystem Ns | Ns Ar mountpoint .Fl a | Ar filesystem Ns | Ns Ar mountpoint
.Xc .Xc
Unmounts currently mounted ZFS file systems. Unmounts currently mounted ZFS file systems.
@ -3470,13 +3470,16 @@ Unmounts currently mounted ZFS file systems.
.It Fl a .It Fl a
Unmount all available ZFS file systems. Unmount all available ZFS file systems.
Invoked automatically as part of the shutdown process. 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 .It Ar filesystem Ns | Ns Ar mountpoint
Unmount the specified filesystem. Unmount the specified filesystem.
The command can also be given a path to a ZFS file system mount point on the The command can also be given a path to a ZFS file system mount point on the
system. system.
.It Fl f
Forcefully unmount the file system, even if it is currently in use.
.El
.It Xo .It Xo
.Nm .Nm
.Cm share .Cm share

View 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', 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_004_pos', 'zfs_unmount_005_pos', 'zfs_unmount_006_pos',
'zfs_unmount_007_neg', 'zfs_unmount_008_neg', 'zfs_unmount_009_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'] tags = ['functional', 'cli_root', 'zfs_unmount']
[tests/functional/cli_root/zfs_unshare] [tests/functional/cli_root/zfs_unshare]

View File

@ -12,7 +12,8 @@ dist_pkgdata_SCRIPTS = \
zfs_unmount_008_neg.ksh \ zfs_unmount_008_neg.ksh \
zfs_unmount_009_pos.ksh \ zfs_unmount_009_pos.ksh \
zfs_unmount_all_001_pos.ksh \ zfs_unmount_all_001_pos.ksh \
zfs_unmount_nested.ksh zfs_unmount_nested.ksh \
zfs_unmount_unload_keys.ksh
dist_pkgdata_DATA = \ dist_pkgdata_DATA = \
zfs_unmount.cfg \ zfs_unmount.cfg \

View File

@ -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"