From patchwork Tue Aug 8 17:12:03 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13346815 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 40161C41513 for ; Tue, 8 Aug 2023 18:55:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234221AbjHHSzO (ORCPT ); Tue, 8 Aug 2023 14:55:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46178 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232708AbjHHSyq (ORCPT ); Tue, 8 Aug 2023 14:54:46 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2C13F696A5; Tue, 8 Aug 2023 10:12:28 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 8E1AF83439; Tue, 8 Aug 2023 13:12:27 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1691514747; bh=vlBC78VJmNMxunWwFnRcE+UT0odKU0TdcAhiXzR6flE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=XVDbAyrLq8XD4ZP7gtLCGYDMwyHsxTXFV86QWDSoaHmtKtSJYjtAyTl4GoSTpdAzU DsW4RdY/gOf4hv9cpDENGYeBR1/cgqXwToR3rmQnAX95ITGh9VATtxQy6bAQ91eHkQ 3yoLaBvsh6CsoLyd6/ZJ28kjZJME943jvH6zhory004/Ndhyh5mfj5oqp4laWBUOkk 8TteVne4nKXacwpiy9+gLv3oscSD80g9OOr88HG2kM23FqyJZrkzgndo9nVacBJV6l JO14Dp/TEeNHGby4geXxUiBbYGfFokkqZtKjEkEPEvzt9/G4YxO35xyTZj1Zc2TdPb L8oPNVOuW0bJw== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, Eric Biggers Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v3 01/17] btrfs: disable various operations on encrypted inodes Date: Tue, 8 Aug 2023 13:12:03 -0400 Message-ID: <6032492d9ee3168248d1d66577fca50464716c01.1691510179.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Omar Sandoval Initially, only normal data extents will be encrypted. This change forbids various other bits: - allows reflinking only if both inodes have the same encryption status - disable inline data on encrypted inodes Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/inode.c | 3 ++- fs/btrfs/reflink.c | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 6a68d5a3ed20..bb9e1e608488 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -629,7 +629,8 @@ static noinline int cow_file_range_inline(struct btrfs_inode *inode, u64 size, * compressed) data fits in a leaf and the configured maximum inline * size. */ - if (size < i_size_read(&inode->vfs_inode) || + if (IS_ENCRYPTED(&inode->vfs_inode) || + size < i_size_read(&inode->vfs_inode) || size > fs_info->sectorsize || data_len > BTRFS_MAX_INLINE_DATA_SIZE(fs_info) || data_len > fs_info->max_inline) diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c index 0474bbe39da7..ad722f495c9b 100644 --- a/fs/btrfs/reflink.c +++ b/fs/btrfs/reflink.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include #include #include "ctree.h" #include "fs.h" @@ -811,6 +812,12 @@ static int btrfs_remap_file_range_prep(struct file *file_in, loff_t pos_in, ASSERT(inode_in->i_sb == inode_out->i_sb); } + /* + * Can only reflink encrypted files if both files are encrypted. + */ + if (IS_ENCRYPTED(inode_in) != IS_ENCRYPTED(inode_out)) + return -EINVAL; + /* Don't make the dst file partly checksummed */ if ((BTRFS_I(inode_in)->flags & BTRFS_INODE_NODATASUM) != (BTRFS_I(inode_out)->flags & BTRFS_INODE_NODATASUM)) { From patchwork Tue Aug 8 17:12:04 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13346816 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A11C2C001B0 for ; Tue, 8 Aug 2023 18:55:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232213AbjHHSzR (ORCPT ); Tue, 8 Aug 2023 14:55:17 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55938 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234251AbjHHSyr (ORCPT ); Tue, 8 Aug 2023 14:54:47 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8CCF0696B0; Tue, 8 Aug 2023 10:12:29 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id EE1498343E; Tue, 8 Aug 2023 13:12:28 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1691514749; bh=qYTOnD2X09d8CWs7gVr1n+nGjMJSg4tguTG1TOz83fw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=a+zN3vdUUOgJKeKRIKi+vFBuveCBaoB+1QKyQXQP3+gzKKPLoLRlLk5zUqtMnT4nw wK/OeYLZXRBPaliFAdsEp6K7/BblQNWTvRGl4HCM998THLchYX39apNynej4bRYciB PbC3/RrmLPLkfNQaWpS+H8PC9s1QXB72bGVAOZWphgZK08hNgM2E3S8VeoB4ieMwm9 q06rYDKDuezeIQDFH2BXg0x+huYxiazCxK40Vb9WoRsCJp/5qzzVps8M5W4dusfKO4 QuPTki9X2GZk2OlHdP+Z/SMmrbyXc2NFojwa3ovmRJcqtxQdZeCxNjb3w2lG9jxbt6 7khnJjgXD4jKA== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, Eric Biggers Cc: Sweet Tea Dorminy Subject: [PATCH v3 02/17] btrfs: disable verity on encrypted inodes Date: Tue, 8 Aug 2023 13:12:04 -0400 Message-ID: <7f5444829adca5f4bd647749382bd0771258c846.1691510179.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org Right now there isn't a way to encrypt things that aren't either filenames in directories or data on blocks on disk with extent encryption, so for now, disable verity usage with encryption on btrfs. Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/verity.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c index c5ff16f9e9fa..cda969c6cb0c 100644 --- a/fs/btrfs/verity.c +++ b/fs/btrfs/verity.c @@ -588,6 +588,9 @@ static int btrfs_begin_enable_verity(struct file *filp) ASSERT(inode_is_locked(file_inode(filp))); + if (IS_ENCRYPTED(&inode->vfs_inode)) + return -EINVAL; + if (test_bit(BTRFS_INODE_VERITY_IN_PROGRESS, &inode->runtime_flags)) return -EBUSY; From patchwork Tue Aug 8 17:12:05 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13346808 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AD4C4C04A94 for ; Tue, 8 Aug 2023 18:54:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232535AbjHHSyr (ORCPT ); Tue, 8 Aug 2023 14:54:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54268 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233301AbjHHSya (ORCPT ); Tue, 8 Aug 2023 14:54:30 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EC7116A3AB; Tue, 8 Aug 2023 10:12:30 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 4F9F68343D; Tue, 8 Aug 2023 13:12:30 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1691514750; bh=3IWatOH7yE2Wr/SMT2Ef+K/OCGYvcAy8aJjTU5cB8IA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=MIWesGwZJs2Ld83oAARL1X6iQj0XZkssLRKTmOpIjyOlAaQ4uvt252QqfRPTqTAvW P59pbJpq84OfO/2l+wHvVV/UEflohwI+osa7MLs89JB0Inm6UYCzXfeEoBYYyKjr3F 6oFu9aYJKLj4qm0gYA9t1/L4NSr6TLZHO+yruHyDzWFLClJL8lPNZNRs92+dD6VxJf NV5XxvObA49t+G8E0J9rHX2+7B0GIltpre+vRmtTR8xEkstrkfBuiPob/3PJ+QI7hs HaE9/K9Y5APICBY0KFxo1SqL6Ay6dAnp/DPP02SA+ezlXDH7f5BzQK1tP/N/qv3XmU /mqg7OOBEFeVg== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, Eric Biggers Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v3 03/17] fscrypt: expose fscrypt_nokey_name Date: Tue, 8 Aug 2023 13:12:05 -0400 Message-ID: <6205fb63ea974cb39be20137f6e4cc7f6ed2d47a.1691510179.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Omar Sandoval btrfs stores its data structures, including filenames in directories, in its own buffer implementation, struct extent_buffer, composed of several non-contiguous pages. We could copy filenames into a temporary buffer and use fscrypt_match_name() against that buffer, such extensive memcpying would be expensive. Instead, exposing fscrypt_nokey_name as in this change allows btrfs to recapitulate fscrypt_match_name() using methods on struct extent_buffer instead of dealing with a raw byte array. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy Reviewed-by: Josef Bacik --- fs/crypto/fname.c | 39 +-------------------------------------- include/linux/fscrypt.h | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index edb78cd1b0e7..fb6bc46302c1 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include "fscrypt_private.h" @@ -26,43 +25,7 @@ #define FSCRYPT_FNAME_MIN_MSG_LEN 16 /* - * struct fscrypt_nokey_name - identifier for directory entry when key is absent - * - * When userspace lists an encrypted directory without access to the key, the - * filesystem must present a unique "no-key name" for each filename that allows - * it to find the directory entry again if requested. Naively, that would just - * mean using the ciphertext filenames. However, since the ciphertext filenames - * can contain illegal characters ('\0' and '/'), they must be encoded in some - * way. We use base64url. But that can cause names to exceed NAME_MAX (255 - * bytes), so we also need to use a strong hash to abbreviate long names. - * - * The filesystem may also need another kind of hash, the "dirhash", to quickly - * find the directory entry. Since filesystems normally compute the dirhash - * over the on-disk filename (i.e. the ciphertext), it's not computable from - * no-key names that abbreviate the ciphertext using the strong hash to fit in - * NAME_MAX. It's also not computable if it's a keyed hash taken over the - * plaintext (but it may still be available in the on-disk directory entry); - * casefolded directories use this type of dirhash. At least in these cases, - * each no-key name must include the name's dirhash too. - * - * To meet all these requirements, we base64url-encode the following - * variable-length structure. It contains the dirhash, or 0's if the filesystem - * didn't provide one; up to 149 bytes of the ciphertext name; and for - * ciphertexts longer than 149 bytes, also the SHA-256 of the remaining bytes. - * - * This ensures that each no-key name contains everything needed to find the - * directory entry again, contains only legal characters, doesn't exceed - * NAME_MAX, is unambiguous unless there's a SHA-256 collision, and that we only - * take the performance hit of SHA-256 on very long filenames (which are rare). - */ -struct fscrypt_nokey_name { - u32 dirhash[2]; - u8 bytes[149]; - u8 sha256[SHA256_DIGEST_SIZE]; -}; /* 189 bytes => 252 bytes base64url-encoded, which is <= NAME_MAX (255) */ - -/* - * Decoded size of max-size no-key name, i.e. a name that was abbreviated using + * Decoded size of max-size nokey name, i.e. a name that was abbreviated using * the strong hash and thus includes the 'sha256' field. This isn't simply * sizeof(struct fscrypt_nokey_name), as the padding at the end isn't included. */ diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index b67054a2c965..fd8cb413e718 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -16,6 +16,7 @@ #include #include #include +#include #include /* @@ -54,6 +55,42 @@ struct fscrypt_name { #define fname_name(p) ((p)->disk_name.name) #define fname_len(p) ((p)->disk_name.len) +/* + * struct fscrypt_nokey_name - identifier for directory entry when key is absent + * + * When userspace lists an encrypted directory without access to the key, the + * filesystem must present a unique "no-key name" for each filename that allows + * it to find the directory entry again if requested. Naively, that would just + * mean using the ciphertext filenames. However, since the ciphertext filenames + * can contain illegal characters ('\0' and '/'), they must be encoded in some + * way. We use base64url. But that can cause names to exceed NAME_MAX (255 + * bytes), so we also need to use a strong hash to abbreviate long names. + * + * The filesystem may also need another kind of hash, the "dirhash", to quickly + * find the directory entry. Since filesystems normally compute the dirhash + * over the on-disk filename (i.e. the ciphertext), it's not computable from + * no-key names that abbreviate the ciphertext using the strong hash to fit in + * NAME_MAX. It's also not computable if it's a keyed hash taken over the + * plaintext (but it may still be available in the on-disk directory entry); + * casefolded directories use this type of dirhash. At least in these cases, + * each no-key name must include the name's dirhash too. + * + * To meet all these requirements, we base64url-encode the following + * variable-length structure. It contains the dirhash, or 0's if the filesystem + * didn't provide one; up to 149 bytes of the ciphertext name; and for + * ciphertexts longer than 149 bytes, also the SHA-256 of the remaining bytes. + * + * This ensures that each no-key name contains everything needed to find the + * directory entry again, contains only legal characters, doesn't exceed + * NAME_MAX, is unambiguous unless there's a SHA-256 collision, and that we only + * take the performance hit of SHA-256 on very long filenames (which are rare). + */ +struct fscrypt_nokey_name { + u32 dirhash[2]; + u8 bytes[149]; + u8 sha256[SHA256_DIGEST_SIZE]; +}; /* 189 bytes => 252 bytes base64url-encoded, which is <= NAME_MAX (255) */ + /* Maximum value for the third parameter of fscrypt_operations.set_context(). */ #define FSCRYPT_SET_CONTEXT_MAX_SIZE 40 From patchwork Tue Aug 8 17:12:06 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13346810 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6CE06C04FE2 for ; Tue, 8 Aug 2023 18:54:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231373AbjHHSyw (ORCPT ); Tue, 8 Aug 2023 14:54:52 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45430 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229821AbjHHSyc (ORCPT ); Tue, 8 Aug 2023 14:54:32 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5C29D6A3B0; Tue, 8 Aug 2023 10:12:32 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id A52C883439; Tue, 8 Aug 2023 13:12:31 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1691514751; bh=xgJA+4pwht0wHrLeGI31MNs1Vme+Qxfp1MH00wkJDIE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=cYQeLG7iDHacmABvg5jpz9+QameN3X9Eo/alh6gImA2kTnU7oTCPVKJHWv0kkFMiT gWUnXx/pjgzt4f6YD9l8dwKQ3GwkzCm1lV/2U/wx3khtmkybwN9B28WD6SjvPf4lkc Jug/HqF1LrslrBAnyAMP3JO5kT54duMiu9xoZsfhJHhWwh0QVUzBW2SYJ2PYjeNMyt P3oj33fY/1s/XWhpclwleyZOjVyNoPt6pwUVmxQFFfJUUKs2ID7dWKTX1Eyg2w6Iey Z93217XX2bccuospgRuTDzta6sEzY7hh1Piq3ExolpBqPQPOzC4ola0RCdy8d8PvF7 K1/MFkPOlSOwQ== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, Eric Biggers Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v3 04/17] btrfs: start using fscrypt hooks Date: Tue, 8 Aug 2023 13:12:06 -0400 Message-ID: <889ec4543ec7403cea1a4480a0d7b6c5f0bed217.1691510179.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Omar Sandoval In order to appropriately encrypt, create, open, rename, and various symlink operations must call fscrypt hooks. These determine whether the inode should be encrypted and do other preparatory actions. The superblock must have fscrypt operations registered, so implement the minimal set also, and introduce the new fscrypt.[ch] files to hold the fscrypt-specific functionality. Also add the key prefix for fscrypt v1 keys. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/Makefile | 1 + fs/btrfs/btrfs_inode.h | 1 + fs/btrfs/file.c | 3 ++ fs/btrfs/fscrypt.c | 8 ++++ fs/btrfs/fscrypt.h | 10 +++++ fs/btrfs/inode.c | 97 ++++++++++++++++++++++++++++++++++-------- fs/btrfs/super.c | 2 + 7 files changed, 104 insertions(+), 18 deletions(-) create mode 100644 fs/btrfs/fscrypt.c create mode 100644 fs/btrfs/fscrypt.h diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile index 90d53209755b..90d30b1e044a 100644 --- a/fs/btrfs/Makefile +++ b/fs/btrfs/Makefile @@ -40,6 +40,7 @@ btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o btrfs-$(CONFIG_BTRFS_FS_REF_VERIFY) += ref-verify.o btrfs-$(CONFIG_BLK_DEV_ZONED) += zoned.o btrfs-$(CONFIG_FS_VERITY) += verity.o +btrfs-$(CONFIG_FS_ENCRYPTION) += fscrypt.o btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/free-space-tests.o \ tests/extent-buffer-tests.o tests/btrfs-tests.o \ diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index bda1fdbba666..7abf98d56181 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -448,6 +448,7 @@ struct btrfs_new_inode_args { struct posix_acl *default_acl; struct posix_acl *acl; struct fscrypt_name fname; + bool encrypt; }; int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args, diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index fd03e689a6be..73038908876a 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -3698,6 +3698,9 @@ static int btrfs_file_open(struct inode *inode, struct file *filp) filp->f_mode |= FMODE_NOWAIT | FMODE_BUF_RASYNC | FMODE_BUF_WASYNC | FMODE_CAN_ODIRECT; + ret = fscrypt_file_open(inode, filp); + if (ret) + return ret; ret = fsverity_file_open(inode, filp); if (ret) diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c new file mode 100644 index 000000000000..3a53dc59c1e4 --- /dev/null +++ b/fs/btrfs/fscrypt.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "ctree.h" +#include "fscrypt.h" + +const struct fscrypt_operations btrfs_fscrypt_ops = { + .key_prefix = "btrfs:" +}; diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h new file mode 100644 index 000000000000..7f4e6888bd43 --- /dev/null +++ b/fs/btrfs/fscrypt.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef BTRFS_FSCRYPT_H +#define BTRFS_FSCRYPT_H + +#include + +extern const struct fscrypt_operations btrfs_fscrypt_ops; + +#endif /* BTRFS_FSCRYPT_H */ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index bb9e1e608488..49ee05ecc03a 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5214,11 +5214,8 @@ void btrfs_evict_inode(struct inode *inode) trace_btrfs_inode_evict(inode); - if (!root) { - fsverity_cleanup_inode(inode); - clear_inode(inode); - return; - } + if (!root) + goto cleanup; evict_inode_truncate_pages(inode); @@ -5318,6 +5315,9 @@ void btrfs_evict_inode(struct inode *inode) * to retry these periodically in the future. */ btrfs_remove_delayed_node(BTRFS_I(inode)); + +cleanup: + fscrypt_put_encryption_info(inode); fsverity_cleanup_inode(inode); clear_inode(inode); } @@ -6068,6 +6068,12 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args, return ret; } + ret = fscrypt_prepare_new_inode(dir, inode, &args->encrypt); + if (ret) { + fscrypt_free_filename(&args->fname); + return ret; + } + /* 1 to add inode item */ *trans_num_items = 1; /* 1 to add compression property */ @@ -6544,9 +6550,13 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir, if (inode->i_nlink >= BTRFS_LINK_MAX) return -EMLINK; + err = fscrypt_prepare_link(old_dentry, dir, dentry); + if (err) + return err; + err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &fname); if (err) - goto fail; + return err; err = btrfs_set_inode_index(BTRFS_I(dir), &index); if (err) @@ -8493,6 +8503,7 @@ void btrfs_test_destroy_inode(struct inode *inode) void btrfs_free_inode(struct inode *inode) { + fscrypt_free_inode(inode); kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode)); } @@ -8563,8 +8574,7 @@ int btrfs_drop_inode(struct inode *inode) /* the snap/subvol tree is on deleting */ if (btrfs_root_refs(&root->root_item) == 0) return 1; - else - return generic_drop_inode(inode); + return generic_drop_inode(inode) || fscrypt_drop_inode(inode); } static void init_once(void *foo) @@ -9155,6 +9165,11 @@ static int btrfs_rename2(struct mnt_idmap *idmap, struct inode *old_dir, if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT)) return -EINVAL; + ret = fscrypt_prepare_rename(old_dir, old_dentry, new_dir, new_dentry, + flags); + if (ret) + return ret; + if (flags & RENAME_EXCHANGE) ret = btrfs_rename_exchange(old_dir, old_dentry, new_dir, new_dentry); @@ -9374,15 +9389,22 @@ static int btrfs_symlink(struct mnt_idmap *idmap, struct inode *dir, }; unsigned int trans_num_items; int err; - int name_len; int datasize; unsigned long ptr; struct btrfs_file_extent_item *ei; struct extent_buffer *leaf; + struct fscrypt_str disk_link; + u32 name_len = strlen(symname); - name_len = strlen(symname); - if (name_len > BTRFS_MAX_INLINE_DATA_SIZE(fs_info)) - return -ENAMETOOLONG; + /* + * fscrypt sets disk_link.len to be len + 1, including a NUL terminator, but we + * don't store that '\0' character. + */ + err = fscrypt_prepare_symlink(dir, symname, name_len, + BTRFS_MAX_INLINE_DATA_SIZE(fs_info) + 1, + &disk_link); + if (err) + return err; inode = new_inode(dir->i_sb); if (!inode) @@ -9391,8 +9413,8 @@ static int btrfs_symlink(struct mnt_idmap *idmap, struct inode *dir, inode->i_op = &btrfs_symlink_inode_operations; inode_nohighmem(inode); inode->i_mapping->a_ops = &btrfs_aops; - btrfs_i_size_write(BTRFS_I(inode), name_len); - inode_set_bytes(inode, name_len); + btrfs_i_size_write(BTRFS_I(inode), disk_link.len - 1); + inode_set_bytes(inode, disk_link.len - 1); new_inode_args.inode = inode; err = btrfs_new_inode_prepare(&new_inode_args, &trans_num_items); @@ -9419,10 +9441,23 @@ static int btrfs_symlink(struct mnt_idmap *idmap, struct inode *dir, inode = NULL; goto out; } + + if (IS_ENCRYPTED(inode)) { + err = fscrypt_encrypt_symlink(inode, symname, name_len, + &disk_link); + if (err) { + btrfs_abort_transaction(trans, err); + btrfs_free_path(path); + discard_new_inode(inode); + inode = NULL; + goto out; + } + } + key.objectid = btrfs_ino(BTRFS_I(inode)); key.offset = 0; key.type = BTRFS_EXTENT_DATA_KEY; - datasize = btrfs_file_extent_calc_inline_size(name_len); + datasize = btrfs_file_extent_calc_inline_size(disk_link.len - 1); err = btrfs_insert_empty_item(trans, root, path, &key, datasize); if (err) { @@ -9441,10 +9476,10 @@ static int btrfs_symlink(struct mnt_idmap *idmap, struct inode *dir, btrfs_set_file_extent_encryption(leaf, ei, 0); btrfs_set_file_extent_compression(leaf, ei, 0); btrfs_set_file_extent_other_encoding(leaf, ei, 0); - btrfs_set_file_extent_ram_bytes(leaf, ei, name_len); + btrfs_set_file_extent_ram_bytes(leaf, ei, disk_link.len - 1); ptr = btrfs_file_extent_inline_start(ei); - write_extent_buffer(leaf, symname, ptr, name_len); + write_extent_buffer(leaf, disk_link.name, ptr, disk_link.len - 1); btrfs_mark_buffer_dirty(leaf); btrfs_free_path(path); @@ -9461,6 +9496,29 @@ static int btrfs_symlink(struct mnt_idmap *idmap, struct inode *dir, return err; } +static const char *btrfs_get_link(struct dentry *dentry, struct inode *inode, + struct delayed_call *done) +{ + struct page *cpage; + const char *paddr; + struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); + + if (!IS_ENCRYPTED(inode)) + return page_get_link(dentry, inode, done); + + if (!dentry) + return ERR_PTR(-ECHILD); + + cpage = read_mapping_page(inode->i_mapping, 0, NULL); + if (IS_ERR(cpage)) + return ERR_CAST(cpage); + + paddr = fscrypt_get_symlink(inode, page_address(cpage), + BTRFS_MAX_INLINE_DATA_SIZE(fs_info), done); + put_page(cpage); + return paddr; +} + static struct btrfs_trans_handle *insert_prealloc_file_extent( struct btrfs_trans_handle *trans_in, struct btrfs_inode *inode, @@ -10939,7 +10997,7 @@ static const struct inode_operations btrfs_special_inode_operations = { .update_time = btrfs_update_time, }; static const struct inode_operations btrfs_symlink_inode_operations = { - .get_link = page_get_link, + .get_link = btrfs_get_link, .getattr = btrfs_getattr, .setattr = btrfs_setattr, .permission = btrfs_permission, @@ -10949,4 +11007,7 @@ static const struct inode_operations btrfs_symlink_inode_operations = { const struct dentry_operations btrfs_dentry_operations = { .d_delete = btrfs_dentry_delete, +#ifdef CONFIG_FS_ENCRYPTION + .d_revalidate = fscrypt_d_revalidate, +#endif }; diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index cffdd6f7f8e8..08b1e2ded5be 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -48,6 +48,7 @@ #include "tests/btrfs-tests.h" #include "block-group.h" #include "discard.h" +#include "fscrypt.h" #include "qgroup.h" #include "raid56.h" #include "fs.h" @@ -1144,6 +1145,7 @@ static int btrfs_fill_super(struct super_block *sb, sb->s_vop = &btrfs_verityops; #endif sb->s_xattr = btrfs_xattr_handlers; + fscrypt_set_ops(sb, &btrfs_fscrypt_ops); sb->s_time_gran = 1; #ifdef CONFIG_BTRFS_FS_POSIX_ACL sb->s_flags |= SB_POSIXACL; From patchwork Tue Aug 8 17:12:07 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13346812 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 395F7C05051 for ; Tue, 8 Aug 2023 18:54:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230051AbjHHSyx (ORCPT ); Tue, 8 Aug 2023 14:54:53 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46298 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234372AbjHHSyd (ORCPT ); Tue, 8 Aug 2023 14:54:33 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6E89B6A3B8; Tue, 8 Aug 2023 10:12:33 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 06EB283440; Tue, 8 Aug 2023 13:12:32 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1691514753; bh=FJrLZI11Ai0Wipo6ZqqWSD3xYlfiGD92yFyPBuB597I=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=W5/T3O3c9n4XGuEyVJ2i/rL8a+u1fjKNUb+XnfoszCiSuA0CM2z5aPQeuUv6PpOVW 0Y28S0AezxdHM7dbglE8+WPxXMKZGMNtIuaJSFYilzyMFiD1MXbgNhSPe6RVcF0SKF lSAyFipcTcMsiXohdlXc5+L1iFPAmrROVfzH+kbkGfeuowe1OTd7Lj5DclL0RB5i07 WzHdCwT6mcoRi3kMxZO0H9L3WE85LK6diRUp2YB9B9ixe9oGvFFzNjnoVgKVshVB+X AamDZVN0PeQDFWyjjUaHZa4gn+xSG3uhxkI9sXYWQqFcT5+WC5o21ggWyhTrreo76x 1gATH0kKOWgkw== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, Eric Biggers Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v3 05/17] btrfs: add inode encryption contexts Date: Tue, 8 Aug 2023 13:12:07 -0400 Message-ID: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Omar Sandoval In order to store encryption information for directories, symlinks, etc., fscrypt stores a context item with each encrypted non-regular inode. fscrypt provides an arbitrary blob for the filesystem to store, and it does not clearly fit into an existing structure, so this goes in a new item type. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/fscrypt.c | 118 ++++++++++++++++++++++++++++++++ fs/btrfs/fscrypt.h | 2 + fs/btrfs/inode.c | 19 +++++ fs/btrfs/ioctl.c | 8 ++- include/uapi/linux/btrfs_tree.h | 10 +++ 5 files changed, 155 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index 3a53dc59c1e4..d09d42210f37 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -1,8 +1,126 @@ // SPDX-License-Identifier: GPL-2.0 +#include #include "ctree.h" +#include "accessors.h" +#include "btrfs_inode.h" +#include "disk-io.h" +#include "fs.h" #include "fscrypt.h" +#include "ioctl.h" +#include "messages.h" +#include "transaction.h" +#include "xattr.h" + +static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len) +{ + struct btrfs_key key = { + .objectid = btrfs_ino(BTRFS_I(inode)), + .type = BTRFS_FSCRYPT_CTX_ITEM_KEY, + .offset = 0, + }; + struct btrfs_path *path; + struct extent_buffer *leaf; + unsigned long ptr; + int ret; + + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + ret = btrfs_search_slot(NULL, BTRFS_I(inode)->root, &key, path, 0, 0); + if (ret) { + len = -ENOENT; + goto out; + } + + leaf = path->nodes[0]; + ptr = btrfs_item_ptr_offset(leaf, path->slots[0]); + /* fscrypt provides max context length, but it could be less */ + len = min_t(size_t, len, btrfs_item_size(leaf, path->slots[0])); + read_extent_buffer(leaf, ctx, ptr, len); + +out: + btrfs_free_path(path); + return len; +} + +static void btrfs_fscrypt_update_context(struct btrfs_path *path, + const void *ctx, size_t len) +{ + struct extent_buffer *leaf = path->nodes[0]; + unsigned long ptr = btrfs_item_ptr_offset(leaf, path->slots[0]); + + len = min_t(size_t, len, btrfs_item_size(leaf, path->slots[0])); + write_extent_buffer(leaf, ctx, ptr, len); + btrfs_mark_buffer_dirty(leaf); +} + +static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx, + size_t len, void *fs_data) +{ + struct btrfs_trans_handle *trans = fs_data; + struct btrfs_key key = { + .objectid = btrfs_ino(BTRFS_I(inode)), + .type = BTRFS_FSCRYPT_CTX_ITEM_KEY, + .offset = 0, + }; + struct btrfs_path *path; + int ret; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + if (!trans) + trans = btrfs_start_transaction(BTRFS_I(inode)->root, 1); + if (IS_ERR(trans)) + return PTR_ERR(trans); + + ret = btrfs_search_slot(trans, BTRFS_I(inode)->root, &key, path, 0, 1); + if (ret < 0) { + btrfs_abort_transaction(trans, ret); + return ret; + } + + if (ret > 0) { + btrfs_release_path(path); + ret = btrfs_insert_empty_item(trans, BTRFS_I(inode)->root, path, &key, len); + if (ret) { + btrfs_abort_transaction(trans, ret); + return ret; + } + } + + btrfs_fscrypt_update_context(path, ctx, len); + + if (fs_data) + return ret; + + BTRFS_I(inode)->flags |= BTRFS_INODE_ENCRYPT; + btrfs_sync_inode_flags_to_i_flags(inode); + inode_inc_iversion(inode); + inode->i_ctime = current_time(inode); + ret = btrfs_update_inode(trans, BTRFS_I(inode)->root, BTRFS_I(inode)); + if (!ret) { + btrfs_end_transaction(trans); + return ret; + } + + btrfs_abort_transaction(trans, ret); + btrfs_end_transaction(trans); + return ret; +} + +static bool btrfs_fscrypt_empty_dir(struct inode *inode) +{ + return inode->i_size == BTRFS_EMPTY_DIR_SIZE; +} const struct fscrypt_operations btrfs_fscrypt_ops = { + .get_context = btrfs_fscrypt_get_context, + .set_context = btrfs_fscrypt_set_context, + .empty_dir = btrfs_fscrypt_empty_dir, .key_prefix = "btrfs:" }; diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h index 7f4e6888bd43..80adb7e56826 100644 --- a/fs/btrfs/fscrypt.h +++ b/fs/btrfs/fscrypt.h @@ -5,6 +5,8 @@ #include +#include "fs.h" + extern const struct fscrypt_operations btrfs_fscrypt_ops; #endif /* BTRFS_FSCRYPT_H */ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 49ee05ecc03a..0d0f6f7f0d27 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -62,6 +62,7 @@ #include "defrag.h" #include "dir-item.h" #include "file-item.h" +#include "fscrypt.h" #include "uuid-tree.h" #include "ioctl.h" #include "file.h" @@ -6055,6 +6056,9 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args, struct inode *inode = args->inode; int ret; + if (fscrypt_is_nokey_name(args->dentry)) + return -ENOKEY; + if (!args->orphan) { ret = fscrypt_setup_filename(dir, &args->dentry->d_name, 0, &args->fname); @@ -6090,6 +6094,9 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args, if (dir->i_security) (*trans_num_items)++; #endif + /* 1 to add fscrypt item, but only for encrypted non-regular files */ + if (args->encrypt && !S_ISREG(inode->i_mode)) + (*trans_num_items)++; if (args->orphan) { /* 1 to add orphan item */ (*trans_num_items)++; @@ -6268,6 +6275,11 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans, inode->i_ctime = inode->i_mtime; BTRFS_I(inode)->i_otime = inode->i_mtime; + if (args->encrypt) { + BTRFS_I(inode)->flags |= BTRFS_INODE_ENCRYPT; + btrfs_sync_inode_flags_to_i_flags(inode); + } + /* * We're going to fill the inode item now, so at this point the inode * must be fully initialized. @@ -6342,6 +6354,13 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans, goto discard; } } + if (args->encrypt && !S_ISREG(inode->i_mode)) { + ret = fscrypt_set_context(inode, trans); + if (ret) { + btrfs_abort_transaction(trans, ret); + goto discard; + } + } inode_tree_add(BTRFS_I(inode)); diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index a895d105464b..05984e5448b4 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -156,6 +156,8 @@ static unsigned int btrfs_inode_flags_to_fsflags(struct btrfs_inode *binode) iflags |= FS_DIRSYNC_FL; if (flags & BTRFS_INODE_NODATACOW) iflags |= FS_NOCOW_FL; + if (flags & BTRFS_INODE_ENCRYPT) + iflags |= FS_ENCRYPT_FL; if (ro_flags & BTRFS_INODE_RO_VERITY) iflags |= FS_VERITY_FL; @@ -185,12 +187,14 @@ void btrfs_sync_inode_flags_to_i_flags(struct inode *inode) new_fl |= S_NOATIME; if (binode->flags & BTRFS_INODE_DIRSYNC) new_fl |= S_DIRSYNC; + if (binode->flags & BTRFS_INODE_ENCRYPT) + new_fl |= S_ENCRYPTED; if (binode->ro_flags & BTRFS_INODE_RO_VERITY) new_fl |= S_VERITY; set_mask_bits(&inode->i_flags, S_SYNC | S_APPEND | S_IMMUTABLE | S_NOATIME | S_DIRSYNC | - S_VERITY, new_fl); + S_VERITY | S_ENCRYPTED, new_fl); } /* @@ -203,7 +207,7 @@ static int check_fsflags(unsigned int old_flags, unsigned int flags) FS_NOATIME_FL | FS_NODUMP_FL | \ FS_SYNC_FL | FS_DIRSYNC_FL | \ FS_NOCOMP_FL | FS_COMPR_FL | - FS_NOCOW_FL)) + FS_NOCOW_FL | FS_ENCRYPT_FL)) return -EOPNOTSUPP; /* COMPR and NOCOMP on new/old are valid */ diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h index ab38d0f411fa..029af0aeb65d 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -161,6 +161,8 @@ #define BTRFS_VERITY_DESC_ITEM_KEY 36 #define BTRFS_VERITY_MERKLE_ITEM_KEY 37 +#define BTRFS_FSCRYPT_CTX_ITEM_KEY 41 + #define BTRFS_ORPHAN_ITEM_KEY 48 /* reserve 2-15 close to the inode for later flexibility */ @@ -399,6 +401,7 @@ static inline __u8 btrfs_dir_flags_to_ftype(__u8 flags) #define BTRFS_INODE_NOATIME (1U << 9) #define BTRFS_INODE_DIRSYNC (1U << 10) #define BTRFS_INODE_COMPRESS (1U << 11) +#define BTRFS_INODE_ENCRYPT (1U << 12) #define BTRFS_INODE_ROOT_ITEM_INIT (1U << 31) @@ -415,6 +418,7 @@ static inline __u8 btrfs_dir_flags_to_ftype(__u8 flags) BTRFS_INODE_NOATIME | \ BTRFS_INODE_DIRSYNC | \ BTRFS_INODE_COMPRESS | \ + BTRFS_INODE_ENCRYPT | \ BTRFS_INODE_ROOT_ITEM_INIT) #define BTRFS_INODE_RO_VERITY (1U << 0) @@ -1016,6 +1020,12 @@ enum { BTRFS_NR_FILE_EXTENT_TYPES = 3, }; +enum { + BTRFS_ENCRYPTION_NONE, + BTRFS_ENCRYPTION_FSCRYPT, + BTRFS_NR_ENCRYPTION_TYPES, +}; + struct btrfs_file_extent_item { /* * transaction id that created this extent From patchwork Tue Aug 8 17:12:08 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13346811 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 55CB8C04A94 for ; Tue, 8 Aug 2023 18:54:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230150AbjHHSyy (ORCPT ); Tue, 8 Aug 2023 14:54:54 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46344 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230253AbjHHSyi (ORCPT ); Tue, 8 Aug 2023 14:54:38 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F0A68696B2; Tue, 8 Aug 2023 10:12:34 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 83F498343D; Tue, 8 Aug 2023 13:12:34 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1691514754; bh=h1UoSMBy1DTYNUEDp5Q4krNvl6d2m/f9p6YPgBTIwOM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=rvTQEYJBqzmJtzFFWoGJZDUSc+lh/mD4XAyKYSUybS5NxmgYhARJNp05tKrggUHCo SBx34oFQ3ePYWLcF+YuGyFbsy5cuW3lQJDOVPLmAcWQbKAAZh53WTdEn4NV27hbwDk uBd2I6kbj8AHhwFj1BBrkI1jhUlU51zU/HMssxBl1niE5wtYrg3CgfOyrqksBHki59 QnJ7g3gUunGzcYbJ6cmBfjoZNDmb3TXVQvoUkrqfzkcGH7QFmaiVB8iROv3QyxfKxE 5TELYR0A0KWdVe1QQo1CJi+Ey7v0JAGgj52j4pHpaQn3sRqxEUj9VI0XGfbUCi7dS7 wIJE68C2BOODA== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, Eric Biggers Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v3 06/17] btrfs: add new FEATURE_INCOMPAT_ENCRYPT flag Date: Tue, 8 Aug 2023 13:12:08 -0400 Message-ID: <5f28a69c531271ee1dc49b5a76918e30b976879a.1691510179.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Omar Sandoval As encrypted files will be incompatible with older filesystem versions, new filesystems should be created with an incompat flag for fscrypt, which will gate access to the encryption ioctls. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/fs.h | 7 ++++--- fs/btrfs/super.c | 5 +++++ fs/btrfs/sysfs.c | 6 ++++++ include/uapi/linux/btrfs.h | 1 + 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h index ef07c6c252d8..15d0dc425e32 100644 --- a/fs/btrfs/fs.h +++ b/fs/btrfs/fs.h @@ -223,9 +223,10 @@ enum { * Features under developmen like Extent tree v2 support is enabled * only under CONFIG_BTRFS_DEBUG. */ -#define BTRFS_FEATURE_INCOMPAT_SUPP \ - (BTRFS_FEATURE_INCOMPAT_SUPP_STABLE | \ - BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2) +#define BTRFS_FEATURE_INCOMPAT_SUPP \ + (BTRFS_FEATURE_INCOMPAT_SUPP_STABLE | \ + BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2 | \ + BTRFS_FEATURE_INCOMPAT_ENCRYPT) #else diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 08b1e2ded5be..0cc9c2909f64 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -2421,6 +2421,11 @@ static int __init btrfs_print_mod_info(void) ", fsverity=yes" #else ", fsverity=no" +#endif +#ifdef CONFIG_FS_ENCRYPTION + ", fscrypt=yes" +#else + ", fscrypt=no" #endif ; pr_info("Btrfs loaded%s\n", options); diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index b1d1ac25237b..ac955f920edf 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -301,6 +301,9 @@ BTRFS_FEAT_ATTR_INCOMPAT(extent_tree_v2, EXTENT_TREE_V2); #ifdef CONFIG_FS_VERITY BTRFS_FEAT_ATTR_COMPAT_RO(verity, VERITY); #endif +#ifdef CONFIG_FS_ENCRYPTION +BTRFS_FEAT_ATTR_INCOMPAT(encryption, ENCRYPT); +#endif /* CONFIG_FS_ENCRYPTION */ /* * Features which depend on feature bits and may differ between each fs. @@ -331,6 +334,9 @@ static struct attribute *btrfs_supported_feature_attrs[] = { #ifdef CONFIG_FS_VERITY BTRFS_FEAT_ATTR_PTR(verity), #endif +#ifdef CONFIG_FS_ENCRYPTION + BTRFS_FEAT_ATTR_PTR(encryption), +#endif /* CONFIG_FS_ENCRYPTION */ NULL }; diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index dbb8b96da50d..98d1fa51a351 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -333,6 +333,7 @@ struct btrfs_ioctl_fs_info_args { #define BTRFS_FEATURE_INCOMPAT_RAID1C34 (1ULL << 11) #define BTRFS_FEATURE_INCOMPAT_ZONED (1ULL << 12) #define BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2 (1ULL << 13) +#define BTRFS_FEATURE_INCOMPAT_ENCRYPT (1ULL << 14) struct btrfs_ioctl_feature_flags { __u64 compat_flags; From patchwork Tue Aug 8 17:12:09 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13346805 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2DB83C04E69 for ; Tue, 8 Aug 2023 18:54:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229615AbjHHSyb (ORCPT ); Tue, 8 Aug 2023 14:54:31 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46206 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231582AbjHHSyP (ORCPT ); Tue, 8 Aug 2023 14:54:15 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6243B1B044; Tue, 8 Aug 2023 10:12:36 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id DAA1E8343F; Tue, 8 Aug 2023 13:12:35 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1691514756; bh=PPKCljAKS/i2Pq+vu5VkyYJUp8mbHG38ZDjIOv0Dhq0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VaUz3VYYV1OTQeLjPB7FFfdktWkPLnOyOj0oI93bnQGvweBFjSp9egV4wqq30eRsW H16tZtWDzbWqf1NABvWoFXl43HKQz+0LFYj54nW9xoQNpMMMSBuLejBhKIxYQT4u8B HiLFOZMoDfQk167SFPAf0FTHNL0Q+UtlG84cFg+hHVu95Bvs/mcwCKNjtqJ/37e4Nf 1934Hl36pGILing7IJy+46DqkOfQ3peo1WfJ7BUTcetO7NmNon/dmSXfklAvj9V/nB lsC+SB2jBOiUp1m3GUhZ7/YS7wiuWgcRIwWVIBESu7lNOQAAgV9EGWAEXvncnplNfv fzzJEMLDSuN0w== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, Eric Biggers Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v3 07/17] btrfs: adapt readdir for encrypted and nokey names Date: Tue, 8 Aug 2023 13:12:09 -0400 Message-ID: <15127cb1d333aaae7110200f9da8d9412ad9302c.1691510179.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Omar Sandoval Deleting an encrypted file must always be permitted, even if the user does not have the appropriate key. Therefore, for listing an encrypted directory, so-called 'nokey' names are provided, and these nokey names must be sufficient to look up and delete the appropriate encrypted files. See 'struct fscrypt_nokey_name' for more information on the format of these names. The first part of supporting nokey names is allowing lookups by nokey name. Only a few entry points need to support these: deleting a directory, file, or subvolume -- each of these call fscrypt_setup_filename() with a '1' argument, indicating that the key is not required and therefore a nokey name may be provided. If a nokey name is provided, the fscrypt_name returned by fscrypt_setup_filename() will not have its disk_name field populated, but will have various other fields set. This change alters the relevant codepaths to pass a complete fscrypt_name anywhere that it might contain a nokey name. When it does contain a nokey name, the first time the name is successfully matched to a stored name populates the disk name field of the fscrypt_name, allowing the caller to use the normal disk name codepaths afterward. Otherwise, the matching functionality is in close analogue to the function fscrypt_match_name(). Functions where most callers are providing a fscrypt_str are duplicated and adapted for a fscrypt_name, and functions where most callers are providing a fscrypt_name are changed to so require at all callsites. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/btrfs_inode.h | 2 +- fs/btrfs/delayed-inode.c | 29 ++++++- fs/btrfs/delayed-inode.h | 4 +- fs/btrfs/dir-item.c | 77 ++++++++++++++++--- fs/btrfs/dir-item.h | 13 +++- fs/btrfs/extent_io.c | 38 ++++++++++ fs/btrfs/extent_io.h | 3 + fs/btrfs/fscrypt.c | 34 +++++++++ fs/btrfs/fscrypt.h | 19 +++++ fs/btrfs/inode.c | 158 ++++++++++++++++++++++++++------------- fs/btrfs/root-tree.c | 8 +- fs/btrfs/root-tree.h | 2 +- fs/btrfs/tree-log.c | 3 +- 13 files changed, 317 insertions(+), 73 deletions(-) diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 7abf98d56181..9effefd4a18c 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -421,7 +421,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry); int btrfs_set_inode_index(struct btrfs_inode *dir, u64 *index); int btrfs_unlink_inode(struct btrfs_trans_handle *trans, struct btrfs_inode *dir, struct btrfs_inode *inode, - const struct fscrypt_str *name); + struct fscrypt_name *name); int btrfs_add_link(struct btrfs_trans_handle *trans, struct btrfs_inode *parent_inode, struct btrfs_inode *inode, const struct fscrypt_str *name, int add_backref, u64 index); diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index 6b457b010cbc..e3aeba44156b 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -1724,7 +1724,9 @@ int btrfs_should_delete_dir_index(struct list_head *del_list, * btrfs_readdir_delayed_dir_index - read dir info stored in the delayed tree * */ -int btrfs_readdir_delayed_dir_index(struct dir_context *ctx, +int btrfs_readdir_delayed_dir_index(struct inode *inode, + struct fscrypt_str *fstr, + struct dir_context *ctx, struct list_head *ins_list) { struct btrfs_dir_item *di; @@ -1734,6 +1736,7 @@ int btrfs_readdir_delayed_dir_index(struct dir_context *ctx, int name_len; int over = 0; unsigned char d_type; + size_t fstr_len = fstr->len; if (list_empty(ins_list)) return 0; @@ -1761,8 +1764,28 @@ int btrfs_readdir_delayed_dir_index(struct dir_context *ctx, d_type = fs_ftype_to_dtype(btrfs_dir_flags_to_ftype(di->type)); btrfs_disk_key_to_cpu(&location, &di->location); - over = !dir_emit(ctx, name, name_len, - location.objectid, d_type); + if (di->type & BTRFS_FT_ENCRYPTED) { + int ret; + struct fscrypt_str iname = FSTR_INIT(name, name_len); + + fstr->len = fstr_len; + /* + * The hash is only used when the encryption key is not + * available. But if we have delayed insertions, then we + * must have the encryption key available or we wouldn't + * have been able to create entries in the directory. + * So, we don't calculate the hash. + */ + ret = fscrypt_fname_disk_to_usr(inode, 0, 0, &iname, + fstr); + if (ret) + return ret; + over = !dir_emit(ctx, fstr->name, fstr->len, + location.objectid, d_type); + } else { + over = !dir_emit(ctx, name, name_len, location.objectid, + d_type); + } if (refcount_dec_and_test(&curr->refs)) kfree(curr); diff --git a/fs/btrfs/delayed-inode.h b/fs/btrfs/delayed-inode.h index 4f21daa3dbc7..a4f9fa27b126 100644 --- a/fs/btrfs/delayed-inode.h +++ b/fs/btrfs/delayed-inode.h @@ -155,7 +155,9 @@ void btrfs_readdir_put_delayed_items(struct inode *inode, struct list_head *del_list); int btrfs_should_delete_dir_index(struct list_head *del_list, u64 index); -int btrfs_readdir_delayed_dir_index(struct dir_context *ctx, +int btrfs_readdir_delayed_dir_index(struct inode *inode, + struct fscrypt_str *fstr, + struct dir_context *ctx, struct list_head *ins_list); /* Used during directory logging. */ diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c index 082eb0e19598..da95ae411d72 100644 --- a/fs/btrfs/dir-item.c +++ b/fs/btrfs/dir-item.c @@ -6,6 +6,7 @@ #include "messages.h" #include "ctree.h" #include "disk-io.h" +#include "fscrypt.h" #include "transaction.h" #include "accessors.h" #include "dir-item.h" @@ -230,6 +231,47 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans, return di; } +/* + * Lookup for a directory item by fscrypt_name. + * + * @trans: The transaction handle to use. + * @root: The root of the target tree. + * @path: Path to use for the search. + * @dir: The inode number (objectid) of the directory. + * @name: The fscrypt_name associated to the directory entry + * @mod: Used to indicate if the tree search is meant for a read only + * lookup or for a deletion lookup, so its value should be 0 or + * -1, respectively. + * + * Returns: NULL if the dir item does not exists, an error pointer if an error + * happened, or a pointer to a dir item if a dir item exists for the given name. + */ +struct btrfs_dir_item *btrfs_lookup_dir_item_fname(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, u64 dir, + struct fscrypt_name *name, int mod) +{ + struct btrfs_key key; + struct btrfs_dir_item *di = NULL; + int ret = 0; + + key.objectid = dir; + key.type = BTRFS_DIR_ITEM_KEY; + key.offset = btrfs_name_hash(name->disk_name.name, name->disk_name.len); + /* XXX get the right hash for no-key names */ + + ret = btrfs_search_slot(trans, root, &key, path, mod, -mod); + if (ret == 0) + di = btrfs_match_dir_item_fname(root->fs_info, path, name); + + if (ret == -ENOENT || (di && IS_ERR(di) && PTR_ERR(di) == -ENOENT)) + return NULL; + if (ret < 0) + di = ERR_PTR(ret); + + return di; +} + int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir, const struct fscrypt_str *name) { @@ -287,9 +329,9 @@ int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir, } /* - * Lookup for a directory index item by name and index number. + * Lookup for a directory index item by fscrypt_name and index number. * - * @trans: The transaction handle to use. Can be NULL if @mod is 0. + * @trans: The transaction handle to use. * @root: The root of the target tree. * @path: Path to use for the search. * @dir: The inode number (objectid) of the directory. @@ -327,7 +369,7 @@ btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans, struct btrfs_dir_item * btrfs_search_dir_index_item(struct btrfs_root *root, struct btrfs_path *path, - u64 dirid, const struct fscrypt_str *name) + u64 dirid, struct fscrypt_name *name) { struct btrfs_dir_item *di; struct btrfs_key key; @@ -340,9 +382,7 @@ btrfs_search_dir_index_item(struct btrfs_root *root, struct btrfs_path *path, btrfs_for_each_slot(root, &key, &key, path, ret) { if (key.objectid != dirid || key.type != BTRFS_DIR_INDEX_KEY) break; - - di = btrfs_match_dir_item_name(root->fs_info, path, - name->name, name->len); + di = btrfs_match_dir_item_fname(root->fs_info, path, name); if (di) return di; } @@ -378,9 +418,9 @@ struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans, * this walks through all the entries in a dir item and finds one * for a specific name. */ -struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info, - struct btrfs_path *path, - const char *name, int name_len) +struct btrfs_dir_item *btrfs_match_dir_item_fname(struct btrfs_fs_info *fs_info, + struct btrfs_path *path, + struct fscrypt_name *name) { struct btrfs_dir_item *dir_item; unsigned long name_ptr; @@ -399,8 +439,8 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info, btrfs_dir_data_len(leaf, dir_item); name_ptr = (unsigned long)(dir_item + 1); - if (btrfs_dir_name_len(leaf, dir_item) == name_len && - memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0) + if (btrfs_fscrypt_match_name(name, leaf, name_ptr, + btrfs_dir_name_len(leaf, dir_item))) return dir_item; cur += this_len; @@ -410,6 +450,21 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info, return NULL; } +/* + * helper function to look at the directory item pointed to by 'path' + * this walks through all the entries in a dir item and finds one + * for a specific name. + */ +struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info, + struct btrfs_path *path, + const char *name, int name_len) +{ + struct fscrypt_name fname = { + .disk_name = FSTR_INIT((char *) name, name_len) + }; + return btrfs_match_dir_item_fname(fs_info, path, &fname); +} + /* * given a pointer into a directory item, delete it. This * handles items that have more than one entry in them. diff --git a/fs/btrfs/dir-item.h b/fs/btrfs/dir-item.h index aab4b7cc7fa0..4f42a4f6a4ec 100644 --- a/fs/btrfs/dir-item.h +++ b/fs/btrfs/dir-item.h @@ -3,6 +3,8 @@ #ifndef BTRFS_DIR_ITEM_H #define BTRFS_DIR_ITEM_H +#include + int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir, const struct fscrypt_str *name); int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, @@ -12,6 +14,11 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, u64 dir, const struct fscrypt_str *name, int mod); +struct btrfs_dir_item *btrfs_lookup_dir_item_fname( + struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, u64 dir, + struct fscrypt_name *name, int mod); struct btrfs_dir_item *btrfs_lookup_dir_index_item( struct btrfs_trans_handle *trans, struct btrfs_root *root, @@ -19,7 +26,7 @@ struct btrfs_dir_item *btrfs_lookup_dir_index_item( u64 index, const struct fscrypt_str *name, int mod); struct btrfs_dir_item *btrfs_search_dir_index_item(struct btrfs_root *root, struct btrfs_path *path, u64 dirid, - const struct fscrypt_str *name); + struct fscrypt_name *name); int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, @@ -39,4 +46,8 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info, const char *name, int name_len); +struct btrfs_dir_item *btrfs_match_dir_item_fname(struct btrfs_fs_info *fs_info, + struct btrfs_path *path, + struct fscrypt_name *name); + #endif diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 177d65d51447..4de42b7ce796 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -4108,6 +4108,44 @@ static void assert_eb_page_uptodate(const struct extent_buffer *eb, } } +/* Take a sha256 of a portion of an extent buffer. */ +void extent_buffer_sha256(const struct extent_buffer *eb, + unsigned long start, + unsigned long len, u8 *out) +{ + size_t cur; + size_t offset; + struct page *page; + char *kaddr; + unsigned long i = get_eb_page_index(start); + struct sha256_state sctx; + + if (check_eb_range(eb, start, len)) + return; + + offset = get_eb_offset_in_page(eb, start); + + /* + * TODO: This should maybe be using the crypto API, not the fallback, + * but fscrypt uses the fallback and this is only used in emulation of + * fscrypt's buffer sha256 method. + */ + sha256_init(&sctx); + while (len > 0) { + page = eb->pages[i]; + assert_eb_page_uptodate(eb, page); + + cur = min(len, PAGE_SIZE - offset); + kaddr = page_address(page); + sha256_update(&sctx, (u8 *)(kaddr + offset), cur); + + len -= cur; + offset = 0; + i++; + } + sha256_final(&sctx, out); +} + static void __write_extent_buffer(const struct extent_buffer *eb, const void *srcv, unsigned long start, unsigned long len, bool use_memmove) diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index adda14c1b763..2646507a2509 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -230,6 +230,9 @@ static inline int extent_buffer_uptodate(const struct extent_buffer *eb) int memcmp_extent_buffer(const struct extent_buffer *eb, const void *ptrv, unsigned long start, unsigned long len); +void extent_buffer_sha256(const struct extent_buffer *eb, + unsigned long start, + unsigned long len, u8 *out); void read_extent_buffer(const struct extent_buffer *eb, void *dst, unsigned long start, unsigned long len); diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index d09d42210f37..fcd926907f82 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -5,13 +5,47 @@ #include "accessors.h" #include "btrfs_inode.h" #include "disk-io.h" +#include "ioctl.h" #include "fs.h" #include "fscrypt.h" #include "ioctl.h" #include "messages.h" +#include "root-tree.h" #include "transaction.h" #include "xattr.h" +/* + * This function is extremely similar to fscrypt_match_name() but uses an + * extent_buffer. + */ +bool btrfs_fscrypt_match_name(struct fscrypt_name *fname, + struct extent_buffer *leaf, unsigned long de_name, + u32 de_name_len) +{ + const struct fscrypt_nokey_name *nokey_name = + (const struct fscrypt_nokey_name *)fname->crypto_buf.name; + u8 digest[SHA256_DIGEST_SIZE]; + + if (likely(fname->disk_name.name)) { + if (de_name_len != fname->disk_name.len) + return false; + return !memcmp_extent_buffer(leaf, fname->disk_name.name, + de_name, de_name_len); + } + + if (de_name_len <= sizeof(nokey_name->bytes)) + return false; + + if (memcmp_extent_buffer(leaf, nokey_name->bytes, de_name, + sizeof(nokey_name->bytes))) + return false; + + extent_buffer_sha256(leaf, de_name + sizeof(nokey_name->bytes), + de_name_len - sizeof(nokey_name->bytes), digest); + + return !memcmp(digest, nokey_name->sha256, sizeof(digest)); +} + static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len) { struct btrfs_key key = { diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h index 80adb7e56826..1647bbbcd609 100644 --- a/fs/btrfs/fscrypt.h +++ b/fs/btrfs/fscrypt.h @@ -4,9 +4,28 @@ #define BTRFS_FSCRYPT_H #include +#include "extent_io.h" #include "fs.h" +#ifdef CONFIG_FS_ENCRYPTION +bool btrfs_fscrypt_match_name(struct fscrypt_name *fname, + struct extent_buffer *leaf, + unsigned long de_name, u32 de_name_len); + +#else +static inline bool btrfs_fscrypt_match_name(struct fscrypt_name *fname, + struct extent_buffer *leaf, + unsigned long de_name, + u32 de_name_len) +{ + if (de_name_len != fname_len(fname)) + return false; + return !memcmp_extent_buffer(leaf, fname->disk_name.name, de_name, + de_name_len); +} +#endif /* CONFIG_FS_ENCRYPTION */ + extern const struct fscrypt_operations btrfs_fscrypt_ops; #endif /* BTRFS_FSCRYPT_H */ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 0d0f6f7f0d27..053c7170ce6e 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4052,7 +4052,7 @@ int btrfs_update_inode_fallback(struct btrfs_trans_handle *trans, static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans, struct btrfs_inode *dir, struct btrfs_inode *inode, - const struct fscrypt_str *name, + struct fscrypt_name *name, struct btrfs_rename_ctx *rename_ctx) { struct btrfs_root *root = dir->root; @@ -4070,7 +4070,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans, goto out; } - di = btrfs_lookup_dir_item(trans, root, path, dir_ino, name, -1); + di = btrfs_lookup_dir_item_fname(trans, root, path, dir_ino, name, -1); if (IS_ERR_OR_NULL(di)) { ret = di ? PTR_ERR(di) : -ENOENT; goto err; @@ -4098,11 +4098,14 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans, } } - ret = btrfs_del_inode_ref(trans, root, name, ino, dir_ino, &index); + ret = btrfs_del_inode_ref(trans, root, &name->disk_name, ino, dir_ino, + &index); if (ret) { + /* This should print a base-64 encoded name if relevant? */ btrfs_info(fs_info, "failed to delete reference to %.*s, inode %llu parent %llu", - name->len, name->name, ino, dir_ino); + name->disk_name.len, name->disk_name.name, ino, + dir_ino); btrfs_abort_transaction(trans, ret); goto err; } @@ -4123,8 +4126,10 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans, * operations on the log tree, increasing latency for applications. */ if (!rename_ctx) { - btrfs_del_inode_ref_in_log(trans, root, name, inode, dir_ino); - btrfs_del_dir_entries_in_log(trans, root, name, dir, index); + btrfs_del_inode_ref_in_log(trans, root, &name->disk_name, + inode, dir_ino); + btrfs_del_dir_entries_in_log(trans, root, &name->disk_name, + dir, index); } /* @@ -4142,7 +4147,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans, if (ret) goto out; - btrfs_i_size_write(dir, dir->vfs_inode.i_size - name->len * 2); + btrfs_i_size_write(dir, dir->vfs_inode.i_size - name->disk_name.len * 2); inode_inc_iversion(&inode->vfs_inode); inode_inc_iversion(&dir->vfs_inode); inode->vfs_inode.i_ctime = current_time(&inode->vfs_inode); @@ -4155,7 +4160,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans, int btrfs_unlink_inode(struct btrfs_trans_handle *trans, struct btrfs_inode *dir, struct btrfs_inode *inode, - const struct fscrypt_str *name) + struct fscrypt_name *name) { int ret; @@ -4206,7 +4211,7 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry) false); ret = btrfs_unlink_inode(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)), - &fname.disk_name); + &fname); if (ret) goto end_trans; @@ -4243,8 +4248,6 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, if (ret) return ret; - /* This needs to handle no-key deletions later on */ - if (btrfs_ino(inode) == BTRFS_FIRST_FREE_OBJECTID) { objectid = inode->root->root_key.objectid; } else if (btrfs_ino(inode) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID) { @@ -4261,8 +4264,8 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, goto out; } - di = btrfs_lookup_dir_item(trans, root, path, dir_ino, - &fname.disk_name, -1); + di = btrfs_lookup_dir_item_fname(trans, root, path, dir_ino, + &fname, -1); if (IS_ERR_OR_NULL(di)) { ret = di ? PTR_ERR(di) : -ENOENT; goto out; @@ -4288,7 +4291,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, * call btrfs_del_root_ref, and it _shouldn't_ fail. */ if (btrfs_ino(inode) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID) { - di = btrfs_search_dir_index_item(root, path, dir_ino, &fname.disk_name); + di = btrfs_search_dir_index_item(root, path, dir_ino, &fname); if (IS_ERR_OR_NULL(di)) { if (!di) ret = -ENOENT; @@ -4305,7 +4308,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, } else { ret = btrfs_del_root_ref(trans, objectid, root->root_key.objectid, dir_ino, - &index, &fname.disk_name); + &index, &fname); if (ret) { btrfs_abort_transaction(trans, ret); goto out; @@ -4629,7 +4632,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) /* now the directory is empty */ err = btrfs_unlink_inode(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)), - &fname.disk_name); + &fname); if (!err) { btrfs_i_size_write(BTRFS_I(inode), 0); /* @@ -5330,32 +5333,23 @@ void btrfs_evict_inode(struct inode *inode) * If no dir entries were found, returns -ENOENT. * If found a corrupted location in dir entry, returns -EUCLEAN. */ -static int btrfs_inode_by_name(struct btrfs_inode *dir, struct dentry *dentry, +static int btrfs_inode_by_name(struct btrfs_inode *dir, + struct fscrypt_name *fname, struct btrfs_key *location, u8 *type) { struct btrfs_dir_item *di; struct btrfs_path *path; struct btrfs_root *root = dir->root; int ret = 0; - struct fscrypt_name fname; path = btrfs_alloc_path(); if (!path) return -ENOMEM; - ret = fscrypt_setup_filename(&dir->vfs_inode, &dentry->d_name, 1, &fname); - if (ret < 0) - goto out; - /* - * fscrypt_setup_filename() should never return a positive value, but - * gcc on sparc/parisc thinks it can, so assert that doesn't happen. - */ - ASSERT(ret == 0); - /* This needs to handle no-key deletions later on */ - di = btrfs_lookup_dir_item(NULL, root, path, btrfs_ino(dir), - &fname.disk_name, 0); + di = btrfs_lookup_dir_item_fname(NULL, root, path, btrfs_ino(dir), + fname, 0); if (IS_ERR_OR_NULL(di)) { ret = di ? PTR_ERR(di) : -ENOENT; goto out; @@ -5367,13 +5361,13 @@ static int btrfs_inode_by_name(struct btrfs_inode *dir, struct dentry *dentry, ret = -EUCLEAN; btrfs_warn(root->fs_info, "%s gets something invalid in DIR_ITEM (name %s, directory ino %llu, location(%llu %u %llu))", - __func__, fname.disk_name.name, btrfs_ino(dir), - location->objectid, location->type, location->offset); + __func__, fname->usr_fname->name, + btrfs_ino(dir), location->objectid, + location->type, location->offset); } if (!ret) *type = btrfs_dir_ftype(path->nodes[0], di); out: - fscrypt_free_filename(&fname); btrfs_free_path(path); return ret; } @@ -5645,20 +5639,27 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry) struct btrfs_root *root = BTRFS_I(dir)->root; struct btrfs_root *sub_root = root; struct btrfs_key location; + struct fscrypt_name fname; u8 di_type = 0; int ret = 0; if (dentry->d_name.len > BTRFS_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); - ret = btrfs_inode_by_name(BTRFS_I(dir), dentry, &location, &di_type); - if (ret < 0) + ret = fscrypt_prepare_lookup(dir, dentry, &fname); + if (ret) return ERR_PTR(ret); + ret = btrfs_inode_by_name(BTRFS_I(dir), &fname, &location, &di_type); + if (ret < 0) { + inode = ERR_PTR(ret); + goto cleanup; + } + if (location.type == BTRFS_INODE_ITEM_KEY) { inode = btrfs_iget(dir->i_sb, location.objectid, root); if (IS_ERR(inode)) - return inode; + goto cleanup; /* Do extra check against inode mode with di_type */ if (btrfs_inode_type(inode) != di_type) { @@ -5667,9 +5668,10 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry) inode->i_mode, btrfs_inode_type(inode), di_type); iput(inode); - return ERR_PTR(-EUCLEAN); + inode = ERR_PTR(-EUCLEAN); + goto cleanup; } - return inode; + goto cleanup; } ret = fixup_tree_root_location(fs_info, BTRFS_I(dir), dentry, @@ -5684,7 +5686,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry) btrfs_put_root(sub_root); if (IS_ERR(inode)) - return inode; + goto cleanup; down_read(&fs_info->cleanup_work_sem); if (!sb_rdonly(inode->i_sb)) @@ -5696,6 +5698,8 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry) } } +cleanup: + fscrypt_free_filename(&fname); return inode; } @@ -5792,18 +5796,32 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) struct list_head del_list; int ret; char *name_ptr; - int name_len; + u32 name_len; int entries = 0; int total_len = 0; bool put = false; struct btrfs_key location; + struct fscrypt_str fstr = FSTR_INIT(NULL, 0); + u32 fstr_len = 0; if (!dir_emit_dots(file, ctx)) return 0; + if (BTRFS_I(inode)->flags & BTRFS_INODE_ENCRYPT) { + ret = fscrypt_prepare_readdir(inode); + if (ret) + return ret; + ret = fscrypt_fname_alloc_buffer(BTRFS_NAME_LEN, &fstr); + if (ret) + return ret; + fstr_len = fstr.len; + } + path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; + if (!path) { + ret = -ENOMEM; + goto err_fstr; + } addr = private->filldir_buf; path->reada = READA_FORWARD; @@ -5821,6 +5839,7 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) struct dir_entry *entry; struct extent_buffer *leaf = path->nodes[0]; u8 ftype; + u32 nokey_len; if (found_key.objectid != key.objectid) break; @@ -5832,8 +5851,13 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) continue; di = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item); name_len = btrfs_dir_name_len(leaf, di); - if ((total_len + sizeof(struct dir_entry) + name_len) >= - PAGE_SIZE) { + nokey_len = DIV_ROUND_UP(name_len * 4, 3); + /* + * If name is encrypted, and we don't have the key, we could + * need up to 4/3rds the bytes to print it. + */ + if ((total_len + sizeof(struct dir_entry) + nokey_len) + >= PAGE_SIZE) { btrfs_release_path(path); ret = btrfs_filldir(private->filldir_buf, entries, ctx); if (ret) @@ -5847,8 +5871,36 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) ftype = btrfs_dir_flags_to_ftype(btrfs_dir_flags(leaf, di)); entry = addr; name_ptr = (char *)(entry + 1); - read_extent_buffer(leaf, name_ptr, - (unsigned long)(di + 1), name_len); + if (btrfs_dir_flags(leaf, di) & BTRFS_FT_ENCRYPTED) { + struct fscrypt_str oname = FSTR_INIT(name_ptr, + nokey_len); + u32 hash = 0, minor_hash = 0; + + read_extent_buffer(leaf, fstr.name, + (unsigned long)(di + 1), name_len); + fstr.len = name_len; + /* + * We're iterating through DIR_INDEX items, so we don't + * have the DIR_ITEM hash handy. Only compute it if + * we'll need it -- the nokey name stores it, so that + * we can look up the appropriate item by nokey name + * later on. + */ + if (!fscrypt_has_encryption_key(inode)) { + u64 name_hash = btrfs_name_hash(fstr.name, + fstr.len); + hash = name_hash; + minor_hash = name_hash >> 32; + } + ret = fscrypt_fname_disk_to_usr(inode, hash, minor_hash, + &fstr, &oname); + if (ret) + goto err; + name_len = oname.len; + } else { + read_extent_buffer(leaf, name_ptr, + (unsigned long)(di + 1), name_len); + } put_unaligned(name_len, &entry->name_len); put_unaligned(fs_ftype_to_dtype(ftype), &entry->type); btrfs_dir_item_key_to_cpu(leaf, di, &location); @@ -5868,7 +5920,8 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) if (ret) goto nopos; - ret = btrfs_readdir_delayed_dir_index(ctx, &ins_list); + fstr.len = fstr_len; + ret = btrfs_readdir_delayed_dir_index(inode, &fstr, ctx, &ins_list); if (ret) goto nopos; @@ -5899,6 +5952,8 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) if (put) btrfs_readdir_put_delayed_items(inode, &ins_list, &del_list); btrfs_free_path(path); +err_fstr: + fscrypt_fname_free_buffer(&fstr); return ret; } @@ -6409,6 +6464,7 @@ int btrfs_add_link(struct btrfs_trans_handle *trans, struct btrfs_root *root = parent_inode->root; u64 ino = btrfs_ino(inode); u64 parent_ino = btrfs_ino(parent_inode); + struct fscrypt_name fname = { .disk_name = *name }; if (unlikely(ino == BTRFS_FIRST_FREE_OBJECTID)) { memcpy(&key, &inode->root->root_key, sizeof(key)); @@ -6466,7 +6522,7 @@ int btrfs_add_link(struct btrfs_trans_handle *trans, int err; err = btrfs_del_root_ref(trans, key.objectid, root->root_key.objectid, parent_ino, - &local_index, name); + &local_index, &fname); if (err) btrfs_abort_transaction(trans, err); } else if (add_backref) { @@ -8839,7 +8895,7 @@ static int btrfs_rename_exchange(struct inode *old_dir, } else { /* src is an inode */ ret = __btrfs_unlink_inode(trans, BTRFS_I(old_dir), BTRFS_I(old_dentry->d_inode), - old_name, &old_rename_ctx); + &old_fname, &old_rename_ctx); if (!ret) ret = btrfs_update_inode(trans, root, BTRFS_I(old_inode)); } @@ -8854,7 +8910,7 @@ static int btrfs_rename_exchange(struct inode *old_dir, } else { /* dest is an inode */ ret = __btrfs_unlink_inode(trans, BTRFS_I(new_dir), BTRFS_I(new_dentry->d_inode), - new_name, &new_rename_ctx); + &new_fname, &new_rename_ctx); if (!ret) ret = btrfs_update_inode(trans, dest, BTRFS_I(new_inode)); } @@ -9103,7 +9159,7 @@ static int btrfs_rename(struct mnt_idmap *idmap, } else { ret = __btrfs_unlink_inode(trans, BTRFS_I(old_dir), BTRFS_I(d_inode(old_dentry)), - &old_fname.disk_name, &rename_ctx); + &old_fname, &rename_ctx); if (!ret) ret = btrfs_update_inode(trans, root, BTRFS_I(old_inode)); } @@ -9122,7 +9178,7 @@ static int btrfs_rename(struct mnt_idmap *idmap, } else { ret = btrfs_unlink_inode(trans, BTRFS_I(new_dir), BTRFS_I(d_inode(new_dentry)), - &new_fname.disk_name); + &new_fname); } if (!ret && new_inode->i_nlink == 0) ret = btrfs_orphan_add(trans, diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c index 859874579456..5fa416ef54ad 100644 --- a/fs/btrfs/root-tree.c +++ b/fs/btrfs/root-tree.c @@ -10,6 +10,7 @@ #include "messages.h" #include "transaction.h" #include "disk-io.h" +#include "fscrypt.h" #include "print-tree.h" #include "qgroup.h" #include "space-info.h" @@ -333,7 +334,7 @@ int btrfs_del_root(struct btrfs_trans_handle *trans, int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id, u64 ref_id, u64 dirid, u64 *sequence, - const struct fscrypt_str *name) + struct fscrypt_name *name) { struct btrfs_root *tree_root = trans->fs_info->tree_root; struct btrfs_path *path; @@ -355,13 +356,14 @@ int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id, if (ret < 0) { goto out; } else if (ret == 0) { + u32 name_len; leaf = path->nodes[0]; ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_ref); ptr = (unsigned long)(ref + 1); + name_len = btrfs_root_ref_name_len(leaf, ref); if ((btrfs_root_ref_dirid(leaf, ref) != dirid) || - (btrfs_root_ref_name_len(leaf, ref) != name->len) || - memcmp_extent_buffer(leaf, name->name, ptr, name->len)) { + !btrfs_fscrypt_match_name(name, leaf, ptr, name_len)) { ret = -ENOENT; goto out; } diff --git a/fs/btrfs/root-tree.h b/fs/btrfs/root-tree.h index cbbaca32126e..a57bbf7b0180 100644 --- a/fs/btrfs/root-tree.h +++ b/fs/btrfs/root-tree.h @@ -13,7 +13,7 @@ int btrfs_add_root_ref(struct btrfs_trans_handle *trans, u64 root_id, const struct fscrypt_str *name); int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id, u64 ref_id, u64 dirid, u64 *sequence, - const struct fscrypt_str *name); + struct fscrypt_name *name); int btrfs_del_root(struct btrfs_trans_handle *trans, const struct btrfs_key *key); int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, const struct btrfs_key *key, diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 8ad7e7e38d18..3bf746668e07 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -901,9 +901,10 @@ static int unlink_inode_for_log_replay(struct btrfs_trans_handle *trans, struct btrfs_inode *inode, const struct fscrypt_str *name) { + struct fscrypt_name fname = { .disk_name = *name, }; int ret; - ret = btrfs_unlink_inode(trans, dir, inode, name); + ret = btrfs_unlink_inode(trans, dir, inode, &fname); if (ret) return ret; /* From patchwork Tue Aug 8 17:12:10 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13346809 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1592FC001DB for ; Tue, 8 Aug 2023 18:54:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234135AbjHHSyu (ORCPT ); Tue, 8 Aug 2023 14:54:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54290 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231582AbjHHSyb (ORCPT ); Tue, 8 Aug 2023 14:54:31 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C23041B046; Tue, 8 Aug 2023 10:12:37 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 568118343D; Tue, 8 Aug 2023 13:12:37 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1691514757; bh=vZtFoQKKsLfpwHLBX9gD6fBDzJ/9AqqYRAOFzdVkosE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=uG811T2FMO1JW4X+P10AWUFIXCj+pjs0SkzPJyJIJJCwkIr6THA3lIHzg/ca51l1y SpEzSWSjVBvdRT9gEKr3u8qGj54sVCu2HMX1eR0Ffvj6SXizjKIE8hYjOC+oXw5Cet TZY4rMxs0lWO2VuQLx0UhyRTvl5npdMVnLhKwuX8iOIq7/E8WNIYOOpB6N3k73/68b lPRTNEA7Yhplh3X2Fjzd8XEqs5ZguMuBtGWfJata1UDrse7BnTpBrPGuA8ZX9VSLNi 5gYR6Os9x4kB1MCq8Cb5cJFHtUhFJwr8ZjR65zCTY+QrMI563xfyNS9SgRxO3VcI1X 4jA61lIKnngfQ== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, Eric Biggers Cc: Sweet Tea Dorminy Subject: [PATCH v3 08/17] btrfs: handle nokey names. Date: Tue, 8 Aug 2023 13:12:10 -0400 Message-ID: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org For encrypted or unencrypted names, we calculate the offset for the dir item by hashing the name for the dir item. However, this doesn't work for a long nokey name, where we do not have the complete ciphertext. Instead, fscrypt stores the filesystem-provided hash in the nokey name, and we can extract it from the fscrypt_name structure in such a case. Additionally, for nokey names, if we find the nokey name on disk we can update the fscrypt_name with the disk name, so add that to searching for diritems. Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/dir-item.c | 37 +++++++++++++++++++++++++++++++++++-- fs/btrfs/fscrypt.c | 27 +++++++++++++++++++++++++++ fs/btrfs/fscrypt.h | 11 +++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c index da95ae411d72..ee7dad888f53 100644 --- a/fs/btrfs/dir-item.c +++ b/fs/btrfs/dir-item.c @@ -231,6 +231,28 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans, return di; } +/* + * If appropriate, populate the disk name for a fscrypt_name looked up without + * a key. + * + * @path: The path to the extent buffer in which the name was found. + * @di: The dir item corresponding. + * @fname: The fscrypt_name to perhaps populate. + * + * Returns: 0 if the name is already populated or the dir item doesn't exist + * or the name was successfully populated, else an error code. + */ +static int ensure_disk_name_from_dir_item(struct btrfs_path *path, + struct btrfs_dir_item *di, + struct fscrypt_name *name) +{ + if (name->disk_name.name || !di) + return 0; + + return btrfs_fscrypt_get_disk_name(path->nodes[0], di, + &name->disk_name); +} + /* * Lookup for a directory item by fscrypt_name. * @@ -257,8 +279,12 @@ struct btrfs_dir_item *btrfs_lookup_dir_item_fname(struct btrfs_trans_handle *tr key.objectid = dir; key.type = BTRFS_DIR_ITEM_KEY; - key.offset = btrfs_name_hash(name->disk_name.name, name->disk_name.len); - /* XXX get the right hash for no-key names */ + + if (!name->disk_name.name) + key.offset = name->hash | ((u64)name->minor_hash << 32); + else + key.offset = btrfs_name_hash(name->disk_name.name, + name->disk_name.len); ret = btrfs_search_slot(trans, root, &key, path, mod, -mod); if (ret == 0) @@ -266,6 +292,8 @@ struct btrfs_dir_item *btrfs_lookup_dir_item_fname(struct btrfs_trans_handle *tr if (ret == -ENOENT || (di && IS_ERR(di) && PTR_ERR(di) == -ENOENT)) return NULL; + if (ret == 0) + ret = ensure_disk_name_from_dir_item(path, di, name); if (ret < 0) di = ERR_PTR(ret); @@ -382,7 +410,12 @@ btrfs_search_dir_index_item(struct btrfs_root *root, struct btrfs_path *path, btrfs_for_each_slot(root, &key, &key, path, ret) { if (key.objectid != dirid || key.type != BTRFS_DIR_INDEX_KEY) break; + di = btrfs_match_dir_item_fname(root->fs_info, path, name); + if (di) + ret = ensure_disk_name_from_dir_item(path, di, name); + if (ret) + break; if (di) return di; } diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index fcd926907f82..bdd58415f93c 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -14,6 +14,33 @@ #include "transaction.h" #include "xattr.h" +/* + * From a given location in a leaf, read a name into a qstr (usually a + * fscrypt_name's disk_name), allocating the required buffer. Used for + * nokey names. + */ +int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf, + struct btrfs_dir_item *dir_item, + struct fscrypt_str *name) +{ + unsigned long de_name_len = btrfs_dir_name_len(leaf, dir_item); + unsigned long de_name = (unsigned long)(dir_item + 1); + /* + * For no-key names, we use this opportunity to find the disk + * name, so future searches don't need to deal with nokey names + * and we know what the encrypted size is. + */ + name->name = kmalloc(de_name_len, GFP_NOFS); + + if (!name->name) + return -ENOMEM; + + read_extent_buffer(leaf, name->name, de_name, de_name_len); + + name->len = de_name_len; + return 0; +} + /* * This function is extremely similar to fscrypt_match_name() but uses an * extent_buffer. diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h index 1647bbbcd609..c08fd52c99b4 100644 --- a/fs/btrfs/fscrypt.h +++ b/fs/btrfs/fscrypt.h @@ -9,11 +9,22 @@ #include "fs.h" #ifdef CONFIG_FS_ENCRYPTION +int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf, + struct btrfs_dir_item *di, + struct fscrypt_str *qstr); + bool btrfs_fscrypt_match_name(struct fscrypt_name *fname, struct extent_buffer *leaf, unsigned long de_name, u32 de_name_len); #else +static inline int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf, + struct btrfs_dir_item *di, + struct fscrypt_str *qstr) +{ + return 0; +} + static inline bool btrfs_fscrypt_match_name(struct fscrypt_name *fname, struct extent_buffer *leaf, unsigned long de_name, From patchwork Tue Aug 8 17:12:11 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13346814 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 920E8C04E69 for ; Tue, 8 Aug 2023 18:55:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229722AbjHHSzQ (ORCPT ); Tue, 8 Aug 2023 14:55:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54242 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232883AbjHHSyq (ORCPT ); Tue, 8 Aug 2023 14:54:46 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 38FD41B057; Tue, 8 Aug 2023 10:12:39 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id BC32A83441; Tue, 8 Aug 2023 13:12:38 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1691514759; bh=a6xP0jJSWbHcthsubcHuP76jkFDSkrf84bFyrS2PycI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=k4MASSa6kgIIgCCAtm7FtQP1c5FYWko50nAA838AHiHR+0yimESWkUSHYmaRv/lQ8 +96mEUgLFCxH1W931euRikDOzesE9tD+WIcMX87rzsLf64BkEtrUBx8C0193GK8G3y xMWQVA8oG1kdGdY40dfTqZuEJf/vYrOJxTG4Wa0wpttLThDaJR1JoxzQOoqh064zOQ vC/3rGAHJRiP8R3pFbPplURiz5yfHL2v2qCTDqqzwRBp3UxswTNb2qiLeaXjha5EAR OnRaPNhBobOxq7TWZQqkvR3pVPjwGs+QqLZNJml+YaYqJDSKbSIhR4b0tbZHZEE2Xa 33HlTFOeu+9Eg== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, Eric Biggers Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v3 09/17] btrfs: implement fscrypt ioctls Date: Tue, 8 Aug 2023 13:12:11 -0400 Message-ID: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Omar Sandoval These ioctls allow encryption to actually be used. The set_encryption_policy ioctl is the thing which actually turns on encryption, and therefore sets the ENCRYPT flag in the superblock. This prevents the filesystem from being loaded on older kernels. fscrypt provides CONFIG_FS_ENCRYPTION-disabled versions of all these functions which just return -EOPNOTSUPP, so the ioctls don't need to be compiled out if CONFIG_FS_ENCRYPTION isn't enabled. We could instead gate this ioctl on the superblock having the flag set, if we wanted to require mkfs with the encrypt flag in order to have a filesystem with any encryption. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/ioctl.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 05984e5448b4..03ea92218749 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4562,6 +4562,34 @@ long btrfs_ioctl(struct file *file, unsigned int return btrfs_ioctl_get_fslabel(fs_info, argp); case FS_IOC_SETFSLABEL: return btrfs_ioctl_set_fslabel(file, argp); + case FS_IOC_SET_ENCRYPTION_POLICY: { + if (!IS_ENABLED(CONFIG_FS_ENCRYPTION)) + return -EOPNOTSUPP; + if (sb_rdonly(fs_info->sb)) + return -EROFS; + /* + * If we crash before we commit, nothing encrypted could have + * been written so it doesn't matter whether the encrypted + * state persists. + */ + btrfs_set_fs_incompat(fs_info, ENCRYPT); + return fscrypt_ioctl_set_policy(file, (const void __user *)arg); + } + case FS_IOC_GET_ENCRYPTION_POLICY: + return fscrypt_ioctl_get_policy(file, (void __user *)arg); + case FS_IOC_GET_ENCRYPTION_POLICY_EX: + return fscrypt_ioctl_get_policy_ex(file, (void __user *)arg); + case FS_IOC_ADD_ENCRYPTION_KEY: + return fscrypt_ioctl_add_key(file, (void __user *)arg); + case FS_IOC_REMOVE_ENCRYPTION_KEY: + return fscrypt_ioctl_remove_key(file, (void __user *)arg); + case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS: + return fscrypt_ioctl_remove_key_all_users(file, + (void __user *)arg); + case FS_IOC_GET_ENCRYPTION_KEY_STATUS: + return fscrypt_ioctl_get_key_status(file, (void __user *)arg); + case FS_IOC_GET_ENCRYPTION_NONCE: + return fscrypt_ioctl_get_nonce(file, (void __user *)arg); case FITRIM: return btrfs_ioctl_fitrim(fs_info, argp); case BTRFS_IOC_SNAP_CREATE: From patchwork Tue Aug 8 17:12:12 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13346661 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 90589C001DB for ; Tue, 8 Aug 2023 18:10:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235390AbjHHSK4 (ORCPT ); Tue, 8 Aug 2023 14:10:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50890 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234222AbjHHSK1 (ORCPT ); Tue, 8 Aug 2023 14:10:27 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 82AB51B061; Tue, 8 Aug 2023 10:12:40 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 1F8F98343D; Tue, 8 Aug 2023 13:12:40 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1691514760; bh=J7il86OlPWYZdN63X7nQwYpmYM3qYx1E/+yZHC0ZBCw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FVB0e4vR3eXQ7/I+IZcwwzZNVnO5JwNLNW+yJ/fUbffbBurBMiIhR5UY1Lhfo4AJy Cxl+EyDys9fUn37t53EkY+TGACO9hvVCpMr9t0H4pCnbl9OVF0W4kDWXrv2fCwilGe sINUAGXVIsBPSw1O4/5WS6GP9kYiFRl4S3eFOZ+ipWjAdvGysFvg4fgMC9djiWZ1FL RigQZAm4zygjBvDPDqgwvt4SysyVz8oUn3cpKN/Gh41swx0kNLDsTtwr+u9N4Ksyxt 4Im16gHtxll8r6B2rZ4muqipsoVniRBNIJSLqjoguqph2GgI7PX2hgD01d18CLPibX FXziFW5aDV+kw== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, Eric Biggers Cc: Sweet Tea Dorminy Subject: [PATCH v3 10/17] btrfs: add encryption to CONFIG_BTRFS_DEBUG Date: Tue, 8 Aug 2023 13:12:12 -0400 Message-ID: <65ff94801ba183ac72f42454e901a1cc1a6b53dc.1691510179.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org Since encryption is currently under BTRFS_DEBUG, this adds its dependencies: inline encryption from fscrypt, and the inline encryption fallback path from the block layer. Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/Kconfig | 2 +- fs/btrfs/ioctl.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig index 3282adc84d52..b6fd4ed78905 100644 --- a/fs/btrfs/Kconfig +++ b/fs/btrfs/Kconfig @@ -82,7 +82,7 @@ config BTRFS_FS_RUN_SANITY_TESTS config BTRFS_DEBUG bool "Btrfs debugging support" - depends on BTRFS_FS + depends on BTRFS_FS && FS_ENCRYPTION_INLINE_CRYPT && BLK_INLINE_ENCRYPTION_FALLBACK help Enable run-time debugging support for the btrfs filesystem. This may enable additional and expensive checks with negative impact on diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 03ea92218749..91ad59519900 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4562,6 +4562,7 @@ long btrfs_ioctl(struct file *file, unsigned int return btrfs_ioctl_get_fslabel(fs_info, argp); case FS_IOC_SETFSLABEL: return btrfs_ioctl_set_fslabel(file, argp); +#ifdef CONFIG_BTRFS_DEBUG case FS_IOC_SET_ENCRYPTION_POLICY: { if (!IS_ENABLED(CONFIG_FS_ENCRYPTION)) return -EOPNOTSUPP; @@ -4590,6 +4591,7 @@ long btrfs_ioctl(struct file *file, unsigned int return fscrypt_ioctl_get_key_status(file, (void __user *)arg); case FS_IOC_GET_ENCRYPTION_NONCE: return fscrypt_ioctl_get_nonce(file, (void __user *)arg); +#endif /* CONFIG_BTRFS_DEBUG */ case FITRIM: return btrfs_ioctl_fitrim(fs_info, argp); case BTRFS_IOC_SNAP_CREATE: From patchwork Tue Aug 8 17:12:13 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13346662 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E533AC41513 for ; Tue, 8 Aug 2023 18:10:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235394AbjHHSK5 (ORCPT ); Tue, 8 Aug 2023 14:10:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59310 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234321AbjHHSK2 (ORCPT ); Tue, 8 Aug 2023 14:10:28 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D8FF51B065; Tue, 8 Aug 2023 10:12:41 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 7528583442; Tue, 8 Aug 2023 13:12:41 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1691514761; bh=rWirf37iqZkz5t+gTTLXe5NpdCjOHBl1ebtWn28/poY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=H/G/J2WlsNju2isdv2I72IEHomsxz1ozmFiP2I066GUdAyVzePy/5FlGtqeO3D8lq ViiT3t2ZyNtwP6gJHQClzHTzGsS1/A0jEtF25lQ3sUqImCJrkGCo93ePnKwDD83iwk gL86tFRyq0Fle9hbnR+DY3rODDd1K7fOCTyJSg2xE6NeLxbEWwraG6j/weuhOz+/n1 L7rrriPccLRyZHhH/GwurNBjrs7pd/4CAHDyWjEvb14b+fn6ZbxLPKBazOoTDmhM8F zDxhONZTCPVo3vwQCVbdPRVJDaDa6BiCMzlGkE7pAohygMUJwuvMiQYHsK+n+jBCyb XEEqBlJ0czp1g== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, Eric Biggers Cc: Sweet Tea Dorminy Subject: [PATCH v3 11/17] btrfs: add get_devices hook for fscrypt Date: Tue, 8 Aug 2023 13:12:13 -0400 Message-ID: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org Since extent encryption requires inline encryption, even though we expect to use the inlinecrypt software fallback most of the time, we need to enumerate all the devices in use by btrfs. Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/fscrypt.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index bdd58415f93c..499073376110 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -11,7 +11,9 @@ #include "ioctl.h" #include "messages.h" #include "root-tree.h" +#include "super.h" #include "transaction.h" +#include "volumes.h" #include "xattr.h" /* @@ -179,9 +181,44 @@ static bool btrfs_fscrypt_empty_dir(struct inode *inode) return inode->i_size == BTRFS_EMPTY_DIR_SIZE; } +static struct block_device **btrfs_fscrypt_get_devices(struct super_block *sb, + unsigned int *num_devs) +{ + struct btrfs_fs_info *fs_info = btrfs_sb(sb); + struct btrfs_fs_devices *fs_devices = fs_info->fs_devices; + int nr_devices = fs_devices->open_devices; + struct block_device **devs; + struct btrfs_device *device; + int i = 0; + + devs = kmalloc_array(nr_devices, sizeof(*devs), GFP_NOFS | GFP_NOWAIT); + if (!devs) + return ERR_PTR(-ENOMEM); + + rcu_read_lock(); + list_for_each_entry_rcu(device, &fs_devices->devices, dev_list) { + if (!test_bit(BTRFS_DEV_STATE_IN_FS_METADATA, + &device->dev_state) || + !device->bdev || + test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state)) + continue; + + devs[i++] = device->bdev; + + if (i >= nr_devices) + break; + + } + rcu_read_unlock(); + + *num_devs = i; + return devs; +} + const struct fscrypt_operations btrfs_fscrypt_ops = { .get_context = btrfs_fscrypt_get_context, .set_context = btrfs_fscrypt_set_context, .empty_dir = btrfs_fscrypt_empty_dir, + .get_devices = btrfs_fscrypt_get_devices, .key_prefix = "btrfs:" }; From patchwork Tue Aug 8 17:12:14 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13346663 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A984FC001DB for ; Tue, 8 Aug 2023 18:12:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234609AbjHHSMK (ORCPT ); Tue, 8 Aug 2023 14:12:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59338 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235291AbjHHSK3 (ORCPT ); Tue, 8 Aug 2023 14:10:29 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3871B1B068; Tue, 8 Aug 2023 10:12:43 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id CA9DB83440; Tue, 8 Aug 2023 13:12:42 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1691514763; bh=zOKNZbAfiK+odlXUuWM8XxSwVUpiOYgsRZM26vBnM0o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=uZDtWdk08CozyHJ09AUWJoLb8cMGOpA7JVPlSIWtQ3gGCaW+cbBMcoqihlF1rHLAh +cFSL+Fw9dCqfNeLe37FlGZe7Jsk9rdiMGRaDopV43583IMDvmWRjJnEcRt5Qfi+xd 4ABD0IYBVoyRNBJQiIBszWmfiflkOCFTc3rdOh067wEQbrLSj+Yhz+slt5C13gGoQu XwTODoOHVxmWv78gA0tVm4DJbMVSBkjbma17WyhnUCdtJJWmoPp1kC/tsS6ghEJfT1 n4ZD1VEMn30jep1hyA+9qhpy+9Jzwl/jwdfRD/BqlEnB6ZWRFx/PAO1Dg+asObBXWI eQ1ZccbsumDHg== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, Eric Biggers Cc: Sweet Tea Dorminy Subject: [PATCH v3 12/17] btrfs: turn on inlinecrypt mount option for encrypt Date: Tue, 8 Aug 2023 13:12:14 -0400 Message-ID: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org fscrypt's extent encryption requires the use of inline encryption or the software fallback that the block layer provides; it is rather complicated to allow software encryption with extent encryption due to the timing of memory allocations. Thus, if btrfs has ever had a encrypted file, or when encryption is enabled on a directory, update the mount flags to include inlinecrypt. Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/ioctl.c | 3 +++ fs/btrfs/super.c | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 91ad59519900..42525a8119ee 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4574,6 +4574,9 @@ long btrfs_ioctl(struct file *file, unsigned int * state persists. */ btrfs_set_fs_incompat(fs_info, ENCRYPT); + if (!(inode->i_sb->s_flags & SB_INLINECRYPT)) { + inode->i_sb->s_flags |= SB_INLINECRYPT; + } return fscrypt_ioctl_set_policy(file, (const void __user *)arg); } case FS_IOC_GET_ENCRYPTION_POLICY: diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 0cc9c2909f64..1e9a93c6750a 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -1165,6 +1165,16 @@ static int btrfs_fill_super(struct super_block *sb, return err; } + if (btrfs_fs_incompat(fs_info, ENCRYPT)) { + if (IS_ENABLED(CONFIG_FS_ENCRYPTION_INLINE_CRYPT)) { + sb->s_flags |= SB_INLINECRYPT; + } else { + btrfs_err(fs_info, "encryption not supported"); + err = -EINVAL; + goto fail_close; + } + } + inode = btrfs_iget(sb, BTRFS_FIRST_FREE_OBJECTID, fs_info->fs_root); if (IS_ERR(inode)) { err = PTR_ERR(inode); From patchwork Tue Aug 8 17:12:15 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13346656 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5CABDC04A94 for ; Tue, 8 Aug 2023 18:10:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234629AbjHHSKe (ORCPT ); Tue, 8 Aug 2023 14:10:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59526 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234628AbjHHSJ6 (ORCPT ); Tue, 8 Aug 2023 14:09:58 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C40A51B073; Tue, 8 Aug 2023 10:12:44 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 430F783441; Tue, 8 Aug 2023 13:12:44 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1691514764; bh=qpXAcm4K7bOAkjEWFWEcnGwbfn1r/rAuBu2to8Bk8DQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jmhGNiDLrSlDWTuDGJaz7ylBl/8CLgG+ppSIjuIpkAv61va8nlmkd8166rAUJTKIy Rk7Oms8SxIE3BBIZGohoMxedWVY+omAA8tTr/nZnPOmuzBVTUPl/pyZyG0babBi+wk J+QM3ydu7IIcWbfjP2irQ32fjolI/3fuCdyA+5Uzp+K+pdIz9Uqj5mUBAxfJvEOz7V o90Oif7SYGd90nVihhasIr5Xp4L3oCWx7APfaRqOQko/5hRw0dQ3dEn2OgBa0/+fNp Sw5pBMlpJdC4g+V6DKeZD4c1524+tBRDXOVj8hWaR1L/da/kokx8ZUT/YoSJqTEWhj bYiys6l+XqXdQ== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, Eric Biggers Cc: Sweet Tea Dorminy Subject: [PATCH v3 13/17] btrfs: turn on the encryption ioctls Date: Tue, 8 Aug 2023 13:12:15 -0400 Message-ID: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org This allows the use of encryption with btrfs. Since the extent encryption interfaces are not currently defined, this is using the normal inode encryption, and that is not the long-term plan. But it allows verifying by test that the steps for inode encryption are correct and pass fstests. Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/extent_io.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 4de42b7ce796..2e13bb615fa6 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -715,6 +715,14 @@ static bool btrfs_bio_is_contig(struct btrfs_bio_ctrl *bio_ctrl, struct bio_vec *bvec = bio_last_bvec_all(bio); const sector_t sector = disk_bytenr >> SECTOR_SHIFT; + if (IS_ENABLED(CONFIG_FS_ENCRYPTION)) { + struct inode *inode = page->mapping->host; + u64 lblk = (page_offset(page) + pg_offset) / inode->i_sb->s_blocksize; + + if (!fscrypt_mergeable_bio(bio, inode, lblk)) + return false; + } + if (bio_ctrl->compress_type != BTRFS_COMPRESS_NONE) { /* * For compression, all IO should have its logical bytenr set @@ -753,6 +761,9 @@ static void alloc_new_bio(struct btrfs_inode *inode, bbio->file_offset = file_offset; bio_ctrl->bbio = bbio; bio_ctrl->len_to_oe_boundary = U32_MAX; + fscrypt_set_bio_crypt_ctx(&bbio->bio, &inode->vfs_inode, + file_offset >> fs_info->sectorsize_bits, + GFP_NOIO); /* Limit data write bios to the ordered boundary. */ if (bio_ctrl->wbc) { From patchwork Tue Aug 8 17:12:16 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13346657 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 752A9C41513 for ; Tue, 8 Aug 2023 18:10:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235370AbjHHSKg (ORCPT ); Tue, 8 Aug 2023 14:10:36 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34190 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235236AbjHHSKB (ORCPT ); Tue, 8 Aug 2023 14:10:01 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4DFC71B07C; Tue, 8 Aug 2023 10:12:46 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 9AE1683446; Tue, 8 Aug 2023 13:12:45 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1691514765; bh=NBpxKgk+H4/khOgT6Ol7/oaU5ORwleA192wOniXULdg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HdhP+lmb3YZt2exXt69KR5DsJO3jSjNxzcr7WZmMT4UEncVS4goV1dWrepzc2JlTR Y/6xv09RdaCvB4EnkY8FqmtadjuGZoq4MQfQ1Ann+1Drqx6J6wSKh/SM/quXVv7YNo O4D6IXllYtdxVGqJ1dbOEJ1PvN2NXV86eNi0UATf1v0MoR1PCpZ1xBR72ZLXN7+G1W Iwll27ZyRkA1u1+Wg9Rq3X5Dws9aJQNDwCKYBAvzgiYbovduws9D7n5rZVDLSCcq80 cv2OgBVDUd72sVlHaYWYWgeO6nZpLsO0LT93yusS/WBwVbg8fcfm1JNlFKhS0GVhbn 4YJhV/WHaiDPg== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, Eric Biggers Cc: Sweet Tea Dorminy Subject: [PATCH v3 14/17] btrfs: create and free extent fscrypt_infos Date: Tue, 8 Aug 2023 13:12:16 -0400 Message-ID: <13fd27bd85084a74b09c46b82d42210d5b9f84ff.1691510179.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org Each extent_map will end up with a pointer to its associated fscrypt_info if any, which should have the same lifetime as the extent_map. Add creation of fscrypt_infos for new extent_maps, and freeing as appropriate. Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/extent_map.c | 18 ++++++++++++++++++ fs/btrfs/extent_map.h | 1 + fs/btrfs/fscrypt.c | 13 +++++++++++++ fs/btrfs/fscrypt.h | 4 ++++ fs/btrfs/inode.c | 6 ++++++ 5 files changed, 42 insertions(+) diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c index 0cdb3e86f29b..cb4b7517b253 100644 --- a/fs/btrfs/extent_map.c +++ b/fs/btrfs/extent_map.c @@ -9,6 +9,7 @@ #include "extent_map.h" #include "compression.h" #include "btrfs_inode.h" +#include "fscrypt.h" static struct kmem_cache *extent_map_cache; @@ -65,6 +66,8 @@ void free_extent_map(struct extent_map *em) if (!em) return; if (refcount_dec_and_test(&em->refs)) { + if (em->fscrypt_info) + fscrypt_free_extent_info(&em->fscrypt_info); WARN_ON(extent_map_in_tree(em)); WARN_ON(!list_empty(&em->list)); if (test_bit(EXTENT_FLAG_FS_MAPPING, &em->flags)) @@ -207,6 +210,12 @@ static int mergable_maps(struct extent_map *prev, struct extent_map *next) if (!list_empty(&prev->list) || !list_empty(&next->list)) return 0; + /* + * Don't merge adjacent encrypted maps. + */ + if (prev->fscrypt_info || next->fscrypt_info) + return 0; + ASSERT(next->block_start != EXTENT_MAP_DELALLOC && prev->block_start != EXTENT_MAP_DELALLOC); @@ -817,6 +826,8 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *inode, u64 start, u64 end, split->generation = gen; split->flags = flags; split->compress_type = em->compress_type; + btrfs_fscrypt_copy_fscrypt_info(inode, em->fscrypt_info, + &split->fscrypt_info); replace_extent_mapping(em_tree, em, split, modified); free_extent_map(split); split = split2; @@ -858,6 +869,8 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *inode, u64 start, u64 end, split->orig_block_len = 0; } + btrfs_fscrypt_copy_fscrypt_info(inode, em->fscrypt_info, + &split->fscrypt_info); if (extent_map_in_tree(em)) { replace_extent_mapping(em_tree, em, split, modified); @@ -1019,6 +1032,8 @@ int split_extent_map(struct btrfs_inode *inode, u64 start, u64 len, u64 pre, split_pre->flags = flags; split_pre->compress_type = em->compress_type; split_pre->generation = em->generation; + btrfs_fscrypt_copy_fscrypt_info(inode, em->fscrypt_info, + &split_pre->fscrypt_info); replace_extent_mapping(em_tree, em, split_pre, 1); @@ -1038,6 +1053,9 @@ int split_extent_map(struct btrfs_inode *inode, u64 start, u64 len, u64 pre, split_mid->flags = flags; split_mid->compress_type = em->compress_type; split_mid->generation = em->generation; + btrfs_fscrypt_copy_fscrypt_info(inode, em->fscrypt_info, + &split_mid->fscrypt_info); + add_extent_mapping(em_tree, split_mid, 1); /* Once for us */ diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h index 35d27c756e08..3ffb74412d31 100644 --- a/fs/btrfs/extent_map.h +++ b/fs/btrfs/extent_map.h @@ -50,6 +50,7 @@ struct extent_map { */ u64 generation; unsigned long flags; + struct fscrypt_info *fscrypt_info; /* Used for chunk mappings, flag EXTENT_FLAG_FS_MAPPING must be set */ struct map_lookup *map_lookup; refcount_t refs; diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index 499073376110..5508cbc6bccb 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -215,6 +215,19 @@ static struct block_device **btrfs_fscrypt_get_devices(struct super_block *sb, return devs; } +void btrfs_fscrypt_copy_fscrypt_info(struct btrfs_inode *inode, + struct fscrypt_info *from, + struct fscrypt_info **to_ptr) +{ + if (from == NULL) { + *to_ptr = NULL; + return; + } + + fscrypt_get_extent_info_ref(from); + *to_ptr = from; +} + const struct fscrypt_operations btrfs_fscrypt_ops = { .get_context = btrfs_fscrypt_get_context, .set_context = btrfs_fscrypt_set_context, diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h index c08fd52c99b4..a489bb54b3b1 100644 --- a/fs/btrfs/fscrypt.h +++ b/fs/btrfs/fscrypt.h @@ -37,6 +37,10 @@ static inline bool btrfs_fscrypt_match_name(struct fscrypt_name *fname, } #endif /* CONFIG_FS_ENCRYPTION */ +void btrfs_fscrypt_copy_fscrypt_info(struct btrfs_inode *inode, + struct fscrypt_info *from, + struct fscrypt_info **to_ptr); + extern const struct fscrypt_operations btrfs_fscrypt_ops; #endif /* BTRFS_FSCRYPT_H */ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 053c7170ce6e..7726957284c1 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7319,6 +7319,12 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start, em->compress_type = compress_type; } + ret = fscrypt_prepare_new_extent(&inode->vfs_inode, &em->fscrypt_info); + if (ret < 0) { + free_extent_map(em); + return ERR_PTR(ret); + } + ret = btrfs_replace_extent_map_range(inode, em, true); if (ret) { free_extent_map(em); From patchwork Tue Aug 8 17:12:17 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13346658 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 471DCC04E69 for ; Tue, 8 Aug 2023 18:10:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235304AbjHHSKi (ORCPT ); Tue, 8 Aug 2023 14:10:38 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37908 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232697AbjHHSKD (ORCPT ); Tue, 8 Aug 2023 14:10:03 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A5FC4676B5; Tue, 8 Aug 2023 10:12:47 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 3C6BF83449; Tue, 8 Aug 2023 13:12:47 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1691514767; bh=ebsvTR61Puad/NrpYHY1xisxcbD4m06UmAR4mNB5pZc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=R95KFicPSI7r3X7/Tcp1eoAheTI7g+sr8WXb1OQAvuyL7yglocwj0EyaSSriSnU7t itl2evR66V7YJaNeiivDltLfgO7w2OHpSbH1zOK/8PNZ2qJ6lFoE6bbhodzWGy/u0y 3IYBE+YWEEUQ+Zva52UHG3luJaXMMMQEXxtHRrMMjO1AKD/RFPDJF0epGuWSIuNk9c vPVRbYl88kQy4ZgvH7ELex7QQkvJbVaX+e+wXHEFVNWPEQOUFTqFp32q9Z9CfHOtXS rV08paeMgQaOGuEKytLqjnzv6y9nlGOB/msJVF837aNAiILcXettWFb8x/f+EAU7Oq i3aNACNZyHDYA== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, Eric Biggers Cc: Sweet Tea Dorminy Subject: [PATCH v3 15/17] btrfs: start tracking extent encryption context info Date: Tue, 8 Aug 2023 13:12:17 -0400 Message-ID: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org This puts the long-preserved 1-byte encryption field to work, storing whether the extent is encrypted. Since there is no way for a btrfs inode to be encrypted right now, we set context length to be 0 in all locations. This also adds the new encryption_context member to the end of the file_extent item, which will begin with a length of the fscrypt context following it; and an accessor for the context size. This allows checking that the context size matches the item size. It could be possible to pack the size and the encryption type into the u8 encryption field, but this allows more flexibility in the future. Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/accessors.h | 3 ++- fs/btrfs/file-item.c | 5 +++++ fs/btrfs/fscrypt.h | 21 +++++++++++++++++++ fs/btrfs/inode.c | 25 ++++++++++++++++++---- fs/btrfs/tree-checker.c | 37 ++++++++++++++++++++++++++------- fs/btrfs/tree-log.c | 2 ++ include/uapi/linux/btrfs_tree.h | 12 ++++++++++- 7 files changed, 91 insertions(+), 14 deletions(-) diff --git a/fs/btrfs/accessors.h b/fs/btrfs/accessors.h index 8cfc8214109c..93b11d5e21a9 100644 --- a/fs/btrfs/accessors.h +++ b/fs/btrfs/accessors.h @@ -935,7 +935,8 @@ BTRFS_SETGET_STACK_FUNCS(stack_file_extent_disk_num_bytes, struct btrfs_file_extent_item, disk_num_bytes, 64); BTRFS_SETGET_STACK_FUNCS(stack_file_extent_compression, struct btrfs_file_extent_item, compression, 8); - +BTRFS_SETGET_STACK_FUNCS(stack_file_extent_encryption, + struct btrfs_file_extent_item, encryption, 8); BTRFS_SETGET_FUNCS(file_extent_type, struct btrfs_file_extent_item, type, 8); BTRFS_SETGET_FUNCS(file_extent_disk_bytenr, struct btrfs_file_extent_item, diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index 696bf695d8eb..f83f7020ed89 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -18,6 +18,7 @@ #include "print-tree.h" #include "compression.h" #include "fs.h" +#include "fscrypt.h" #include "accessors.h" #include "file-item.h" #include "super.h" @@ -1272,6 +1273,7 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode, em->generation = btrfs_file_extent_generation(leaf, fi); if (type == BTRFS_FILE_EXTENT_REG || type == BTRFS_FILE_EXTENT_PREALLOC) { + u8 ctxsize; em->start = extent_start; em->len = extent_end - extent_start; em->orig_start = extent_start - @@ -1294,6 +1296,9 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode, if (type == BTRFS_FILE_EXTENT_PREALLOC) set_bit(EXTENT_FLAG_PREALLOC, &em->flags); } + + ctxsize = btrfs_file_extent_ctxsize_from_item(leaf, path); + ASSERT(ctxsize == btrfs_file_extent_encryption_ctxsize(leaf, fi)); } else if (type == BTRFS_FILE_EXTENT_INLINE) { em->block_start = EXTENT_MAP_INLINE; em->start = extent_start; diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h index a489bb54b3b1..26627df9d5f9 100644 --- a/fs/btrfs/fscrypt.h +++ b/fs/btrfs/fscrypt.h @@ -4,10 +4,31 @@ #define BTRFS_FSCRYPT_H #include +#include "accessors.h" #include "extent_io.h" #include "fs.h" +static inline u32 +btrfs_file_extent_encryption_ctxsize(const struct extent_buffer *eb, + struct btrfs_file_extent_item *e) +{ + if (!btrfs_file_extent_encryption(eb, e)) + return 0; + + return btrfs_get_32(eb, e, offsetof(struct btrfs_file_extent_item, + encryption_context)); +} + +static inline u8 +btrfs_file_extent_ctxsize_from_item(const struct extent_buffer *leaf, + const struct btrfs_path *path) +{ + return (btrfs_item_size(leaf, path->slots[0]) - + sizeof(struct btrfs_file_extent_item)); +} + + #ifdef CONFIG_FS_ENCRYPTION int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf, struct btrfs_dir_item *di, diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 7726957284c1..ed0579577263 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3000,7 +3000,7 @@ static int insert_ordered_extent_file_extent(struct btrfs_trans_handle *trans, btrfs_set_stack_file_extent_num_bytes(&stack_fi, num_bytes); btrfs_set_stack_file_extent_ram_bytes(&stack_fi, ram_bytes); btrfs_set_stack_file_extent_compression(&stack_fi, oe->compress_type); - /* Encryption and other encoding is reserved and all 0 */ + /* Other encoding is reserved and always 0 */ /* * For delalloc, when completing an ordered extent we update the inode's @@ -6929,8 +6929,23 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode, btrfs_extent_item_to_extent_map(inode, path, item, em); - if (extent_type == BTRFS_FILE_EXTENT_REG || - extent_type == BTRFS_FILE_EXTENT_PREALLOC) { + if (extent_type == BTRFS_FILE_EXTENT_REG) { + u8 item_ctxsize = btrfs_file_extent_ctxsize_from_item(leaf, path); + u8 encryption = btrfs_file_extent_encryption(leaf, item); + u32 ctxsize = btrfs_file_extent_encryption_ctxsize(leaf, item); + + + if (encryption == BTRFS_ENCRYPTION_FSCRYPT) { + if (ctxsize != item_ctxsize) { + btrfs_crit(fs_info, + "invalid encryption context size for inode %llu: itemsize %d item %d", + btrfs_ino(inode), ctxsize, item_ctxsize); + ret = -EUCLEAN; + goto out; + } + } + goto insert; + } else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC) { goto insert; } else if (extent_type == BTRFS_FILE_EXTENT_INLINE) { /* @@ -9623,7 +9638,9 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent( btrfs_set_stack_file_extent_num_bytes(&stack_fi, len); btrfs_set_stack_file_extent_ram_bytes(&stack_fi, len); btrfs_set_stack_file_extent_compression(&stack_fi, BTRFS_COMPRESS_NONE); - /* Encryption and other encoding is reserved and all 0 */ + btrfs_set_stack_file_extent_encryption(&stack_fi, + BTRFS_ENCRYPTION_NONE); + /* Other encoding is reserved and always 0 */ qgroup_released = btrfs_qgroup_release_data(inode, file_offset, len); if (qgroup_released < 0) diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index 038dfa8f1788..7f0fa170f3de 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -26,6 +26,7 @@ #include "volumes.h" #include "misc.h" #include "fs.h" +#include "fscrypt.h" #include "accessors.h" #include "file-item.h" #include "inode-item.h" @@ -208,6 +209,7 @@ static int check_extent_data_item(struct extent_buffer *leaf, u32 sectorsize = fs_info->sectorsize; u32 item_size = btrfs_item_size(leaf, slot); u64 extent_end; + u8 policy; if (unlikely(!IS_ALIGNED(key->offset, sectorsize))) { file_extent_err(leaf, slot, @@ -259,10 +261,11 @@ static int check_extent_data_item(struct extent_buffer *leaf, BTRFS_NR_COMPRESS_TYPES - 1); return -EUCLEAN; } - if (unlikely(btrfs_file_extent_encryption(leaf, fi))) { + policy = btrfs_file_extent_encryption(leaf, fi); + if (unlikely(policy >= BTRFS_NR_ENCRYPTION_TYPES)) { file_extent_err(leaf, slot, - "invalid encryption for file extent, have %u expect 0", - btrfs_file_extent_encryption(leaf, fi)); + "invalid encryption for file extent, have %u expect range [0, %u]", + policy, BTRFS_NR_ENCRYPTION_TYPES - 1); return -EUCLEAN; } if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE) { @@ -291,12 +294,30 @@ static int check_extent_data_item(struct extent_buffer *leaf, return 0; } - /* Regular or preallocated extent has fixed item size */ - if (unlikely(item_size != sizeof(*fi))) { - file_extent_err(leaf, slot, + if (policy == BTRFS_ENCRYPTION_FSCRYPT) { + u8 ctxsize = btrfs_file_extent_encryption_ctxsize(leaf, fi); + + if (unlikely(item_size != sizeof(*fi) + ctxsize)) { + file_extent_err(leaf, slot, + "invalid item size for encrypted file extent, have %u expect = %zu + context of size %u", + item_size, sizeof(*fi), ctxsize); + return -EUCLEAN; + } + /* Only regular extents should be encrypted. */ + if (btrfs_file_extent_type(leaf, fi) != BTRFS_FILE_EXTENT_REG) { + file_extent_err(leaf, slot, + "invalid type for encrypted file extent, have %u expect %u", + btrfs_file_extent_type(leaf, fi), + BTRFS_FILE_EXTENT_REG); + return -EUCLEAN; + } + } else { + if (unlikely(item_size != sizeof(*fi))) { + file_extent_err(leaf, slot, "invalid item size for reg/prealloc file extent, have %u expect %zu", - item_size, sizeof(*fi)); - return -EUCLEAN; + item_size, sizeof(*fi)); + return -EUCLEAN; + } } if (unlikely(CHECK_FE_ALIGNED(leaf, slot, fi, ram_bytes, sectorsize) || CHECK_FE_ALIGNED(leaf, slot, fi, disk_bytenr, sectorsize) || diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 3bf746668e07..9c73ae09f0a1 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -4634,6 +4634,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans, u64 extent_offset = em->start - em->orig_start; u64 block_len; int ret; + u8 encryption = 0; btrfs_set_stack_file_extent_generation(&fi, trans->transid); if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) @@ -4655,6 +4656,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans, btrfs_set_stack_file_extent_num_bytes(&fi, em->len); btrfs_set_stack_file_extent_ram_bytes(&fi, em->ram_bytes); btrfs_set_stack_file_extent_compression(&fi, em->compress_type); + btrfs_set_stack_file_extent_encryption(&fi, encryption); ret = log_extent_csums(trans, inode, log, em, ctx); if (ret) diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h index 029af0aeb65d..62b95ceee735 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -1048,8 +1048,14 @@ struct btrfs_file_extent_item { * but not for stat. */ __u8 compression; + + /* + * Type of encryption in use. Unencrypted value is 0. + */ __u8 encryption; - __le16 other_encoding; /* spare for later use */ + + /* spare for later use */ + __le16 other_encoding; /* are we inline data or a real extent? */ __u8 type; @@ -1075,6 +1081,10 @@ struct btrfs_file_extent_item { * always reflects the size uncompressed and without encoding. */ __le64 num_bytes; + /* + * the encryption context, if any + */ + __u8 encryption_context[0]; } __attribute__ ((__packed__)); From patchwork Tue Aug 8 17:12:18 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13346659 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DD262C04FDF for ; Tue, 8 Aug 2023 18:10:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235376AbjHHSKk (ORCPT ); Tue, 8 Aug 2023 14:10:40 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36398 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235336AbjHHSKF (ORCPT ); Tue, 8 Aug 2023 14:10:05 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 449F4696BF; Tue, 8 Aug 2023 10:12:48 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 9250583440; Tue, 8 Aug 2023 13:12:48 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1691514768; bh=a+43fN8N9kk58gaXoYpi7X/1eJimrrwBfXdzKUoLG/M=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dh1AQn3cabQf5EOG2D5Q0vU8bGOwV/FeMJHF/BaXVO8TNvFRzigFwZQyqdUbwPyZf k1e0NMRC3IK0yrzGU1Od9Jck4f9IO12JnFv9qwosAsRsfnKzs2VyduAqbEh4Fq3+8x HrJUixwTE5KU3qMzb+6PlXi4iqEFr0euBjrqwOdCK/+aEB3wgIv5gwZHaX7ddm0yEp atYdTXktAv9/DlU9k4ard3QNJP4uzNgnYtHB9kWPMqsvo6uEbpMxFLIrj9cSVPjoYF bYuWvf/+/B0w1dq9/qMPbzpacHUEkPVxOZdJEl3KPZ8xAuBsnWi4HeHJh89pGFBxwi blc8MY9XagJzw== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, Eric Biggers Cc: Sweet Tea Dorminy Subject: [PATCH v3 16/17] btrfs: explicitly track file extent length and encryption Date: Tue, 8 Aug 2023 13:12:18 -0400 Message-ID: <13ee63623ae96a6d2da7b19b779a3d324a76eaf6.1691510179.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org With the advent of storing fscrypt contexts with each encrypted extent, extents will have a variable length depending on encryption status. Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/ctree.h | 2 ++ fs/btrfs/file.c | 4 ++-- fs/btrfs/inode.c | 9 +++++++-- fs/btrfs/reflink.c | 1 + fs/btrfs/tree-log.c | 8 +++++--- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index f2d2b313bde5..b1afcfc62f75 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -364,6 +364,8 @@ struct btrfs_replace_extent_info { u64 file_offset; /* Pointer to a file extent item of type regular or prealloc. */ char *extent_buf; + /* The length of @extent_buf */ + u32 extent_buf_size; /* * Set to true when attempting to replace a file range with a new extent * described by this structure, set to false when attempting to clone an diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 73038908876a..4988c9317234 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -2246,14 +2246,14 @@ static int btrfs_insert_replace_extent(struct btrfs_trans_handle *trans, key.type = BTRFS_EXTENT_DATA_KEY; key.offset = extent_info->file_offset; ret = btrfs_insert_empty_item(trans, root, path, &key, - sizeof(struct btrfs_file_extent_item)); + extent_info->extent_buf_size); if (ret) return ret; leaf = path->nodes[0]; slot = path->slots[0]; write_extent_buffer(leaf, extent_info->extent_buf, btrfs_item_ptr_offset(leaf, slot), - sizeof(struct btrfs_file_extent_item)); + extent_info->extent_buf_size); extent = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item); ASSERT(btrfs_file_extent_type(leaf, extent) != BTRFS_FILE_EXTENT_INLINE); btrfs_set_file_extent_offset(leaf, extent, extent_info->data_offset); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index ed0579577263..de6778fe65c8 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2885,6 +2885,9 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, u64 num_bytes = btrfs_stack_file_extent_num_bytes(stack_fi); u64 ram_bytes = btrfs_stack_file_extent_ram_bytes(stack_fi); struct btrfs_drop_extents_args drop_args = { 0 }; + size_t fscrypt_context_size = + btrfs_stack_file_extent_encryption(stack_fi) ? + FSCRYPT_SET_CONTEXT_MAX_SIZE : 0; int ret; path = btrfs_alloc_path(); @@ -2904,7 +2907,7 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, drop_args.start = file_pos; drop_args.end = file_pos + num_bytes; drop_args.replace_extent = true; - drop_args.extent_item_size = sizeof(*stack_fi); + drop_args.extent_item_size = sizeof(*stack_fi) + fscrypt_context_size; ret = btrfs_drop_extents(trans, root, inode, &drop_args); if (ret) goto out; @@ -2915,7 +2918,7 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, ins.type = BTRFS_EXTENT_DATA_KEY; ret = btrfs_insert_empty_item(trans, root, path, &ins, - sizeof(*stack_fi)); + sizeof(*stack_fi) + fscrypt_context_size); if (ret) goto out; } @@ -9629,6 +9632,7 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent( u64 len = ins->offset; int qgroup_released; int ret; + size_t fscrypt_context_size = 0; memset(&stack_fi, 0, sizeof(stack_fi)); @@ -9661,6 +9665,7 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent( extent_info.data_len = len; extent_info.file_offset = file_offset; extent_info.extent_buf = (char *)&stack_fi; + extent_info.extent_buf_size = sizeof(stack_fi) + fscrypt_context_size; extent_info.is_new_extent = true; extent_info.update_times = true; extent_info.qgroup_reserved = qgroup_released; diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c index ad722f495c9b..9f3b5748f39b 100644 --- a/fs/btrfs/reflink.c +++ b/fs/btrfs/reflink.c @@ -502,6 +502,7 @@ static int btrfs_clone(struct inode *src, struct inode *inode, clone_info.data_len = datal; clone_info.file_offset = new_key.offset; clone_info.extent_buf = buf; + clone_info.extent_buf_size = size; clone_info.is_new_extent = false; clone_info.update_times = !no_time_update; ret = btrfs_replace_file_extents(BTRFS_I(inode), path, diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 9c73ae09f0a1..49001bf8a0b2 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -4634,7 +4634,9 @@ static int log_one_extent(struct btrfs_trans_handle *trans, u64 extent_offset = em->start - em->orig_start; u64 block_len; int ret; - u8 encryption = 0; + size_t fscrypt_context_size = 0; + u8 encryption = IS_ENCRYPTED(&inode->vfs_inode) ? + BTRFS_ENCRYPTION_FSCRYPT : 0; btrfs_set_stack_file_extent_generation(&fi, trans->transid); if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) @@ -4676,7 +4678,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans, drop_args.start = em->start; drop_args.end = em->start + em->len; drop_args.replace_extent = true; - drop_args.extent_item_size = sizeof(fi); + drop_args.extent_item_size = sizeof(fi) + fscrypt_context_size; ret = btrfs_drop_extents(trans, log, inode, &drop_args); if (ret) return ret; @@ -4688,7 +4690,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans, key.offset = em->start; ret = btrfs_insert_empty_item(trans, log, path, &key, - sizeof(fi)); + sizeof(fi) + fscrypt_context_size); if (ret) return ret; } From patchwork Tue Aug 8 17:12:19 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13346660 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0DC4CC001DB for ; Tue, 8 Aug 2023 18:10:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235381AbjHHSKn (ORCPT ); Tue, 8 Aug 2023 14:10:43 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50542 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235342AbjHHSKJ (ORCPT ); Tue, 8 Aug 2023 14:10:09 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7D94F160AC7; Tue, 8 Aug 2023 10:12:50 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 1A8508343D; Tue, 8 Aug 2023 13:12:50 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1691514770; bh=fisPgf4IomYDyc/gnfxhQqEMa1WSDEUx0M4E649/8mc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hVVQGy6Qd/JPl0w0skt1YqXWbVgQg+chHtXCujvFad3kF0rXeyZDlN+zQDBMORG4i vv282XboraJUz0p5NKbNCfmwl3CfbVjP83N1QDFLdeP/5iN+gbN8p44U3ZWlcJ7Scf CE6B7/zl/iItDqeuKdzM9wpdRMTIrl4yUWmnk3ORTd5/x29FoVTEcLALU6mrBIIGdZ qZrJoyVaA4Ic8RH6BGGYXyeozQvbYpW5u+ozV3Y/LCBmG5j2jt6M+gWAklSnadG6fu pXNtXQTKFfJBA3mOyZXNzqhyXY0tJUJTRMuhsaR57inP3EbZgaU3VOGDxCgGHFiHVb dQflINT/3tHhg== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org, Eric Biggers Cc: Sweet Tea Dorminy Subject: [PATCH v3 17/17] btrfs: save and load fscrypt extent contexts Date: Tue, 8 Aug 2023 13:12:19 -0400 Message-ID: <3fee78c12452ab3176900d082cb5401dd0ca5b53.1691510179.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org This change actually saves and loads the extent contexts created and freed by the last change. Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/file-item.c | 7 +++ fs/btrfs/fscrypt.c | 108 ++++++++++++++++++++++++++++++++++++++++++- fs/btrfs/fscrypt.h | 35 ++++++++++++++ fs/btrfs/inode.c | 37 +++++++++++++-- fs/btrfs/tree-log.c | 14 +++++- 5 files changed, 194 insertions(+), 7 deletions(-) diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index f83f7020ed89..880fb7810152 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -1299,6 +1299,13 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode, ctxsize = btrfs_file_extent_ctxsize_from_item(leaf, path); ASSERT(ctxsize == btrfs_file_extent_encryption_ctxsize(leaf, fi)); + + if (ctxsize) { + unsigned long ptr = (unsigned long)fi->encryption_context; + int res = btrfs_fscrypt_load_extent_info(inode, leaf, ptr, + ctxsize, &em->fscrypt_info); + ASSERT(res == 0); + } } else if (type == BTRFS_FILE_EXTENT_INLINE) { em->block_start = EXTENT_MAP_INLINE; em->start = extent_start; diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index 5508cbc6bccb..7324375af0ac 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -75,6 +75,65 @@ bool btrfs_fscrypt_match_name(struct fscrypt_name *fname, return !memcmp(digest, nokey_name->sha256, sizeof(digest)); } +int btrfs_fscrypt_fill_extent_context(struct btrfs_inode *inode, + struct fscrypt_info *info, + u8 *context_buffer, size_t *context_len) +{ + struct btrfs_fs_info *fs_info = inode->root->fs_info; + int ret; + + if (!IS_ENCRYPTED(&inode->vfs_inode)) + return 0; + + + ret = fscrypt_set_extent_context(info, context_buffer + sizeof(u32), + FSCRYPT_SET_CONTEXT_MAX_SIZE); + if (ret < 0) { + btrfs_err(fs_info, "fscrypt context could not be saved"); + return ret; + } + + /* the return value, if nonnegative, is the fscrypt context size */ + ret += sizeof(u32); + + put_unaligned_le32(ret, context_buffer); + + *context_len = ret; + return 0; +} + +int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode, + struct extent_buffer *leaf, + unsigned long ptr, + u8 ctxsize, + struct fscrypt_info **info_ptr) +{ + struct btrfs_fs_info *fs_info = inode->root->fs_info; + u8 context[BTRFS_FSCRYPT_EXTENT_CONTEXT_MAX_SIZE]; + int res; + unsigned int nofs_flags; + u32 len; + + read_extent_buffer(leaf, context, ptr, ctxsize); + + nofs_flags = memalloc_nofs_save(); + res = fscrypt_load_extent_info(&inode->vfs_inode, + context + sizeof(u32), + ctxsize - sizeof(u32), info_ptr); + memalloc_nofs_restore(nofs_flags); + + if (res) + btrfs_err(fs_info, "Unable to load fscrypt info: %d", res); + + len = get_unaligned_le32(context); + if (len != ctxsize) { + res = -EINVAL; + btrfs_err(fs_info, "fscrypt info size mismatches"); + } + + return res; +} + static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len) { struct btrfs_key key = { @@ -138,11 +197,14 @@ static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx, if (!trans) trans = btrfs_start_transaction(BTRFS_I(inode)->root, 1); - if (IS_ERR(trans)) + if (IS_ERR(trans)) { + btrfs_free_path(path); return PTR_ERR(trans); + } ret = btrfs_search_slot(trans, BTRFS_I(inode)->root, &key, path, 0, 1); if (ret < 0) { + btrfs_free_path(path); btrfs_abort_transaction(trans, ret); return ret; } @@ -151,12 +213,13 @@ static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx, btrfs_release_path(path); ret = btrfs_insert_empty_item(trans, BTRFS_I(inode)->root, path, &key, len); if (ret) { + btrfs_free_path(path); btrfs_abort_transaction(trans, ret); return ret; } } - btrfs_fscrypt_update_context(path, ctx, len); + btrfs_free_path(path); if (fs_data) return ret; @@ -166,6 +229,7 @@ static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx, inode_inc_iversion(inode); inode->i_ctime = current_time(inode); ret = btrfs_update_inode(trans, BTRFS_I(inode)->root, BTRFS_I(inode)); + if (!ret) { btrfs_end_transaction(trans); return ret; @@ -181,6 +245,45 @@ static bool btrfs_fscrypt_empty_dir(struct inode *inode) return inode->i_size == BTRFS_EMPTY_DIR_SIZE; } +int btrfs_fscrypt_get_extent_info(const struct inode *inode, + u64 lblk_num, + struct fscrypt_info **info_ptr, + u64 *extent_offset, + u64 *extent_length) +{ + u64 offset = lblk_num << inode->i_blkbits; + struct extent_map *em; + + /* Since IO must be in progress on this extent, this must succeed */ + em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, PAGE_SIZE); + if (!em) { + btrfs_err(BTRFS_I(inode)->root->fs_info, + "extent context requested for block %llu of inode %lu without an extent", + lblk_num, inode->i_ino); + return -EINVAL; + } + + if (em->block_start == EXTENT_MAP_HOLE) { + btrfs_info(BTRFS_I(inode)->root->fs_info, + "extent context requested for block %llu of inode %lu without an extent", + lblk_num, inode->i_ino); + free_extent_map(em); + return -ENOENT; + } + + *info_ptr = em->fscrypt_info; + + if (extent_offset) + *extent_offset + = (offset - em->orig_start) >> inode->i_blkbits; + + if (extent_length) + *extent_length = em->len >> inode->i_blkbits; + + free_extent_map(em); + return 0; +} + static struct block_device **btrfs_fscrypt_get_devices(struct super_block *sb, unsigned int *num_devs) { @@ -232,6 +335,7 @@ const struct fscrypt_operations btrfs_fscrypt_ops = { .get_context = btrfs_fscrypt_get_context, .set_context = btrfs_fscrypt_set_context, .empty_dir = btrfs_fscrypt_empty_dir, + .get_extent_info = btrfs_fscrypt_get_extent_info, .get_devices = btrfs_fscrypt_get_devices, .key_prefix = "btrfs:" }; diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h index 26627df9d5f9..46d003b0e4c3 100644 --- a/fs/btrfs/fscrypt.h +++ b/fs/btrfs/fscrypt.h @@ -9,6 +9,9 @@ #include "fs.h" +#define BTRFS_FSCRYPT_EXTENT_CONTEXT_MAX_SIZE \ + (sizeof(u32) + FSCRYPT_SET_CONTEXT_MAX_SIZE) + static inline u32 btrfs_file_extent_encryption_ctxsize(const struct extent_buffer *eb, struct btrfs_file_extent_item *e) @@ -38,6 +41,15 @@ bool btrfs_fscrypt_match_name(struct fscrypt_name *fname, struct extent_buffer *leaf, unsigned long de_name, u32 de_name_len); +int btrfs_fscrypt_fill_extent_context(struct btrfs_inode *inode, + struct fscrypt_info *info, + u8 *context_buffer, size_t *context_len); + +int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode, + struct extent_buffer *leaf, + unsigned long ptr, u8 ctxsize, + struct fscrypt_info **info_ptr); + #else static inline int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf, struct btrfs_dir_item *di, @@ -56,8 +68,31 @@ static inline bool btrfs_fscrypt_match_name(struct fscrypt_name *fname, return !memcmp_extent_buffer(leaf, fname->disk_name.name, de_name, de_name_len); } + +static inline int btrfs_fscrypt_fill_extent_context(struct btrfs_inode *inode, + struct fscrypt_info *info, + u8 *context_buffer, + size_t *context_len) +{ + return -EINVAL; +} + +static inline int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode, + struct extent_buffer *leaf, + unsigned long ptr, + u8 ctxsize, + struct fscrypt_info **info_ptr) +{ + return -EINVAL; +} + #endif /* CONFIG_FS_ENCRYPTION */ +int btrfs_fscrypt_get_extent_info(const struct inode *inode, + u64 lblk_num, + struct fscrypt_info **info_ptr, + u64 *extent_offset, + u64 *extent_length); void btrfs_fscrypt_copy_fscrypt_info(struct btrfs_inode *inode, struct fscrypt_info *from, struct fscrypt_info **to_ptr); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index de6778fe65c8..de9e7073017f 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2885,17 +2885,41 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, u64 num_bytes = btrfs_stack_file_extent_num_bytes(stack_fi); u64 ram_bytes = btrfs_stack_file_extent_ram_bytes(stack_fi); struct btrfs_drop_extents_args drop_args = { 0 }; - size_t fscrypt_context_size = - btrfs_stack_file_extent_encryption(stack_fi) ? - FSCRYPT_SET_CONTEXT_MAX_SIZE : 0; + size_t fscrypt_context_size = 0; + u8 context[BTRFS_FSCRYPT_EXTENT_CONTEXT_MAX_SIZE]; + u8 encryption = 0; + int ret; path = btrfs_alloc_path(); if (!path) return -ENOMEM; + if (IS_ENCRYPTED(&inode->vfs_inode)) { + struct fscrypt_info *fscrypt_info; + u64 lblk_num = file_pos >> root->fs_info->sectorsize_bits; + + ret = btrfs_fscrypt_get_extent_info(&inode->vfs_inode, + lblk_num, &fscrypt_info, + NULL, NULL); + if (ret) { + btrfs_err(root->fs_info, "No fscrypt context found"); + goto out; + } + + ret = btrfs_fscrypt_fill_extent_context(inode, fscrypt_info, + context, + &fscrypt_context_size); + if (ret) + goto out; + encryption = BTRFS_ENCRYPTION_FSCRYPT; + + } + + btrfs_set_stack_file_extent_encryption(stack_fi, encryption); + /* - * we may be replacing one extent in the tree with another. + * We may be replacing one extent in the tree with another. * The new extent is pinned in the extent map, and we don't want * to drop it from the cache until it is completely in the btree. * @@ -2928,6 +2952,11 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, btrfs_item_ptr_offset(leaf, path->slots[0]), sizeof(struct btrfs_file_extent_item)); + write_extent_buffer(leaf, context, + btrfs_item_ptr_offset(leaf, path->slots[0]) + + sizeof(struct btrfs_file_extent_item), + fscrypt_context_size); + btrfs_mark_buffer_dirty(leaf); btrfs_release_path(path); diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 49001bf8a0b2..28b86e338434 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -22,6 +22,7 @@ #include "zoned.h" #include "inode-item.h" #include "fs.h" +#include "fscrypt.h" #include "accessors.h" #include "extent-tree.h" #include "root-tree.h" @@ -4637,6 +4638,14 @@ static int log_one_extent(struct btrfs_trans_handle *trans, size_t fscrypt_context_size = 0; u8 encryption = IS_ENCRYPTED(&inode->vfs_inode) ? BTRFS_ENCRYPTION_FSCRYPT : 0; + u8 context[BTRFS_FSCRYPT_EXTENT_CONTEXT_MAX_SIZE]; + + ret = btrfs_fscrypt_fill_extent_context(inode, em->fscrypt_info, + context, &fscrypt_context_size); + if (ret) + return ret; + + btrfs_set_stack_file_extent_encryption(&fi, encryption); btrfs_set_stack_file_extent_generation(&fi, trans->transid); if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) @@ -4658,7 +4667,6 @@ static int log_one_extent(struct btrfs_trans_handle *trans, btrfs_set_stack_file_extent_num_bytes(&fi, em->len); btrfs_set_stack_file_extent_ram_bytes(&fi, em->ram_bytes); btrfs_set_stack_file_extent_compression(&fi, em->compress_type); - btrfs_set_stack_file_extent_encryption(&fi, encryption); ret = log_extent_csums(trans, inode, log, em, ctx); if (ret) @@ -4698,6 +4706,10 @@ static int log_one_extent(struct btrfs_trans_handle *trans, write_extent_buffer(leaf, &fi, btrfs_item_ptr_offset(leaf, path->slots[0]), sizeof(fi)); + write_extent_buffer(leaf, context, + btrfs_item_ptr_offset(leaf, path->slots[0]) + + sizeof(fi), fscrypt_context_size); + btrfs_mark_buffer_dirty(leaf); btrfs_release_path(path);