From patchwork Thu Jun 29 00:35:24 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: 13296463 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 A8BF5C001DB for ; Thu, 29 Jun 2023 00:35:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230449AbjF2Afx (ORCPT ); Wed, 28 Jun 2023 20:35:53 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58674 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231447AbjF2Aft (ORCPT ); Wed, 28 Jun 2023 20:35:49 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E64262728; Wed, 28 Jun 2023 17:35: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 3F3AC8030E; Wed, 28 Jun 2023 20:35:47 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1687998947; bh=mS8xcbyfJ2L+bJJYin0TxhoCv7CCnF7NZeAWVfGmOqw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WWltYi1FfNjf/ZCGmuELIxDthB2YhneXl44dzR4QmVcCWB/CXRq8vPf/bcuhDFdP8 pwFi3qhJ0OOOgRDMxGT7aiQhWnjnii6wefF+wqgXYqS1rwpkQ0R+bwnwB0ajcCffL8 mQXj2m2j58Rq67BBkLlvoPKZJFnARBHYrVyOiYwzNXzZasPARzGM0zUTF4agINmktw wzPbfclehwHmQWqpFyTzaNmM8FCKIiqKU2S1Bq1GC+YHHD3CY/4aGQM0VaRlejcaBs KBrTYmR6AQzH8lNLUUoDmdkV+6MjAmXDHnc2yRAyQJ1ydOb+zBkAdEHut6+ouJ7Cq3 S9tKfDBea1B3g== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v1 01/17] btrfs: disable various operations on encrypted inodes Date: Wed, 28 Jun 2023 20:35:24 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Omar Sandoval Initially, only normal data extents, using the normal (non-direct) IO path, will be encrypted. This change forbids various other bits: - allows reflinking only if both inodes have the same encryption status - disables direct IO on encrypted inodes - disable inline data on encrypted inodes Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/file.c | 4 ++-- fs/btrfs/inode.c | 3 ++- fs/btrfs/reflink.c | 7 +++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 392bc7d512a0..354962a7dd72 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1502,7 +1502,7 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from) goto relock; } - if (check_direct_IO(fs_info, from, pos)) { + if (IS_ENCRYPTED(inode) || check_direct_IO(fs_info, from, pos)) { btrfs_inode_unlock(BTRFS_I(inode), ilock_flags); goto buffered; } @@ -3741,7 +3741,7 @@ static ssize_t btrfs_direct_read(struct kiocb *iocb, struct iov_iter *to) ssize_t read = 0; ssize_t ret; - if (fsverity_active(inode)) + if (IS_ENCRYPTED(inode) || fsverity_active(inode)) return 0; if (check_direct_read(btrfs_sb(inode->i_sb), to, iocb->ki_pos)) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index dbbb67293e34..48eadc4f187f 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -630,7 +630,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 Thu Jun 29 00:35:25 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: 13296465 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 71ECBC001B1 for ; Thu, 29 Jun 2023 00:35:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231502AbjF2Afy (ORCPT ); Wed, 28 Jun 2023 20:35:54 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58688 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231453AbjF2Afv (ORCPT ); Wed, 28 Jun 2023 20:35:51 -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 F1B4F2972; Wed, 28 Jun 2023 17:35:49 -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 3F26580727; Wed, 28 Jun 2023 20:35:49 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1687998949; bh=AtxgWhk5lNBLM6gRG58Tv02HFvsxRv3MxdlBvwTanfs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=GTlmXPw4dsJg4K++rqUa3lRarPKkWCe6BjnxuypVM9CCL7E6/xnyjJn5dlBBfv+xX i1O2xmPw7F5q4Gv9rLyHRpmA/s5QS4eA5N5LsO55m4ouZuxYoKnIsBawNhG166cCsX twYJXhe4DuxcGwioApoxjWt0gEtGpYTLXniNvWbH5q7TKXD+fDfefmQ+hYl0uar7tb jU8yv3roN0xfISi1got2Vv3coJoL9G0vz20TLfxQ7ILz1O0LdBVewDqSpnzlShbvzG zh0Ms8ZxMlF4jtkwxig8DYwws/kwtUyHScKLde1AYgncTaR3rguHb/NK+05jbgJqei WwlTPvONPbB4w== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org Cc: Sweet Tea Dorminy Subject: [PATCH v1 02/17] btrfs: disable verity on encrypted inodes Date: Wed, 28 Jun 2023 20:35:25 -0400 Message-Id: <3179a926bcc5e7fd138f9c1d6766b7412c3af5ec.1687988380.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@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 Thu Jun 29 00:35:26 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: 13296466 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 183CDEB64DD for ; Thu, 29 Jun 2023 00:35:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231522AbjF2Afz (ORCPT ); Wed, 28 Jun 2023 20:35:55 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58700 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231492AbjF2Afx (ORCPT ); Wed, 28 Jun 2023 20:35:53 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 228291FC2; Wed, 28 Jun 2023 17:35:52 -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 75D7680794; Wed, 28 Jun 2023 20:35:51 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1687998951; bh=6UGnG5woQyVvSY1kqpcu50DX5cxNvzxMEyYTNQCYGSQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=F+S4Nor+BXsLh7fXa7NtW7VUFP5kdWNWXPWwiy7lprpXB3yCIi1bRsC3pvxaNc+1V slWUSVbuJUpjxm2LjXwr47rm+its1/txiPn1uBckB4CFyuLQTbatx/sNLUn2ZMAgX7 L57MwSTzxbkPS/0GBpGKYednQstEHjEK31Hq7EgSP2ed2F1ELibsnIFl11ecyKns6u bfmmLcoAEQd6fT+pngatFHMkYKe4ztO49YSFgINRTPl7D3aCr6zCsWZNN3wOljEFTD 6xObJw8ePuT/jPNbK/0mC4dId+kBmWIneQW94IwgALDzSjEO3gNz8z1oKfAO4firkP YSOOX9RxaZSbw== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v1 03/17] fscrypt: expose fscrypt_nokey_name Date: Wed, 28 Jun 2023 20:35:26 -0400 Message-Id: <4b98de9d4c49bc91417923746c7397aabef54eea.1687988380.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@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 9c7255b48f1d..74bfb91a0f6b 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 Thu Jun 29 00:35:27 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: 13296467 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 B4F71C001B0 for ; Thu, 29 Jun 2023 00:35:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231561AbjF2Af5 (ORCPT ); Wed, 28 Jun 2023 20:35:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58730 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231543AbjF2Af4 (ORCPT ); Wed, 28 Jun 2023 20:35:56 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7E44C1FC2; Wed, 28 Jun 2023 17:35:54 -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 D629380727; Wed, 28 Jun 2023 20:35:53 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1687998954; bh=5zav5i4Hs98TMB2ipHk2vbp+Jp17jYw7slfMi+B9Kc4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=JAkPMGduluMKNvM3o0OEvAhUTqDa8PcJ1PBW/DCXBKE8v7ec/zYXcpH1z2p6kekzY kyHgnbk/1ziu3bxDA61RQtFuSUPBR49yO1EUG5lQjpvmRMdg91JwkHF/x/ynoGh6WM SKR2PeP7AHsVoQopSVjSiy+g6x5bQdmIFJZdsGQBAXUcBvDReZVjLhXx73XpcQVCPa 33ArGCWDNJRDVqH0HZowPUuS+rw39jGPpCw5fBfHb2EZ/f/oSbhgEIcpCVDpTlMzVy LVcTLW14ztaSmZTkE5PFE102xhCU0hQZ3UDxeBcajm4I16dnDH+hfuX8W9bzEKXRuX 4VjU1dUrb1YKg== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v1 04/17] btrfs: start using fscrypt hooks Date: Wed, 28 Jun 2023 20:35:27 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@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 | 87 +++++++++++++++++++++++++++++++++++------- fs/btrfs/super.c | 2 + 7 files changed, 99 insertions(+), 13 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 d47a927b3504..ec4a06a78aff 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 354962a7dd72..0de95c8375a1 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -3702,6 +3702,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 48eadc4f187f..5ce167cfa1dc 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5337,6 +5337,7 @@ void btrfs_evict_inode(struct inode *inode) trace_btrfs_inode_evict(inode); if (!root) { + fscrypt_put_encryption_info(inode); fsverity_cleanup_inode(inode); clear_inode(inode); return; @@ -5440,6 +5441,7 @@ void btrfs_evict_inode(struct inode *inode) * to retry these periodically in the future. */ btrfs_remove_delayed_node(BTRFS_I(inode)); + fscrypt_put_encryption_info(inode); fsverity_cleanup_inode(inode); clear_inode(inode); } @@ -6190,6 +6192,10 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args, return ret; } + ret = fscrypt_prepare_new_inode(dir, inode, &args->encrypt); + if (ret) + return ret; + /* 1 to add inode item */ *trans_num_items = 1; /* 1 to add compression property */ @@ -6666,9 +6672,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) @@ -8612,6 +8622,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)); } @@ -8682,8 +8693,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) @@ -9274,6 +9284,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); @@ -9493,15 +9508,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) @@ -9510,8 +9532,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); @@ -9538,10 +9560,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) { @@ -9560,10 +9595,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); @@ -9580,6 +9615,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, @@ -11058,7 +11116,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, @@ -11068,4 +11126,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 8b1c1225245e..d0df46370c5d 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" @@ -1140,6 +1141,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 Thu Jun 29 00:35:28 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: 13296468 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 C2F30EB64D7 for ; Thu, 29 Jun 2023 00:36:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231557AbjF2AgA (ORCPT ); Wed, 28 Jun 2023 20:36:00 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58754 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231359AbjF2Af6 (ORCPT ); Wed, 28 Jun 2023 20:35: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 5F0481FC2; Wed, 28 Jun 2023 17:35:57 -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 4F5B68030E; Wed, 28 Jun 2023 20:35:56 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1687998957; bh=Sc7tLoedkSEEG+7H7YqiMVhRPpP/0QuVxtkT61Zui3s=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=va9HsydcFmPLYWqJYJ3dBXOIdl4Xuq50uW3mXgizLDfSh2Z/AVN1VHXwJWh0FT39v KTi5VA+FJZuUhSl70LcWSlZx3RMA8o48lxjqB8kK4A+0cs5+lob4nyrlawFW+Nie4Q XNEai/1qOGbZni9m3Z8fxq7vmpn6yJuJEwBgqqgZqmFmhfOE8navza4X1nu5bgHOOt 6elawm+xLcPxJwCGcNJAA4f93RBJ9Bg0ODOzxm16h5jf2br4AQCttG1RXw3TOFEzcM FJCRgyZ0kwF91qVV34A9/z14pwxUCLsWGRX7srYqm/PbAlnP14iuXPZpOOOEjHeiBS xMp2o5GrwgNlw== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v1 05/17] btrfs: add inode encryption contexts Date: Wed, 28 Jun 2023 20:35:28 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@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 | 116 ++++++++++++++++++++++++++++++++ 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, 153 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index 3a53dc59c1e4..235f65e43d96 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -1,8 +1,124 @@ // 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_CTXT_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 = -EINVAL; + 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_path *path; + int ret; + struct btrfs_trans_handle *trans = fs_data; + struct btrfs_key key = { + .objectid = btrfs_ino(BTRFS_I(inode)), + .type = BTRFS_FSCRYPT_CTXT_ITEM_KEY, + .offset = 0, + }; + 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_fscrypt_update_context(path, ctx, len); + btrfs_free_path(path); + return ret; + } + + btrfs_free_path(path); + if (ret < 0) { + btrfs_abort_transaction(trans, ret); + return ret; + } + + ret = btrfs_insert_item(trans, BTRFS_I(inode)->root, &key, (void *) ctx, len); + if (ret) { + btrfs_abort_transaction(trans, ret); + 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) { + if (!fs_data) + btrfs_end_transaction(trans); + return ret; + } + + btrfs_abort_transaction(trans, ret); + 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 5ce167cfa1dc..9dc47f769641 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" @@ -6179,6 +6180,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); @@ -6212,6 +6216,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)++; @@ -6390,6 +6397,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. @@ -6464,6 +6476,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 edbbd5cf23fc..11564e48d736 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..ea88dd69957f 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_CTXT_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 Thu Jun 29 00:35:29 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: 13296509 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 ADA04EB64DA for ; Thu, 29 Jun 2023 00:36:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231631AbjF2AgH (ORCPT ); Wed, 28 Jun 2023 20:36:07 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58760 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231537AbjF2Af7 (ORCPT ); Wed, 28 Jun 2023 20:35:59 -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 8E48A2102; Wed, 28 Jun 2023 17:35:58 -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 13C9E80727; Wed, 28 Jun 2023 20:35:57 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1687998958; bh=RLuxTQSqMbxMmWOXZwiByw9cEBeb2fq1lbwKHYgbcl4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=if3mNvbYnhTu1PD8bWSegrc8h3g18vy34zJW1ubTFQcngBbhl+/fHVBdW+M4X/Ony xNfBOx+vcf38OGS+ee5NBr2bEFW2vICW3F8d1Q4aYJ9BvLmfwVzs6hKpHrVjQY2BYt JhIyySys25tXErv2fOOSZJKZyGD4G3KAVTzpYBdicXY/p9UGE/jWreX350MjTJMjsx lTe2tTw2MAnfBjCzndlMFKsPPGwvtdutg7s31YuOT/yZ0YSpYiaev1ynj09LjwAnZ/ E57FOywxJgEGW8IowrjHR2/fbHvJ8kU2sU9ZCSc2MBPGFH9cfNxjV+OwcFWjOo1L8Y hwgsAHd42LaYQ== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v1 06/17] btrfs: add new FEATURE_INCOMPAT_ENCRYPT flag Date: Wed, 28 Jun 2023 20:35:29 -0400 Message-Id: <77cb8934d438551b23f09dd9931a9dcb31d01a16.1687988380.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@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 +++++ include/uapi/linux/btrfs.h | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h index 203d2a267828..578874fbc98f 100644 --- a/fs/btrfs/fs.h +++ b/fs/btrfs/fs.h @@ -225,9 +225,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 d0df46370c5d..ad18127cee10 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -2422,6 +2422,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/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 Thu Jun 29 00:35:30 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: 13296514 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 B9A8FEB64DD for ; Thu, 29 Jun 2023 00:36:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231633AbjF2AgM (ORCPT ); Wed, 28 Jun 2023 20:36:12 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58782 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231573AbjF2AgC (ORCPT ); Wed, 28 Jun 2023 20:36:02 -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 00CCE210E; Wed, 28 Jun 2023 17:35:59 -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 5927280794; Wed, 28 Jun 2023 20:35:59 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1687998959; bh=kCpbJaEr2gtYgV8qN0Ugk9cDtMER0wOGUPzIbbBSQgg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=KukCQby7EA44atN6l/uaEbAp1U4BYWuI2OUUj1GB80qqCotF0xy0ds39WP90ucXKG iCWNJPHveCKhbKeWdXqDvudQjJkhSIJCt5ii1Yd6v3Ar60r9I6oxCW+5PT02RUF4ZL W3SC2xtUqVoWkGD4RrYBm8RGo2gJRzKJ2bZPUz+IIbTnYPTNtZiwMAoZzmYKuS+SiO 1dvklAxUjn/rKeH1p/+1r8Ukyssl0Ay6VzjVkYC/wWrA5Wu6wkAy4F1NEOVRPd7k9z RMu4LTfv/yUA22c13H5WnldHcYH2ua1HysFr9CAoVNGZ2YM78RSoGCdTwkbGy8esYx GM5nUfBbag+rQ== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v1 07/17] btrfs: adapt readdir for encrypted and nokey names Date: Wed, 28 Jun 2023 20:35:30 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@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 | 30 +++++++- 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 | 46 +++++++++++++ fs/btrfs/fscrypt.h | 19 ++++++ fs/btrfs/inode.c | 143 ++++++++++++++++++++++++++------------- fs/btrfs/root-tree.c | 8 ++- fs/btrfs/root-tree.h | 2 +- fs/btrfs/tree-log.c | 3 +- 13 files changed, 320 insertions(+), 68 deletions(-) diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index ec4a06a78aff..464059674ae5 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..919303d29b76 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -1497,6 +1497,7 @@ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans, ret = __btrfs_add_delayed_item(delayed_node, delayed_item); if (unlikely(ret)) { + // TODO: It would be nice to print the base64encoded name here maybe? btrfs_err(trans->fs_info, "err add delayed dir index item(name: %.*s) into the insertion tree of the delayed node(root id: %llu, inode id: %llu, errno: %d)", name_len, name, delayed_node->root->root_key.objectid, @@ -1724,7 +1725,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 +1737,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 +1765,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 a91d5ad27984..10a06719811c 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -4180,6 +4180,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); +} + void write_extent_buffer_chunk_tree_uuid(const struct extent_buffer *eb, const void *srcv) { diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index c5fae3a7d911..9dc8c6f3af0f 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 235f65e43d96..20727d920841 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -5,13 +5,59 @@ #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. Also, it edits the provided argument to populate the disk_name + * if we successfully match and previously were using a nokey name. + */ +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 void *)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); + if (!memcmp(digest, nokey_name->sha256, sizeof(digest))) { + /* + * 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. + */ + fname->disk_name.name = kmalloc(de_name_len, GFP_KERNEL | GFP_NOFS); + if (!fname->disk_name.name) + fname->disk_name.name = ERR_PTR(-ENOMEM); + else + read_extent_buffer(leaf, fname->disk_name.name, + de_name, de_name_len); + fname->disk_name.len = de_name_len; + return true; + } + return false; +} + 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 9dc47f769641..7fa74693e154 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4182,7 +4182,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; @@ -4200,7 +4200,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; @@ -4228,11 +4228,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; } @@ -4253,8 +4256,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); } /* @@ -4272,7 +4277,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); @@ -4285,7 +4290,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; @@ -4336,7 +4341,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; @@ -4373,8 +4378,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) { @@ -4391,8 +4394,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; @@ -4418,7 +4421,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; @@ -4435,7 +4438,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; @@ -4759,7 +4762,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); /* @@ -5454,32 +5457,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; @@ -5491,13 +5485,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; } @@ -5769,13 +5763,18 @@ 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); + 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) return ERR_PTR(ret); @@ -5916,18 +5915,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; @@ -5945,6 +5958,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; @@ -5956,8 +5970,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) @@ -5971,8 +5990,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); @@ -5992,7 +6039,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; @@ -6023,6 +6071,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; } @@ -6531,6 +6581,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)); @@ -6588,7 +6639,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) { @@ -8958,7 +9009,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)); } @@ -8973,7 +9024,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)); } @@ -9222,7 +9273,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)); } @@ -9241,7 +9292,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 365a1cc0a3c3..f32f961adc7b 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 Thu Jun 29 00:35:31 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: 13296510 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 229B9C001B0 for ; Thu, 29 Jun 2023 00:36:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231563AbjF2AgK (ORCPT ); Wed, 28 Jun 2023 20:36:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58776 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231571AbjF2AgC (ORCPT ); Wed, 28 Jun 2023 20:36:02 -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 41EDF2961; Wed, 28 Jun 2023 17:36:01 -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 C2AE38030E; Wed, 28 Jun 2023 20:36:00 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1687998961; bh=ygx6Fucsv/PfbNluCySqHdaXBDBAud2k2FS6o3SlCkM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dRt+br6hPkVTDaU5CLyyWMrHIPkWwPfOR1lRCyKwKV0zsKdr+K6aNRTczTF7kJ9YA NCWsFvwSFBUL/Kj7MOgPvOIW5iw1oS5UTSKn3TYG70YYCEEZJ7AaUtSjFwcb2OxcYP qafLpoOa5A1r1wkVThPnQkvBj6+C/9ZfQjAjnWen/E6l63ceeyBIr6L4m/LIcEmiUQ 5zW5LC9169y/SV6J9cY53zbKlCpQgm3x8e8u/VehrCzzHW79qtCmINsrToCkDoIZ0O t/c+qJ+YOQhjFGEE8PTkJ0oBHaN3snhxBK+5H7cTR8/2tAH9FEOCiqYQF0/45Vxr5N xc/luygE4d8Gw== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org Cc: Sweet Tea Dorminy Subject: [PATCH v1 08/17] btrfs: use correct name hash for nokey names Date: Wed, 28 Jun 2023 20:35:31 -0400 Message-Id: <7490cfa3f5d851c622fc63a6b873f20104666322.1687988380.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@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. Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/dir-item.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c index da95ae411d72..87993b19cd9d 100644 --- a/fs/btrfs/dir-item.c +++ b/fs/btrfs/dir-item.c @@ -257,8 +257,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) From patchwork Thu Jun 29 00:35:32 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: 13296513 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 C42D3C001E0 for ; Thu, 29 Jun 2023 00:36:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231668AbjF2AgO (ORCPT ); Wed, 28 Jun 2023 20:36:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58812 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231615AbjF2AgG (ORCPT ); Wed, 28 Jun 2023 20:36:06 -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 899722974; Wed, 28 Jun 2023 17:36:02 -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 2422F80727; Wed, 28 Jun 2023 20:36:02 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1687998962; bh=WKxI5TpcGaunA+XWuZd3YW2F1EwVjJXDkMxPDtltkbU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Zctr2CN41DcTv1czz/5y4aCdFGHSnPk2ATLP6TQmrdUTAPVBE7MEMwyK6WKq0Jglj HrPdxpAmY29MQxM7sAJ32FYFflSBnCZMq3YScTo7ddCqIBPzSQ2sym8kocuY2p4F+T wK2KjOZzzOsWQPRUR9Ob5wBKYjiYxgZLVCOIQU8V53e6r48fV3rRMPDVb3Z5b4dExF fR05I4BPLXJXITA4tjv4CJTVFipB0YNEmu//HG4rKumqK56QsS+LiPsVpUhm/GTzHp a9PtmHDh+t/3iwDP62II0V8GwNca2uS23msvxSHdVAmGFo5tRiQigTfCNh69qcFTE2 VnmG5RNIWZzEw== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v1 09/17] btrfs: implement fscrypt ioctls Date: Wed, 28 Jun 2023 20:35:32 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@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 11564e48d736..71d1610444b4 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 Thu Jun 29 00:35:33 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: 13296512 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 10A21C001B1 for ; Thu, 29 Jun 2023 00:36:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231655AbjF2AgO (ORCPT ); Wed, 28 Jun 2023 20:36:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58824 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231616AbjF2AgG (ORCPT ); Wed, 28 Jun 2023 20:36:06 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D8F8B297B; Wed, 28 Jun 2023 17:36:03 -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 693968030E; Wed, 28 Jun 2023 20:36:03 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1687998963; bh=QVlHFQb+aOmVOh6Tft4Rn0vXRANjaMBj0UL6vP1nbUA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=G1uw/fJ9nBQbwDJxtlJThBlLNILMlvMb4HNlr31dDvPI2O1ctTvQf+FyIc7/zJaEi xNMNG1IbgEBqX+tFRhbrjM7hd+za/edP9/GSZtEOYR8qSkNAYhjFBO7vnNtoW7Sq3g gpdRAMtKhGckT8fmfbJS6ojWEE3FJ2hkXz0KS3A3j5G8KfIoA3+PtcubkoqJha1Vlc I6vq4osfniu2CTiTeUxAeb1RdyFsQok3YMqMcuJTuGphAgnj2hA8Cvykifpn4Tr2A9 XjwPAzbfJ1qP9Lmtex+VW7cKrCTFdt/L12BYax/QZbPfh1Gen0PFI8g/aAkvXLchDa LqzOHW9zkrI/A== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org Cc: Sweet Tea Dorminy Subject: [PATCH v1 10/17] btrfs: add encryption to CONFIG_BTRFS_DEBUG Date: Wed, 28 Jun 2023 20:35:33 -0400 Message-Id: <8c1baf02f2c6ca424917c1b0ef65d92012f43e93.1687988380.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@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 66fa9ab2c046..c7861e579d72 100644 --- a/fs/btrfs/Kconfig +++ b/fs/btrfs/Kconfig @@ -80,7 +80,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 71d1610444b4..7c4360b59aae 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 Thu Jun 29 00:35:34 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: 13296511 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 B127AC001DE for ; Thu, 29 Jun 2023 00:36:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231645AbjF2AgN (ORCPT ); Wed, 28 Jun 2023 20:36:13 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58830 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231638AbjF2AgH (ORCPT ); Wed, 28 Jun 2023 20:36:07 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4BAA22979; Wed, 28 Jun 2023 17:36:05 -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 CF53080794; Wed, 28 Jun 2023 20:36:04 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1687998965; bh=Gsyo6UI5OxD1zrZt4kHcLSsHKxT2/RB4lv7sLpxhsWE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=N7m63AHfRANuFOPqcyRSwRKoGKzDfQ3yfQiCPJ1KdhyrW28W4wUAcvF5AdnQaFcba 2OgYnPAYmcqWd7dFeIoTXq39zuDTvUxlsoHE6PbH2/ziUH2Jl8zJYZ7JCshiNWJ0m2 HsrQVemHlEGXnAPjz+2OWT65mTlPL2hOysMQvP+d4x9JqzRKuTE9TN5P9GXyHqxhOU lO4pvWDP+vQlb5JsALHfcBE10dPFeGKN3lrqHl10cWXJqKnCedK4nN5mRbWFCnHo5C zEnaJZufzc6Vo7xJIMzn8381kbFiXjr212sjwouD1ThD0Tro58AZxaVCdb52WFI6is PWdgcliKO1SuA== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org Cc: Sweet Tea Dorminy Subject: [PATCH v1 11/17] btrfs: add get_devices hook for fscrypt Date: Wed, 28 Jun 2023 20:35:34 -0400 Message-Id: <2a8b091cdb0993ad1cc618643e6df49d1aac9045.1687988380.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@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. I'm not sure this is the correct list of active devices and it isn't tested with any multi-device test yet. Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/fscrypt.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index 20727d920841..658a67856de6 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" /* @@ -162,9 +164,37 @@ 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; + struct block_device **devs; + struct btrfs_device *device; + int i; + + devs = kmalloc_array(fs_devices->num_devices, sizeof(*devs), + GFP_KERNEL); + if (!devs) + return ERR_PTR(-ENOMEM); + + mutex_lock(&fs_devices->device_list_mutex); + i = 0; + list_for_each_entry(device, &fs_devices->devices, dev_list) { + if (test_bit(BTRFS_DEV_STATE_MISSING, &device->dev_state)) + continue; + + devs[i++] = device->bdev; + } + mutex_unlock(&fs_devices->device_list_mutex); + *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 Thu Jun 29 00:35:35 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: 13296516 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 C528FC001B1 for ; Thu, 29 Jun 2023 00:36:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231689AbjF2AgQ (ORCPT ); Wed, 28 Jun 2023 20:36:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58814 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231641AbjF2AgJ (ORCPT ); Wed, 28 Jun 2023 20:36:09 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 84AC12D4E; Wed, 28 Jun 2023 17:36:06 -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 1B56980727; Wed, 28 Jun 2023 20:36:06 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1687998966; bh=j/41nbP/PqVxCE9M/BBFHhnZO/6K9lDr/ywwQLgMU1c=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NSrLdJ6dH7hsOTkeSObTPUrZC1apBCFcr0mitS1NP/px3p2RtamVcrSXryGvI4/5v 1Ylf4PL9GStMu0h5ezhOXjTfKx8CCM5zQCtfO6bZPTJNzhUAySaOQXKO2QSmk1byEX tAt/elfCU5YJE80Inze6czsU3XkAJ39Os435DYYmMEAvl5qcbiIJMn+zO0xH5QpE/A AX1vUuuYe17AWKE/gCGdPiVY2pfRPXLGRBrsqbWDYlpqUast+wwRpHdFOnNZ+XsBym OWj3Vf96W4QmTet5DVKxwxL3HC3z11tSUj6Crxl/rUtHCS5QiEABQniXrFvMbW81Hp MQJQkP4SYFhPw== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org Cc: Sweet Tea Dorminy Subject: [PATCH v1 12/17] btrfs: turn on inlinecrypt mount option for encrypt Date: Wed, 28 Jun 2023 20:35:35 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@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. --- fs/btrfs/ioctl.c | 4 ++++ fs/btrfs/super.c | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 7c4360b59aae..384bad37fa9d 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4574,6 +4574,10 @@ 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; + mb(); + } 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 ad18127cee10..d1c192dcd800 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -1161,6 +1161,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 Thu Jun 29 00:35:36 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: 13296515 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 4A7A9EB64D7 for ; Thu, 29 Jun 2023 00:36:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231681AbjF2AgQ (ORCPT ); Wed, 28 Jun 2023 20:36:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58846 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231650AbjF2AgJ (ORCPT ); Wed, 28 Jun 2023 20:36: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 E0C8E1FC2; Wed, 28 Jun 2023 17:36:07 -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 6054D8030E; Wed, 28 Jun 2023 20:36:07 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1687998967; bh=/c5GI1fbHHc0vOvX8HtVjo+f3WiIoAbs3+8uMWkXtqU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=p9thH9oUeBJlAzwKJT9C3BSA4nUPW6RBDCyuokle7VeYm8S7DmjM45yGgaUU6loPM RvANNZ4kQ6HWN0sxRYpw79xHnqA/TojM08+54S4orN0VAnPYIBDNWf2aarPs6khZK/ h3fPHSTiEgYsGi9C7XTQ1YxqoQX5pzSbctmMsOovfshGysdVSuUBj1r/hDGaa1L4ET OiKpcFmOmBO64/flbRzep+MTTSiDixeL7QgXltWdKjcgqLZNyMDsisL5bc3LT1FTMk c/XLpOdYK3982gC/wiChSnVTVv2ddH/M1HJGXatntQnQc7lQKvmXDM+1CGPFoFExRd U2hNBcL6eC3ZQ== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org Cc: Sweet Tea Dorminy Subject: [PATCH v1 13/17] btrfs: turn on the encryption ioctls Date: Wed, 28 Jun 2023 20:35:36 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@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 10a06719811c..3abf2b64e92e 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -787,6 +787,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 = 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 @@ -825,6 +833,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 Thu Jun 29 00:35:37 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: 13296517 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 3F04FEB64DA for ; Thu, 29 Jun 2023 00:36:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231709AbjF2AgR (ORCPT ); Wed, 28 Jun 2023 20:36:17 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58824 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231543AbjF2AgK (ORCPT ); Wed, 28 Jun 2023 20:36:10 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 68A612961; Wed, 28 Jun 2023 17:36:09 -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 EE3A980727; Wed, 28 Jun 2023 20:36:08 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1687998969; bh=8FtJx9q+CvP9a/KHxQSq5bil81DUVHK6Vfp3uHNy/r0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=rUpadTXYjXRsaJcN/gQZBGghafFL76akzrgTzrzCAciwCAbbHR3VdezTJ5CbPFHIW rTIHMIZZGdH8QvzkxvlgVEsWvTCzS4iqIDiuzFYutY+8WlgBi3aerfrQlrzyBDeG8e aoTxDCvZGiF1hD6Tx8u6R3+WkgV4e9+DtzOJinxT9skC2ARYF4JduDTdXNu3vbEede gJw/Iq2vg/KdVKyKzk0ScG0DfadESMjejNURIUMqQX6L6V026jKC9U2Bmlbqa9jppO ug0+Ugaby+iilrX2D/z6L88hcUDyQLQzb8CoLnw7HphYdQDBE9ufyKvpqZy2R61HIw fTZF/rwOeywGw== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org Cc: Sweet Tea Dorminy Subject: [PATCH v1 14/17] btrfs: create and free extent fscrypt_infos Date: Wed, 28 Jun 2023 20:35:37 -0400 Message-Id: <6cd87e7876990492636b187b5acf2f0e7db0f631.1687988380.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@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 | 9 +++++++++ fs/btrfs/extent_map.h | 3 +++ fs/btrfs/inode.c | 8 ++++++++ 3 files changed, 20 insertions(+) diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c index 0cdb3e86f29b..876ecd317c8a 100644 --- a/fs/btrfs/extent_map.c +++ b/fs/btrfs/extent_map.c @@ -65,6 +65,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 +209,13 @@ 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 maps with different fscrypt_contexts. + */ + if (!memcmp(&prev->fscrypt_info, &next->fscrypt_info, + sizeof(next->fscrypt_info))) + return 0; + ASSERT(next->block_start != EXTENT_MAP_DELALLOC && prev->block_start != EXTENT_MAP_DELALLOC); diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h index 35d27c756e08..3a1b66b1cedf 100644 --- a/fs/btrfs/extent_map.h +++ b/fs/btrfs/extent_map.h @@ -27,6 +27,8 @@ enum { EXTENT_FLAG_FS_MAPPING, /* This em is merged from two or more physically adjacent ems */ EXTENT_FLAG_MERGED, + /* This em has a fscrypt info */ + EXTENT_FLAG_ENCRYPTED, }; struct extent_map { @@ -50,6 +52,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/inode.c b/fs/btrfs/inode.c index 7fa74693e154..72da8f5e32af 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7436,6 +7436,14 @@ 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); + } + if (em->fscrypt_info) + set_bit(EXTENT_FLAG_ENCRYPTED, &em->flags); + ret = btrfs_replace_extent_map_range(inode, em, true); if (ret) { free_extent_map(em); From patchwork Thu Jun 29 00:35:38 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: 13296518 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 9ABFEEB64D7 for ; Thu, 29 Jun 2023 00:36:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231691AbjF2AgT (ORCPT ); Wed, 28 Jun 2023 20:36:19 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58868 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231609AbjF2AgM (ORCPT ); Wed, 28 Jun 2023 20:36:12 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B68B22728; Wed, 28 Jun 2023 17:36:10 -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 493508030E; Wed, 28 Jun 2023 20:36:10 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1687998970; bh=LjgdUVTk/4TKiQb+LcDMUjcZ2Mh194QHEhld30f2HwU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=No85yExNB0NsQcpl+RXoZk6nbfdmP2sWVrxSlP8DU0Gp6W6mW1LdI0Ob/0+jiP3Ty LMIl6newWOPyNo1Qwmkr6dsA4y8+J2NCq1Hb8cNvY2vrVHL3z/bK+rAQqMb40tInGk G89RcFBJRA6Ohz9vGDwwsfRGhID1cp1aOxu2eYem2N8KXMsEvKva3TtvDVWddX3dqY pvW8/2ZNxnrUEI/vi5j/ftQHtrBF1orBK+tjOiskOay2c1x2B8NsYywWgdo1K6MJqp ndOGjI0rXbZQ2g1U9dEA6sF+IyOeRuXEVKffuK+G+qnhnqaxpjPQ02kV9s6D/fi1RB KHu9eC94qeqXA== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org Cc: Sweet Tea Dorminy Subject: [PATCH v1 15/17] btrfs: start tracking extent encryption context info Date: Wed, 28 Jun 2023 20:35:38 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org This puts the long-preserved 1-byte encryption field to work, storing whether the extent is encrypted and the length of its fscrypt_context. Since there is no way for a btrfs inode to be encrypted right now, we set context length to be 0 in all locations. We then update all the locations which check the size of an extent item to expect the size to agree with the context length. The 1-byte field packs the encryption type and context length together, to avoid a format change. Right now we don't anticipate very many encryption policies, so 2 bits should be ample; similarly right now we can't imagine a context larger than fscrypt's current contexts, which are 40 bytes, so 6 bits is ample to store the context's size; and therefore we can pack these together into the one-byte encryption field without touching other space reserved for future use. Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/accessors.h | 31 +++++++++++++++++++++++++++++++ fs/btrfs/file-item.c | 8 ++++++++ fs/btrfs/fscrypt.h | 24 ++++++++++++++++++++++++ fs/btrfs/inode.c | 26 ++++++++++++++++++++++---- fs/btrfs/tree-checker.c | 37 +++++++++++++++++++++++++++++-------- fs/btrfs/tree-log.c | 3 +++ 6 files changed, 117 insertions(+), 12 deletions(-) diff --git a/fs/btrfs/accessors.h b/fs/btrfs/accessors.h index ceadfc5d6c66..fbee45b9d9c2 100644 --- a/fs/btrfs/accessors.h +++ b/fs/btrfs/accessors.h @@ -3,6 +3,8 @@ #ifndef BTRFS_ACCESSORS_H #define BTRFS_ACCESSORS_H +#include "fscrypt.h" + struct btrfs_map_token { struct extent_buffer *eb; char *kaddr; @@ -936,6 +938,16 @@ 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); +static inline u8 btrfs_stack_file_extent_encryption_ctxsize( + struct btrfs_file_extent_item *e) +{ + u8 ctxsize; + + btrfs_unpack_encryption(e->encryption, NULL, &ctxsize); + return ctxsize; +} BTRFS_SETGET_FUNCS(file_extent_type, struct btrfs_file_extent_item, type, 8); @@ -958,6 +970,25 @@ BTRFS_SETGET_FUNCS(file_extent_encryption, struct btrfs_file_extent_item, BTRFS_SETGET_FUNCS(file_extent_other_encoding, struct btrfs_file_extent_item, other_encoding, 16); +static inline u8 +btrfs_file_extent_encryption_ctxsize(const struct extent_buffer *eb, + struct btrfs_file_extent_item *e) +{ + u8 ctxsize; + + btrfs_unpack_encryption(btrfs_file_extent_encryption(eb, e), + NULL, &ctxsize); + return ctxsize; +} + +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)); +} + /* btrfs_qgroup_status_item */ BTRFS_SETGET_FUNCS(qgroup_status_generation, struct btrfs_qgroup_status_item, generation, 64); diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index 696bf695d8eb..8095fc2e7ca1 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -1272,6 +1272,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 - @@ -1287,6 +1288,10 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode, em->compress_type = compress_type; em->block_start = bytenr; em->block_len = em->orig_block_len; + } else if (btrfs_file_extent_encryption(leaf, fi)) { + set_bit(EXTENT_FLAG_ENCRYPTED, &em->flags); + em->block_start = bytenr; + em->block_len = em->orig_block_len; } else { bytenr += btrfs_file_extent_offset(leaf, fi); em->block_start = bytenr; @@ -1294,6 +1299,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 1647bbbcd609..2d405d54cbc7 100644 --- a/fs/btrfs/fscrypt.h +++ b/fs/btrfs/fscrypt.h @@ -8,6 +8,30 @@ #include "fs.h" +#define BTRFS_ENCRYPTION_POLICY_BITS 2 +#define BTRFS_ENCRYPTION_CTXSIZE_BITS 6 + +#define BTRFS_ENCRYPTION_POLICY_MASK ((1 << BTRFS_ENCRYPTION_POLICY_BITS) - 1) +#define BTRFS_ENCRYPTION_CTXSIZE_MASK \ + (((1 << BTRFS_ENCRYPTION_CTXSIZE_BITS) - 1) << \ + BTRFS_ENCRYPTION_POLICY_BITS) + +static inline void btrfs_unpack_encryption(u8 encryption, + u8 *policy, + u8 *ctxsize) +{ + if (policy) + *policy = encryption & BTRFS_ENCRYPTION_POLICY_MASK; + if (ctxsize) + *ctxsize = ((encryption & BTRFS_ENCRYPTION_CTXSIZE_MASK) >> + BTRFS_ENCRYPTION_POLICY_BITS); +} + +static inline u8 btrfs_pack_encryption(u8 policy, u8 ctxsize) +{ + return policy | (ctxsize << BTRFS_ENCRYPTION_POLICY_BITS); +} + #ifdef CONFIG_FS_ENCRYPTION bool btrfs_fscrypt_match_name(struct fscrypt_name *fname, struct extent_buffer *leaf, diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 72da8f5e32af..931f948b42f7 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3151,7 +3151,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 @@ -7046,8 +7046,24 @@ 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); + u8 policy, ctxsize; + + btrfs_unpack_encryption(encryption, &policy, &ctxsize); + + if (policy == 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) { /* @@ -9739,7 +9755,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 351ba9e90675..c1244fd60d3f 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -208,6 +208,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 +260,12 @@ 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))) { + btrfs_unpack_encryption(btrfs_file_extent_encryption(leaf, fi), + &policy, NULL); + 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 f32f961adc7b..5a636a6b0d82 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -4634,6 +4634,8 @@ 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 = btrfs_pack_encryption(IS_ENCRYPTED(&inode->vfs_inode) ? + BTRFS_ENCRYPTION_FSCRYPT : 0, 0); btrfs_set_stack_file_extent_generation(&fi, trans->transid); if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) @@ -4655,6 +4657,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) From patchwork Thu Jun 29 00:35:39 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: 13296519 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 20ED0EB64DD for ; Thu, 29 Jun 2023 00:36:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231714AbjF2AgT (ORCPT ); Wed, 28 Jun 2023 20:36:19 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58814 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231359AbjF2AgN (ORCPT ); Wed, 28 Jun 2023 20:36:13 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 08CCB2102; Wed, 28 Jun 2023 17:36:11 -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 895C980727; Wed, 28 Jun 2023 20:36:11 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1687998971; bh=yJgkGvzyUzTSQg47/gFnDcclgoJOe4ixpHDaoU7TDpg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ktwPNB8PSW75ROcJYjBy3wmUjrq9PKMsm+Xb+wkPz4msfYFu8jhFp6cy8YjlOmmBp 4+q7U1CAkZGo4sc6iL45iHCgZWuleYTjK7Jq3xdJswoymiad51jmHt/wTOcjyvR80a ZeY1ORobuSEWDN2MjOKypPap3byv6q8Arq4rjlR72Vi6DN/gmJ+9wEtieH+udJJ5da FJp6gPzHOUV8XSj1aKkMkPoZe88XFEO6lwQHyZKxjLiELZl7HWBinX+XHEDEIdOKnX C5Uc9KulTxpI53jvK1xCzQ7JmqDbSUimi8r5BHBGx6H+kQZANOvE5sVqtKtlHlCVlG Gndaa/iDIB2Mg== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org Cc: Sweet Tea Dorminy Subject: [PATCH v1 16/17] btrfs: explicitly track file extent length and encryption Date: Wed, 28 Jun 2023 20:35:39 -0400 Message-Id: <31f44df875657de3e657fa7150408b222f72419d.1687988380.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org With the advent of storing fscrypt contexts with each encrypted extent, extents will have a variable length depending on encryption status. Add accessors for the encryption field, and update all the checks for file extents. 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 | 2 +- include/uapi/linux/btrfs_tree.h | 5 +++++ 6 files changed, 18 insertions(+), 5 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 0de95c8375a1..703033816876 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -2250,14 +2250,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 931f948b42f7..2f709874124d 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3036,6 +3036,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(); @@ -3055,7 +3058,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; @@ -3066,7 +3069,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; } @@ -9746,6 +9749,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)); @@ -9778,6 +9782,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 5a636a6b0d82..eaf014ab7cac 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -4689,7 +4689,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; } diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h index ea88dd69957f..e780eee85e6e 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -1048,6 +1048,11 @@ struct btrfs_file_extent_item { * but not for stat. */ __u8 compression; + + /* + * 2 bits of encryption type in the lower bits, 6 bits of context size + * in the upper bits. Unencrypted value is 0. + */ __u8 encryption; __le16 other_encoding; /* spare for later use */ From patchwork Thu Jun 29 00:35:40 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: 13296520 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 1B50CC001B1 for ; Thu, 29 Jun 2023 00:36:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231718AbjF2AgT (ORCPT ); Wed, 28 Jun 2023 20:36:19 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58880 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231663AbjF2AgO (ORCPT ); Wed, 28 Jun 2023 20:36:14 -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 759AE1FC2; Wed, 28 Jun 2023 17:36:13 -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 032F48030E; Wed, 28 Jun 2023 20:36:12 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1687998973; bh=Wg/P/roxPK97mxjDl6BDrb/CzpaKq2ejoIy8fDAWKrc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NFNnLZsgdtS4J1+4ZztIHSoEs1vDvBhk1KavUkGlN+5BxKr/RLHq6I3BvQvZSgck1 jO8MWCi0C80MJ6BfNtt4Tpl2axPMl3946Ox1+7mJK+J43TioiChVuFGNEiJz0XxSze sCts+t74YcCg3Tn5oFXGkJHEd08URmMFYL68fSCJesQQPHaBMUM61+oq7V7AnMmK+x Xz0yjGlbMa4ThBLbVYMmDJcJ2GJxZdp9xEspL336sK2byt7EimSIdmMLmgUtLV+qk8 dzjc0xiSTg8srpGZtA+EpQHZ6lQNtTnf/iu7rFprF9fet06eqG1M4hLUZ4B5m5cpsv 1C0Xi2eobG3pw== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , kernel-team@meta.com, linux-btrfs@vger.kernel.org, linux-fscrypt@vger.kernel.org Cc: Sweet Tea Dorminy Subject: [PATCH v1 17/17] btrfs: save and load fscrypt extent contexts Date: Wed, 28 Jun 2023 20:35:40 -0400 Message-Id: <7fc592b75dc227b29b37c2b5e1ac9a1729c38b01.1687988380.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@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 | 21 ++++++++++++++++ fs/btrfs/fscrypt.c | 36 +++++++++++++++++++++++++++ fs/btrfs/fscrypt.h | 6 +++++ fs/btrfs/inode.c | 44 ++++++++++++++++++++++++++++++--- fs/btrfs/tree-log.c | 24 ++++++++++++++++-- include/uapi/linux/btrfs_tree.h | 5 ++++ 6 files changed, 130 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index 8095fc2e7ca1..ccc2d12faba3 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -1302,6 +1302,27 @@ 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)); + +#ifdef CONFIG_FS_ENCRYPTION + if (ctxsize) { + u8 context[FSCRYPT_SET_CONTEXT_MAX_SIZE]; + int res; + unsigned int nofs_flag; + + read_extent_buffer(leaf, context, + (unsigned long)fi->fscrypt_context, + ctxsize); + nofs_flag = memalloc_nofs_save(); + res = fscrypt_load_extent_info(&inode->vfs_inode, + context, ctxsize, + &em->fscrypt_info); + memalloc_nofs_restore(nofs_flag); + if (res) + btrfs_err(fs_info, + "Unable to load fscrypt info: %d", + res); + } +#endif /* CONFIG_FS_ENCRYPTION */ } 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 658a67856de6..e98b0c31f841 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -164,6 +164,41 @@ 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) + 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->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) { @@ -195,6 +230,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 2d405d54cbc7..1cab721a64e5 100644 --- a/fs/btrfs/fscrypt.h +++ b/fs/btrfs/fscrypt.h @@ -50,6 +50,12 @@ static inline bool btrfs_fscrypt_match_name(struct fscrypt_name *fname, } #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); + 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 2f709874124d..b12b60120b13 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3036,17 +3036,46 @@ 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; +#ifdef CONFIG_FS_ENCRYPTION + u8 context[FSCRYPT_SET_CONTEXT_MAX_SIZE]; +#endif /* CONFIG_FS_ENCRYPTION */ + int ret; path = btrfs_alloc_path(); if (!path) return -ENOMEM; +#ifdef CONFIG_FS_ENCRYPTION + if (IS_ENCRYPTED(&inode->vfs_inode)) { + u8 encryption; + 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; + } + + fscrypt_context_size = + fscrypt_set_extent_context(fscrypt_info, context, + FSCRYPT_SET_CONTEXT_MAX_SIZE); + if (fscrypt_context_size < 0) { + ret = fscrypt_context_size; + goto out; + } + encryption = btrfs_pack_encryption(BTRFS_ENCRYPTION_FSCRYPT, + fscrypt_context_size); + btrfs_set_stack_file_extent_encryption(stack_fi, encryption); + } +#endif /* CONFIG_FS_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. * @@ -3079,6 +3108,13 @@ 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)); +#ifdef CONFIG_FS_ENCRYPTION + write_extent_buffer(leaf, context, + btrfs_item_ptr_offset(leaf, path->slots[0]) + + sizeof(struct btrfs_file_extent_item), + fscrypt_context_size); +#endif /* CONFIG_FS_ENCRYPTION */ + btrfs_mark_buffer_dirty(leaf); btrfs_release_path(path); diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index eaf014ab7cac..25966ae95eea 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -4634,8 +4634,22 @@ 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 = btrfs_pack_encryption(IS_ENCRYPTED(&inode->vfs_inode) ? - BTRFS_ENCRYPTION_FSCRYPT : 0, 0); + u8 encryption = 0; + size_t fscrypt_context_size = 0; +#ifdef CONFIG_FS_ENCRYPTION + u8 context[FSCRYPT_SET_CONTEXT_MAX_SIZE]; + + if (em->fscrypt_info) { + fscrypt_context_size = + fscrypt_set_extent_context(em->fscrypt_info, context, + FSCRYPT_SET_CONTEXT_MAX_SIZE); + if (fscrypt_context_size < 0) + return fscrypt_context_size; + + encryption = btrfs_pack_encryption(BTRFS_ENCRYPTION_FSCRYPT, + fscrypt_context_size); + } +#endif /* CONFIG_FS_ENCRYPTION */ btrfs_set_stack_file_extent_generation(&fi, trans->transid); if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) @@ -4697,6 +4711,12 @@ 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)); +#ifdef CONFIG_FS_ENCRYPTION + write_extent_buffer(leaf, context, + btrfs_item_ptr_offset(leaf, path->slots[0]) + + sizeof(fi), fscrypt_context_size); +#endif /* CONFIG_FS_ENCRYPTION */ + btrfs_mark_buffer_dirty(leaf); btrfs_release_path(path); diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h index e780eee85e6e..787c9e67b491 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -1080,6 +1080,11 @@ struct btrfs_file_extent_item { * always reflects the size uncompressed and without encoding. */ __le64 num_bytes; + /* + * fscrypt extent encryption context. Only present if extent is + * encrypted (as per the encryption field). + */ + __u8 fscrypt_context[0]; } __attribute__ ((__packed__));