From patchwork Wed Nov 2 11:52:50 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13028035 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 61B2AC43217 for ; Wed, 2 Nov 2022 11:53:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230185AbiKBLxO (ORCPT ); Wed, 2 Nov 2022 07:53:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48728 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230159AbiKBLxN (ORCPT ); Wed, 2 Nov 2022 07:53:13 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7A89728725; Wed, 2 Nov 2022 04:53:12 -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 BD16681460; Wed, 2 Nov 2022 07:53:11 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1667389992; bh=nFCZ139WVEVoQUXUAAJJhyGBvQMLzp162SW26J98GEk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RfFbVmLuWhU3RGw2d8NddbaxTLCaBbnI03r0WGOBybfSm2C4hP9bICaZ9YuA5+fCZ 8KU4updYIh9NK679hG46IeVnCcLp7BbfDi5/1BkiD7S6Hu2X9iXB6Uy/FdriZjo3Gf UtKuJNwun90IzKeosF4jO1go+Bt+UMF7jgYRDuzX+w7y0cXLHSn/iaaNkjDA/zw003 hUV9I55pFrwQrQTrNMuJRZMAfo9jySAHkWV5XlOTdfJiuSJD35SV6EGaoKz2SVKxbn zBA5Jtmx1nlS4OuGsU2Td3KGxiBVOUSPe35Q8IHZpH/jVVdnVnFVXWYS1lTOoCwUbX IEbsM2VylbHAA== From: Sweet Tea Dorminy To: "Theodore Y. Ts'o" , Jaegeuk Kim , Eric Biggers , Chris Mason , Josef Bacik , David Sterba , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@meta.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v5 01/18] fscrypt: expose fscrypt_nokey_name Date: Wed, 2 Nov 2022 07:52:50 -0400 Message-Id: <714aa9149e19c4a826866236992e265fa29f57c6.1667389115.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Omar Sandoval btrfs stores its data structures, including filenames in directories, in its own buffer implementation, struct extent_buffer, composed of several non-contiguous pages. We could copy filenames into a temporary buffer and use fscrypt_match_name() against that buffer, such extensive memcpying would be expensive. Instead, exposing fscrypt_nokey_name as in this change allows btrfs to recapitulate fscrypt_match_name() using methods on struct extent_buffer instead of dealing with a raw byte array. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy Reviewed-by: Josef Bacik --- fs/crypto/fname.c | 39 +-------------------------------------- include/linux/fscrypt.h | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 12bd61d20f69..6c092a1533f7 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 4f5f8a651213..7661db0b5bec 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 Wed Nov 2 11:52:51 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13028036 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 05C94C4167B for ; Wed, 2 Nov 2022 11:53:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230338AbiKBLxQ (ORCPT ); Wed, 2 Nov 2022 07:53:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48734 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230159AbiKBLxP (ORCPT ); Wed, 2 Nov 2022 07:53:15 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 297FE28714; Wed, 2 Nov 2022 04:53:14 -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 989F68146E; Wed, 2 Nov 2022 07:53:13 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1667389993; bh=ikGo+VvZ8X13dvcigfL01v7Xa1i4xUOngR+ezlUxGAo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dBKuGEIhHJ3DSqyy9WBF/ZkHB9PPd/fznxQOabOcP33e9+84ZdTgymVtDaKZdGhes 6z/U5jYorIF7hhmTvtjXWrtBWM/X9JeSvdQ1295i1/wQkVzpUEB9D6Esfl0n3bJinX TnYJFqhkfPLB5BhEXAH7VG7qK8sgjdSOrdiC5Qdxawnrqr6C4zpMs6V26E9WFlYRJv 5vsMkxYeyEc75ZcPhcNMwVut8lPj6wJSERdJC87ElUeTgAJhqsOld3N30+bnMeNkim 02zM8tVvbm7I09lpg20/H0H2AIQOks2Ljl7OW/Gj3DNLBZuyQFVY4srRs9p6IdC0Qu 9LTHPV/W7asBw== From: Sweet Tea Dorminy To: "Theodore Y. Ts'o" , Jaegeuk Kim , Eric Biggers , Chris Mason , Josef Bacik , David Sterba , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@meta.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v5 02/18] fscrypt: add fscrypt_have_same_policy() to check inode compatibility Date: Wed, 2 Nov 2022 07:52:51 -0400 Message-Id: <0c9c616c46ae86e51d153316d55918eac74b83ad.1667389115.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Omar Sandoval Btrfs will need to check whether inode policies are identical for various purposes: if two inodes want to share an extent, they must have the same policy, including key identifier; symlinks must not span the encrypted/unencrypted border; and certain encryption policies will allow btrfs to store one fscrypt_context for multiple objects. Therefore, add a function which allows checking the encryption policies of two inodes to ensure they are identical. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/crypto/policy.c | 35 +++++++++++++++++++++++++++++++++++ include/linux/fscrypt.h | 8 ++++++++ 2 files changed, 43 insertions(+) diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 46757c3052ef..715870d4e530 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -415,6 +415,41 @@ static int fscrypt_get_policy(struct inode *inode, union fscrypt_policy *policy) return fscrypt_policy_from_context(policy, &ctx, ret); } +/** + * fscrypt_have_same_policy() - check whether two inodes have the same policy + * @inode1: the first inode + * @inode2: the second inode + * @same_ptr: a pointer to return whether they are the same + * + * Return: 0 or an error code. + */ +int fscrypt_have_same_policy(struct inode *inode1, struct inode *inode2, + bool *same_ptr) +{ + union fscrypt_policy policy1, policy2; + int err; + + if (!IS_ENCRYPTED(inode1) && !IS_ENCRYPTED(inode2)) { + *same_ptr = true; + return 0; + } + if (!IS_ENCRYPTED(inode1) || !IS_ENCRYPTED(inode2)) { + *same_ptr = false; + return 0; + } + + err = fscrypt_get_policy(inode1, &policy1); + if (err) + return err; + err = fscrypt_get_policy(inode2, &policy2); + if (err) + return err; + + *same_ptr = fscrypt_policies_equal(&policy1, &policy2); + return 0; +} +EXPORT_SYMBOL(fscrypt_have_same_policy); + static int set_encryption_policy(struct inode *inode, const union fscrypt_policy *policy) { diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 7661db0b5bec..0069f92ee3da 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -313,6 +313,8 @@ static inline struct page *fscrypt_pagecache_page(struct page *bounce_page) void fscrypt_free_bounce_page(struct page *bounce_page); /* policy.c */ +int fscrypt_have_same_policy(struct inode *inode1, struct inode *inode2, + bool *same_ptr); int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg); int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg); int fscrypt_ioctl_get_policy_ex(struct file *filp, void __user *arg); @@ -490,6 +492,12 @@ static inline void fscrypt_free_bounce_page(struct page *bounce_page) } /* policy.c */ +static inline int fscrypt_have_same_policy(struct inode *inode1, + struct inode *inode2) +{ + return 1; +} + static inline int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg) { From patchwork Wed Nov 2 11:52:52 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13028037 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 EF291C43217 for ; Wed, 2 Nov 2022 11:53:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230333AbiKBLxR (ORCPT ); Wed, 2 Nov 2022 07:53:17 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48740 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229618AbiKBLxQ (ORCPT ); Wed, 2 Nov 2022 07:53:16 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CBEA228714; Wed, 2 Nov 2022 04:53:15 -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 3DB5D81480; Wed, 2 Nov 2022 07:53:15 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1667389995; bh=OI2zMAYFyYpaswq4kOyLZ4AvuIXk2wStaybbpLyZrgg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=rVb+R3xy+Oj+UO8Ez3t+b/CEJIDMt7y9rUBBRK6rCOayTybvOrDNZUp+nxpq1HoHP iYp3er4FREXe+Pyaf1keP7krGaMxpdBCBMJfcXX6AWMEkqHcEI6HbDPgCXXmrpQZSb EDP6pxk62ctnrn0fzTdCWs+6gV94Ok2QzMEhXTurDe2LswtQA19skElKP3Y7bP9s4F zDCXIiBaI5W1JM4WwnmYGXF7n5Sq3qJ82X8trEY5V62DmXT3yeRzJYr5c2fjTkOdJe k7TZcxf+D3ZG4ZWLIvokm4gL28VSqD45a9Fw+2lUoEL5l1e34sCzKkiV6JgKc9tqmB YbInL1N+apgQg== From: Sweet Tea Dorminy To: "Theodore Y. Ts'o" , Jaegeuk Kim , Eric Biggers , Chris Mason , Josef Bacik , David Sterba , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@meta.com Cc: Sweet Tea Dorminy Subject: [PATCH v5 03/18] fscrypt: allow fscrypt_generate_iv() to distinguish filenames Date: Wed, 2 Nov 2022 07:52:52 -0400 Message-Id: <6140620518064e2ddbc362dba38e44f51651fc8c.1667389115.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org For extent-based file contents encryption, filenames will need to generate an IV based on the inode context, while file contents will need to generate an IV based on the extent context. Currently filenames and the first block of file contents both pass fscrypt_generate_iv() a block number of 0, making it hard to distinguish the two cases. To enable distinguishing these two cases for extent-based encryption, this change adjusts all callers to pass U64_MAX when requesting an IV for filename encryption, and then changes fscrypt_generate_iv() to convert U64_MAX to 0 for traditional inode-context encryption. For extent-based encryption, any block number other than U64_MAX will get an IV from the extent context, while U64_MAX will indicate falling back to inode contexts. Signed-off-by: Sweet Tea Dorminy --- fs/crypto/crypto.c | 9 ++++++++- fs/crypto/fname.c | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index e78be66bbf01..7fe5979fbea2 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -71,7 +71,7 @@ EXPORT_SYMBOL(fscrypt_free_bounce_page); /* * Generate the IV for the given logical block number within the given file. - * For filenames encryption, lblk_num == 0. + * For filenames encryption, lblk_num == U64_MAX. * * Keep this in sync with fscrypt_limit_io_blocks(). fscrypt_limit_io_blocks() * needs to know about any IV generation methods where the low bits of IV don't @@ -84,6 +84,13 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num, memset(iv, 0, ci->ci_mode->ivsize); + /* + * Filename encryption. For inode-based policies, filenames are + * encrypted as though they are lblk 0. + */ + if (lblk_num == U64_MAX) + lblk_num = 0; + if (flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) { WARN_ON_ONCE(lblk_num > U32_MAX); WARN_ON_ONCE(ci->ci_inode->i_ino > U32_MAX); diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 6c092a1533f7..b3e7e3a66312 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -79,7 +79,7 @@ int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname, memset(out + iname->len, 0, olen - iname->len); /* Initialize the IV */ - fscrypt_generate_iv(&iv, 0, ci); + fscrypt_generate_iv(&iv, U64_MAX, ci); /* Set up the encryption request */ req = skcipher_request_alloc(tfm, GFP_NOFS); @@ -134,7 +134,7 @@ static int fname_decrypt(const struct inode *inode, crypto_req_done, &wait); /* Initialize IV */ - fscrypt_generate_iv(&iv, 0, ci); + fscrypt_generate_iv(&iv, U64_MAX, ci); /* Create decryption request */ sg_init_one(&src_sg, iname->name, iname->len); From patchwork Wed Nov 2 11:52:53 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13028038 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 1E0B9C43217 for ; Wed, 2 Nov 2022 11:53:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230358AbiKBLxT (ORCPT ); Wed, 2 Nov 2022 07:53:19 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48770 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229618AbiKBLxS (ORCPT ); Wed, 2 Nov 2022 07:53:18 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D883428E37; Wed, 2 Nov 2022 04:53:17 -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 5345E81472; Wed, 2 Nov 2022 07:53:17 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1667389997; bh=NLk0mAvqQq+wQha+9Dj3s2saRUURY3zNNib+Ugaa9ZA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=xQxPkPsgjiOVmX3F1XWkKKMwfy8E4K8y20/5+vs42/CwlgW1rxHm+Se2qlKe8YTvM 2sDUaWJ+MGATV+Zgj0O8jz+0jzyveXMKtfmphuBDNvaQLezRRk4oa7qJFDOX9uuar+ 4/EicpD4iF2JtY8zbtJPoumzYTWMfZ5ies1+RsxBI6Hq6/ZJEoGmAoru4S2hwNTIwX mrL0+WMG25KViLHhB1fzpe9WrZ+SpuIlPGT9bSTCuOBeBNXq9QhI2czH7q0RfnvVd/ ZZ8TksA0I5ZbdIAT2t3utGrmuXA9A/IhQ3FsUDDVnvnSpmzQ2Fc1sPZGlWKymL8gEv v6Wpvz8mphIfw== From: Sweet Tea Dorminy To: "Theodore Y. Ts'o" , Jaegeuk Kim , Eric Biggers , Chris Mason , Josef Bacik , David Sterba , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@meta.com Cc: Sweet Tea Dorminy Subject: [PATCH v5 04/18] fscrypt: add extent-based encryption Date: Wed, 2 Nov 2022 07:52:53 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org Some filesystems need to encrypt data based on extents, rather than on inodes, due to features incompatible with inode-based encryption. For instance, btrfs can have multiple inodes referencing a single block of data, and moves logical data blocks to different physical locations on disk in the background; these two features mean traditional inode-based file contents encryption will not work for btrfs. This change introduces fscrypt_extent_context objects, in analogy to existing context objects based on inodes. For a filesystem which opts to use extent-based encryption, a new hook provides a new fscrypt_extent_context. During file content encryption/decryption, the existing fscrypt_context object provides key information, while the new fscrypt_extent_context provides nonce information. For filename encryption, the existing IV generation methods are still used, since filenames are not stored in extents. Only direct key policies are allowed, but any encryption mode is allowed within that constraint. Signed-off-by: Sweet Tea Dorminy --- fs/crypto/crypto.c | 20 +++++++++-- fs/crypto/fscrypt_private.h | 25 +++++++++++++- fs/crypto/inline_crypt.c | 28 ++++++++++++---- fs/crypto/policy.c | 66 +++++++++++++++++++++++++++++++++++++ include/linux/fscrypt.h | 47 ++++++++++++++++++++++++++ 5 files changed, 176 insertions(+), 10 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 7fe5979fbea2..08b495dc5c0c 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -81,8 +81,22 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num, const struct fscrypt_info *ci) { u8 flags = fscrypt_policy_flags(&ci->ci_policy); + struct inode *inode = ci->ci_inode; + const struct fscrypt_operations *s_cop = inode->i_sb->s_cop; - memset(iv, 0, ci->ci_mode->ivsize); + memset(iv, 0, sizeof(*iv)); + if (s_cop->get_extent_context && lblk_num != U64_MAX) { + size_t extent_offset; + union fscrypt_extent_context ctx; + int ret; + + ret = fscrypt_get_extent_context(inode, lblk_num, &ctx, + &extent_offset, NULL); + WARN_ON_ONCE(ret); + memcpy(iv->raw, ctx.v1.iv.raw, sizeof(*iv)); + iv->lblk_num += cpu_to_le64(extent_offset); + return; + } /* * Filename encryption. For inode-based policies, filenames are @@ -93,8 +107,8 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num, if (flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) { WARN_ON_ONCE(lblk_num > U32_MAX); - WARN_ON_ONCE(ci->ci_inode->i_ino > U32_MAX); - lblk_num |= (u64)ci->ci_inode->i_ino << 32; + WARN_ON_ONCE(inode->i_ino > U32_MAX); + lblk_num |= (u64)inode->i_ino << 32; } else if (flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) { WARN_ON_ONCE(lblk_num > U32_MAX); lblk_num = (u32)(ci->ci_hashed_ino + lblk_num); diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index d5f68a0c5d15..9c4cae2580de 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -280,7 +280,6 @@ fscrypt_msg(const struct inode *inode, const char *level, const char *fmt, ...); fscrypt_msg((inode), KERN_ERR, fmt, ##__VA_ARGS__) #define FSCRYPT_MAX_IV_SIZE 32 - union fscrypt_iv { struct { /* logical block number within the file */ @@ -293,6 +292,27 @@ union fscrypt_iv { __le64 dun[FSCRYPT_MAX_IV_SIZE / sizeof(__le64)]; }; + +/* + * fscrypt_extent_context - the encryption context for an extent + * + * For filesystems that support extent encryption, this context provides the + * necessary randomly-initialized IV in order to encrypt/decrypt the data + * stored in the extent. It is stored alongside each extent, and is + * insufficient to decrypt the extent: the extent's owning inode(s) provide the + * policy information (including key identifier) necessary to decrypt. + */ +struct fscrypt_extent_context_v1 { + u8 version; + union fscrypt_iv iv; +} __packed; + +union fscrypt_extent_context { + u8 version; + struct fscrypt_extent_context_v1 v1; +}; + + void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num, const struct fscrypt_info *ci); @@ -662,5 +682,8 @@ int fscrypt_policy_from_context(union fscrypt_policy *policy_u, const union fscrypt_context *ctx_u, int ctx_size); const union fscrypt_policy *fscrypt_policy_to_inherit(struct inode *dir); +int fscrypt_get_extent_context(const struct inode *inode, u64 lblk_num, + union fscrypt_extent_context *ctx, + size_t *extent_offset, size_t *extent_length); #endif /* _FSCRYPT_PRIVATE_H */ diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index cea8b14007e6..6adb72c52ce2 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -460,6 +460,7 @@ EXPORT_SYMBOL_GPL(fscrypt_dio_supported); */ u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks) { + const struct fscrypt_operations *s_cop = inode->i_sb->s_cop; const struct fscrypt_info *ci; u32 dun; @@ -470,14 +471,29 @@ u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks) return nr_blocks; ci = inode->i_crypt_info; - if (!(fscrypt_policy_flags(&ci->ci_policy) & - FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) - return nr_blocks; - /* With IV_INO_LBLK_32, the DUN can wrap around from U32_MAX to 0. */ + if (s_cop->get_extent_context) { + size_t extent_offset, extent_length; + int ret = fscrypt_get_extent_context(inode, lblk, NULL, + &extent_offset, + &extent_length); + if (ret < 0) { + WARN_ON_ONCE(ret < 0); + return 1; + } + return extent_length - extent_offset; + } - dun = ci->ci_hashed_ino + lblk; + if ((fscrypt_policy_flags(&ci->ci_policy) & + FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) { + /* + * With IV_INO_LBLK_32, the DUN can wrap around from U32_MAX to + * 0. + */ + dun = ci->ci_hashed_ino + lblk; + return min_t(u64, nr_blocks, (u64)U32_MAX + 1 - dun); + } - return min_t(u64, nr_blocks, (u64)U32_MAX + 1 - dun); + return nr_blocks; } EXPORT_SYMBOL_GPL(fscrypt_limit_io_blocks); diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 715870d4e530..a874afcf9652 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -222,6 +222,10 @@ static bool fscrypt_supported_v2_policy(const struct fscrypt_policy_v2 *policy, return false; } + if (inode->i_sb->s_cop->get_extent_context && + !(policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY)) + return false; + if ((policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) && !supported_direct_key_modes(inode, policy->contents_encryption_mode, policy->filenames_encryption_mode)) @@ -786,6 +790,68 @@ int fscrypt_set_context(struct inode *inode, void *fs_data) } EXPORT_SYMBOL_GPL(fscrypt_set_context); +/** + * fscrypt_get_extent_context() - Get the fscrypt extent context for a location + * + * @inode: an inode associated with the extent + * @lblk_num: a logical block number within the inode owned by the extent + * @ctx: a pointer to return the context found (may be NULL) + * @extent_offset: a pointer to return the offset of @lblk_num within the + * extent (may be NULL) + * @extent_length: a pointer to return the length of the extent found (may be + * NULL) + * + * Return: 0 on success, -errno on failure + */ +int fscrypt_get_extent_context(const struct inode *inode, u64 lblk_num, + union fscrypt_extent_context *ctx, + size_t *extent_offset, size_t *extent_length) +{ + int ret; + int ctxsize = (ctx == NULL ? 0 : sizeof(*ctx)); + + if (!IS_ENCRYPTED(inode)) + return -ENODATA; + + ret = inode->i_sb->s_cop->get_extent_context(inode, lblk_num, ctx, + ctxsize, extent_offset, + extent_length); + if (ret == ctxsize && (!ctx || ctx->version == 1)) + return 0; + if (ret >= 0) + return -EINVAL; + return ret; +} +EXPORT_SYMBOL_GPL(fscrypt_get_extent_context); + +/** + * fscrypt_set_extent_context() - Set an extent's fscrypt context + * + * @inode: an inode to which the extent belongs + * @lblk_num: the offset into the inode at which the extent starts + * @extent: private data referring to the extent, given by the FS and passed + * to ->set_extent_context() + * + * This should be called after fscrypt_prepare_new_inode(), generally during a + * filesystem transaction. Everything here must be %GFP_NOFS-safe. + * + * Return: 0 on success, -errno on failure + */ +int fscrypt_set_extent_context(struct inode *inode, u64 lblk_num, void *extent) +{ + union fscrypt_extent_context ctx; + + if (!IS_ENCRYPTED(inode)) + return -ENODATA; + + ctx.v1.version = 1; + get_random_bytes(ctx.v1.nonce, FSCRYPT_FILE_NONCE_SIZE); + + return inode->i_sb->s_cop->set_extent_context(extent, + &ctx, sizeof(ctx)); +} +EXPORT_SYMBOL_GPL(fscrypt_set_extent_context); + /** * fscrypt_parse_test_dummy_encryption() - parse the test_dummy_encryption mount option * @param: the mount option diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 0069f92ee3da..402fc0ca7abf 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -94,6 +94,13 @@ struct fscrypt_nokey_name { /* Maximum value for the third parameter of fscrypt_operations.set_context(). */ #define FSCRYPT_SET_CONTEXT_MAX_SIZE 40 +/* + * Maximum value for the third parameter of + * fscrypt_operations.set_extent_context(). Update if fscrypt_private.h: + * FSCRYPT_MAX_IVSIZE changes + */ +#define FSCRYPT_EXTENT_CONTEXT_MAX_SIZE 33 + #ifdef CONFIG_FS_ENCRYPTION /* @@ -150,6 +157,39 @@ struct fscrypt_operations { int (*set_context)(struct inode *inode, const void *ctx, size_t len, void *fs_data); + /* + * Get the fscrypt extent context for a given inode and lblk number. + * + * @inode: the inode to which the extent belongs + * @lblk_num: the block number within the file whose extent is being + * queried + * @ctx: the buffer into which to get the context (may be NULL) + * @len: the length of the @ctx buffer in bytes + * @extent_offset: a pointer to return the offset of @lblk_num within + * the extent whose context is returned (may be NULL) + * @extent_length: a pointer to return the total length of the extent + * whose context was found (may be NULL) + * + * Return: On success, returns the length of the context in bytes, + * which may be less than @len. On failure, returns -ENODATA if the + * extent doesn't have a context, -ERANGE if the context is longer + * than @len, or another -errno code. + */ + int (*get_extent_context)(const struct inode *inode, u64 lblk_num, + void *ctx, size_t len, + size_t *extent_offset, size_t *extent_length); + + /* + * Set the fscrypt extent context for an extent. + * + * @extent: an opaque pointer to the filesystem's extent object + * @ctx: the buffer containing the extent context to set + * @len: the length of the @ctx buffer in bytes + * + * Return: 0 on success, -errno on failure. + */ + int (*set_extent_context)(void *extent, void *ctx, size_t len); + /* * Get the dummy fscrypt policy in use on the filesystem (if any). * @@ -322,6 +362,7 @@ int fscrypt_ioctl_get_nonce(struct file *filp, void __user *arg); int fscrypt_has_permitted_context(struct inode *parent, struct inode *child); int fscrypt_context_for_new_inode(void *ctx, struct inode *inode); int fscrypt_set_context(struct inode *inode, void *fs_data); +int fscrypt_set_extent_context(struct inode *inode, u64 offset, void *extent); struct fscrypt_dummy_policy { const union fscrypt_policy *policy; @@ -531,6 +572,12 @@ static inline int fscrypt_set_context(struct inode *inode, void *fs_data) return -EOPNOTSUPP; } +static inline int fscrypt_set_extent_context(struct inode *inode, u64 offset, + void *extent) +{ + return -EOPNOTSUPP; +} + struct fscrypt_dummy_policy { }; From patchwork Wed Nov 2 11:52:54 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13028039 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 3DF91C4332F for ; Wed, 2 Nov 2022 11:53:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229877AbiKBLxW (ORCPT ); Wed, 2 Nov 2022 07:53:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48842 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230290AbiKBLxV (ORCPT ); Wed, 2 Nov 2022 07:53:21 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D901228E34; Wed, 2 Nov 2022 04:53:19 -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 217DA81462; Wed, 2 Nov 2022 07:53:19 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1667389999; bh=2lp+qYuc8tIXu6f0eDTY6kJSGdqRQdWs1nDI6UaO5l4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=XhDqqDDdh/BcVR4+4DZaqB2hYqusHAbxeJ1X9SoablHmVHi0VL0p2cAxNZzoGlSVu pnX+z3PyKyefqZRmp4TQ5dm5a0hIsGq6Jy3tT7FWsy5MRHSXKhwQEuzipAdhOVK2p3 blhMkDS5FTGtiuH+TqSL3x7hy9MI4iBkTRGCXNbHh1w8IAySfSWIGrjuSvC4eEqXwW 3kPZzPmGzGbVO3z3xLiRzD44mcvw4AgGz6RlHBB4tdGKV8rjJBOX3+tdhiX9QM9lGS ZVhhzHS1T6HG9ZuaVBloB8s667+VdiIDcXzqLp3ttVt+GU3DpwcpEOWzFx/GbwL8O3 UvKXYlNILDtgA== From: Sweet Tea Dorminy To: "Theodore Y. Ts'o" , Jaegeuk Kim , Eric Biggers , Chris Mason , Josef Bacik , David Sterba , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@meta.com Cc: Sweet Tea Dorminy Subject: [PATCH v5 05/18] fscrypt: extent direct key policies for extent-based encryption Date: Wed, 2 Nov 2022 07:52:54 -0400 Message-Id: <857cdf24a4b74b32f0c3e955f7e3d1e940cf1a43.1667389115.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org For inode-based direct key encryption policies, the inode provides a nonce, and the encryption IV is generated by concatenating the nonce and the offset into the inode. For extent-based direct key policies, however, we would like to use 16-byte nonces in combination with various AES modes with 16-byte IVs. Additionally, since contents and filenames are encrypted with different context items in this case, we don't need to require the encryption modes match in the two cases. This change allows extent-based encryption to use 16-byte IVs with direct key policies, and allows a mismatch of modes (under the usual compatible modes constraints). Signed-off-by: Sweet Tea Dorminy --- fs/crypto/crypto.c | 15 +++++++++++++-- fs/crypto/fscrypt_private.h | 4 +--- fs/crypto/policy.c | 4 ++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 08b495dc5c0c..144a3a59ce51 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -93,8 +93,19 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num, ret = fscrypt_get_extent_context(inode, lblk_num, &ctx, &extent_offset, NULL); WARN_ON_ONCE(ret); - memcpy(iv->raw, ctx.v1.iv.raw, sizeof(*iv)); - iv->lblk_num += cpu_to_le64(extent_offset); + if (ci->ci_mode->ivsize < offsetofend(union fscrypt_iv, nonce)) { + /* + * We need a 16 byte IV, but our nonce is 16 bytes. + * Copy to the start of the buffer and add the extent + * offset manually. + */ + memcpy(iv->raw, ctx.v1.nonce, FSCRYPT_FILE_NONCE_SIZE); + iv->lblk_num = cpu_to_le64(extent_offset + + le64_to_cpu(iv->lblk_num)); + return; + } + memcpy(iv->nonce, ctx.v1.nonce, FSCRYPT_FILE_NONCE_SIZE); + iv->lblk_num = cpu_to_le64(extent_offset); return; } diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 9c4cae2580de..bb2a18c83e56 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -292,7 +292,6 @@ union fscrypt_iv { __le64 dun[FSCRYPT_MAX_IV_SIZE / sizeof(__le64)]; }; - /* * fscrypt_extent_context - the encryption context for an extent * @@ -304,7 +303,7 @@ union fscrypt_iv { */ struct fscrypt_extent_context_v1 { u8 version; - union fscrypt_iv iv; + u8 nonce[FSCRYPT_FILE_NONCE_SIZE]; } __packed; union fscrypt_extent_context { @@ -312,7 +311,6 @@ union fscrypt_extent_context { struct fscrypt_extent_context_v1 v1; }; - void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num, const struct fscrypt_info *ci); diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index a874afcf9652..39ef447ebaec 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -91,6 +91,10 @@ static bool supported_direct_key_modes(const struct inode *inode, { const struct fscrypt_mode *mode; + /* Extent-based encryption allows any mixed mode and IV size */ + if (inode->i_sb->s_cop->get_extent_context) + return true; + if (contents_mode != filenames_mode) { fscrypt_warn(inode, "Direct key flag not allowed with different contents and filenames modes"); From patchwork Wed Nov 2 11:52:55 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13028040 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 E052CC43219 for ; Wed, 2 Nov 2022 11:54:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230342AbiKBLxa (ORCPT ); Wed, 2 Nov 2022 07:53:30 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48940 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230290AbiKBLxX (ORCPT ); Wed, 2 Nov 2022 07:53:23 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 519631128; Wed, 2 Nov 2022 04:53:21 -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 ED81181472; Wed, 2 Nov 2022 07:53:20 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1667390001; bh=jE2leRa5ZMbP3asOwLPiuZHq7U+RHYnAn/YhcDunJ/k=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=htuquujX46HzwHuhqMOrMKbzpvC/5SPDtyUi9GONUUN7ob4ICrfF8ypConvwiX6dD Smf1bAAOsOj87Kmo8eww57Ytyora5aR7cc1O35b9YGK8JcFv+bInEfHh90UF1U2tWP 5qnPL0WKcn0Vu5g6sNKSN/FwYYdROmEvxzhOxQJOy2ASRvTDEnnODJ4l13YBehbbFs LxSwwkdISoju6E8lfCZ0brFY8e7bOnFS7JZvOslt/BJM0l1/0xDrzPQpJCUefPqHJ+ 8pZMwkGhUF9NMipZUItpu6jKhhmk8mtrGN3Cq1fwPvUF//K2NsPNeGhTmvxBSgdLJy 8dEK2WjIj6cFQ== From: Sweet Tea Dorminy To: "Theodore Y. Ts'o" , Jaegeuk Kim , Eric Biggers , Chris Mason , Josef Bacik , David Sterba , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@meta.com Cc: Sweet Tea Dorminy Subject: [PATCH v5 06/18] fscrypt: document btrfs' fscrypt quirks. Date: Wed, 2 Nov 2022 07:52:55 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org As btrfs has a couple of quirks in its encryption compared to other filesystems, they should be documented like ext4's quirks. Additionally, extent-based contents encryption, being wholly new, deserves its own section to compare against inode-based contents encryption. Signed-off-by: Sweet Tea Dorminy Reviewed-by: Josef Bacik --- Documentation/filesystems/fscrypt.rst | 31 +++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index 5ba5817c17c2..d551635865c3 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -31,7 +31,7 @@ However, except for filenames, fscrypt does not encrypt filesystem metadata. Unlike eCryptfs, which is a stacked filesystem, fscrypt is integrated -directly into supported filesystems --- currently ext4, F2FS, and +directly into supported filesystems --- currently btrfs, ext4, F2FS, and UBIFS. This allows encrypted files to be read and written without caching both the decrypted and encrypted pages in the pagecache, thereby nearly halving the memory used and bringing it in line with @@ -280,6 +280,11 @@ included in the IV. Moreover: key derived using the KDF. Users may use the same master key for other v2 encryption policies. +For filesystems with extent-based content encryption (e.g. btrfs), +this is the only choice. Data shared among multiple inodes must share +the exact same key, therefore necessitating inodes using the same key +for contents encryption. + IV_INO_LBLK_64 policies ----------------------- @@ -374,12 +379,12 @@ to individual filesystems. However, authenticated encryption (AE) modes are not currently supported because of the difficulty of dealing with ciphertext expansion. -Contents encryption -------------------- +Inode-based contents encryption +------------------------------- -For file contents, each filesystem block is encrypted independently. -Starting from Linux kernel 5.5, encryption of filesystems with block -size less than system's page size is supported. +For most filesystems, each filesystem block within each file is +encrypted independently. Starting from Linux kernel 5.5, encryption of +filesystems with block size less than system's page size is supported. Each block's IV is set to the logical block number within the file as a little endian number, except that: @@ -403,6 +408,20 @@ Note that because file logical block numbers are included in the IVs, filesystems must enforce that blocks are never shifted around within encrypted files, e.g. via "collapse range" or "insert range". +Extent-based contents encryption +-------------------------------- + +For certain filesystems (currently only btrfs), data is encrypted on a +per-extent basis. Each filesystem block in a data extent is encrypted +independently. Multiple files may refer to the extent, as long as they +all share the same key. The filesystem may relocate the extent on disk, +as long as the encrypted data within the extent retains its offset +within the data extent. + +Each extent stores a nonce; each block within the extent has an IV +based on this nonce and the logical block number within the extent as a +little endian number. + Filenames encryption -------------------- From patchwork Wed Nov 2 11:52:56 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13028041 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 3C737C4167E for ; Wed, 2 Nov 2022 11:54:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230375AbiKBLxb (ORCPT ); Wed, 2 Nov 2022 07:53:31 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48986 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230362AbiKBLxX (ORCPT ); Wed, 2 Nov 2022 07:53:23 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6583215FE5; Wed, 2 Nov 2022 04:53:23 -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 A837181462; Wed, 2 Nov 2022 07:53:22 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1667390003; bh=aqjICWMWEkFmnf+NXI0OdrOT/6k2CqAp2kYwI+bnz44=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=UwtHqNYnoCQKcrJiO+e8VVEjtR0TtMXDmD5oOwI4WjRWqTR2P3APxVfVuvrM7e7IE jBEumuSy5QjPxyKtsIiglnKecu1x7nZ2GwavKguAbPj9M9noIcbpGF2xP0whhQcAsn H6hnl9hfPIHAzBGPQxLuuUqpRPJchLExoDgneEWpZQKlYfzkpv+NcZdrsHQReJua1m WTxAe0l9z+v37NBHnzrgCtMUc+xyo0k7R1ttwd+VbN7jRDtS2btk+dj7fnz+tM5hNB A3qhs8llJBA8seC0pqEcxF/l66c2pMZrJymJ2awtaAYJBR6biqWr88NdJJgAB0510F IstGgH3QttAEg== From: Sweet Tea Dorminy To: "Theodore Y. Ts'o" , Jaegeuk Kim , Eric Biggers , Chris Mason , Josef Bacik , David Sterba , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@meta.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v5 07/18] btrfs: disable various operations on encrypted inodes Date: Wed, 2 Nov 2022 07:52:56 -0400 Message-Id: <56e36fb5b9530c96adcde5b3b91289bffddd89fd.1667389115.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Omar Sandoval Initially, only normal data extents, 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 compressing encrypted inodes - 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/btrfs_inode.h | 3 +++ fs/btrfs/file.c | 4 ++-- fs/btrfs/inode.c | 3 ++- fs/btrfs/reflink.c | 10 ++++++++++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index d21c30bf7053..5b94609f138d 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -385,6 +385,9 @@ static inline bool btrfs_inode_in_log(struct btrfs_inode *inode, u64 generation) */ static inline bool btrfs_inode_can_compress(const struct btrfs_inode *inode) { + if (IS_ENCRYPTED(&inode->vfs_inode)) + return false; + if (inode->flags & BTRFS_INODE_NODATACOW || inode->flags & BTRFS_INODE_NODATASUM) return false; diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index b7855f794ba6..1724ec898f40 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1491,7 +1491,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(inode, ilock_flags); goto buffered; } @@ -3737,7 +3737,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 255959574724..972a49796bb9 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -445,7 +445,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 9d728107536e..579dbf057ffa 100644 --- a/fs/btrfs/reflink.c +++ b/fs/btrfs/reflink.c @@ -801,6 +801,7 @@ static int btrfs_remap_file_range_prep(struct file *file_in, loff_t pos_in, u64 bs = BTRFS_I(inode_out)->root->fs_info->sb->s_blocksize; u64 wb_len; int ret; + bool same; if (!(remap_flags & REMAP_FILE_DEDUP)) { struct btrfs_root *root_out = BTRFS_I(inode_out)->root; @@ -811,6 +812,15 @@ 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. + */ + ret = fscrypt_have_same_policy(inode_in, inode_out, &same); + if (ret) + return ret; + if (!same) + 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 Wed Nov 2 11:52:57 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13028044 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 4BEEEC4167D for ; Wed, 2 Nov 2022 11:54:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230383AbiKBLxc (ORCPT ); Wed, 2 Nov 2022 07:53:32 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49112 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230363AbiKBLx0 (ORCPT ); Wed, 2 Nov 2022 07:53:26 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3D52618B15; Wed, 2 Nov 2022 04:53:25 -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 5B30B8146E; Wed, 2 Nov 2022 07:53:24 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1667390004; bh=DB3ZJM6kteR6dtMcXBX5zEJW2jI7P8+YuZQl5T5Dj7s=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lF3yF5hh5CD4bviyqfLnVSnWNX6hR3rEp5rvcJ4upxeZ5JgeGbWnqemaD6T0Mke81 vZnjBqI3fxVcnnlbqrBYM8HtcytF2vgQBIxTl1NF0atgxKkKca0NXacM8shYjpnQWU w9g1l23QoCAre7AKhminySKOVS3RrVwav16Tv030n/DDjvqLHheM6S+BxiYhbF0Aon hVbEePsvBlPtVnBZZPftIAntu6Rmivb2pnGlboBk1TNM88d21akjpFeDagsgeDBIT0 xvE8+muFHOBGb0tT9Nb5EpZ1cjlih2HCoO+erq7Y6mZygCBy55B1jlbsZ3/c4et3kI yzWqlcwiTR+OA== From: Sweet Tea Dorminy To: "Theodore Y. Ts'o" , Jaegeuk Kim , Eric Biggers , Chris Mason , Josef Bacik , David Sterba , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@meta.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v5 08/18] btrfs: start using fscrypt hooks Date: Wed, 2 Nov 2022 07:52:57 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Omar Sandoval In order to 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. 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 | 7 ++++ fs/btrfs/fscrypt.h | 10 +++++ fs/btrfs/inode.c | 85 ++++++++++++++++++++++++++++++++++++------ fs/btrfs/super.c | 2 + 7 files changed, 97 insertions(+), 12 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 84fb3b4c35b0..dcdfc35ece76 100644 --- a/fs/btrfs/Makefile +++ b/fs/btrfs/Makefile @@ -38,6 +38,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 5b94609f138d..32fa68946f07 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -462,6 +462,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 1724ec898f40..2a9808f0c012 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -3703,6 +3703,9 @@ static int btrfs_file_open(struct inode *inode, struct file *filp) int ret; filp->f_mode |= FMODE_NOWAIT | FMODE_BUF_RASYNC | FMODE_BUF_WASYNC; + 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..48ab99dfe48d --- /dev/null +++ b/fs/btrfs/fscrypt.c @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "ctree.h" +#include "fscrypt.h" + +const struct fscrypt_operations btrfs_fscrypt_ops = { +}; 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 972a49796bb9..6904b2228112 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5462,6 +5462,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; @@ -5563,6 +5564,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); } @@ -6308,6 +6310,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 */ @@ -6784,9 +6790,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) @@ -8908,6 +8918,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)); } @@ -8978,8 +8989,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) @@ -9568,6 +9578,11 @@ static int btrfs_rename2(struct user_namespace *mnt_userns, struct inode *old_di 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); @@ -9787,15 +9802,22 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, 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) @@ -9804,7 +9826,7 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, 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); + btrfs_i_size_write(BTRFS_I(inode), disk_link.len - 1); inode_set_bytes(inode, name_len); new_inode_args.inode = inode; @@ -9832,10 +9854,23 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, 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) { @@ -9854,10 +9889,10 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, 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); @@ -9874,6 +9909,29 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, 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, @@ -11445,7 +11503,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, @@ -11455,4 +11513,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 d54bfec8e506..1b32103b14d5 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" @@ -1135,6 +1136,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 Wed Nov 2 11:52:58 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13028042 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 5A61CC41535 for ; Wed, 2 Nov 2022 11:54:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230389AbiKBLxe (ORCPT ); Wed, 2 Nov 2022 07:53:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49202 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230372AbiKBLx1 (ORCPT ); Wed, 2 Nov 2022 07:53:27 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E9B9F2DC1; Wed, 2 Nov 2022 04:53:26 -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 EB9E981480; Wed, 2 Nov 2022 07:53:25 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1667390006; bh=QmVA9H0nSPcygFxTf/MqkL34QZs5kM7ggaeduE2USWQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Ey8PerdN2yGDRylbYBArs9+ulFif5q+PBK16WkJ1mRHREkHNEQPM/eUwCvyuVIj14 PRVBWPdseCGpB6cRsQxJtzVr0Vqr4YGp7rwn5f+IAOkJTwG4dflKq72AjCElcrkVGQ w5wiY7uXyB19Nt6rqDgdUAfGPE2nylJ+baS4xoKmpD8AOrWcg31q9/wwoKWy8Aos9G lGCdIr8R07ecU5MhfaiZVXnPDNoWn4qLIpCFYpXE8QVMFFIH+rJ1HGHLu0oXOeSlJ2 ZUGGSoAlvv+qf48ipShjlJOMRmAD316jFOOF35/1beVyNH1Ng5sSwL2pUTRzjGizpT piVoJ/ggPdwaw== From: Sweet Tea Dorminy To: "Theodore Y. Ts'o" , Jaegeuk Kim , Eric Biggers , Chris Mason , Josef Bacik , David Sterba , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@meta.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v5 09/18] btrfs: add fscrypt_context items Date: Wed, 2 Nov 2022 07:52:58 -0400 Message-Id: <8ec52752965fa3cddc73c3da2b3826b1fed31d5c.1667389115.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Omar Sandoval In order to store per-inode information such as the inode nonce and the key identifier, fscrypt stores a context item with each encrypted inode. This can be implemented as a new item type, as fscrypt provides an arbitrary blob for the filesystem to store. This also provides a good place to implement full-subvolume encryption: a subvolume flag permits setting one context for the whole subvolume. However, since an unencrypted subvolume would be unable to read encrypted data, encrypted subvolumes should only be snapshottable to other encrypted subvolumes. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/ctree.h | 1 + fs/btrfs/fscrypt.c | 184 ++++++++++++++++++++++++++++++++ fs/btrfs/inode.c | 39 +++++++ fs/btrfs/ioctl.c | 7 +- fs/btrfs/tree-checker.c | 1 + include/uapi/linux/btrfs_tree.h | 12 +++ 6 files changed, 243 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 5649f8907984..be628dd3ecd4 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -32,6 +32,7 @@ #include "extent-io-tree.h" #include "extent_io.h" #include "extent_map.h" +#include "fscrypt.h" #include "async-thread.h" #include "block-rsv.h" #include "locking.h" diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index 48ab99dfe48d..babaa0fffed4 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -1,7 +1,191 @@ // 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 "messages.h" +#include "transaction.h" +#include "xattr.h" + +static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len) +{ + struct btrfs_root *root = BTRFS_I(inode)->root; + struct btrfs_key key = { + .objectid = btrfs_ino(BTRFS_I(inode)), + .type = BTRFS_FSCRYPT_CTXT_ITEM_KEY, + .offset = 0, + }; + struct inode *put_inode = NULL; + struct btrfs_path *path; + struct extent_buffer *leaf; + unsigned long ptr; + int ret; + + + if (btrfs_root_flags(&root->root_item) & BTRFS_ROOT_SUBVOL_FSCRYPT) { + inode = btrfs_iget(inode->i_sb, BTRFS_FIRST_FREE_OBJECTID, + root); + if (IS_ERR(inode)) + return PTR_ERR(inode); + put_inode = inode; + } + + 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); + iput(put_inode); + 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, + struct btrfs_trans_handle *trans, + const void *ctx, size_t len) +{ + struct btrfs_path *path; + int ret; + 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; + + 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) + return ret; + + ret = btrfs_insert_item(trans, BTRFS_I(inode)->root, &key, (void *) ctx, len); + if (ret) + return ret; + + BTRFS_I(inode)->flags |= BTRFS_INODE_FSCRYPT_CONTEXT; + 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) + return ret; + + btrfs_abort_transaction(trans, ret); + return ret; +} + +static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx, + size_t len, void *fs_data) +{ + struct btrfs_root *root = BTRFS_I(inode)->root; + struct btrfs_trans_handle *trans; + int is_subvolume = inode->i_ino == BTRFS_FIRST_FREE_OBJECTID; + int ret; + + /* + * If the whole subvolume is encrypted, we expect that all children + * have the same policy. + */ + if (btrfs_root_flags(&root->root_item) & BTRFS_ROOT_SUBVOL_FSCRYPT) { + bool same_policy; + struct inode *root_inode = NULL; + + root_inode = btrfs_iget(inode->i_sb, BTRFS_FIRST_FREE_OBJECTID, + root); + if (IS_ERR(inode)) + return PTR_ERR(inode); + ret = fscrypt_have_same_policy(inode, root_inode, &same_policy); + iput(root_inode); + + if (ret) + return ret; + if (same_policy) + return 0; + } + + if (fs_data) { + /* + * We are setting the context as part of an existing + * transaction. This happens when we are inheriting the context + * for a new inode. + */ + return __btrfs_fscrypt_set_context(inode, fs_data, ctx, len); + } + + /* + * 1 for the inode item + * 1 for the fscrypt item + * 1 for the root item if the inode is a subvolume + */ + trans = btrfs_start_transaction(root, 2 + is_subvolume); + if (IS_ERR(trans)) + return PTR_ERR(trans); + + ret = __btrfs_fscrypt_set_context(inode, trans, ctx, len); + + /* + * For new subvolumes, the root item is already initialized with + * the BTRFS_ROOT_SUBVOL_FSCRYPT flag. + */ + if (!ret && is_subvolume) { + u64 root_flags = btrfs_root_flags(&root->root_item); + + btrfs_set_root_flags(&root->root_item, + root_flags | + BTRFS_ROOT_SUBVOL_FSCRYPT); + ret = btrfs_update_root(trans, root->fs_info->tree_root, + &root->root_key, + &root->root_item); + } + + btrfs_end_transaction(trans); + return ret; +} + +static bool btrfs_fscrypt_empty_dir(struct inode *inode) +{ + return inode->i_size == BTRFS_EMPTY_DIR_SIZE; +} const struct fscrypt_operations btrfs_fscrypt_ops = { + .key_prefix = "btrfs:", + .get_context = btrfs_fscrypt_get_context, + .set_context = btrfs_fscrypt_set_context, + .empty_dir = btrfs_fscrypt_empty_dir, }; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 6904b2228112..1be0b53abc83 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6297,6 +6297,34 @@ 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 (IS_ENCRYPTED(dir) && + !(BTRFS_I(dir)->flags & BTRFS_INODE_FSCRYPT_CONTEXT)) { + struct inode *root_inode; + bool encrypt; + + root_inode = btrfs_iget(inode->i_sb, BTRFS_FIRST_FREE_OBJECTID, + BTRFS_I(dir)->root); + if (IS_ERR(root_inode)) + return PTR_ERR(root_inode); + /* + * TODO: perhaps instead of faking making a new dir to get a + * new context, it would be better to expose + * fscrypt_setup_encryption_info() for our use. + */ + ret = fscrypt_prepare_new_inode(root_inode, dir, &encrypt); + if (!ret) { + ret = fscrypt_set_context(dir, NULL); + if (ret) + fscrypt_put_encryption_info(dir); + } + iput(root_inode); + if (ret) + return ret; + } + if (!args->orphan) { ret = fscrypt_setup_filename(dir, &args->dentry->d_name, 0, &args->fname); @@ -6330,6 +6358,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 */ + if (args->encrypt) + (*trans_num_items)++; if (args->orphan) { /* 1 to add orphan item */ (*trans_num_items)++; @@ -6583,6 +6614,14 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans, } } + if (args->encrypt) { + ret = fscrypt_set_context(inode, trans); + if (ret) { + btrfs_abort_transaction(trans, ret); + goto discard; + } + } + inode_tree_add(inode); trace_btrfs_inode_new(inode); diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 9c1cb5113178..5bb34da6ee25 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -669,7 +669,8 @@ static noinline int create_subvol(struct user_namespace *mnt_userns, fs_info->nodesize); btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755); - btrfs_set_root_flags(root_item, 0); + btrfs_set_root_flags(root_item, new_inode_args.encrypt ? + BTRFS_ROOT_SUBVOL_FSCRYPT : 0); btrfs_set_root_limit(root_item, 0); btrfs_set_stack_inode_flags(inode_item, BTRFS_INODE_ROOT_ITEM_INIT); @@ -798,6 +799,10 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir, return -ETXTBSY; } + if ((btrfs_root_flags(&root->root_item) & BTRFS_ROOT_SUBVOL_FSCRYPT) && + !IS_ENCRYPTED(dir)) + return -EXDEV; + pending_snapshot = kzalloc(sizeof(*pending_snapshot), GFP_KERNEL); if (!pending_snapshot) return -ENOMEM; diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index 1c2d418dda6a..77e678efef65 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -1123,6 +1123,7 @@ static int check_root_item(struct extent_buffer *leaf, struct btrfs_key *key, struct btrfs_fs_info *fs_info = leaf->fs_info; struct btrfs_root_item ri = { 0 }; const u64 valid_root_flags = BTRFS_ROOT_SUBVOL_RDONLY | + BTRFS_ROOT_SUBVOL_FSCRYPT | BTRFS_ROOT_SUBVOL_DEAD; int ret; diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h index 29895ffa470d..52641fdc65bc 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_FSCRYPT_CONTEXT (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_FSCRYPT_CONTEXT | \ BTRFS_INODE_ROOT_ITEM_INIT) #define BTRFS_INODE_RO_VERITY (1U << 0) @@ -860,6 +864,8 @@ struct btrfs_dir_item { } __attribute__ ((__packed__)); #define BTRFS_ROOT_SUBVOL_RDONLY (1ULL << 0) +/* Top-level subvolume directory is encrypted with fscrypt. */ +#define BTRFS_ROOT_SUBVOL_FSCRYPT (1ULL << 1) /* * Internal in-memory flag that a subvolume has been marked for deletion but @@ -1015,6 +1021,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 Wed Nov 2 11:52:59 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13028043 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 68AECC38A2B for ; Wed, 2 Nov 2022 11:54:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230410AbiKBLxe (ORCPT ); Wed, 2 Nov 2022 07:53:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49268 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230373AbiKBLx3 (ORCPT ); Wed, 2 Nov 2022 07:53:29 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4F89FBD1; Wed, 2 Nov 2022 04:53:28 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 93C9781462; Wed, 2 Nov 2022 07:53:27 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1667390007; bh=Us3jw/9xY/dBCkDCwFmhlDbT1pJz0u58bNzal0riTaU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ZYukNvDSSb/ewmGmb3D+UWsyDXQsYFgFlfMZ5oTiH+pZr+7U+60GM/xqRwkImbWQx ErvWZ8zCl0aDLnZ9+DkMVPAFtbvc28YHRJNflEAYBqHlV7ZTBv/Fm+4RTbpNyyzv8u vZTMAHniRk9pgu0msKr/77CA9d0h0hK76o9qZHqvhUxfcSIU42qKZz49E1VdpMOP5D GRo1X3TVFWemFe44fHR7sAeWZs1eVOhllxr+ZVGhigvSWP9PW/UcyLeTQkn94ldd9R DuzprmVHMMNqJaud79OFLbNx1mJWFo6y6uvCqMSQ6tlXwjdA0OnY3FF1/8GMWzPduf wsSKOXBEhl4EQ== From: Sweet Tea Dorminy To: "Theodore Y. Ts'o" , Jaegeuk Kim , Eric Biggers , Chris Mason , Josef Bacik , David Sterba , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@meta.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v5 10/18] btrfs: translate btrfs encryption flags and encrypted inode flag Date: Wed, 2 Nov 2022 07:52:59 -0400 Message-Id: <49a5c056040d7955215938713d7fe6a8f1c392d5.1667389115.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Omar Sandoval In btrfs, a file can be encrypted either if its directory is encrypted or its root subvolume is encrypted, so translate both to the standard flags. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy Reviewed-by: Josef Bacik --- fs/btrfs/ioctl.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 5bb34da6ee25..a40025e18216 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -158,6 +159,10 @@ static unsigned int btrfs_inode_flags_to_fsflags(struct btrfs_inode *binode) iflags |= FS_NOCOW_FL; if (ro_flags & BTRFS_INODE_RO_VERITY) iflags |= FS_VERITY_FL; + if ((binode->flags & BTRFS_INODE_FSCRYPT_CONTEXT) || + (btrfs_root_flags(&binode->root->root_item) & + BTRFS_ROOT_SUBVOL_FSCRYPT)) + iflags |= FS_ENCRYPT_FL; if (flags & BTRFS_INODE_NOCOMPRESS) iflags |= FS_NOCOMP_FL; @@ -187,10 +192,14 @@ void btrfs_sync_inode_flags_to_i_flags(struct inode *inode) new_fl |= S_DIRSYNC; if (binode->ro_flags & BTRFS_INODE_RO_VERITY) new_fl |= S_VERITY; + if ((binode->flags & BTRFS_INODE_FSCRYPT_CONTEXT) || + (btrfs_root_flags(&binode->root->root_item) & + BTRFS_ROOT_SUBVOL_FSCRYPT)) + new_fl |= S_ENCRYPTED; 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 +212,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 */ From patchwork Wed Nov 2 11:53:00 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13028051 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 4ECA5C4332F for ; Wed, 2 Nov 2022 11:55:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229496AbiKBLye (ORCPT ); Wed, 2 Nov 2022 07:54:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50810 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230442AbiKBLyC (ORCPT ); Wed, 2 Nov 2022 07:54:02 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 27E1F201A3; Wed, 2 Nov 2022 04:53: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 2A52881480; Wed, 2 Nov 2022 07:53:29 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1667390009; bh=7fOhas0Zw+Lw5PCP09Cn8+AreZCFZtN1wZw2X9qs2hY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=DwGeozDbxMfy+rZkP5tuGBD3dGg0N9GThxipF4tVrkbJfQQm4ocjpK1pl4ucuc9K6 8G2fl/YeOg7UM5O33f8G2QEK57uVU6VbTUPLb4eYh0kyBSKYK8ZzzEZ9j/FqptqpX3 GtZ5tHujGffUHexMRvypkhZib1SL0KbT7uIFY+rF5rKIjLIDqeRTH0Ba35OtZNnE1p VUr/HsX+ern4QjwL3n13qn5xn51VcKwxlHstWWcAzr+HPSbZBU80yXx44EjMF2qVnS C4V/oqFw7OxmpMzW50KTzzFAQdkcMQGIkyEi96o53G7eWBTE9pevSrc0zAeq2FmT0k P/7G8jXO5YJdw== From: Sweet Tea Dorminy To: "Theodore Y. Ts'o" , Jaegeuk Kim , Eric Biggers , Chris Mason , Josef Bacik , David Sterba , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@meta.com Cc: Sweet Tea Dorminy Subject: [PATCH v5 11/18] btrfs: store a fscrypt extent context per normal file extent Date: Wed, 2 Nov 2022 07:53:00 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org In order to encrypt data, each file extent must have its own persistent fscrypt_extent_context, which is then provided to fscrypt upon request. This is only needed for encrypted extents and is of variable size on disk, so file extents must additionally keep track of their actual length. This puts the long-preserved 1-byte encryption field to work. Right now we don't anticipate very many encryption policies, so 2 bits should be ample; similarly right now we can't imagine a extent context larger than fscrypt's current inode contexts, which are 40 bytes, so 6 bits is ample to store the extent 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 | 29 +++++++++++ fs/btrfs/ctree.h | 3 ++ fs/btrfs/extent_map.c | 7 +++ fs/btrfs/extent_map.h | 4 ++ fs/btrfs/file-item.c | 16 ++++++ fs/btrfs/file.c | 4 +- fs/btrfs/fscrypt.c | 22 ++++++++ fs/btrfs/fscrypt.h | 35 +++++++++++++ fs/btrfs/inode.c | 90 ++++++++++++++++++++++++++------- fs/btrfs/ordered-data.c | 11 +++- fs/btrfs/ordered-data.h | 4 +- fs/btrfs/reflink.c | 1 + fs/btrfs/tree-checker.c | 37 +++++++++++--- fs/btrfs/tree-log.c | 13 ++++- include/uapi/linux/btrfs_tree.h | 9 ++++ 15 files changed, 252 insertions(+), 33 deletions(-) diff --git a/fs/btrfs/accessors.h b/fs/btrfs/accessors.h index cb59b69d2af1..8cc06c2abb6a 100644 --- a/fs/btrfs/accessors.h +++ b/fs/btrfs/accessors.h @@ -944,6 +944,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; +} static inline unsigned long btrfs_file_extent_inline_start( const struct btrfs_file_extent_item *e) @@ -976,6 +986,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)); +} + /* * Returns the number of bytes used by the item on disk, minus the size of any * extent headers. If a file is compressed on disk, this is the compressed diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index be628dd3ecd4..79d1e5d41209 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -38,6 +38,7 @@ #include "locking.h" #include "misc.h" #include "fs.h" +#include "fscrypt.h" struct btrfs_trans_handle; struct btrfs_transaction; @@ -348,6 +349,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/extent_map.c b/fs/btrfs/extent_map.c index 4a4362f5cc52..849c253650cb 100644 --- a/fs/btrfs/extent_map.c +++ b/fs/btrfs/extent_map.c @@ -207,6 +207,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_context, &next->fscrypt_context, + sizeof(next->fscrypt_context))) + 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 68d3f2c9ea1d..3fa70d6b4750 100644 --- a/fs/btrfs/extent_map.h +++ b/fs/btrfs/extent_map.h @@ -5,6 +5,7 @@ #include #include +#include "fscrypt.h" #define EXTENT_MAP_LAST_BYTE ((u64)-4) #define EXTENT_MAP_HOLE ((u64)-3) @@ -27,6 +28,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 extent context */ + EXTENT_FLAG_ENCRYPTED, }; struct extent_map { @@ -50,6 +53,7 @@ struct extent_map { */ u64 generation; unsigned long flags; + struct btrfs_fscrypt_extent_context fscrypt_context; /* 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/file-item.c b/fs/btrfs/file-item.c index 456f71b42a9c..36b7e2a8d698 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -1233,6 +1233,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 - @@ -1248,6 +1249,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; @@ -1255,6 +1260,17 @@ 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)); + +#ifdef CONFIG_FS_ENCRYPTION + em->fscrypt_context.len = ctxsize; + + read_extent_buffer(leaf, em->fscrypt_context.buffer, + (unsigned long)fi->fscrypt_context, + ctxsize); +#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/file.c b/fs/btrfs/file.c index 2a9808f0c012..2d9c001c63aa 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -2246,14 +2246,14 @@ static int btrfs_insert_replace_extent(struct btrfs_trans_handle *trans, key.type = BTRFS_EXTENT_DATA_KEY; key.offset = extent_info->file_offset; ret = btrfs_insert_empty_item(trans, root, path, &key, - sizeof(struct btrfs_file_extent_item)); + extent_info->extent_buf_size); if (ret) return ret; leaf = path->nodes[0]; slot = path->slots[0]; write_extent_buffer(leaf, extent_info->extent_buf, btrfs_item_ptr_offset(leaf, slot), - sizeof(struct btrfs_file_extent_item)); + extent_info->extent_buf_size); extent = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item); ASSERT(btrfs_file_extent_type(leaf, extent) != BTRFS_FILE_EXTENT_INLINE); btrfs_set_file_extent_offset(leaf, extent, extent_info->data_offset); diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index babaa0fffed4..2c9f2d84b539 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -10,6 +10,7 @@ #include "messages.h" #include "transaction.h" #include "xattr.h" +#include "fscrypt.h" static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len) { @@ -183,9 +184,30 @@ static bool btrfs_fscrypt_empty_dir(struct inode *inode) return inode->i_size == BTRFS_EMPTY_DIR_SIZE; } +static int btrfs_fscrypt_get_extent_context(const struct inode *inode, + u64 lblk_num, void *ctx, + size_t len, + size_t *extent_offset, + size_t *extent_length) +{ + return len; +} + +static int btrfs_fscrypt_set_extent_context(void *extent, void *ctx, + size_t len) +{ + struct btrfs_fscrypt_extent_context *extent_context = extent; + + memcpy(extent_context->buffer, ctx, len); + extent_context->len = len; + return 0; +} + const struct fscrypt_operations btrfs_fscrypt_ops = { .key_prefix = "btrfs:", .get_context = btrfs_fscrypt_get_context, .set_context = btrfs_fscrypt_set_context, .empty_dir = btrfs_fscrypt_empty_dir, + .get_extent_context = btrfs_fscrypt_get_extent_context, + .set_extent_context = btrfs_fscrypt_set_extent_context, }; diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h index 7f4e6888bd43..86dc0e0b91b9 100644 --- a/fs/btrfs/fscrypt.h +++ b/fs/btrfs/fscrypt.h @@ -5,6 +5,41 @@ #include +#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) + +#ifdef CONFIG_FS_ENCRYPTION +struct btrfs_fscrypt_extent_context { + u8 buffer[FSCRYPT_EXTENT_CONTEXT_MAX_SIZE]; + u8 len; +}; +#else +struct btrfs_fscrypt_extent_context { + u8 len; +}; +#endif + +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); +} + 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 1be0b53abc83..332900b41488 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1051,7 +1051,6 @@ static int submit_one_async_extent(struct btrfs_inode *inode, ret = PTR_ERR(em); goto out_free_reserve; } - free_extent_map(em); ret = btrfs_add_ordered_extent(inode, start, /* file_offset */ async_extent->ram_size, /* num_bytes */ @@ -1060,7 +1059,9 @@ static int submit_one_async_extent(struct btrfs_inode *inode, ins.offset, /* disk_num_bytes */ 0, /* offset */ 1 << BTRFS_ORDERED_COMPRESSED, - async_extent->compress_type); + async_extent->compress_type, + &em->fscrypt_context); + free_extent_map(em); if (ret) { btrfs_drop_extent_map_range(inode, start, end, false); goto out_free_reserve; @@ -1332,12 +1333,13 @@ static noinline int cow_file_range(struct btrfs_inode *inode, ret = PTR_ERR(em); goto out_reserve; } - free_extent_map(em); ret = btrfs_add_ordered_extent(inode, start, ram_size, ram_size, ins.objectid, cur_alloc_size, 0, 1 << BTRFS_ORDERED_REGULAR, - BTRFS_COMPRESS_NONE); + BTRFS_COMPRESS_NONE, + &em->fscrypt_context); + free_extent_map(em); if (ret) goto out_drop_extent_cache; @@ -2131,14 +2133,15 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode, ret = PTR_ERR(em); goto error; } - free_extent_map(em); ret = btrfs_add_ordered_extent(inode, cur_offset, nocow_args.num_bytes, nocow_args.num_bytes, nocow_args.disk_bytenr, nocow_args.num_bytes, 0, 1 << BTRFS_ORDERED_PREALLOC, - BTRFS_COMPRESS_NONE); + BTRFS_COMPRESS_NONE, + &em->fscrypt_context); + free_extent_map(em); if (ret) { btrfs_drop_extent_map_range(inode, cur_offset, nocow_end, false); @@ -2152,7 +2155,8 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode, nocow_args.num_bytes, 0, 1 << BTRFS_ORDERED_NOCOW, - BTRFS_COMPRESS_NONE); + BTRFS_COMPRESS_NONE, + NULL); if (ret) goto error; } @@ -3084,6 +3088,7 @@ int btrfs_writepage_cow_fixup(struct page *page) static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, struct btrfs_inode *inode, u64 file_pos, struct btrfs_file_extent_item *stack_fi, + struct btrfs_fscrypt_extent_context *fscrypt_context, const bool update_inode_bytes, u64 qgroup_reserved) { @@ -3098,6 +3103,7 @@ 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 context_len = fscrypt_context ? fscrypt_context->len : 0; int ret; path = btrfs_alloc_path(); @@ -3117,7 +3123,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) + context_len; ret = btrfs_drop_extents(trans, root, inode, &drop_args); if (ret) goto out; @@ -3128,7 +3134,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) + context_len); if (ret) goto out; } @@ -3137,6 +3143,13 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, write_extent_buffer(leaf, stack_fi, btrfs_item_ptr_offset(leaf, path->slots[0]), sizeof(struct btrfs_file_extent_item)); +#ifdef CONFIG_FS_ENCRYPTION + if (context_len) + write_extent_buffer(leaf, fscrypt_context->buffer, + btrfs_item_ptr_offset(leaf, path->slots[0]) + + sizeof(struct btrfs_file_extent_item), + context_len); +#endif /* CONFIG_FS_ENCRYPTION */ btrfs_mark_buffer_dirty(leaf); btrfs_release_path(path); @@ -3213,7 +3226,12 @@ 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 */ + if (IS_ENCRYPTED(oe->inode)) { + u8 encryption = btrfs_pack_encryption(BTRFS_ENCRYPTION_FSCRYPT, + oe->fscrypt_context.len); + btrfs_set_stack_file_extent_encryption(&stack_fi, encryption); + } + /* Other encoding is reserved and always 0 */ /* * For delalloc, when completing an ordered extent we update the inode's @@ -3227,6 +3245,7 @@ static int insert_ordered_extent_file_extent(struct btrfs_trans_handle *trans, return insert_reserved_file_extent(trans, BTRFS_I(oe->inode), oe->file_offset, &stack_fi, + &oe->fscrypt_context, update_inode_bytes, oe->qgroup_rsv); } @@ -7133,8 +7152,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) { /* @@ -7216,7 +7251,8 @@ static struct extent_map *btrfs_create_dio_extent(struct btrfs_inode *inode, block_len, 0, (1 << type) | (1 << BTRFS_ORDERED_DIRECT), - BTRFS_COMPRESS_NONE); + BTRFS_COMPRESS_NONE, + em ? &em->fscrypt_context : NULL); if (ret) { if (em) { free_extent_map(em); @@ -7491,6 +7527,7 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start, int type) { struct extent_map *em; + struct btrfs_fs_info *fs_info = inode->root->fs_info; int ret; ASSERT(type == BTRFS_ORDERED_PREALLOC || @@ -7518,6 +7555,16 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start, em->compress_type = compress_type; } + if (IS_ENCRYPTED(&inode->vfs_inode)) { + u64 lblk = em->start >> fs_info->sectorsize_bits; + ret = fscrypt_set_extent_context(&inode->vfs_inode, lblk, + &em->fscrypt_context); + if (ret < 0) { + free_extent_map(em); + return ERR_PTR(ret); + } + } + ret = btrfs_replace_extent_map_range(inode, em, true); if (ret) { free_extent_map(em); @@ -9847,6 +9894,7 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, struct extent_buffer *leaf; struct fscrypt_str disk_link; u32 name_len = strlen(symname); + u8 encryption; /* * fscrypt sets disk_link.len to be len + 1, including a NUL terminator, but we @@ -9994,16 +10042,18 @@ 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) return ERR_PTR(qgroup_released); if (trans) { - ret = insert_reserved_file_extent(trans, inode, - file_offset, &stack_fi, - true, qgroup_released); + ret = insert_reserved_file_extent(trans, inode, file_offset, + &stack_fi, NULL, true, + qgroup_released); if (ret) goto free_qgroup; return trans; @@ -10015,6 +10065,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); extent_info.is_new_extent = true; extent_info.update_times = true; extent_info.qgroup_reserved = qgroup_released; @@ -10945,14 +10996,15 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from, ret = PTR_ERR(em); goto out_free_reserved; } - free_extent_map(em); ret = btrfs_add_ordered_extent(inode, start, num_bytes, ram_bytes, ins.objectid, ins.offset, encoded->unencoded_offset, (1 << BTRFS_ORDERED_ENCODED) | (1 << BTRFS_ORDERED_COMPRESSED), - compression); + compression, + &em->fscrypt_context); + free_extent_map(em); if (ret) { btrfs_drop_extent_map_range(inode, start, end, false); goto out_free_reserved; diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 8fda1949b71b..c5df41843cce 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -167,7 +167,8 @@ static inline struct rb_node *tree_search(struct btrfs_ordered_inode_tree *tree, int btrfs_add_ordered_extent(struct btrfs_inode *inode, u64 file_offset, u64 num_bytes, u64 ram_bytes, u64 disk_bytenr, u64 disk_num_bytes, u64 offset, unsigned flags, - int compress_type) + int compress_type, + struct btrfs_fscrypt_extent_context *fscrypt_context) { struct btrfs_root *root = inode->root; struct btrfs_fs_info *fs_info = root->fs_info; @@ -202,6 +203,11 @@ int btrfs_add_ordered_extent(struct btrfs_inode *inode, u64 file_offset, entry->disk_bytenr = disk_bytenr; entry->disk_num_bytes = disk_num_bytes; entry->offset = offset; +#ifdef CONFIG_FS_ENCRYPTION + if (fscrypt_context && fscrypt_context->len) + memcpy(&entry->fscrypt_context, fscrypt_context, + sizeof(*fscrypt_context)); +#endif /* CONFIG_FS_ENCRYPTION */ entry->bytes_left = num_bytes; entry->inode = igrab(&inode->vfs_inode); entry->compress_type = compress_type; @@ -1110,7 +1116,8 @@ static int clone_ordered_extent(struct btrfs_ordered_extent *ordered, u64 pos, WARN_ON_ONCE(flags & (1 << BTRFS_ORDERED_COMPRESSED)); return btrfs_add_ordered_extent(BTRFS_I(inode), file_offset, len, len, disk_bytenr, len, 0, flags, - ordered->compress_type); + ordered->compress_type, + &ordered->fscrypt_context); } int btrfs_split_ordered_extent(struct btrfs_ordered_extent *ordered, u64 pre, diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h index 89f82b78f590..a25c63dea8e0 100644 --- a/fs/btrfs/ordered-data.h +++ b/fs/btrfs/ordered-data.h @@ -99,6 +99,7 @@ struct btrfs_ordered_extent { u64 disk_bytenr; u64 disk_num_bytes; u64 offset; + struct btrfs_fscrypt_extent_context fscrypt_context; /* number of bytes that still need writing */ u64 bytes_left; @@ -182,7 +183,8 @@ bool btrfs_dec_test_ordered_pending(struct btrfs_inode *inode, int btrfs_add_ordered_extent(struct btrfs_inode *inode, u64 file_offset, u64 num_bytes, u64 ram_bytes, u64 disk_bytenr, u64 disk_num_bytes, u64 offset, unsigned flags, - int compress_type); + int compress_type, + struct btrfs_fscrypt_extent_context *fscrypt_context); void btrfs_add_ordered_sum(struct btrfs_ordered_extent *entry, struct btrfs_ordered_sum *sum); struct btrfs_ordered_extent *btrfs_lookup_ordered_extent(struct btrfs_inode *inode, diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c index 579dbf057ffa..4b09d559cc8f 100644 --- a/fs/btrfs/reflink.c +++ b/fs/btrfs/reflink.c @@ -501,6 +501,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-checker.c b/fs/btrfs/tree-checker.c index 77e678efef65..e72c8176f7bc 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -207,6 +207,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, @@ -258,10 +259,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) { @@ -290,12 +293,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 8d559b076405..9c211802efb1 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -4610,6 +4610,9 @@ static int log_one_extent(struct btrfs_trans_handle *trans, u64 extent_offset = em->start - em->orig_start; u64 block_len; int ret; + u8 encryption = btrfs_pack_encryption(IS_ENCRYPTED(&inode->vfs_inode) ? + BTRFS_ENCRYPTION_FSCRYPT : 0, + em->fscrypt_context.len); btrfs_set_stack_file_extent_generation(&fi, trans->transid); if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) @@ -4631,6 +4634,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) @@ -4662,7 +4666,8 @@ 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) + + em->fscrypt_context.len); if (ret) return ret; } @@ -4670,6 +4675,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, &em->fscrypt_context.buffer, + btrfs_item_ptr_offset(leaf, path->slots[0]) + + sizeof(fi), em->fscrypt_context.len); +#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 52641fdc65bc..4c0f77f3cfa9 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -1049,6 +1049,10 @@ struct btrfs_file_extent_item { * but not for stat. */ __u8 compression; + /* + * This field contains 2 bits of encryption type in the lower bits, + * 6 bits of context size in the upper bits. The unencrypted value is 0. + */ __u8 encryption; __le16 other_encoding; /* spare for later use */ @@ -1077,6 +1081,11 @@ struct btrfs_file_extent_item { */ __le64 num_bytes; + /* + * Fscrypt extent encryption context. Only present if extent is + * encrypted (stored in the encryption field). + */ + __u8 fscrypt_context[0]; } __attribute__ ((__packed__)); struct btrfs_csum_item { From patchwork Wed Nov 2 11:53:01 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13028045 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 B3216C43219 for ; Wed, 2 Nov 2022 11:54:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230307AbiKBLyD (ORCPT ); Wed, 2 Nov 2022 07:54:03 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49438 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230380AbiKBLxc (ORCPT ); Wed, 2 Nov 2022 07:53:32 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AE172E004; Wed, 2 Nov 2022 04:53:31 -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 0B2E281462; Wed, 2 Nov 2022 07:53:30 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1667390011; bh=1XhfSCiKluVAYhbmx4i3yfdlOK4Zbh6VrYXNw1i+Esc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=N2cJq3U0j9ONaCtH86cmDe1/2wJHYgfu6MdHA8kyr2NFmGYzKRx7ADY1DuiXE5yre 1AgMA2CbB0epA0h0uqyz8jRk9kuQTL3s2qZVSeFUVeMJVSUteotnKPQqk68AWF12sZ lpicz/3Jdk3WBFf5+f56JOewfm3XNWuSJo7CB52ZlAWFbQTdYc4WduhYyDB96UQoTx kGRq8fobKXFbbf2w3C9di03d8VBUXLi7g2+4dAz6M3m8FMs25H0/CmyzlVeMrIT6c4 d5hwdEZjJU3wsOd4pc1QNugAL+vydTHl0JcPXa/e/1+IFqf1IW8LDoEjSW4XxJbKLG IhAksfXSZ+Ltg== From: Sweet Tea Dorminy To: "Theodore Y. Ts'o" , Jaegeuk Kim , Eric Biggers , Chris Mason , Josef Bacik , David Sterba , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@meta.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v5 12/18] btrfs: encrypt normal file extent data if appropriate Date: Wed, 2 Nov 2022 07:53:01 -0400 Message-Id: <9e6e11154b096255ad1541192ddb18610991bb28.1667389115.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Omar Sandoval Add in the necessary calls to encrypt and decrypt data to achieve encryption of normal data. Since these are all page cache pages being encrypted, we can't encrypt them in place and must encrypt/decrypt into a new page. fscrypt provides a pool of pages for this purpose, which it calls bounce pages. For IO of encrypted data, we use a bounce page for the actual IO, and encrypt/decrypt from/to the actual page cache page on either side of the IO. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/extent_io.c | 56 ++++++++++++++++++++++++++++++++++++----- fs/btrfs/file-item.c | 9 +++++-- fs/btrfs/fscrypt.c | 33 +++++++++++++++++++++++- fs/btrfs/tree-checker.c | 11 +++++--- 4 files changed, 97 insertions(+), 12 deletions(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 2ec989b83f54..97aaa74c4822 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -117,6 +117,7 @@ static void submit_one_bio(struct btrfs_bio_ctrl *bio_ctrl) { struct bio *bio; struct bio_vec *bv; + struct page *first_page; struct inode *inode; int mirror_num; @@ -125,13 +126,17 @@ static void submit_one_bio(struct btrfs_bio_ctrl *bio_ctrl) bio = bio_ctrl->bio; bv = bio_first_bvec_all(bio); - inode = bv->bv_page->mapping->host; + first_page = bio_first_page_all(bio); + if (fscrypt_is_bounce_page(first_page)) + inode = fscrypt_pagecache_page(first_page)->mapping->host; + else + inode = first_page->mapping->host; mirror_num = bio_ctrl->mirror_num; /* Caller should ensure the bio has at least some range added */ ASSERT(bio->bi_iter.bi_size); - btrfs_bio(bio)->file_offset = page_offset(bv->bv_page) + bv->bv_offset; + btrfs_bio(bio)->file_offset = page_offset(first_page) + bv->bv_offset; if (!is_data_inode(inode)) btrfs_submit_metadata_bio(inode, bio, mirror_num); @@ -1018,9 +1023,19 @@ static void end_bio_extent_writepage(struct btrfs_bio *bbio) ASSERT(!bio_flagged(bio, BIO_CLONED)); bio_for_each_segment_all(bvec, bio, iter_all) { struct page *page = bvec->bv_page; - struct inode *inode = page->mapping->host; - struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); - const u32 sectorsize = fs_info->sectorsize; + struct inode *inode; + struct btrfs_fs_info *fs_info; + u32 sectorsize; + struct page *bounce_page = NULL; + + if (fscrypt_is_bounce_page(page)) { + bounce_page = page; + page = fscrypt_pagecache_page(bounce_page); + } + + inode = page->mapping->host; + fs_info = btrfs_sb(inode->i_sb); + sectorsize = fs_info->sectorsize; /* Our read/write should always be sector aligned. */ if (!IS_ALIGNED(bvec->bv_offset, sectorsize)) @@ -1041,7 +1056,7 @@ static void end_bio_extent_writepage(struct btrfs_bio *bbio) } end_extent_writepage(page, error, start, end); - + fscrypt_free_bounce_page(bounce_page); btrfs_page_clear_writeback(fs_info, page, start, bvec->bv_len); } @@ -1233,6 +1248,17 @@ static void end_bio_extent_readpage(struct btrfs_bio *bbio) } } + if (likely(uptodate)) { + if (fscrypt_inode_uses_fs_layer_crypto(inode)) { + int ret = fscrypt_decrypt_pagecache_blocks(page, + bvec->bv_len, + bvec->bv_offset); + if (ret) { + error_bitmap = (unsigned int) -1; + uptodate = false; + } + } + } if (likely(uptodate)) { loff_t i_size = i_size_read(inode); pgoff_t end_index = i_size >> PAGE_SHIFT; @@ -1567,11 +1593,29 @@ static int submit_extent_page(blk_opf_t opf, bool force_bio_submit) { int ret = 0; + struct page *bounce_page = NULL; struct btrfs_inode *inode = BTRFS_I(page->mapping->host); unsigned int cur = pg_offset; ASSERT(bio_ctrl); + if ((opf & REQ_OP_MASK) == REQ_OP_WRITE && + fscrypt_inode_uses_fs_layer_crypto(&inode->vfs_inode)) { + gfp_t gfp_flags = GFP_NOFS; + + if (bio_ctrl->bio) + gfp_flags = GFP_NOWAIT | __GFP_NOWARN; + else + gfp_flags = GFP_NOFS; + bounce_page = fscrypt_encrypt_pagecache_blocks(page, size, + pg_offset, + gfp_flags); + if (IS_ERR(bounce_page)) + return PTR_ERR(bounce_page); + page = bounce_page; + pg_offset = 0; + } + ASSERT(pg_offset < PAGE_SIZE && size <= PAGE_SIZE && pg_offset + size <= PAGE_SIZE); diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index 36b7e2a8d698..2faac05ce137 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -681,8 +681,13 @@ blk_status_t btrfs_csum_one_bio(struct btrfs_inode *inode, struct bio *bio, shash->tfm = fs_info->csum_shash; bio_for_each_segment(bvec, bio, iter) { - if (use_page_offsets) - offset = page_offset(bvec.bv_page) + bvec.bv_offset; + if (use_page_offsets) { + struct page *page = bvec.bv_page; + + if (fscrypt_is_bounce_page(page)) + page = fscrypt_pagecache_page(page); + offset = page_offset(page) + bvec.bv_offset; + } if (!ordered) { ordered = btrfs_lookup_ordered_extent(inode, offset); diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index 2c9f2d84b539..661fe7cc119c 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -190,7 +190,38 @@ static int btrfs_fscrypt_get_extent_context(const struct inode *inode, size_t *extent_offset, size_t *extent_length) { - return len; + u64 offset = lblk_num << inode->i_blkbits; + struct extent_map *em; + int ret; + + /* 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; + } + + ret = ctx ? em->fscrypt_context.len : 0; + + if (ctx) + memcpy(ctx, em->fscrypt_context.buffer, + em->fscrypt_context.len); + + 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 ret; } static int btrfs_fscrypt_set_extent_context(void *extent, void *ctx, diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index e72c8176f7bc..84bdd6ca3d2b 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -276,9 +276,14 @@ static int check_extent_data_item(struct extent_buffer *leaf, return -EUCLEAN; } - /* Compressed inline extent has no on-disk size, skip it */ - if (btrfs_file_extent_compression(leaf, fi) != - BTRFS_COMPRESS_NONE) + /* + * Compressed inline extent has no on-disk size; encrypted has + * variable size; skip them + */ + if ((btrfs_file_extent_compression(leaf, fi) != + BTRFS_COMPRESS_NONE) || + (btrfs_file_extent_encryption(leaf, fi) != + BTRFS_ENCRYPTION_NONE)) return 0; /* Uncompressed inline extent size must match item size */ From patchwork Wed Nov 2 11:53:02 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13028046 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 D4BA8C4167D for ; Wed, 2 Nov 2022 11:54:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230440AbiKBLyF (ORCPT ); Wed, 2 Nov 2022 07:54:05 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49530 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230402AbiKBLxe (ORCPT ); Wed, 2 Nov 2022 07:53:34 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 95003BD1; Wed, 2 Nov 2022 04:53:33 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id CC73980237; Wed, 2 Nov 2022 07:53:32 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1667390013; bh=fBW5/zONytD5ZLev5PXGwSdh7O/fMCNQZDREBak2ZmI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=tc6u3qgcd9i6yL56kpv2l4haLg1mrYpWkBI7G9Q4V1qNo83Mk0TSUtgRO/kSuIseF U0DorQP6m4kobPQqqZkvEJQYn0Xxxc2VFmwzdCuAWaLWSt5dhvVBK+noHtf008zvIm ZHHbBjEDFwwPPiTB2WbniOWEWbDnSuQp6dF1sVFLulsHaR7a+4wYD+/dN3RH8myp7I g8uVuwDdXKXVd1lCW4YWtbnh0p5rabdaFX5cv+5hvxRRE/6MTUp+mggbT50uP57vfd f4wKmIJU2De5xyOu7XpUbhPHExxHebIw2/kbxHU2bqovXX+k6TltKtIKZyDzREoDbI qJ6fX5z2B7Tbw== From: Sweet Tea Dorminy To: "Theodore Y. Ts'o" , Jaegeuk Kim , Eric Biggers , Chris Mason , Josef Bacik , David Sterba , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@meta.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v5 13/18] btrfs: Add new FEATURE_INCOMPAT_ENCRYPT feature flag. Date: Wed, 2 Nov 2022 07:53:02 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Omar Sandoval 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 | 5 +++-- fs/btrfs/super.c | 5 +++++ include/uapi/linux/btrfs.h | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h index c7f2a512fba2..b12146d34dc5 100644 --- a/fs/btrfs/fs.h +++ b/fs/btrfs/fs.h @@ -185,7 +185,7 @@ enum { #ifdef CONFIG_BTRFS_DEBUG /* - * Extent tree v2 supported only with CONFIG_BTRFS_DEBUG + * Extent tree v2 and encryption supported only with CONFIG_BTRFS_DEBUG */ #define BTRFS_FEATURE_INCOMPAT_SUPP \ (BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF | \ @@ -201,7 +201,8 @@ enum { BTRFS_FEATURE_INCOMPAT_METADATA_UUID | \ BTRFS_FEATURE_INCOMPAT_RAID1C34 | \ BTRFS_FEATURE_INCOMPAT_ZONED | \ - BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2) + BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2 | \ + BTRFS_FEATURE_INCOMPAT_ENCRYPT) #else #define BTRFS_FEATURE_INCOMPAT_SUPP \ (BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF | \ diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 1b32103b14d5..a1e6b2446749 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -2411,6 +2411,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, crc32c=%s%s\n", crc32c_impl(), options); diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index 5655e89b962b..1d29f0df995b 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -316,6 +316,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 Wed Nov 2 11:53:03 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13028050 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 5434BC3A5A1 for ; Wed, 2 Nov 2022 11:54:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230456AbiKBLyG (ORCPT ); Wed, 2 Nov 2022 07:54:06 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49644 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230373AbiKBLxg (ORCPT ); Wed, 2 Nov 2022 07:53:36 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 998DDBD1; Wed, 2 Nov 2022 04:53:35 -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 0B2C781462; Wed, 2 Nov 2022 07:53:34 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1667390015; bh=Jzf2meALQNwbu6QzJ4+dSLFPhD/DJS2tyJYuVKeMWWQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=UTM7ZFyGslVlRBtooHK3AFRm6XB8Omg9S1Q8dpW7hO90eSEGx6tp7k3CpIxNw7msJ R+H4mbyw9eFAr8jrPvyjIUZg8vMLszODtwcuTFk+E0tz0U7EZXInxhu+fKYPxlalZW 1u+P+ip4A7tPvPRD8CzD9F9nCNlDFHzydGyQ3dtRgX0yHPAo7EXcIoHlH2EpADtEaL ei0n8ccDxH/XHkZt5wRzj5+EMjmoyIKfzoCuH/+7c7kPH9ox/lx5GCKVrzt4tQWesu GdoDrrfYG9T7BpGN1IzG4UYCkNQsMeBLFB/o55lzqwuLqwWt3mAaBLwp9j+Nj1uh0g gKJPF4ShZikVw== From: Sweet Tea Dorminy To: "Theodore Y. Ts'o" , Jaegeuk Kim , Eric Biggers , Chris Mason , Josef Bacik , David Sterba , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@meta.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v5 14/18] btrfs: implement fscrypt ioctls Date: Wed, 2 Nov 2022 07:53:03 -0400 Message-Id: <83b95b5a864fa17384b21b26d080813151f9cb6c.1667389115.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Omar Sandoval 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 a40025e18216..9869a26e36ad 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4557,6 +4557,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 Wed Nov 2 11:53:04 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13028052 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 6BD0AC4321E for ; Wed, 2 Nov 2022 11:55:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230026AbiKBLyg (ORCPT ); Wed, 2 Nov 2022 07:54:36 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51262 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230483AbiKBLyK (ORCPT ); Wed, 2 Nov 2022 07:54:10 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BF32629367; Wed, 2 Nov 2022 04:54: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 EDD0780237; Wed, 2 Nov 2022 07:53:36 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1667390017; bh=1CKVFk63rf0E58jyUQYltmVJY3bR1jFgfinnjnNYCUw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=f7yA6+RZWSh7cNviDRQseglnHNTA2fzepoFOSsL1dTTePOkeTkX843ZLAcojiIdZa 2DnoUU+8DBHordVB6i4NZuat0Px/bGx1g4QAkMuH84tLrfOd6SUwP0CNjouEGdX/vy mqwBHtJnWibXYp6OGYD+Yky7C6G0gGJ8TsknWT2W0hY+Rp4fsCN2Op9wZXtr5g4z6R EZULs3qCBy2ubdQGAeOQKHK/OoaXbfOCHFXwFkNJ1NBooQpLuRcXYhQ4rgithWBDNX u/HO9hx+/DaZWbeZ+UxHWlE3HqofXC8dDm4nm6+zQRvJgCMACOXo4ri0KnQCK8I86U Nc7Q4AT+dP4Ow== From: Sweet Tea Dorminy To: "Theodore Y. Ts'o" , Jaegeuk Kim , Eric Biggers , Chris Mason , Josef Bacik , David Sterba , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@meta.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v5 15/18] btrfs: permit searching for nokey names for removal Date: Wed, 2 Nov 2022 07:53:04 -0400 Message-Id: <8f877d65a661708ec56727dabb940644b21809e8.1667389115.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org From: Omar Sandoval Deleting an encrypted file must always be permitted, even if the user does not have the appropriate key. Therefore, for listing an encrypted directory, so-called 'nokey' names are provided, and these nokey names must be sufficient to look up and delete the appropriate encrypted files. See 'struct fscrypt_nokey_name' for more information on the format of these names. The first part of supporting nokey names is allowing lookups by nokey name. Only a few entry points need to support these: deleting a directory, file, or subvolume -- each of these call fscrypt_setup_filename() with a '1' argument, indicating that the key is not required and therefore a nokey name may be provided. If a nokey name is provided, the fscrypt_name returned by fscrypt_setup_filename() will not have its disk_name field populated, but will have various other fields set. This change alters the relevant codepaths to pass a complete fscrypt_name anywhere that it might contain a nokey name. When it does contain a nokey name, the first time the name is successfully matched to a stored name populates the disk name field of the fscrypt_name, allowing the caller to use the normal disk name codepaths afterward. Otherwise, the matching functionality is in close analogue to the function fscrypt_match_name(). Functions where most callers are providing a fscrypt_str are duplicated and adapted for a fscrypt_name, and functions where most callers are providing a fscrypt_name are changed to so require at all callsites. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/btrfs_inode.h | 2 +- fs/btrfs/delayed-inode.c | 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 | 47 ++++++++++++- fs/btrfs/fscrypt.h | 19 ++++++ fs/btrfs/inode.c | 141 ++++++++++++++++++++++++++------------- fs/btrfs/root-tree.c | 8 ++- fs/btrfs/root-tree.h | 2 +- fs/btrfs/tree-log.c | 3 +- 13 files changed, 319 insertions(+), 68 deletions(-) diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 32fa68946f07..f0935a95ec70 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -435,7 +435,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 c024f97de9e0..f775e1111d3e 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..ce046cdabbe9 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; + 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 || (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 97aaa74c4822..a0450e4401e9 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -5329,6 +5329,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 a5ec1475988f..0a82604b0feb 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -203,6 +203,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 661fe7cc119c..6f4c843720c1 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -5,12 +5,57 @@ #include "accessors.h" #include "btrfs_inode.h" #include "disk-io.h" +#include "ioctl.h" #include "fs.h" #include "fscrypt.h" #include "messages.h" #include "transaction.h" #include "xattr.h" -#include "fscrypt.h" +#include "root-tree.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) { diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h index 86dc0e0b91b9..65c082e91f65 100644 --- a/fs/btrfs/fscrypt.h +++ b/fs/btrfs/fscrypt.h @@ -4,6 +4,7 @@ #define BTRFS_FSCRYPT_H #include +#include "extent_io.h" #define BTRFS_ENCRYPTION_POLICY_BITS 2 #define BTRFS_ENCRYPTION_CTXSIZE_BITS 6 @@ -24,6 +25,24 @@ struct btrfs_fscrypt_extent_context { }; #endif +#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 */ + static inline void btrfs_unpack_encryption(u8 encryption, u8 *policy, u8 *ctxsize) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 332900b41488..236f6e91b440 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4319,7 +4319,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; @@ -4337,7 +4337,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; @@ -4365,11 +4365,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; } @@ -4390,8 +4393,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); } /* @@ -4409,7 +4414,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); @@ -4422,7 +4427,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; @@ -4480,7 +4485,7 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry) 0); ret = btrfs_unlink_inode(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)), - &fname.disk_name); + &fname); if (ret) goto out; @@ -4516,8 +4521,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) { @@ -4534,8 +4537,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; @@ -4561,7 +4564,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; @@ -4578,7 +4581,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; @@ -4881,8 +4884,6 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) if (err) return err; - /* This needs to handle no-key deletions later on */ - trans = __unlink_start_trans(dir); if (IS_ERR(trans)) { err = PTR_ERR(trans); @@ -4902,7 +4903,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); /* @@ -5595,27 +5596,20 @@ 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 inode *dir, struct dentry *dentry, +static int btrfs_inode_by_name(struct 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 = BTRFS_I(dir)->root; int ret = 0; - struct fscrypt_name fname; path = btrfs_alloc_path(); if (!path) return -ENOMEM; - ret = fscrypt_setup_filename(dir, &dentry->d_name, 1, &fname); - if (ret) - goto out; - - /* This needs to handle no-key deletions later on */ - - di = btrfs_lookup_dir_item(NULL, root, path, btrfs_ino(BTRFS_I(dir)), - &fname.disk_name, 0); + di = btrfs_lookup_dir_item_fname(NULL, root, path, + btrfs_ino(BTRFS_I(dir)), fname, 0); if (IS_ERR_OR_NULL(di)) { ret = di ? PTR_ERR(di) : -ENOENT; goto out; @@ -5627,13 +5621,13 @@ static int btrfs_inode_by_name(struct 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(BTRFS_I(dir)), - location->objectid, location->type, location->offset); + __func__, fname->usr_fname->name, + btrfs_ino(BTRFS_I(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; } @@ -5905,13 +5899,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(dir, dentry, &location, &di_type); + ret = fscrypt_prepare_lookup(dir, dentry, &fname); + if (ret) + return ERR_PTR(ret); + + ret = btrfs_inode_by_name(dir, &fname, &location, &di_type); if (ret < 0) return ERR_PTR(ret); @@ -6052,18 +6051,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_FSCRYPT_CONTEXT) { + 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; @@ -6081,6 +6094,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; @@ -6092,8 +6106,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) @@ -6107,8 +6126,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); @@ -6128,7 +6175,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; @@ -6159,6 +6207,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; } @@ -6688,6 +6738,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)); @@ -6745,7 +6796,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) { @@ -9321,7 +9372,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)); } @@ -9336,7 +9387,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)); } @@ -9583,7 +9634,7 @@ static int btrfs_rename(struct user_namespace *mnt_userns, } 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)); } @@ -9602,7 +9653,7 @@ static int btrfs_rename(struct user_namespace *mnt_userns, } 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 9c211802efb1..331f35972d77 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -925,9 +925,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 Wed Nov 2 11:53:05 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13028047 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 0F9C2C38A2B for ; Wed, 2 Nov 2022 11:54:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230458AbiKBLyG (ORCPT ); Wed, 2 Nov 2022 07:54:06 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49852 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230414AbiKBLxk (ORCPT ); Wed, 2 Nov 2022 07:53:40 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DBB86BD1; Wed, 2 Nov 2022 04:53:39 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 523A081462; Wed, 2 Nov 2022 07:53:39 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1667390019; bh=3UxUdaE8Y3AyL6HiYBztrGBNltkcHDrtYHCOHGCq7MM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lVca+Uocac4jRQmAsOcPgVRVxNLfHMmpAtoPD7nn/lEWCadlLA/2TZxWCgybqUU3p 6CtUkTizFzcnSsIQ+ldHR5Ox2pIwc0qJ8Usy0PkhU1RC1NMoCL/7DFWPJ/I5xhjmUM oYs5kr7u/9r9NYM4ZFvdYyBD1ztCInIq5tzdb/PehvYaCBDO1ulxqZmxYmymLDoE0k tnq3nDiE6F6UBDezcprkLLJQmx+8O/FLpNccSWTWaNEY5FqUGK7YXu2sEcU0WCyoRN gSfifJgUMrHhAychPwrN3B3ImkcSKbyYhPPvTcK6VM66VxDS9EgF86E+E69FSgQFxr en6g33tY8GHiA== From: Sweet Tea Dorminy To: "Theodore Y. Ts'o" , Jaegeuk Kim , Eric Biggers , Chris Mason , Josef Bacik , David Sterba , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@meta.com Cc: Sweet Tea Dorminy Subject: [PATCH v5 16/18] btrfs: use correct name hash for nokey names Date: Wed, 2 Nov 2022 07:53:05 -0400 Message-Id: <0c127efa18553d7aec7cfdc854800a7b22fe3306.1667389116.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org 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 ce046cdabbe9..70cba4003efa 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 Wed Nov 2 11:53:06 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13028049 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 63094C47088 for ; Wed, 2 Nov 2022 11:54:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230463AbiKBLyH (ORCPT ); Wed, 2 Nov 2022 07:54:07 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49974 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230416AbiKBLxn (ORCPT ); Wed, 2 Nov 2022 07:53:43 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EFA4DBD1; Wed, 2 Nov 2022 04:53:41 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 32FB681472; Wed, 2 Nov 2022 07:53:41 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1667390021; bh=IQRToKMmjUM+53ysQVVAJFNybuNa5MMWtNOLXU7bfFo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IHwVL8SIwsVVjN8x3mnvY6H51b4h6dotCyOE5K7IKz0OBFgYXHbl+YIX2O0nMwV2L IXlpipTK4j0WvXot3ovsGHOomkuv1z4xsRciyKs56VWK6pIyiYZ/QBWGmEFWQMVavD orVMd2CRnRjh/vvhRAncroNNcz4pYod9XC7WQg40np3GXzfqxWvAN3cXfaHx724wb3 A8dmY7Jsvr68m7Ki7Jv3gqkIT2xZtmtDt3gpudbvZKOHXQjye5ZQMvLozTqrDrzVOE kgIXjGzOiFtTOToCFUH4X3bxPntWqJGIBkmjvNMyYuG6BGUGuDTu6eZDgczUr9Z3+B jI8PYS3y0+Urw== From: Sweet Tea Dorminy To: "Theodore Y. Ts'o" , Jaegeuk Kim , Eric Biggers , Chris Mason , Josef Bacik , David Sterba , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@meta.com Cc: Sweet Tea Dorminy Subject: [PATCH v5 17/18] btrfs: encrypt verity items Date: Wed, 2 Nov 2022 07:53:06 -0400 Message-Id: <746be0f68a5a9e34ae82d6484daac02826104b74.1667389116.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org Verity items are deemed to have sensitive information about the file contents, so verity items for a encrypted file should be encrypted. In order to make sure there are extent contexts with which to encrypt, encrypted verity items wait for the file data to be written before writing the verity items; it may be better to store a new fscrypt extent context with verity items. Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/verity.c | 124 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 109 insertions(+), 15 deletions(-) diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c index bf9eb693a6a7..c5784994c03e 100644 --- a/fs/btrfs/verity.c +++ b/fs/btrfs/verity.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include "messages.h" @@ -224,14 +225,52 @@ static int write_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset, struct btrfs_key key; unsigned long copy_bytes; unsigned long src_offset = 0; - void *data; + void *data_pos; int ret = 0; +#ifdef CONFIG_FS_ENCRYPTION + struct page *ciphertext_page = NULL; + char *ciphertext_buf; + + if (IS_ENCRYPTED(&inode->vfs_inode)) { + ciphertext_page = alloc_page(GFP_NOFS); + if (!ciphertext_page) + return -ENOMEM; + ciphertext_buf = kmap_local_page(ciphertext_page); + } +#endif /* CONFIG_FS_ENCRYPTION */ path = btrfs_alloc_path(); if (!path) return -ENOMEM; while (len > 0) { + const char *data = src + src_offset; + /* + * Insert 2K at a time mostly to be friendly for smaller leaf + * size filesystems + */ + copy_bytes = min_t(u64, len, 2048); + +#ifdef CONFIG_FS_ENCRYPTION + if (ciphertext_page) { + struct btrfs_fs_info *fs_info = inode->root->fs_info; + u64 lblk_num = offset >> fs_info->sectorsize_bits; + + memset(ciphertext_buf, 0, PAGE_SIZE); + memcpy(ciphertext_buf, data, copy_bytes); + copy_bytes = ALIGN(copy_bytes, + FSCRYPT_CONTENTS_ALIGNMENT); + ret = fscrypt_encrypt_block_inplace(&inode->vfs_inode, + ciphertext_page, + copy_bytes, 0, + lblk_num, + GFP_NOFS); + if (ret) + break; + data = ciphertext_buf; + } +#endif /* CONFIG_FS_ENCRYPTION */ + /* 1 for the new item being inserted */ trans = btrfs_start_transaction(root, 1); if (IS_ERR(trans)) { @@ -243,12 +282,6 @@ static int write_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset, key.type = key_type; key.offset = offset; - /* - * Insert 2K at a time mostly to be friendly for smaller leaf - * size filesystems - */ - copy_bytes = min_t(u64, len, 2048); - ret = btrfs_insert_empty_item(trans, root, path, &key, copy_bytes); if (ret) { btrfs_end_transaction(trans); @@ -257,18 +290,25 @@ static int write_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset, leaf = path->nodes[0]; - data = btrfs_item_ptr(leaf, path->slots[0], void); - write_extent_buffer(leaf, src + src_offset, - (unsigned long)data, copy_bytes); + data_pos = btrfs_item_ptr(leaf, path->slots[0], void); + write_extent_buffer(leaf, data, + (unsigned long)data_pos, copy_bytes); offset += copy_bytes; src_offset += copy_bytes; - len -= copy_bytes; + len -= min_t(u64, copy_bytes, len); btrfs_release_path(path); btrfs_end_transaction(trans); } btrfs_free_path(path); +#ifdef CONFIG_FS_ENCRYPTION + if (ciphertext_page) { + kunmap_local(ciphertext_buf); + __free_page(ciphertext_page); + } +#endif /* CONFIG_FS_ENCRYPTION */ + return ret; } @@ -310,6 +350,17 @@ static int read_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset, void *data; char *kaddr = dest; int ret; +#ifdef CONFIG_FS_ENCRYPTION + char *ciphertext_buf; + struct page *ciphertext_page = NULL; + + if (dest && IS_ENCRYPTED(&inode->vfs_inode)) { + ciphertext_page = alloc_page(GFP_NOFS); + if (!ciphertext_page) + return -ENOMEM; + ciphertext_buf = kmap_local_page(ciphertext_page); + } +#endif /* CONFIG_FS_ENCRYPTION */ path = btrfs_alloc_path(); if (!path) @@ -371,14 +422,41 @@ static int read_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset, /* Offset from the start of item for copying */ copy_offset = offset - key.offset; + data = btrfs_item_ptr(leaf, path->slots[0], void); if (dest) { +#ifdef CONFIG_FS_ENCRYPTION + if (ciphertext_page) { + struct btrfs_fs_info *fs_info = + inode->root->fs_info; + u64 lblk_num = offset >> fs_info->sectorsize_bits; + + read_extent_buffer(leaf, ciphertext_buf, + (unsigned long)data + copy_offset, + item_end - offset); + ret = fscrypt_decrypt_block_inplace(&inode->vfs_inode, + ciphertext_page, + item_end - offset, 0, + lblk_num); + if (ret) + break; + } +#endif /* CONFIG_FS_ENCRYPTION */ + if (dest_page) kaddr = kmap_local_page(dest_page); - data = btrfs_item_ptr(leaf, path->slots[0], void); - read_extent_buffer(leaf, kaddr + dest_offset, - (unsigned long)data + copy_offset, - copy_bytes); + if (IS_ENABLED(CONFIG_FS_ENCRYPTION) && + IS_ENCRYPTED(&inode->vfs_inode)) { +#ifdef CONFIG_FS_ENCRYPTION + memcpy(kaddr + dest_offset, + ciphertext_buf + copy_offset, + copy_bytes); +#endif /* CONFIG_FS_ENCRYPTION */ + } else { + read_extent_buffer(leaf, kaddr + dest_offset, + (unsigned long)data + copy_offset, + copy_bytes); + } if (dest_page) kunmap_local(kaddr); @@ -405,6 +483,12 @@ static int read_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset, } } out: +#ifdef CONFIG_FS_ENCRYPTION + if (ciphertext_page) { + kunmap_local(ciphertext_buf); + __free_page(ciphertext_page); + } +#endif /* CONFIG_FS_ENCRYPTION */ btrfs_free_path(path); if (!ret) ret = copied; @@ -601,6 +685,16 @@ static int btrfs_begin_enable_verity(struct file *filp) if (ret) return ret; + if (IS_ENCRYPTED(file_inode(filp))) { + /* + * Make sure the data has been written (so that we can reuse + * its encryption info to encrypt verity items). + */ + ret = btrfs_wait_ordered_range(file_inode(filp), 0, (u64)-1); + if (ret) + return ret; + } + /* 1 for the orphan item */ trans = btrfs_start_transaction(root, 1); if (IS_ERR(trans)) From patchwork Wed Nov 2 11:53:07 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sweet Tea Dorminy X-Patchwork-Id: 13028048 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 722DAC3A5A3 for ; Wed, 2 Nov 2022 11:54:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230474AbiKBLyI (ORCPT ); Wed, 2 Nov 2022 07:54:08 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50062 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230424AbiKBLxo (ORCPT ); Wed, 2 Nov 2022 07:53:44 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0E247BD1; Wed, 2 Nov 2022 04:53:43 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 43AAB81462; Wed, 2 Nov 2022 07:53:43 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1667390023; bh=Y7i/Jzp5z12W/F5OGHJJyvkneUKSA1fRLs5m/r5eQvE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=urXc+jE9JYnppE2uCMrid+NGu6M1/xiPqSs0dqnlXCfAxjkuGjoVj5bIevLo9cOxC ZJmBq4QkcKn+Npjrxbzn1E1Um2fPFwy8GIlXZXtYhWHqwbHSKT10z6RvMzq8ank+AJ FW0hJKC+MhZaMW0DYP7uAUBEcsmwImoUurhXQ3cBuL+IEdip4GcWUHU0iNmq8FV1NR NTAsV4Y/B8wtAK6ujpSUXumPpSe5534oOC11+BqN8BGMYYtfOe8jJbwQpeDmw1BVmi 9ZGfj/+FItHxy0GlQrAW3xhq8KCAk8jBp4FJBGDoP9/DFKCpnvDOjFlG+5r2W5gm4z A2+YXObNnYAtg== From: Sweet Tea Dorminy To: "Theodore Y. Ts'o" , Jaegeuk Kim , Eric Biggers , Chris Mason , Josef Bacik , David Sterba , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@meta.com Cc: Sweet Tea Dorminy Subject: [PATCH v5 18/18] btrfs: allow encrypting compressed extents Date: Wed, 2 Nov 2022 07:53:07 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org Conveniently, compressed extents are already padded to sector size, so they can be encrypted in-place (which requires 16-byte alignment). Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/btrfs_inode.h | 3 --- fs/btrfs/compression.c | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index f0935a95ec70..d7f2b9a3d42b 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -385,9 +385,6 @@ static inline bool btrfs_inode_in_log(struct btrfs_inode *inode, u64 generation) */ static inline bool btrfs_inode_can_compress(const struct btrfs_inode *inode) { - if (IS_ENCRYPTED(&inode->vfs_inode)) - return false; - if (inode->flags & BTRFS_INODE_NODATACOW || inode->flags & BTRFS_INODE_NODATASUM) return false; diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index 52df6c06cc91..038721a66414 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -202,6 +202,16 @@ static void end_compressed_bio_read(struct btrfs_bio *bbio) status = errno_to_blk_status(ret); } } + if (fscrypt_inode_uses_fs_layer_crypto(inode)) { + int err; + u64 lblk_num = start >> fs_info->sectorsize_bits; + err = fscrypt_decrypt_block_inplace(inode, bv.bv_page, + fs_info->sectorsize, + bv.bv_offset, + lblk_num); + if (err) + status = errno_to_blk_status(err); + } } if (status) @@ -451,6 +461,19 @@ blk_status_t btrfs_submit_compressed_write(struct btrfs_inode *inode, u64 start, real_size = min_t(u64, real_size, compressed_len - offset); ASSERT(IS_ALIGNED(real_size, fs_info->sectorsize)); + if (fscrypt_inode_uses_fs_layer_crypto(&inode->vfs_inode)) { + int err; + u64 lblk_num = start >> fs_info->sectorsize_bits; + + err = fscrypt_encrypt_block_inplace(&inode->vfs_inode, + page, real_size, 0, + lblk_num, GFP_NOFS); + if (err) { + ret = errno_to_blk_status(err); + break; + } + } + if (use_append) added = bio_add_zone_append_page(bio, page, real_size, offset_in_page(offset));