From patchwork Wed Aug 17 14:49:45 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: 12946017 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 12E61C3F6B0 for ; Wed, 17 Aug 2022 14:50:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236768AbiHQOuk (ORCPT ); Wed, 17 Aug 2022 10:50:40 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36360 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237192AbiHQOuW (ORCPT ); Wed, 17 Aug 2022 10:50:22 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1A4DE1C907; Wed, 17 Aug 2022 07:50: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 485F280A9D; Wed, 17 Aug 2022 10:50:20 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1660747820; bh=6ttS17HMX9jnRHmi2B73jJMPx8T5zrFXGQxy/qaXLVw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=gAEOwZifAm3PoUnzwB8GTVp0Sd/pXYjnEOihZCv3n66aC8quuPvFIyn+QgdsY2UyD N3Ulq9Aomst7b9HuDF72DU/tzfOaraAFBOc3e+SEv8QM71Yquxo70W16Seuym1zITY ab4+3OylZnaVi4+uDSY64ev/ezVraZ15BmrXV5Y43fRsaQCB5yTMLY919Tht9SbHlH sMqwRnowzLOAAWRGS4l6yFQu9i2i0LcTJmt+M1my59ilOAeRkK+Wt3K5zSvxcFuVhH 9yzurBOTYLk/jBxf67CUqPFOR5NjB8CXyaiXUZtmJtKv/ht5SzPMF7cGRqEXsQ3SEG m0iy4cKZPAxYg== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o " , Jaegeuk Kim , Eric Biggers , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@fb.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH 01/21] fscrypt: expose fscrypt_nokey_name Date: Wed, 17 Aug 2022 10:49:45 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Omar Sandoval 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 --- 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 14e0ef5e9a20..5d5c26d827fd 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 e60d57c99cb6..6020b738c3b2 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 Aug 17 14:49:46 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: 12946018 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 D6D15C32771 for ; Wed, 17 Aug 2022 14:50:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240305AbiHQOul (ORCPT ); Wed, 17 Aug 2022 10:50:41 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36376 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240200AbiHQOuY (ORCPT ); Wed, 17 Aug 2022 10:50:24 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 14BFC33A26; Wed, 17 Aug 2022 07:50: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 7F54F80B43; Wed, 17 Aug 2022 10:50:22 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1660747822; bh=GtmYH3TgxqB5l7PKC/VM40EVlreaKDd+6X5hNidwOXM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=bUevRMfxzvl5Hi9ZpHRcO+kGEC1ApvsLrhPzPJ8WHAwpQCboSTMPnP1/XkseClEuy P4jFU8Jl1wsxYnBTphhzT6YSc/pSMhgw3rpXgTn0fhHY8F3t90VjbRJSKZu/qCD1SW n4kPwbWKWvysaWsaDwxwOUAKtdp+Q2ZXyy1Lt+vjK9UpnAysCUBMwrMTqYc+vurfqN Z8phT9nPg9bjBAyrHAcPp3k7hJBki6rb8axcIXy9dgGdy55Ak3o9E17YEtFHfk4ycH RTyqNywGS1hGbOGWGx7242LgbmghHcL/s3FAS5z2MIzuJ2RQWkxH6ALxmEruQTjG3o TxB2F7A/cCZVg== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o " , Jaegeuk Kim , Eric Biggers , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@fb.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH 02/21] fscrypt: add flag allowing partially-encrypted directories Date: Wed, 17 Aug 2022 10:49:46 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Omar Sandoval Creating several new subvolumes out of snapshots of another subvolume, each for a different VM's storage, is a important usecase for btrfs. We would like to give each VM a unique encryption key to use for new writes to its subvolume, so that secure deletion of the VM's data is as simple as securely deleting the key; to avoid needing multiple keys in each VM, we envision the initial subvolume being unencrypted. However, this means that the snapshot's directories would have a mix of encrypted and unencrypted files. During lookup with a key, both unencrypted and encrypted forms of the desired name must be queried. To allow this, add another FS_CFLG to allow filesystems to opt into partially encrypted directories. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/crypto/fname.c | 17 ++++++++++++++++- include/linux/fscrypt.h | 2 ++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 5d5c26d827fd..c5dd19c1d19e 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -389,6 +389,7 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, fname->usr_fname = iname; if (!IS_ENCRYPTED(dir) || fscrypt_is_dot_dotdot(iname)) { +unencrypted: fname->disk_name.name = (unsigned char *)iname->name; fname->disk_name.len = iname->len; return 0; @@ -424,8 +425,16 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, * user-supplied name */ - if (iname->len > FSCRYPT_NOKEY_NAME_MAX_ENCODED) + if (iname->len > FSCRYPT_NOKEY_NAME_MAX_ENCODED) { + /* + * This isn't a valid nokey name, but it could be an unencrypted + * name if the filesystem allows partially encrypted + * directories. + */ + if (dir->i_sb->s_cop->flags & FS_CFLG_ALLOW_PARTIAL) + goto unencrypted; return -ENOENT; + } fname->crypto_buf.name = kmalloc(FSCRYPT_NOKEY_NAME_MAX, GFP_KERNEL); if (fname->crypto_buf.name == NULL) @@ -436,6 +445,12 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, if (ret < (int)offsetof(struct fscrypt_nokey_name, bytes[1]) || (ret > offsetof(struct fscrypt_nokey_name, sha256) && ret != FSCRYPT_NOKEY_NAME_MAX)) { + /* Again, this could be an unencrypted name. */ + if (dir->i_sb->s_cop->flags & FS_CFLG_ALLOW_PARTIAL) { + kfree(fname->crypto_buf.name); + fname->crypto_buf.name = NULL; + goto unencrypted; + } ret = -ENOENT; goto errout; } diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 6020b738c3b2..fb48961c46f6 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -102,6 +102,8 @@ struct fscrypt_nokey_name { * pages for writes and therefore won't need the fscrypt bounce page pool. */ #define FS_CFLG_OWN_PAGES (1U << 1) +/* The filesystem allows partially encrypted directories/files. */ +#define FS_CFLG_ALLOW_PARTIAL (1U << 2) /* Crypto operations for filesystems */ struct fscrypt_operations { From patchwork Wed Aug 17 14:49:47 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: 12946019 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 7207CC32772 for ; Wed, 17 Aug 2022 14:50:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237192AbiHQOum (ORCPT ); Wed, 17 Aug 2022 10:50:42 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36382 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240255AbiHQOuZ (ORCPT ); Wed, 17 Aug 2022 10:50:25 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 206D61C907; Wed, 17 Aug 2022 07:50: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 70BAC81140; Wed, 17 Aug 2022 10:50:24 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1660747824; bh=yY8S8o2qAHPyyq6T2MD0XGq4HDMU4SIfT6mesD4Qhxg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YQ7CUKaB/iRTBvh5tCjoqJzFft0Lbc/6QdGSvVJDRzz2ljnOtChqBSa9mkZqb2zyO hBV74jIWJs5ByMqDaXymG7uos0dt4sSciylsm96VG3siHitb9hrH0Vj17knacwo5fT 7Sb5CmETuR17cvM9ehvD511dytd3G6w/uYZrcfdcraSfQaFJcbDv7Qno0whRwiGn76 vrVRETyYAvjKxUyV8TqRXJo9VJ3hVGKdtfV6qhHP9irkP8nsxtxLH1A+QjgnVZ7uk+ BwUdxKeQtVHXtc7syWcIBhyUb+C8GBLPNSdUbwvqb2vei7DHlARDtfTE2arexfWULb WLxBxkujxqOmA== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o " , Jaegeuk Kim , Eric Biggers , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@fb.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH 03/21] fscrypt: add fscrypt_have_same_policy() to check inode's compatibility Date: Wed, 17 Aug 2022 10:49:47 -0400 Message-Id: <0930fb773bcf56f8aacd079ec86a264cc746628a.1660744500.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Omar Sandoval btrfs will have the possibility of encrypted and unencrypted files in the same directory, and it's important to not allow these two files to become linked together. Therefore, add a function which allows checking the encryption policies of two inodes to ensure they are compatible. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/crypto/policy.c | 26 ++++++++++++++++++++++++++ include/linux/fscrypt.h | 1 + 2 files changed, 27 insertions(+) diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 5f858cee1e3b..5763462af9e8 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -407,6 +407,32 @@ 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 + * + * Return: %true if equal, else %false + */ +int fscrypt_have_same_policy(struct inode *inode1, struct inode *inode2) +{ + union fscrypt_policy policy1, policy2; + int err; + + if (!IS_ENCRYPTED(inode1) && !IS_ENCRYPTED(inode2)) + return true; + else if (!IS_ENCRYPTED(inode1) || !IS_ENCRYPTED(inode2)) + return false; + err = fscrypt_get_policy(inode1, &policy1); + if (err) + return err; + err = fscrypt_get_policy(inode2, &policy2); + if (err) + return err; + return fscrypt_policies_equal(&policy1, &policy2); +} +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 fb48961c46f6..1686b25f6d9c 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -318,6 +318,7 @@ 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); 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); From patchwork Wed Aug 17 14:49:48 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: 12946020 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 092CAC32774 for ; Wed, 17 Aug 2022 14:50:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240379AbiHQOun (ORCPT ); Wed, 17 Aug 2022 10:50:43 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36406 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240312AbiHQOu2 (ORCPT ); Wed, 17 Aug 2022 10:50:28 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8FF4933A26; Wed, 17 Aug 2022 07:50:27 -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 E24C88042B; Wed, 17 Aug 2022 10:50:26 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1660747827; bh=JHa6cPgPBqm+c/l427NbSPkp5eYmufffl4SozhKvgaQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HXfA/tBlDmIjYMx7blrpDRIeKa4OoG9RVTZXgCuyMRPhW3ILu0EzLusuAGDriz/vl aAkEdpuQRfRLBxcPfrsasjyuA2Nn7+EjmN5sk4xQ2EtXFu2I1v2NQclXm+ZijiBLsf CfeJLfG2eqBZMsPK77MOlW1gsXz69Io9i0alFHKX4y3PTL11dZ7kc4ajKBsroLGc55 NjjWuXtxF9bpQhiHBhwKryeffDie0Ui4HI8bFKWMcCinL0rDTgAW4hLyggXzdS4ghz UvRjKXz2gqP5Hg/MyV+XsDBqLXCUc9hHnZUD8s5jZNY6o4/sKNkxzeeUAzhx1uTVtt FMyYalr2DP8Tg== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o " , Jaegeuk Kim , Eric Biggers , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@fb.com Cc: Sweet Tea Dorminy Subject: [PATCH 04/21] fscrypt: add a function for a filesystem to generate an IV Date: Wed, 17 Aug 2022 10:49:48 -0400 Message-Id: <40f17d7f64a80e0d2746972b4f6b4b5831d4f455.1660744500.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org Unlike other filesystems, which store all necessary encryption context in the per-inode fscrypt context, btrfs will need to store an IV per extent in order to support snapshots and reflinks. To avoid exposing the internal details of extents to fscrypt, and to centralize IV generation in fscrypt, this change provides fscrypt_generate_random_iv(), which will be called for each newly created btrfs extent and will populate a btrfs-provided buffer with a new IV. Additionally, a function to get the necessary buffer size, fscrypt_mode_ivsize(), is also necessary. Signed-off-by: Sweet Tea Dorminy --- fs/crypto/crypto.c | 26 ++++++++++++++++++++++++++ include/linux/fscrypt.h | 3 +++ 2 files changed, 29 insertions(+) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index e78be66bbf01..e0e30d64837e 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +70,31 @@ void fscrypt_free_bounce_page(struct page *bounce_page) } EXPORT_SYMBOL(fscrypt_free_bounce_page); +int fscrypt_mode_ivsize(struct inode *inode) +{ + struct fscrypt_info *ci; + + if (!fscrypt_needs_contents_encryption(inode)) + return 0; + + ci = inode->i_crypt_info; + if (WARN_ON_ONCE(!ci)) + return 0; + return ci->ci_mode->ivsize; +} +EXPORT_SYMBOL(fscrypt_mode_ivsize); + +/** + * fscrypt_generate_random_iv() - initialize a new iv for an IV_FROM_FS filesystem + * @inode: the inode to which the new IV will belong + * @iv: an output buffer, long enough for the requisite IV + */ +void fscrypt_generate_random_iv(struct inode *inode, u8 *iv) +{ + get_random_bytes(iv, fscrypt_mode_ivsize(inode)); +} +EXPORT_SYMBOL(fscrypt_generate_random_iv); + /* * Generate the IV for the given logical block number within the given file. * For filenames encryption, lblk_num == 0. diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 1686b25f6d9c..ff572f8a88f8 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -317,6 +317,9 @@ static inline struct page *fscrypt_pagecache_page(struct page *bounce_page) void fscrypt_free_bounce_page(struct page *bounce_page); +int fscrypt_mode_ivsize(struct inode *inode); +void fscrypt_generate_random_iv(struct inode *inode, u8 *iv); + /* policy.c */ int fscrypt_have_same_policy(struct inode *inode1, struct inode *inode2); int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg); From patchwork Wed Aug 17 14:49:49 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: 12946021 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 7C765C25B08 for ; Wed, 17 Aug 2022 14:50:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240376AbiHQOun (ORCPT ); Wed, 17 Aug 2022 10:50:43 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36426 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240365AbiHQOua (ORCPT ); Wed, 17 Aug 2022 10:50:30 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 21B0B1C907; Wed, 17 Aug 2022 07:50:29 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 9B63B80A9D; Wed, 17 Aug 2022 10:50:28 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1660747828; bh=7J4FcTMLQuAzhV6/TraBMCLD32c6C2Dmk69Ur4b8XMo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=MXsjWKvFWy0IiuvLSgOQwPaojjnEQiIoj04LN8ws+bFvZquNeQOakhkQohyeHL8O3 Vm+qMquGk1WhwwlqIocWz37fqjF8wyTB7OSMTBaxXn5tGYKFudoOy8SU6wKETY/See DRLFPJe5RQnM8eVoNfsb1lXuDatJTGYeYdcHIqzAh1BoXagO+/7xqLQP+5AKfZBfZO kIn0ChL8XHvozdyC8dg4nu5WrXoXxyjp622sMhoTV9xBSjkwse/uoHsdrltwfWiihC oNNAuZkP+CRwfB/V4kns/hc0MrEOdmcgOfnNESWnNfI+W4vM90MOa8UDPMiYFVUxNF dQJzZ3FSHGtZw== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o " , Jaegeuk Kim , Eric Biggers , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@fb.com Cc: Sweet Tea Dorminy Subject: [PATCH 05/21] fscrypt: add new encryption policy for btrfs. Date: Wed, 17 Aug 2022 10:49:49 -0400 Message-Id: <66fcd64620c0c0711bbe80aa85e92f04d539bd83.1660744500.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org Encryption for btrfs must be extent-based, rather than fscrypt's inode-based IV generation policies. In particular, btrfs can have multiple inodes referencing a single block of data, and moves logical data blocks to different physical locations on disk; these two features mean inode or physical-location-based IV generation policies will not work for btrfs. Instead, btrfs can store an IV per extent, generated by fscrypt and iterated per block within the extent, and provide that IV to fscrypt for encryption/decryption. Plumbing filesystem internals into fscrypt for IV generation would be ungainly and fragile. Thus, this change adds a new policy, IV_FROM_FS, and a new operation function pointer, get_fs_derived_iv. btrfs will require this policy to be used, and implements get_fs_derived_iv by getting the IV stored with the relevant file extent and iterating the IV to the appropriate block number. Thus, each individual block has its own IV, but all blocks within a file extent have iterated IVs according to their block number, similarly to the IV_INO_LBLK* policy iterating IVs for a given inode by lblk number. The IV buffer passed to get_fs_derived_iv() is pre-populated with the inode contexts' nonce, as not all data to be encrypted is associated with a file extent: for btrfs, this is used for filename encryption. Filesystems implementing this policy are expected to be incompatible with existing IV generation policies, so if the function pointer is set, only dummy or IV_FROM_FS policies are permitted. If there is a filesystem which allows other policies as well as IV_FROM_FS, it may be better to expose the policy to filesystems, so they can determine whether any given policy is compatible with their operation. Signed-off-by: Sweet Tea Dorminy --- fs/crypto/crypto.c | 14 ++++++++++++-- fs/crypto/fscrypt_private.h | 4 ++-- fs/crypto/inline_crypt.c | 20 ++++++++++++++------ fs/crypto/keysetup.c | 5 +++++ fs/crypto/policy.c | 22 +++++++++++++++++++++- include/linux/fscrypt.h | 19 ++++++++++++++++++- include/uapi/linux/fscrypt.h | 1 + 7 files changed, 73 insertions(+), 12 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index e0e30d64837e..7194a30c7651 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -107,13 +107,23 @@ 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; memset(iv, 0, ci->ci_mode->ivsize); + if (flags & FSCRYPT_POLICY_FLAG_IV_FROM_FS) { + const struct fscrypt_operations *s_cop = inode->i_sb->s_cop; + /* Provide the nonce in case the filesystem wants to use it */ + memcpy(iv->nonce, ci->ci_nonce, FSCRYPT_FILE_NONCE_SIZE); + s_cop->get_fs_defined_iv(iv->raw, ci->ci_mode->ivsize, inode, + lblk_num); + return; + } + 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 6b4c8094cc7b..084c6ba1e766 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -279,8 +279,6 @@ fscrypt_msg(const struct inode *inode, const char *level, const char *fmt, ...); #define fscrypt_err(inode, 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 */ @@ -326,6 +324,7 @@ int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key, #define HKDF_CONTEXT_DIRHASH_KEY 5 /* info=file_nonce */ #define HKDF_CONTEXT_IV_INO_LBLK_32_KEY 6 /* info=mode_num||fs_uuid */ #define HKDF_CONTEXT_INODE_HASH_KEY 7 /* info= */ +#define HKDF_CONTEXT_IV_FROM_FS_KEY 8 /* info=mode_num */ int fscrypt_hkdf_expand(const struct fscrypt_hkdf *hkdf, u8 context, const u8 *info, unsigned int infolen, @@ -498,6 +497,7 @@ struct fscrypt_master_key { struct fscrypt_prepared_key mk_direct_keys[FSCRYPT_MODE_MAX + 1]; struct fscrypt_prepared_key mk_iv_ino_lblk_64_keys[FSCRYPT_MODE_MAX + 1]; struct fscrypt_prepared_key mk_iv_ino_lblk_32_keys[FSCRYPT_MODE_MAX + 1]; + struct fscrypt_prepared_key mk_iv_from_fs_keys[FSCRYPT_MODE_MAX + 1]; /* Hash key for inode numbers. Initialized only when needed. */ siphash_key_t mk_ino_hash_key; diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 90f3e68f166e..8a8330caadfa 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -476,14 +476,22 @@ 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 (fscrypt_policy_flags(&ci->ci_policy) & + FSCRYPT_POLICY_FLAG_IV_FROM_FS) { + return 1; + } - 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/keysetup.c b/fs/crypto/keysetup.c index c35711896bd4..62b30b567c0d 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -323,6 +323,11 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci, */ err = setup_per_mode_enc_key(ci, mk, mk->mk_direct_keys, HKDF_CONTEXT_DIRECT_KEY, false); + } else if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_FROM_FS) { + err = setup_per_mode_enc_key(ci, mk, + mk->mk_iv_from_fs_keys, + HKDF_CONTEXT_IV_FROM_FS_KEY, + false); } else if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) { /* diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 5763462af9e8..f88db570a3eb 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -178,6 +178,12 @@ static bool fscrypt_supported_v1_policy(const struct fscrypt_policy_v1 *policy, "v1 policies can't be used on casefolded directories"); return false; } + + if (inode->i_sb->s_cop->get_fs_defined_iv) { + fscrypt_warn(inode, + "v1 policies can't be used with this filesystem"); + return false; + } return true; } @@ -199,7 +205,8 @@ static bool fscrypt_supported_v2_policy(const struct fscrypt_policy_v2 *policy, if (policy->flags & ~(FSCRYPT_POLICY_FLAGS_PAD_MASK | FSCRYPT_POLICY_FLAG_DIRECT_KEY | FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 | - FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) { + FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32 | + FSCRYPT_POLICY_FLAG_IV_FROM_FS)) { fscrypt_warn(inode, "Unsupported encryption flags (0x%02x)", policy->flags); return false; @@ -208,6 +215,7 @@ static bool fscrypt_supported_v2_policy(const struct fscrypt_policy_v2 *policy, count += !!(policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY); count += !!(policy->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64); count += !!(policy->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32); + count += !!(policy->flags & FSCRYPT_POLICY_FLAG_IV_FROM_FS); if (count > 1) { fscrypt_warn(inode, "Mutually exclusive encryption flags (0x%02x)", policy->flags); @@ -235,6 +243,18 @@ static bool fscrypt_supported_v2_policy(const struct fscrypt_policy_v2 *policy, 32, 32)) return false; + if ((policy->flags & FSCRYPT_POLICY_FLAG_IV_FROM_FS) && + !inode->i_sb->s_cop->get_fs_defined_iv) + return false; + + /* + * If the filesystem defines its own IVs, presumably it does so because + * no existing policy works, so disallow other policies. + */ + if (inode->i_sb->s_cop->get_fs_defined_iv && + !(policy->flags & FSCRYPT_POLICY_FLAG_IV_FROM_FS)) + return false; + if (memchr_inv(policy->__reserved, 0, sizeof(policy->__reserved))) { fscrypt_warn(inode, "Reserved bits set in encryption policy"); return false; diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index ff572f8a88f8..b3fb88064852 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -94,6 +94,12 @@ struct fscrypt_nokey_name { /* Maximum value for the third parameter of fscrypt_operations.set_context(). */ #define FSCRYPT_SET_CONTEXT_MAX_SIZE 40 +/* + * Maximum size needed for an IV. Defines the size of the buffer passed to a + * get_fs_defined_iv() function. + */ +#define FSCRYPT_MAX_IV_SIZE 32 + #ifdef CONFIG_FS_ENCRYPTION /* @@ -198,7 +204,13 @@ struct fscrypt_operations { */ void (*get_ino_and_lblk_bits)(struct super_block *sb, int *ino_bits_ret, int *lblk_bits_ret); - + /* + * Generate an IV for a given inode and lblk number, for filesystems + * where additional filesystem-internal information is necessary to + * keep the IV stable. + */ + void (*get_fs_defined_iv)(u8 *iv, int ivsize, struct inode *inode, + u64 lblk_num); /* * Return the number of block devices to which the filesystem may write * encrypted file contents. @@ -495,6 +507,11 @@ static inline void fscrypt_free_bounce_page(struct page *bounce_page) { } +static inline int fscrypt_mode_ivsize(struct inode *inode) +{ + return 0; +} + /* policy.c */ static inline int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg) diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h index 9f4428be3e36..3fbde7668b07 100644 --- a/include/uapi/linux/fscrypt.h +++ b/include/uapi/linux/fscrypt.h @@ -20,6 +20,7 @@ #define FSCRYPT_POLICY_FLAG_DIRECT_KEY 0x04 #define FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 0x08 #define FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32 0x10 +#define FSCRYPT_POLICY_FLAG_IV_FROM_FS 0x20 /* Encryption algorithms */ #define FSCRYPT_MODE_AES_256_XTS 1 From patchwork Wed Aug 17 14:49: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: 12946022 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 19711C32792 for ; Wed, 17 Aug 2022 14:50:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240424AbiHQOuo (ORCPT ); Wed, 17 Aug 2022 10:50:44 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36488 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240393AbiHQOud (ORCPT ); Wed, 17 Aug 2022 10:50:33 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B4538870AE; Wed, 17 Aug 2022 07:50: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 723A88042B; Wed, 17 Aug 2022 10:50:30 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1660747830; bh=h4McOA8bEUdl5V31vMSPbzNkIMJ7XHtqRnH+qnwzeYw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NRtONoRjdgh2/rjzQPnVT4m7204C0DzepysTRDPA2D7UDtgv6TUcuNhhG9s5wkGRV aPKcf835DOQensVcQNv1e244ou0g4Ie/u4m+U6dB0OEWsqM4hayD/J8j4T2+p6drF+ w+Ya/qwDWz1hkABncSb9S0xaLhD01kKnflKsIEqfQWlWrFXx0MNmkbIGdLJqckvHbc lpjl+vjY5aFtp7PIC1XK0RHag4PseCAgWNeJZaIEe+wcJkgUxj+mZ28IZiMa9eDTjg dniKmQzpmCh4TYf90cruhxjEoUXa1BtNsZJeY/jMWx4Wop1zTO0pHYzFgvafiYwza1 0U7J09wQ/X51w== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o " , Jaegeuk Kim , Eric Biggers , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@fb.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH 06/21] btrfs: store directorys' encryption state Date: Wed, 17 Aug 2022 10:49:50 -0400 Message-Id: <7b24b0301ba9d3bfc42476835dff2c4a8c0dba2d.1660744500.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Omar Sandoval For directories with encrypted files/filenames, we need to store a flag indicating this fact. There's no room in other fields, so we'll need to borrow a bit from dir_type. Since it's now a combination of type and flags, we rename it to dir_flags to reflect its new usage. The new flag, FT_FSCRYPT, indicates a (perhaps partially) encrypted directory, which is orthogonal to file type; therefore, add the new flag, and make conversion from directory type to file type strip the flag. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/ctree.h | 10 ++++++++-- fs/btrfs/delayed-inode.c | 6 +++--- fs/btrfs/delayed-inode.h | 2 +- fs/btrfs/dir-item.c | 4 ++-- fs/btrfs/inode.c | 15 +++++++++------ fs/btrfs/print-tree.c | 4 ++-- fs/btrfs/send.c | 2 +- fs/btrfs/tree-checker.c | 2 +- fs/btrfs/tree-log.c | 18 +++++++++--------- include/uapi/linux/btrfs_tree.h | 7 +++++++ 10 files changed, 43 insertions(+), 27 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 51c480439263..53d68fe9c355 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -2191,10 +2191,10 @@ BTRFS_SETGET_FUNCS(root_ref_name_len, struct btrfs_root_ref, name_len, 16); /* struct btrfs_dir_item */ BTRFS_SETGET_FUNCS(dir_data_len, struct btrfs_dir_item, data_len, 16); -BTRFS_SETGET_FUNCS(dir_type, struct btrfs_dir_item, type, 8); +BTRFS_SETGET_FUNCS(dir_flags, struct btrfs_dir_item, type, 8); BTRFS_SETGET_FUNCS(dir_name_len, struct btrfs_dir_item, name_len, 16); BTRFS_SETGET_FUNCS(dir_transid, struct btrfs_dir_item, transid, 64); -BTRFS_SETGET_STACK_FUNCS(stack_dir_type, struct btrfs_dir_item, type, 8); +BTRFS_SETGET_STACK_FUNCS(stack_dir_flags, struct btrfs_dir_item, type, 8); BTRFS_SETGET_STACK_FUNCS(stack_dir_data_len, struct btrfs_dir_item, data_len, 16); BTRFS_SETGET_STACK_FUNCS(stack_dir_name_len, struct btrfs_dir_item, @@ -2202,6 +2202,12 @@ BTRFS_SETGET_STACK_FUNCS(stack_dir_name_len, struct btrfs_dir_item, BTRFS_SETGET_STACK_FUNCS(stack_dir_transid, struct btrfs_dir_item, transid, 64); +static inline u8 btrfs_dir_ftype(const struct extent_buffer *eb, + const struct btrfs_dir_item *item) +{ + return btrfs_dir_flags_to_ftype(btrfs_dir_flags(eb, item)); +} + static inline void btrfs_dir_item_key(const struct extent_buffer *eb, const struct btrfs_dir_item *item, struct btrfs_disk_key *key) diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index e7f34871a132..ec13ae9ca332 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -1441,7 +1441,7 @@ void btrfs_balance_delayed_items(struct btrfs_fs_info *fs_info) int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans, const char *name, int name_len, struct btrfs_inode *dir, - struct btrfs_disk_key *disk_key, u8 type, + struct btrfs_disk_key *disk_key, u8 flags, u64 index) { struct btrfs_fs_info *fs_info = trans->fs_info; @@ -1473,7 +1473,7 @@ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans, btrfs_set_stack_dir_transid(dir_item, trans->transid); btrfs_set_stack_dir_data_len(dir_item, 0); btrfs_set_stack_dir_name_len(dir_item, name_len); - btrfs_set_stack_dir_type(dir_item, type); + btrfs_set_stack_dir_flags(dir_item, flags); memcpy((char *)(dir_item + 1), name, name_len); data_len = delayed_item->data_len + sizeof(struct btrfs_item); @@ -1791,7 +1791,7 @@ int btrfs_readdir_delayed_dir_index(struct dir_context *ctx, name = (char *)(di + 1); name_len = btrfs_stack_dir_name_len(di); - d_type = fs_ftype_to_dtype(di->type); + 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, diff --git a/fs/btrfs/delayed-inode.h b/fs/btrfs/delayed-inode.h index 9795dc295a18..c565f15e7af5 100644 --- a/fs/btrfs/delayed-inode.h +++ b/fs/btrfs/delayed-inode.h @@ -99,7 +99,7 @@ static inline void btrfs_init_delayed_root( int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans, const char *name, int name_len, struct btrfs_inode *dir, - struct btrfs_disk_key *disk_key, u8 type, + struct btrfs_disk_key *disk_key, u8 flags, u64 index); int btrfs_delete_delayed_dir_index(struct btrfs_trans_handle *trans, diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c index 72fb2c518a2b..e37b075afa96 100644 --- a/fs/btrfs/dir-item.c +++ b/fs/btrfs/dir-item.c @@ -81,7 +81,7 @@ int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans, leaf = path->nodes[0]; btrfs_cpu_key_to_disk(&disk_key, &location); btrfs_set_dir_item_key(leaf, dir_item, &disk_key); - btrfs_set_dir_type(leaf, dir_item, BTRFS_FT_XATTR); + btrfs_set_dir_flags(leaf, dir_item, BTRFS_FT_XATTR); btrfs_set_dir_name_len(leaf, dir_item, name_len); btrfs_set_dir_transid(leaf, dir_item, trans->transid); btrfs_set_dir_data_len(leaf, dir_item, data_len); @@ -140,7 +140,7 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, const char *name, leaf = path->nodes[0]; btrfs_set_dir_item_key(leaf, dir_item, &disk_key); - btrfs_set_dir_type(leaf, dir_item, type); + btrfs_set_dir_flags(leaf, dir_item, type); btrfs_set_dir_data_len(leaf, dir_item, 0); btrfs_set_dir_name_len(leaf, dir_item, name_len); btrfs_set_dir_transid(leaf, dir_item, trans->transid); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 8cdb173331c7..e47a1a38256f 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5567,7 +5567,7 @@ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry, location->objectid, location->type, location->offset); } if (!ret) - *type = btrfs_dir_type(path->nodes[0], di); + *type = btrfs_dir_ftype(path->nodes[0], di); out: btrfs_free_path(path); return ret; @@ -6005,6 +6005,7 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) btrfs_for_each_slot(root, &key, &found_key, path, ret) { struct dir_entry *entry; struct extent_buffer *leaf = path->nodes[0]; + u8 di_flags; if (found_key.objectid != key.objectid) break; @@ -6028,13 +6029,15 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) goto again; } + di_flags = btrfs_dir_flags(leaf, di); entry = addr; - put_unaligned(name_len, &entry->name_len); name_ptr = (char *)(entry + 1); - read_extent_buffer(leaf, name_ptr, (unsigned long)(di + 1), - name_len); - put_unaligned(fs_ftype_to_dtype(btrfs_dir_type(leaf, di)), - &entry->type); + 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(btrfs_dir_flags_to_ftype(di_flags)), + &entry->type); btrfs_dir_item_key_to_cpu(leaf, di, &location); put_unaligned(location.objectid, &entry->ino); put_unaligned(found_key.offset, &entry->offset); diff --git a/fs/btrfs/print-tree.c b/fs/btrfs/print-tree.c index dd8777872143..6d9d99bf3536 100644 --- a/fs/btrfs/print-tree.c +++ b/fs/btrfs/print-tree.c @@ -240,9 +240,9 @@ void btrfs_print_leaf(struct extent_buffer *l) case BTRFS_DIR_ITEM_KEY: di = btrfs_item_ptr(l, i, struct btrfs_dir_item); btrfs_dir_item_key_to_cpu(l, di, &found_key); - pr_info("\t\tdir oid %llu type %u\n", + pr_info("\t\tdir oid %llu flags %u\n", found_key.objectid, - btrfs_dir_type(l, di)); + btrfs_dir_flags(l, di)); break; case BTRFS_ROOT_ITEM_KEY: ri = btrfs_item_ptr(l, i, struct btrfs_root_item); diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index e7671afcee4f..b02e991b2c06 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -1077,7 +1077,7 @@ static int iterate_dir_item(struct btrfs_root *root, struct btrfs_path *path, data_len = btrfs_dir_data_len(eb, di); btrfs_dir_item_key_to_cpu(eb, di, &di_key); - if (btrfs_dir_type(eb, di) == BTRFS_FT_XATTR) { + if (btrfs_dir_ftype(eb, di) == BTRFS_FT_XATTR) { if (name_len > XATTR_NAME_MAX) { ret = -ENAMETOOLONG; goto out; diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index 43f905ab0a18..77c53a7491cf 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -528,7 +528,7 @@ static int check_dir_item(struct extent_buffer *leaf, } /* dir type check */ - dir_type = btrfs_dir_type(leaf, di); + dir_type = btrfs_dir_ftype(leaf, di); if (unlikely(dir_type >= BTRFS_FT_MAX)) { dir_item_err(leaf, slot, "invalid dir item type, have %u expect [0, %u)", diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 4d2d19fea112..c727ffea547c 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -1809,7 +1809,7 @@ static int delete_conflicting_dir_entry(struct btrfs_trans_handle *trans, struct btrfs_path *path, struct btrfs_dir_item *dst_di, const struct btrfs_key *log_key, - u8 log_type, + u8 log_flags, bool exists) { struct btrfs_key found_key; @@ -1819,7 +1819,7 @@ static int delete_conflicting_dir_entry(struct btrfs_trans_handle *trans, if (found_key.objectid == log_key->objectid && found_key.type == log_key->type && found_key.offset == log_key->offset && - btrfs_dir_type(path->nodes[0], dst_di) == log_type) + btrfs_dir_flags(path->nodes[0], dst_di) == log_flags) return 1; /* @@ -1864,7 +1864,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans, struct btrfs_key log_key; struct btrfs_key search_key; struct inode *dir; - u8 log_type; + u8 log_flags; bool exists; int ret; bool update_size = true; @@ -1881,7 +1881,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans, goto out; } - log_type = btrfs_dir_type(eb, di); + log_flags = btrfs_dir_flags(eb, di); read_extent_buffer(eb, name, (unsigned long)(di + 1), name_len); @@ -1900,8 +1900,8 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans, goto out; } else if (dir_dst_di) { ret = delete_conflicting_dir_entry(trans, BTRFS_I(dir), path, - dir_dst_di, &log_key, log_type, - exists); + dir_dst_di, &log_key, + log_flags, exists); if (ret < 0) goto out; dir_dst_matches = (ret == 1); @@ -1918,7 +1918,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans, } else if (index_dst_di) { ret = delete_conflicting_dir_entry(trans, BTRFS_I(dir), path, index_dst_di, &log_key, - log_type, exists); + log_flags, exists); if (ret < 0) goto out; index_dst_matches = (ret == 1); @@ -2028,7 +2028,7 @@ static noinline int replay_one_dir_item(struct btrfs_trans_handle *trans, * to ever delete the parent directory has it would result in stale * dentries that can never be deleted. */ - if (ret == 1 && btrfs_dir_type(eb, di) != BTRFS_FT_DIR) { + if (ret == 1 && btrfs_dir_ftype(eb, di) != BTRFS_FT_DIR) { struct btrfs_path *fixup_path; struct btrfs_key di_key; @@ -6043,7 +6043,7 @@ static int log_new_dir_dentries(struct btrfs_trans_handle *trans, goto next_dir_inode; di = btrfs_item_ptr(leaf, i, struct btrfs_dir_item); - type = btrfs_dir_type(leaf, di); + type = btrfs_dir_ftype(leaf, di); if (btrfs_dir_transid(leaf, di) < trans->transid) continue; btrfs_dir_item_key_to_cpu(leaf, di, &di_key); diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h index d4117152d907..428ae75b9f73 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -359,6 +359,13 @@ enum btrfs_csum_type { #define BTRFS_FT_SYMLINK 7 #define BTRFS_FT_XATTR 8 #define BTRFS_FT_MAX 9 +/* Name is encrypted. */ +#define BTRFS_FT_FSCRYPT_NAME 0x80 + +static inline __u8 btrfs_dir_flags_to_ftype(__u8 flags) +{ + return flags & ~BTRFS_FT_FSCRYPT_NAME; +} /* * The key defines the order in the tree, and so it also defines (optimal) From patchwork Wed Aug 17 14:49: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: 12946023 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 AFDCAC28B2B for ; Wed, 17 Aug 2022 14:50:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240427AbiHQOup (ORCPT ); Wed, 17 Aug 2022 10:50:45 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36518 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240417AbiHQOui (ORCPT ); Wed, 17 Aug 2022 10:50:38 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 322BF33A26; Wed, 17 Aug 2022 07:50:37 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id B2D3C8042B; Wed, 17 Aug 2022 10:50:35 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1660747837; bh=gO03JrjFMU9KjtZh35gPfuD4CW7aFI/0juADJjCzBTY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=LlfMmj8HX3WQ64OCiuFvL2PL+Qhz8n9TU+qdYl5nek+LLvl+tZA1Z8SgFC32cog9y n9fjpgY5+EX6vA5i/3QYiQke/8dUxnijmUffT+R0Wdx76baMVrJpKRI1z/o+haoyMl NRa0o27SLK78GKl7tXEygx2VoIh0Bdd10Yb71AyWdPxbZHKhB01CRavKgG5MkDhWJT BF9VGmmLp86wKjwaxR4U6kVvJdqzuyqP838juhfWL01UCCRhRRaVnFxNT1vPyxPwrK 9XoBUGCQKOX9bhIHkOrtHtUqlA/VWkv6U3ep1SyuQJIMWBqSokfd2QFJlkAf8rKEJ2 v9wAeTF7mHrKg== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o " , Jaegeuk Kim , Eric Biggers , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@fb.com Cc: Sweet Tea Dorminy Subject: [PATCH 08/21] btrfs: setup fscrypt_names from dentrys using helper Date: Wed, 17 Aug 2022 10:49:52 -0400 Message-Id: <33ab6691c8038f52a448844f0ebc363477db30da.1660744500.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org Most places that we create fscrypt_names, we are doing so from a dentry. Fscrypt provides a helper for this common pattern: fscrypt_setup_filename() initializes a filename to search for from a dentry, performing encryption of the plaintext if it can and should be done. This converts each setup of a fscrypt_name from a dentry to use this helper; at present, since there are no encrypted directories, nothing goes down the filename encryption paths. Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/inode.c | 153 ++++++++++++++++++++++++++--------------- fs/btrfs/transaction.c | 26 +++++-- fs/btrfs/tree-log.c | 12 ++-- 3 files changed, 123 insertions(+), 68 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index dbb2ef802b9a..f3861c58309a 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4408,14 +4408,17 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry) struct btrfs_trans_handle *trans; struct inode *inode = d_inode(dentry); int ret; - struct fscrypt_name fname = { - .disk_name = FSTR_INIT((unsigned char *)dentry->d_name.name, dentry->d_name.len) - }; + struct fscrypt_name fname; + ret = fscrypt_setup_filename(dir, &dentry->d_name, 1, &fname); + if (ret) + return ret; trans = __unlink_start_trans(dir); - if (IS_ERR(trans)) - return PTR_ERR(trans); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + goto out; + } btrfs_record_unlink_dir(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)), 0); @@ -4432,6 +4435,7 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry) } out: + fscrypt_free_filename(&fname); btrfs_end_transaction(trans); btrfs_btree_balance_dirty(BTRFS_I(dir)->root->fs_info); return ret; @@ -4450,10 +4454,11 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, int ret; u64 objectid; u64 dir_ino = btrfs_ino(BTRFS_I(dir)); - struct fscrypt_name fname = { - .disk_name = FSTR_INIT((char *) dentry->d_name.name, - dentry->d_name.len) - }; + struct fscrypt_name fname; + + ret = fscrypt_setup_filename(dir, &dentry->d_name, 1, &fname); + if (ret) + return ret; if (btrfs_ino(inode) == BTRFS_FIRST_FREE_OBJECTID) { objectid = inode->root->root_key.objectid; @@ -4461,12 +4466,15 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, objectid = inode->location.objectid; } else { WARN_ON(1); + fscrypt_free_filename(&fname); return -EINVAL; } path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; + if (!path) { + ret = -ENOMEM; + goto out; + } di = btrfs_lookup_dir_item(trans, root, path, dir_ino, &fname, -1); if (IS_ERR_OR_NULL(di)) { @@ -4533,6 +4541,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, btrfs_abort_transaction(trans, ret); out: btrfs_free_path(path); + fscrypt_free_filename(&fname); return ret; } @@ -4799,9 +4808,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) int err = 0; struct btrfs_trans_handle *trans; u64 last_unlink_trans; - struct fscrypt_name fname = { - .disk_name = FSTR_INIT((unsigned char *)dentry->d_name.name, dentry->d_name.len) - }; + struct fscrypt_name fname; if (inode->i_size > BTRFS_EMPTY_DIR_SIZE) return -ENOTEMPTY; @@ -4814,9 +4821,15 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) return btrfs_delete_subvolume(dir, dentry); } + err = fscrypt_setup_filename(dir, &dentry->d_name, 1, &fname); + if (err) + return err; + trans = __unlink_start_trans(dir); - if (IS_ERR(trans)) - return PTR_ERR(trans); + if (IS_ERR(trans)) { + err = PTR_ERR(trans); + goto out_notrans; + } if (unlikely(btrfs_ino(BTRFS_I(inode)) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)) { err = btrfs_unlink_subvol(trans, dir, dentry); @@ -4850,7 +4863,9 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) } out: btrfs_end_transaction(trans); +out_notrans: btrfs_btree_balance_dirty(fs_info); + fscrypt_free_filename(&fname); return err; } @@ -5544,7 +5559,7 @@ void btrfs_evict_inode(struct inode *inode) /* * Return the key found in the dir entry in the location pointer, fill @type - * with BTRFS_FT_*, and return 0. + * with BTRFS_FT_*, and return 0. Used only for lookups, not removals. * * If no dir entries were found, returns -ENOENT. * If found a corrupted location in dir entry, returns -EUCLEAN. @@ -5556,15 +5571,16 @@ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry, struct btrfs_path *path; struct btrfs_root *root = BTRFS_I(dir)->root; int ret = 0; - struct fscrypt_name fname = { - .disk_name = FSTR_INIT((char *) dentry->d_name.name, - dentry->d_name.len) - }; + 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; + di = btrfs_lookup_dir_item(NULL, root, path, btrfs_ino(BTRFS_I(dir)), &fname, 0); if (IS_ERR_OR_NULL(di)) { @@ -5584,6 +5600,7 @@ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry, if (!ret) *type = btrfs_dir_ftype(path->nodes[0], di); out: + fscrypt_free_filename(&fname); btrfs_free_path(path); return ret; } @@ -5604,9 +5621,15 @@ static int fixup_tree_root_location(struct btrfs_fs_info *fs_info, struct btrfs_root_ref *ref; struct extent_buffer *leaf; struct btrfs_key key; + struct fscrypt_name fname; int ret; int err = 0; + ret = fscrypt_setup_filename(dir, &dentry->d_name, 0, + &fname); + if (ret) + return ret; + path = btrfs_alloc_path(); if (!path) { err = -ENOMEM; @@ -5652,6 +5675,7 @@ static int fixup_tree_root_location(struct btrfs_fs_info *fs_info, err = 0; out: btrfs_free_path(path); + fscrypt_free_filename(&fname); return err; } @@ -6258,16 +6282,17 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args, struct inode *inode = args->inode; int ret; + if (!args->orphan) { + ret = fscrypt_setup_filename(dir, &args->dentry->d_name, 0, + &args->fname); + if (ret) + return ret; + } + ret = posix_acl_create(dir, &inode->i_mode, &args->default_acl, &args->acl); - if (ret) + if (ret) { + fscrypt_free_filename(&args->fname); return ret; - - if (!args->orphan) { - char *name = (char *) args->dentry->d_name.name; - int name_len = args->dentry->d_name.len; - args->fname = (struct fscrypt_name) { - .disk_name = FSTR_INIT(name, name_len), - }; } /* 1 to add inode item */ @@ -6308,6 +6333,7 @@ void btrfs_new_inode_args_destroy(struct btrfs_new_inode_args *args) { posix_acl_release(args->acl); posix_acl_release(args->default_acl); + fscrypt_free_filename(&args->fname); } /* @@ -6735,10 +6761,7 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir, struct btrfs_root *root = BTRFS_I(dir)->root; struct inode *inode = d_inode(old_dentry); struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); - struct fscrypt_name fname = { - .disk_name = FSTR_INIT((char *) dentry->d_name.name, - dentry->d_name.len) - }; + struct fscrypt_name fname; u64 index; int err; int drop_inode = 0; @@ -6750,6 +6773,10 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir, if (inode->i_nlink >= BTRFS_LINK_MAX) return -EMLINK; + err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &fname); + if (err) + goto fail; + err = btrfs_set_inode_index(BTRFS_I(dir), &index); if (err) goto fail; @@ -6800,6 +6827,7 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir, } fail: + fscrypt_free_filename(&fname); if (trans) btrfs_end_transaction(trans); if (drop_inode) { @@ -9181,14 +9209,7 @@ static int btrfs_rename_exchange(struct inode *old_dir, int ret; int ret2; bool need_abort = false; - struct fscrypt_name old_name = { - .disk_name = FSTR_INIT((char *) old_dentry->d_name.name, - old_dentry->d_name.len) - }; - struct fscrypt_name new_name = { - .disk_name = FSTR_INIT((char *) new_dentry->d_name.name, - new_dentry->d_name.len) - }; + struct fscrypt_name old_fname, new_fname; /* * For non-subvolumes allow exchange only within one subvolume, in the @@ -9200,6 +9221,16 @@ static int btrfs_rename_exchange(struct inode *old_dir, new_ino != BTRFS_FIRST_FREE_OBJECTID)) return -EXDEV; + ret = fscrypt_setup_filename(old_dir, &old_dentry->d_name, 0, &old_fname); + if (ret) + return ret; + + ret = fscrypt_setup_filename(new_dir, &new_dentry->d_name, 0, &new_fname); + if (ret) { + fscrypt_free_filename(&old_fname); + return ret; + } + /* close the race window with snapshot create/destroy ioctl */ if (old_ino == BTRFS_FIRST_FREE_OBJECTID || new_ino == BTRFS_FIRST_FREE_OBJECTID) @@ -9267,7 +9298,7 @@ static int btrfs_rename_exchange(struct inode *old_dir, /* force full log commit if subvolume involved. */ btrfs_set_log_full_commit(trans); } else { - ret = btrfs_insert_inode_ref(trans, dest, &new_name, old_ino, + ret = btrfs_insert_inode_ref(trans, dest, &new_fname, old_ino, btrfs_ino(BTRFS_I(new_dir)), old_idx); if (ret) @@ -9280,7 +9311,7 @@ static int btrfs_rename_exchange(struct inode *old_dir, /* force full log commit if subvolume involved. */ btrfs_set_log_full_commit(trans); } else { - ret = btrfs_insert_inode_ref(trans, root, &old_name, new_ino, + ret = btrfs_insert_inode_ref(trans, root, &old_fname, new_ino, btrfs_ino(BTRFS_I(old_dir)), new_idx); if (ret) { @@ -9314,7 +9345,7 @@ static int btrfs_rename_exchange(struct inode *old_dir, ret = btrfs_unlink_subvol(trans, old_dir, old_dentry); } else { /* src is an inode */ ret = __btrfs_unlink_inode(trans, BTRFS_I(old_dir), - BTRFS_I(old_dentry->d_inode), &old_name, + BTRFS_I(old_dentry->d_inode), &old_fname, &old_rename_ctx); if (!ret) ret = btrfs_update_inode(trans, root, BTRFS_I(old_inode)); @@ -9329,7 +9360,7 @@ static int btrfs_rename_exchange(struct inode *old_dir, ret = btrfs_unlink_subvol(trans, new_dir, new_dentry); } else { /* dest is an inode */ ret = __btrfs_unlink_inode(trans, BTRFS_I(new_dir), - BTRFS_I(new_dentry->d_inode), &new_name, + BTRFS_I(new_dentry->d_inode), &new_fname, &new_rename_ctx); if (!ret) ret = btrfs_update_inode(trans, dest, BTRFS_I(new_inode)); @@ -9340,14 +9371,14 @@ static int btrfs_rename_exchange(struct inode *old_dir, } ret = btrfs_add_link(trans, BTRFS_I(new_dir), BTRFS_I(old_inode), - &new_name, 0, old_idx); + &new_fname, 0, old_idx); if (ret) { btrfs_abort_transaction(trans, ret); goto out_fail; } ret = btrfs_add_link(trans, BTRFS_I(old_dir), BTRFS_I(new_inode), - &old_name, 0, new_idx); + &old_fname, 0, new_idx); if (ret) { btrfs_abort_transaction(trans, ret); goto out_fail; @@ -9390,6 +9421,8 @@ static int btrfs_rename_exchange(struct inode *old_dir, old_ino == BTRFS_FIRST_FREE_OBJECTID) up_read(&fs_info->subvol_sem); + fscrypt_free_filename(&new_fname); + fscrypt_free_filename(&old_fname); return ret; } @@ -9429,14 +9462,7 @@ static int btrfs_rename(struct user_namespace *mnt_userns, int ret; int ret2; u64 old_ino = btrfs_ino(BTRFS_I(old_inode)); - struct fscrypt_name old_fname = { - .disk_name = FSTR_INIT((char *)old_dentry->d_name.name, - old_dentry->d_name.len) - }; - struct fscrypt_name new_fname = { - .disk_name = FSTR_INIT((char *)new_dentry->d_name.name, - new_dentry->d_name.len) - }; + struct fscrypt_name old_fname, new_fname; if (btrfs_ino(BTRFS_I(new_dir)) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID) return -EPERM; @@ -9453,6 +9479,16 @@ static int btrfs_rename(struct user_namespace *mnt_userns, new_inode->i_size > BTRFS_EMPTY_DIR_SIZE) return -ENOTEMPTY; + ret = fscrypt_setup_filename(old_dir, &old_dentry->d_name, 0, &old_fname); + if (ret) + return ret; + + ret = fscrypt_setup_filename(new_dir, &new_dentry->d_name, 0, &new_fname); + if (ret) { + fscrypt_free_filename(&old_fname); + return ret; + } + /* check for collisions, even if the name isn't there */ ret = btrfs_check_dir_item_collision(dest, new_dir->i_ino, &new_fname); @@ -9461,11 +9497,11 @@ static int btrfs_rename(struct user_namespace *mnt_userns, /* we shouldn't get * eexist without a new_inode */ if (WARN_ON(!new_inode)) { - return ret; + goto out_fscrypt_names; } } else { /* maybe -EOVERFLOW */ - return ret; + goto out_fscrypt_names; } } ret = 0; @@ -9639,6 +9675,9 @@ static int btrfs_rename(struct user_namespace *mnt_userns, out_whiteout_inode: if (flags & RENAME_WHITEOUT) iput(whiteout_args.inode); +out_fscrypt_names: + fscrypt_free_filename(&old_fname); + fscrypt_free_filename(&new_fname); return ret; } diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 3d1459dccc8d..c95cd917b54f 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -1660,10 +1661,8 @@ create_pending_snapshot(struct btrfs_trans_handle *trans, u64 index = 0; u64 objectid; u64 root_flags; - struct fscrypt_name fname = { - .disk_name = FSTR_INIT((char *) pending->dentry->d_name.name, - pending->dentry->d_name.len) - }; + unsigned int mem_flags; + struct fscrypt_name fname; ASSERT(pending->path); path = pending->path; @@ -1671,9 +1670,22 @@ create_pending_snapshot(struct btrfs_trans_handle *trans, ASSERT(pending->root_item); new_root_item = pending->root_item; + /* + * Since this is during btrfs_commit_transaction() and more items + * joining the transaction at this point would be bad, use NOFS + * allocations so that no new writes are kicked off. + */ + mem_flags = memalloc_nofs_save(); + pending->error = fscrypt_setup_filename(parent_inode, + &pending->dentry->d_name, 0, + &fname); + memalloc_nofs_restore(mem_flags); + if (pending->error) + goto free_pending; + pending->error = btrfs_get_free_objectid(tree_root, &objectid); if (pending->error) - goto no_free_objectid; + goto free_fname; /* * Make qgroup to skip current new snapshot's qgroupid, as it is @@ -1887,7 +1899,9 @@ create_pending_snapshot(struct btrfs_trans_handle *trans, trans->bytes_reserved = 0; clear_skip_qgroup: btrfs_clear_skip_qgroup(trans); -no_free_objectid: +free_fname: + fscrypt_free_filename(&fname); +free_pending: kfree(new_root_item); pending->root_item = NULL; btrfs_free_path(path); diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 0cd515e02fe4..ee7b80d953e9 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -6906,13 +6906,13 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans, if (old_dir && old_dir->logged_trans == trans->transid) { struct btrfs_root *log = old_dir->root->log_root; struct btrfs_path *path; - struct fscrypt_name fname = { - .disk_name = FSTR_INIT((char *) old_dentry->d_name.name, - old_dentry->d_name.len) - }; - + struct fscrypt_name fname; ASSERT(old_dir_index >= BTRFS_DIR_START_INDEX); + ret = fscrypt_setup_filename(&old_dir->vfs_inode, + &old_dentry->d_name, 0, &fname); + if (ret) + goto out; /* * We have two inodes to update in the log, the old directory and * the inode that got renamed, so we must pin the log to prevent @@ -6932,6 +6932,7 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans, path = btrfs_alloc_path(); if (!path) { ret = -ENOMEM; + fscrypt_free_filename(&fname); goto out; } @@ -6961,6 +6962,7 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans, mutex_unlock(&old_dir->log_mutex); btrfs_free_path(path); + fscrypt_free_filename(&fname); if (ret < 0) goto out; } From patchwork Wed Aug 17 14:49: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: 12946024 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 24CA4C32772 for ; Wed, 17 Aug 2022 14:50:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240255AbiHQOuq (ORCPT ); Wed, 17 Aug 2022 10:50:46 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36524 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240423AbiHQOuk (ORCPT ); Wed, 17 Aug 2022 10:50:40 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 33F142A42B; Wed, 17 Aug 2022 07:50: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 975CA80A9D; Wed, 17 Aug 2022 10:50:38 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1660747838; bh=Gx8D4FqXm0Co3pilNA38S58oJZODZrxmkcwGhpEPvwg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=tr0rO2iMGycU0d2brKtVbXXxDvP/XwRIGvTDxabcsLt4wVM70ICLTtOYdlOoVOcVA dVtW8zQW77uMxZUgqVrQ7lR7QYZ5D+hhTlrVk18SIHqbLkKi+MVzph1Wr4SQABp4+/ lunS8x6dEE0ttE6ead/7Z+LiJ0AVpiS7Gzr1Vy1l6m6yUCPhld4wWoWi41N88OFrUu H98cHg4/oEAu1n6gwG6vffZ94xsz0zB0f2EKZH+TaLribYieZNOdSG7FUuLFhHEvXR TQenLxuxKgqdy+bOMRO/AEzfpzY+B8Bl0tpW2oLBZ/ZbdvOk3SgRHE+NOT31HW5MVW fmqkagZio1nwA== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o " , Jaegeuk Kim , Eric Biggers , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@fb.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH 09/21] btrfs: factor a fscrypt_name matching method Date: Wed, 17 Aug 2022 10:49:53 -0400 Message-Id: <1b11a53ab2979ffcf769d5a883d5f31ac4eb1d03.1660744500.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Omar Sandoval Now that everything in btrfs is dealing in fscrypt_names, fscrypt has a useful function, fscrypt_match_name(), to check whether a fscrypt_name matches a provided buffer. However, btrfs buffers are struct extent_buffer rather than a raw char array, so we need to implement our own imitation of fscrypt_match_name() that deals in extent_buffers, falling back to a simple memcpy if fscrypt isn't compiled. We can then use this matching method in btrfs_match_dir_item_name() and other locations. This also provides a useful occasion to introduce the new fscrypt file for btrfs, handling the fscrypt-specific functions needed. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/Makefile | 1 + fs/btrfs/dir-item.c | 13 +++++++------ fs/btrfs/extent_io.c | 37 +++++++++++++++++++++++++++++++++++++ fs/btrfs/extent_io.h | 2 ++ fs/btrfs/fscrypt.c | 32 ++++++++++++++++++++++++++++++++ fs/btrfs/fscrypt.h | 25 +++++++++++++++++++++++++ fs/btrfs/inode-item.c | 8 ++++---- fs/btrfs/inode.c | 11 ++++------- fs/btrfs/root-tree.c | 7 ++++--- 9 files changed, 116 insertions(+), 20 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 99f9995670ea..b6444490cdbc 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/dir-item.c b/fs/btrfs/dir-item.c index b4c1e2a40401..8d7c3c32ed8e 100644 --- a/fs/btrfs/dir-item.c +++ b/fs/btrfs/dir-item.c @@ -5,6 +5,7 @@ #include "ctree.h" #include "disk-io.h" +#include "fscrypt.h" #include "transaction.h" /* @@ -390,15 +391,15 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info, total_len = btrfs_item_size(leaf, path->slots[0]); while (cur < total_len) { - unsigned long name_ptr = (unsigned long)(dir_item + 1); - this_len = sizeof(*dir_item) + - btrfs_dir_name_len(leaf, dir_item) + + int dir_name_len = btrfs_dir_name_len(leaf, dir_item); + this_len = sizeof(*dir_item) + dir_name_len + btrfs_dir_data_len(leaf, dir_item); - if (btrfs_dir_name_len(leaf, dir_item) == fname_len(fname) && - memcmp_extent_buffer(leaf, fname_name(fname), name_ptr, - fname_len(fname)) == 0) + if (btrfs_fscrypt_match_name(fname, leaf, + (unsigned long)(dir_item + 1), + dir_name_len)) { return dir_item; + } cur += this_len; dir_item = (struct btrfs_dir_item *)((char *)dir_item + diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 6e8e936a8a1e..ff2b995ded6e 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 +#include #include #include #include @@ -6956,6 +6957,42 @@ void memzero_extent_buffer(const struct extent_buffer *eb, unsigned long start, } } +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 copy_extent_buffer_full(const struct extent_buffer *dst, const struct extent_buffer *src) { diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index 4bc72a87b9a9..b239f6ae2c80 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -223,6 +223,8 @@ void memmove_extent_buffer(const struct extent_buffer *dst, unsigned long len); void memzero_extent_buffer(const struct extent_buffer *eb, unsigned long start, unsigned long len); +void extent_buffer_sha256(const struct extent_buffer *eb, unsigned long start, + unsigned long len, u8 *out); int extent_buffer_test_bit(const struct extent_buffer *eb, unsigned long start, unsigned long pos); void extent_buffer_bitmap_set(const struct extent_buffer *eb, unsigned long start, diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c new file mode 100644 index 000000000000..2ed844dd61d0 --- /dev/null +++ b/fs/btrfs/fscrypt.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Facebook + */ + +#include "ctree.h" +#include "fscrypt.h" + +/* fscrypt_match_name() but for an extent_buffer. */ +bool btrfs_fscrypt_match_name(const 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); + return !memcmp(digest, nokey_name->sha256, sizeof(digest)); +} diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h new file mode 100644 index 000000000000..7f24d12e6ee0 --- /dev/null +++ b/fs/btrfs/fscrypt.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef BTRFS_FSCRYPT_H +#define BTRFS_FSCRYPT_H + +#include + +#ifdef CONFIG_FS_ENCRYPTION +bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname, + struct extent_buffer *leaf, + unsigned long de_name, u32 de_name_len); + +#else +static bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname, + struct extent_buffer *leaf, + unsigned long de_name, u32 de_name_len) +{ + if (de_name_len != fname->disk_name.len) + return false; + return !memcmp_extent_buffer(leaf, fname->disk_name.name, + de_name, de_name_len); +} +#endif + +#endif /* BTRFS_FSCRYPT_H */ diff --git a/fs/btrfs/inode-item.c b/fs/btrfs/inode-item.c index 78053eb9589c..4ad75f9573aa 100644 --- a/fs/btrfs/inode-item.c +++ b/fs/btrfs/inode-item.c @@ -7,6 +7,7 @@ #include "ctree.h" #include "inode-item.h" #include "disk-io.h" +#include "fscrypt.h" #include "transaction.h" #include "print-tree.h" @@ -62,10 +63,9 @@ struct btrfs_inode_extref *btrfs_find_name_in_ext_backref( name_ptr = (unsigned long)(&extref->name); ref_name_len = btrfs_inode_extref_name_len(leaf, extref); - if (ref_name_len == fname_len(fname) && - btrfs_inode_extref_parent(leaf, extref) == ref_objectid && - (memcmp_extent_buffer(leaf, fname_name(fname), name_ptr, - fname_len(fname)) == 0)) + if (btrfs_inode_extref_parent(leaf, extref) == ref_objectid && + btrfs_fscrypt_match_name(fname, leaf, name_ptr, + ref_name_len)) return extref; cur_offset += ref_name_len + sizeof(*extref); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index f3861c58309a..016b3fb856ce 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -55,6 +55,7 @@ #include "zoned.h" #include "subpage.h" #include "inode-item.h" +#include "fscrypt.h" struct btrfs_iget_args { u64 ino; @@ -5650,14 +5651,10 @@ static int fixup_tree_root_location(struct btrfs_fs_info *fs_info, leaf = path->nodes[0]; ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_ref); - if (btrfs_root_ref_dirid(leaf, ref) != btrfs_ino(BTRFS_I(dir)) || - btrfs_root_ref_name_len(leaf, ref) != dentry->d_name.len) + if (btrfs_root_ref_dirid(leaf, ref) != btrfs_ino(BTRFS_I(dir))) goto out; - - ret = memcmp_extent_buffer(leaf, dentry->d_name.name, - (unsigned long)(ref + 1), - dentry->d_name.len); - if (ret) + if (!btrfs_fscrypt_match_name(&fname, leaf, (unsigned long)(ref + 1), + btrfs_root_ref_name_len(leaf, ref))) goto out; btrfs_release_path(path); diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c index 8eb6cbe19326..47c5572df8b1 100644 --- a/fs/btrfs/root-tree.c +++ b/fs/btrfs/root-tree.c @@ -8,6 +8,7 @@ #include "ctree.h" #include "transaction.h" #include "disk-io.h" +#include "fscrypt.h" #include "print-tree.h" #include "qgroup.h" #include "space-info.h" @@ -352,14 +353,14 @@ int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id, if (ret < 0) goto out; 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) != fname_len(fname)) || - memcmp_extent_buffer(leaf, fname_name(fname), ptr, - fname_len(fname))) { + !btrfs_fscrypt_match_name(fname, leaf, ptr, name_len)) { err = -ENOENT; goto out; } From patchwork Wed Aug 17 14:49: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: 12946025 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 EB1D7C25B08 for ; Wed, 17 Aug 2022 14:50:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240436AbiHQOuq (ORCPT ); Wed, 17 Aug 2022 10:50:46 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36546 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240369AbiHQOum (ORCPT ); Wed, 17 Aug 2022 10:50:42 -0400 Received: from box.fidei.email (box.fidei.email [IPv6:2605:2700:0:2:a800:ff:feba:dc44]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 086503AE62; Wed, 17 Aug 2022 07:50:40 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 4B2F68042B; Wed, 17 Aug 2022 10:50:40 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1660747840; bh=45mPsaVSUVEKFgCwQQlfVFXsKBuzOzIhLQqKX5S1IBI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jhfBTNmRyVkiPxGRmTHIxFx8+17R9Ol3cPdRUSN9TW5mZvEAEhmQfabrF7tUfyfT5 w8hcGqQOETyMfU8CTePjfRORiqg6SZ/Wsp/eTwqJg0JRGaSV1bCr9L/dGZdSAY+w8r 5ae6KtqlP/GJxqwsmfpXMZ0xQ6712ZH3++GDwU2Vo08UZrSo8nQkQuEEA4fHyJG+JT l5+pq1RNHei868M2XC3pEKMLsmbn50z6UbGgkCUWi9ly9MoLtoz5REmoCpudlCC5zT GjsBWE5Zz53IYvmvhQw7f+aQuCPhsbB6e8v5a6ULEwEYIruYSMMlrBPWkfdrU4Ho5F AA/pGu7h/r5og== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o " , Jaegeuk Kim , Eric Biggers , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@fb.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH 10/21] btrfs: disable various operations on encrypted inodes Date: Wed, 17 Aug 2022 10:49:54 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Omar Sandoval Initially, only normal data extents, using the normal (non-direct) IO path, will be encrypted. This change forbids various other bits: - allows reflinking only if both inodes have the same encryption status - disables 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 | 7 +++++++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index b160b8e124e0..ff668686717b 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -400,6 +400,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 c9649b7b2f18..b2ffacef9abb 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1896,7 +1896,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; } @@ -3743,7 +3743,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 016b3fb856ce..15104263059a 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -409,7 +409,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 9acf47b11fe6..d22086e1cbc8 100644 --- a/fs/btrfs/reflink.c +++ b/fs/btrfs/reflink.c @@ -805,6 +805,13 @@ static int btrfs_remap_file_range_prep(struct file *file_in, loff_t pos_in, ASSERT(inode_in->i_sb == inode_out->i_sb); } + /* + * Can only reflink encrypted files if both files are encrypted. + */ + if (!fscrypt_have_same_policy(inode_in, inode_out)) { + return -EINVAL; + } + /* Don't make the dst file partly checksummed */ if ((BTRFS_I(inode_in)->flags & BTRFS_INODE_NODATASUM) != (BTRFS_I(inode_out)->flags & BTRFS_INODE_NODATASUM)) { From patchwork Wed Aug 17 14:49: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: 12946026 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 97672C32774 for ; Wed, 17 Aug 2022 14:50:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240429AbiHQOur (ORCPT ); Wed, 17 Aug 2022 10:50:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36580 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237148AbiHQOun (ORCPT ); Wed, 17 Aug 2022 10:50:43 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A85303E749; Wed, 17 Aug 2022 07:50:42 -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 2C31C80A9D; Wed, 17 Aug 2022 10:50:42 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1660747842; bh=COQBy9pOcpCvnrHMZE0Cv+NvvD5RaLx8uFecu+F6YvM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=UWDAkMKB4TSwXHzok5EXjvuxfI4umyMCrzYvyklWAJZc42UvDFTKhbcjq4XXCIp08 ZsfZ/5gHhfR0hKF6P7pnrXkROD9RAFVhRAuJl3QshDNTIyASQBygHTZqK4xkUy/0fC Lw+otxkemZ2Cvvd1jqJyAB+8VvQQVmfS6rlBOd0uCqeU1o4t1t6hjX/w1ZDYPkj0Yo 6tT7JedK8m63IGviBK0cjAcgQwmHlYjD7bJnSn9ZlIRozfbSigBTJSQLax2teeRN0D kQqCNSa2eLmn4oUBsdd04F0fB1euvkxS5DtPlmFOdU26pPQUokuDOGIpKKYkR/Um2b tOJLl416ErV8A== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o " , Jaegeuk Kim , Eric Biggers , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@fb.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH 11/21] btrfs: add fscrypt operation table to superblock Date: Wed, 17 Aug 2022 10:49:55 -0400 Message-Id: <04ce4773ce1b1dd14eca4783dfe2439f44e77653.1660744500.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Omar Sandoval To use fscrypt_prepare_new_inode(), the superblock must have fscrypt operations registered. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/fscrypt.c | 3 +++ fs/btrfs/fscrypt.h | 1 + fs/btrfs/super.c | 3 +++ 3 files changed, 7 insertions(+) diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index 2ed844dd61d0..9829d280a6bc 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -30,3 +30,6 @@ bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname, de_name_len - sizeof(nokey_name->bytes), digest); return !memcmp(digest, nokey_name->sha256, sizeof(digest)); } + +const struct fscrypt_operations btrfs_fscrypt_ops = { +}; diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h index 7f24d12e6ee0..07884206c8a1 100644 --- a/fs/btrfs/fscrypt.h +++ b/fs/btrfs/fscrypt.h @@ -22,4 +22,5 @@ static bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname, } #endif +extern const struct fscrypt_operations btrfs_fscrypt_ops; #endif /* BTRFS_FSCRYPT_H */ diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 55af04fae075..e017e976c679 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -47,6 +47,8 @@ #include "tests/btrfs-tests.h" #include "block-group.h" #include "discard.h" +#include "fscrypt.h" + #include "qgroup.h" #include "raid56.h" #define CREATE_TRACE_POINTS @@ -1443,6 +1445,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 Aug 17 14:49: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: 12946027 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 28255C32772 for ; Wed, 17 Aug 2022 14:50:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240431AbiHQOus (ORCPT ); Wed, 17 Aug 2022 10:50:48 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36668 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240435AbiHQOuq (ORCPT ); Wed, 17 Aug 2022 10:50:46 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CB77076478; Wed, 17 Aug 2022 07:50:44 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id CE0998042B; Wed, 17 Aug 2022 10:50:43 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1660747844; bh=kd21Lb7+1Nj7Vt5NjDQav8c7piL0R5Zgect4xUxW++o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SimowJ8Kgw6nOVQ1BNK3Al5g5Uq8i29w1L++oUYiciMwE7U5LxQiu10YZTXvBpdAY /Lx5BoouLmQauQDQTbjBfGYG5RwnRhegd8/ye7IK96opELmPbEYNE4nZwpS2kR2P/f 69b+eaqBSPczKhixM6gjvUmAbr2wPHxuYiD03mK/X4ttlEsO/Bphyj2f6fodJBLz5o x/pIVcNONk8eeqieWXHP2tHeZgFYsDUiG0cmZcBQWM4H5qhpZMMYRPwTImmaWtpCOf nA7QYNF7kd3mlb9bWRF6evRuwc9akkjpH7oE6sDpf74c7PdclMPzc1fZBbnIw3SSg+ zxGpEjNJfSLnA== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o " , Jaegeuk Kim , Eric Biggers , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@fb.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH 12/21] btrfs: start using fscrypt hooks. Date: Wed, 17 Aug 2022 10:49:56 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Omar Sandoval In order to appropriately encrypt, create, open, rename, and various symlink operations must call fscrypt hooks. These determine whether the inode should be encrypted and do other preparatory actions. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/ctree.h | 1 + fs/btrfs/file.c | 3 ++ fs/btrfs/inode.c | 91 +++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 83 insertions(+), 12 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 96b72b90a1d0..a7e09b90d326 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3436,6 +3436,7 @@ struct btrfs_new_inode_args { */ struct posix_acl *default_acl; struct posix_acl *acl; + bool encrypt; }; int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args, unsigned int *trans_num_items); diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index b2ffacef9abb..7b0f64ace064 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -3709,6 +3709,9 @@ static int btrfs_file_open(struct inode *inode, struct file *filp) int ret; filp->f_mode |= FMODE_NOWAIT | FMODE_BUF_RASYNC; + ret = fscrypt_file_open(inode, filp); + if (ret) + return ret; ret = fsverity_file_open(inode, filp); if (ret) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 15104263059a..0fdd162d30bc 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5454,6 +5454,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; @@ -5555,6 +5556,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); } @@ -6293,6 +6295,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 */ @@ -6771,9 +6777,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) @@ -7055,6 +7065,7 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode, unsigned long ptr; char *map; size_t size; + size_t inline_size; size_t extent_offset; size_t copy_size; @@ -7062,9 +7073,13 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode, goto out; size = btrfs_file_extent_ram_bytes(leaf, item); + inline_size = btrfs_file_extent_inline_item_len(leaf, path->slots[0]); + ASSERT(btrfs_file_extent_encryption(leaf, item) || + btrfs_file_extent_compression(leaf, item) || + (size == inline_size)); extent_offset = page_offset(page) + pg_offset - extent_start; copy_size = min_t(u64, PAGE_SIZE - pg_offset, - size - extent_offset); + inline_size - extent_offset); em->start = extent_start + extent_offset; em->len = ALIGN(copy_size, fs_info->sectorsize); em->orig_block_len = em->len; @@ -9003,6 +9018,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)); } @@ -9073,8 +9089,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) @@ -9688,6 +9703,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); @@ -9907,15 +9927,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 NULL terminator, but we + * don't store that NULL. + */ + 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) @@ -9924,7 +9951,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; @@ -9952,10 +9979,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) { @@ -9974,10 +10014,11 @@ 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); + /* ram size is the unencoded size */ btrfs_set_file_extent_ram_bytes(leaf, ei, name_len); 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); @@ -9994,6 +10035,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, @@ -11580,7 +11644,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, @@ -11590,4 +11654,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 }; From patchwork Wed Aug 17 14:49: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: 12946028 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 A61F4C32774 for ; Wed, 17 Aug 2022 14:50:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239492AbiHQOuu (ORCPT ); Wed, 17 Aug 2022 10:50:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36654 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240437AbiHQOur (ORCPT ); Wed, 17 Aug 2022 10:50:47 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 82A0C8FD6B; Wed, 17 Aug 2022 07:50:46 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id C0BCB80A9D; Wed, 17 Aug 2022 10:50:45 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1660747846; bh=Rb3/Yvh2vqCnu8VOdaEHR/xCVuwgqNq/Bjv8ql59VhE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NEbWz+CumEEpC38qniNKL53IrP37m4hFwSjRlhL0F4sZ9Ft2brSMTokjYn2JVBQ2t 8h0Jt3re+EOa+d6d47NBip8VfT8OipHSp+MrA8osLwmVPtIK/ehXVuUa5hL3H+xssr tqF5CIi/mkhY2sCxOVEyyQ88rbuDP9YDZ+IcgEmZQ8xcfKniorh7Zm0O5nA2+46qMm xEf0cbZamK/loixJdy+NEjD9swRb6cNdeIJ0OCJTLZ3UBgOj2ByjSwWw1mD3hFgBXp vsB+Toev8fcGUS7sVUjlsGD5gv5CjTB+1DkeaoeT2nlllMpZdEJVutQSN7jmt/YzxL e3mlrOGI6CPHA== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o " , Jaegeuk Kim , Eric Biggers , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@fb.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH 13/21] btrfs: add fscrypt_context items. Date: Wed, 17 Aug 2022 10:49:57 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Omar Sandoval In order to store 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 fix set context item --- fs/btrfs/ctree.h | 3 + fs/btrfs/fscrypt.c | 160 ++++++++++++++++++++++++++++++++ fs/btrfs/inode.c | 38 ++++++++ fs/btrfs/ioctl.c | 10 +- fs/btrfs/tree-checker.c | 1 + include/uapi/linux/btrfs_tree.h | 10 ++ 6 files changed, 221 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index a7e09b90d326..ce2fccc67db5 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -33,6 +33,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" @@ -1674,6 +1675,7 @@ do { \ #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) @@ -1690,6 +1692,7 @@ do { \ 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) diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index 9829d280a6bc..959efc4c179b 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -3,8 +3,13 @@ * Copyright (C) 2020 Facebook */ +#include #include "ctree.h" +#include "btrfs_inode.h" +#include "disk-io.h" #include "fscrypt.h" +#include "transaction.h" +#include "xattr.h" /* fscrypt_match_name() but for an extent_buffer. */ bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname, @@ -31,5 +36,160 @@ bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname, return !memcmp(digest, nokey_name->sha256, sizeof(digest)); } +static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len) +{ + struct btrfs_root *root = BTRFS_I(inode)->root; + struct inode *put_inode = NULL; + struct btrfs_key key; + struct btrfs_path *path; + struct extent_buffer *leaf; + unsigned long ptr; + int ret; + + if (S_ISREG(inode->i_mode) && + (btrfs_root_flags(&root->root_item) & BTRFS_ROOT_SUBVOL_FSCRYPT)) { + /* TODO: maybe cache the item */ + 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; + + key = (struct btrfs_key) { + .objectid = btrfs_ino(BTRFS_I(inode)), + .type = BTRFS_FSCRYPT_CTXT_ITEM_KEY, + .offset = 0, + }; + + 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 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; + struct btrfs_path *path; + struct btrfs_key key = { + .objectid = btrfs_ino(BTRFS_I(inode)), + .type = BTRFS_FSCRYPT_CTXT_ITEM_KEY, + .offset = 0, + }; + + /* + * If the whole subvolume is encrypted, we can get the policy for + * regular files from the root inode. + */ + if (S_ISREG(inode->i_mode) && + (btrfs_root_flags(&root->root_item) & BTRFS_ROOT_SUBVOL_FSCRYPT)) + 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. + */ + trans = fs_data; + } else { + /* + * 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); + } + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + ret = btrfs_search_slot(trans, BTRFS_I(inode)->root, &key, path, 0, 1); + if (ret == 0) { + 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); + btrfs_free_path(path); + goto out; + } else if (ret < 0) { + goto out; + } + btrfs_free_path(path); + + ret = btrfs_insert_item(trans, BTRFS_I(inode)->root, &key, (void *) ctx, len); + if (ret) + goto out; + + 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, root, BTRFS_I(inode)); + if (ret) + goto out; + + /* + * For new subvolumes, the root item is already initialized with + * the BTRFS_ROOT_SUBVOL_FSCRYPT flag. + */ + if (!fs_data && 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); + } +out: + if (fs_data) + return ret; + + if (ret) + btrfs_abort_transaction(trans, ret); + else + btrfs_end_transaction(trans); + return ret; +} + +static bool btrfs_fscrypt_empty_dir(struct inode *inode) +{ + /* + * We don't care about turning on encryption on a non-empty directory + * so we always return true. + */ + return true; +} + 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 0fdd162d30bc..5762e7795838 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6282,6 +6282,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); @@ -6315,6 +6343,8 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args, if (dir->i_security) (*trans_num_items)++; #endif + if (args->encrypt) + (*trans_num_items)++; /* 1 to add fscrypt item */ if (args->orphan) { /* 1 to add orphan item */ (*trans_num_items)++; @@ -6568,6 +6598,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 10b8db56edda..8f5b65c43c8d 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -658,7 +658,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); @@ -787,6 +788,13 @@ 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)) { + btrfs_warn(fs_info, + "cannot snapshot encrypted volume to unencrypted destination"); + 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 2c6d48af1d2a..05f404c7498e 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -1124,6 +1124,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 428ae75b9f73..da44d3355385 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -144,6 +144,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 */ @@ -633,6 +635,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 @@ -788,6 +792,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 Aug 17 14:49: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: 12946029 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 90A80C32789 for ; Wed, 17 Aug 2022 14:50:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240439AbiHQOuu (ORCPT ); Wed, 17 Aug 2022 10:50:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36700 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240433AbiHQOus (ORCPT ); Wed, 17 Aug 2022 10:50:48 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0B3A72A42B; Wed, 17 Aug 2022 07:50:47 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 706768042B; Wed, 17 Aug 2022 10:50:47 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1660747847; bh=AgDYp0BiOtfI423w4RuFeYp4IwenEY64iE6iTJOoayc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kbJw4T6N6spBUk8r8b0Pncnmnyzab+4xsIrJFD6Tgt1IfHFvV9LHSFZPZfVXPl4sl gCm/5r6z9FREdn0L2I+8Rd+cnMiPcbmn3kF4I3ocgmWhcaMuTCh4jUTuL3gFu8aa2V MSHfkyJWuZtRMcDWoLWvS2hghU5M9tnJAmVEfnC5eGakIZBcjqIJqp4cmRDdTECjZp xbMuUSqZ2NDhP7yJVm+g2k+UG5VfUh5NjW+mVBkeoqAvd3zMW5wEyefNDZt8ezy/Pc XT9l2rCqEP1bVw4wlc2trrNImD5W6NC7uRWGP7+dhJU4G03tzZ5oMv4jYDg8vYXOwW /KjqQ6sUeocVQ== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o " , Jaegeuk Kim , Eric Biggers , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@fb.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH 14/21] btrfs: translate btrfs encryption flags and encrypted inode flag. Date: Wed, 17 Aug 2022 10:49:58 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Omar Sandoval In 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 --- 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 8f5b65c43c8d..708e514aca25 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -147,6 +148,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; @@ -176,10 +181,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); } /* @@ -192,7 +201,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 Aug 17 14:49: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: 12946030 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 346F8C3F6B0 for ; Wed, 17 Aug 2022 14:50:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237148AbiHQOuw (ORCPT ); Wed, 17 Aug 2022 10:50:52 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36728 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240442AbiHQOuv (ORCPT ); Wed, 17 Aug 2022 10:50:51 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CAFA2870AE; Wed, 17 Aug 2022 07:50:49 -0700 (PDT) Received: from authenticated-user (box.fidei.email [71.19.144.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by box.fidei.email (Postfix) with ESMTPSA id 31DF180B43; Wed, 17 Aug 2022 10:50:49 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1660747849; bh=5ltf9+aHzLL7uF7ovh9MyC2pwbWq7b6ckluppS6/LWc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Y+VWLScwjDI71g9mMJUqR8xeG9eF9byVgwkTzJ6net42j+7IMS7nnUHsyfPFPTgo7 uiygcoRZSlw3OhAFY3/8TDLGRr0ch59CbPkAXIqvC4ihGSon51O04BPcDopS2laGot V+dK4FXkHs3h/Sy8gaTFxRbyWxQoe/Ity35ES5TbicoasxEUhWcCDZUZSTZlFCAn37 8DE+nEEXEzQkzKSLqHPaCUi+Rp/HlBuSMLJlZSAJZe8V87qoIVFk0awCP9jajz9VX0 V5dPNdRftBzgX+gXhAUCzSKbXZ5yY+Qx3XQUPN6HXOAbxPhxGxaozmgfwistEmngmW wS1O89hyEeAAg== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o " , Jaegeuk Kim , Eric Biggers , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@fb.com Cc: Sweet Tea Dorminy Subject: [PATCH 15/21] btrfs: add iv generation function for fscrypt Date: Wed, 17 Aug 2022 10:49:59 -0400 Message-Id: <229f4296f3105acb17477bf791ecbbb9e5e587a8.1660744500.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org As btrfs cannot use the standard inode or logical block based encryption for data block encryption, it must provide a IV generation function and users must use the IV_FROM_FS policy. For filenames, we can just use the nonce that fscrypt stores per-inode, since these encrypted datum are not shared between inodes; later on, we will store an IV per file extent, and return it in this function for data encryption. Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/fscrypt.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index 959efc4c179b..95a84d426d06 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -187,9 +187,21 @@ static bool btrfs_fscrypt_empty_dir(struct inode *inode) return true; } +static void btrfs_fscrypt_get_iv(u8 *iv, int ivsize, struct inode *inode, + u64 lblk_num) +{ + /* + * For encryption that doesn't involve extent data, juse use the + * nonce already loaded into the iv buffer. + */ + return; +} + const struct fscrypt_operations btrfs_fscrypt_ops = { + .flags = FS_CFLG_ALLOW_PARTIAL, .key_prefix = "btrfs:", .get_context = btrfs_fscrypt_get_context, .set_context = btrfs_fscrypt_set_context, .empty_dir = btrfs_fscrypt_empty_dir, + .get_fs_defined_iv = btrfs_fscrypt_get_iv, }; From patchwork Wed Aug 17 14:50: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: 12946031 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 472CCC28B2B for ; Wed, 17 Aug 2022 14:50:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240433AbiHQOuz (ORCPT ); Wed, 17 Aug 2022 10:50:55 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36744 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240166AbiHQOuy (ORCPT ); Wed, 17 Aug 2022 10:50:54 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B65921C907; Wed, 17 Aug 2022 07:50:51 -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 E16CE80A9D; Wed, 17 Aug 2022 10:50:50 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1660747851; bh=XditLWTxkR/uSOWyTQgMCd7tp15uOOHfDUY2N6XS/dY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=k5vc2rtOrHXaHh5A3UUgmnSeOROcenwkCF8x1LN0b5lu95wWrszXcBWM6+m0++AWJ yKQvGojLKCmtFmIRW7nHDz2vCLPofT7eWAnihmTc6pZ4OzJTdhBfvU+iuKYZoQUld4 eEwdS331jO/nUMrCkb1lD7yW/1nNTFCYY4NP8kuPKZGEtYjLoqK+BJVXudeee74cFl X02CVt+ZzD1DLEKAQLTTCJq6Zu/F1WulJVPSTlQb9fS1W72m5DtVUr3oWfZ09pj89Y zadYSLCc1NNpx70tXQhUQK+PE/uMB9rO5p8lv/Vyt0hEXJXNQGwUPjECv3k6RvhT9J zrm3GMsCP8fng== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o " , Jaegeuk Kim , Eric Biggers , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@fb.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH 16/21] btrfs: store an IV per encrypted normal file extent Date: Wed, 17 Aug 2022 10:50:00 -0400 Message-Id: <9f9e66cde4181368b691c94649df56f952e55ca4.1660744500.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Omar Sandoval In order to encrypt data, each file extent must have its own persistent random IV, which is then provided to fscrypt upon request. This IV is only needed for encrypted extents and is of variable size on disk, so file extents must additionally keep track of their actual length. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/ctree.h | 29 +++++++++++++ fs/btrfs/extent_map.h | 8 ++++ fs/btrfs/file-item.c | 11 +++++ fs/btrfs/file.c | 4 +- fs/btrfs/fscrypt.h | 23 +++++++++++ fs/btrfs/inode.c | 72 +++++++++++++++++++++++++-------- fs/btrfs/ordered-data.c | 13 +++++- fs/btrfs/ordered-data.h | 3 +- fs/btrfs/reflink.c | 1 + fs/btrfs/tree-checker.c | 36 +++++++++++++---- fs/btrfs/tree-log.c | 9 +++++ include/uapi/linux/btrfs_tree.h | 9 +++++ 12 files changed, 189 insertions(+), 29 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index ce2fccc67db5..40ceaef462ac 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -37,6 +37,7 @@ #include "async-thread.h" #include "block-rsv.h" #include "locking.h" +#include "fscrypt.h" struct btrfs_trans_handle; struct btrfs_transaction; @@ -1444,6 +1445,7 @@ struct btrfs_replace_extent_info { u64 file_offset; /* Pointer to a file extent item of type regular or prealloc. */ char *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 @@ -2679,6 +2681,15 @@ 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_ivsize(struct btrfs_file_extent_item *e) +{ + u8 ivsize; + btrfs_unpack_encryption(e->encryption, NULL, &ivsize); + return ivsize; +} static inline unsigned long btrfs_file_extent_inline_start(const struct btrfs_file_extent_item *e) @@ -2711,6 +2722,24 @@ 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_ivsize(const struct extent_buffer *eb, + struct btrfs_file_extent_item *e) +{ + u8 ivsize; + btrfs_unpack_encryption(btrfs_file_extent_encryption(eb, e), + NULL, &ivsize); + return ivsize; +} + +static inline u8 +btrfs_file_extent_ivsize_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)); +} + /* * this 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 diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h index d2fa32ffe304..cc077d15062b 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 iv */ + EXTENT_FLAG_ENCRYPTED, }; struct extent_map { @@ -50,6 +53,11 @@ struct extent_map { */ u64 generation; unsigned long flags; + /* + * TODO: could either make FSCRYPT_MAX_IV_SIZE public or allocate this + * separately, of size 16 if applicable to the specific policy. + */ + u8 iv[FSCRYPT_MAX_IV_SIZE]; /* 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 29999686d234..066d59707408 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -1216,6 +1216,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 ivsize; em->start = extent_start; em->len = extent_end - extent_start; em->orig_start = extent_start - @@ -1231,6 +1232,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; @@ -1238,6 +1243,12 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode, if (type == BTRFS_FILE_EXTENT_PREALLOC) set_bit(EXTENT_FLAG_PREALLOC, &em->flags); } + + ivsize = btrfs_file_extent_ivsize_from_item(leaf, path); + ASSERT(ivsize == btrfs_file_extent_encryption_ivsize(leaf, fi)); + + read_extent_buffer(leaf, em->iv, (unsigned long)fi->iv, + ivsize); } 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 7b0f64ace064..f763837917fb 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -2658,14 +2658,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.h b/fs/btrfs/fscrypt.h index 07884206c8a1..52646bd84e43 100644 --- a/fs/btrfs/fscrypt.h +++ b/fs/btrfs/fscrypt.h @@ -5,6 +5,9 @@ #include +#define BTRFS_ENCRYPTION_POLICY_MASK 0x0f +#define BTRFS_ENCRYPTION_IVSIZE_MASK 0xf0 + #ifdef CONFIG_FS_ENCRYPTION bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname, struct extent_buffer *leaf, @@ -22,5 +25,25 @@ static bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname, } #endif +static inline void btrfs_unpack_encryption(u8 encryption, + u8 *policy, + u8 *ivsize) +{ + if (policy) + *policy = encryption & BTRFS_ENCRYPTION_POLICY_MASK; + if (ivsize) { + u8 transformed_ivsize = + (encryption & BTRFS_ENCRYPTION_IVSIZE_MASK) >> 4; + *ivsize = (transformed_ivsize ? + (1 << (transformed_ivsize - 1)) : 0); + } +} + +static inline u8 btrfs_pack_encryption(u8 policy, u8 ivsize) +{ + u8 transformed_ivsize = ivsize ? ilog2(ivsize) + 1 : 0; + return policy | (transformed_ivsize << 4); +} + 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 5762e7795838..836b6f25c283 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include "misc.h" @@ -1024,7 +1025,7 @@ 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, NULL); if (ret) { btrfs_drop_extent_cache(inode, start, end, 0); goto out_free_reserve; @@ -1302,7 +1303,7 @@ static noinline int cow_file_range(struct btrfs_inode *inode, 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, NULL); if (ret) goto out_drop_extent_cache; @@ -2100,7 +2101,7 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode, nocow_args.disk_bytenr, nocow_args.num_bytes, 0, 1 << BTRFS_ORDERED_PREALLOC, - BTRFS_COMPRESS_NONE); + BTRFS_COMPRESS_NONE, NULL); if (ret) { btrfs_drop_extent_cache(inode, cur_offset, nocow_end, 0); @@ -2114,7 +2115,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; } @@ -3050,6 +3052,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, + const u8 *iv, const bool update_inode_bytes, u64 qgroup_reserved) { @@ -3065,6 +3068,7 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, u64 ram_bytes = btrfs_stack_file_extent_ram_bytes(stack_fi); struct btrfs_drop_extents_args drop_args = { 0 }; int ret; + int ivsize = fscrypt_mode_ivsize(&inode->vfs_inode); path = btrfs_alloc_path(); if (!path) @@ -3083,7 +3087,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) + ivsize; ret = btrfs_drop_extents(trans, root, inode, &drop_args); if (ret) goto out; @@ -3094,7 +3098,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) + ivsize); if (ret) goto out; } @@ -3103,6 +3107,11 @@ 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)); + if (ivsize) + write_extent_buffer(leaf, iv, + btrfs_item_ptr_offset(leaf, path->slots[0]) + + sizeof(struct btrfs_file_extent_item), + ivsize); btrfs_mark_buffer_dirty(leaf); btrfs_release_path(path); @@ -3179,7 +3188,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, + fscrypt_mode_ivsize(oe->inode)); + 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 @@ -3193,6 +3207,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->iv, update_inode_bytes, oe->qgroup_rsv); } @@ -7096,8 +7111,25 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode, btrfs_extent_item_to_extent_map(inode, path, item, !page, em); - if (extent_type == BTRFS_FILE_EXTENT_REG || - extent_type == BTRFS_FILE_EXTENT_PREALLOC) { + if (extent_type == BTRFS_FILE_EXTENT_REG) { + u8 ivsize = btrfs_file_extent_ivsize_from_item(leaf, path); + u8 encryption = btrfs_file_extent_encryption(leaf, item); + u8 policy, item_ivsize; + btrfs_unpack_encryption(encryption, &policy, &item_ivsize); + + if (policy == BTRFS_ENCRYPTION_FSCRYPT) { + u8 inode_ivsize = fscrypt_mode_ivsize(&inode->vfs_inode); + + if (ivsize != inode_ivsize || ivsize != item_ivsize) { + btrfs_crit(fs_info, + "invalid encryption IV size for inode %llu: itemsize %d item %d inode %d", + btrfs_ino(inode), ivsize, item_ivsize, inode_ivsize); + ret = -EUCLEAN; + goto out; + } + } + goto insert; + } else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC) { goto insert; } else if (extent_type == BTRFS_FILE_EXTENT_INLINE) { unsigned long ptr; @@ -7331,7 +7363,7 @@ 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, NULL); if (ret) { if (em) { free_extent_map(em); @@ -7632,6 +7664,8 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start, em->compress_type = compress_type; } + fscrypt_generate_random_iv(&inode->vfs_inode, em->iv); + do { btrfs_drop_extent_cache(inode, em->start, em->start + em->len - 1, 0); @@ -9971,6 +10005,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 NULL terminator, but we @@ -10049,7 +10084,9 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, btrfs_set_file_extent_generation(leaf, ei, trans->transid); btrfs_set_file_extent_type(leaf, ei, BTRFS_FILE_EXTENT_INLINE); - btrfs_set_file_extent_encryption(leaf, ei, 0); + encryption = btrfs_pack_encryption(IS_ENCRYPTED(inode) ? + BTRFS_ENCRYPTION_FSCRYPT : 0, 0); + btrfs_set_file_extent_encryption(leaf, ei, encryption); btrfs_set_file_extent_compression(leaf, ei, 0); btrfs_set_file_extent_other_encoding(leaf, ei, 0); /* ram size is the unencoded size */ @@ -10119,16 +10156,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; @@ -10140,6 +10179,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; @@ -11090,7 +11130,7 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from, encoded->unencoded_offset, (1 << BTRFS_ORDERED_ENCODED) | (1 << BTRFS_ORDERED_COMPRESSED), - compression); + compression, NULL); if (ret) { btrfs_drop_extent_cache(inode, start, end, 0); goto out_free_reserved; diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index eb24a6d20ff8..7e1d2812527e 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include "misc.h" @@ -164,13 +165,14 @@ 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, u8 *iv) { struct btrfs_root *root = inode->root; struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_ordered_inode_tree *tree = &inode->ordered_tree; struct rb_node *node; struct btrfs_ordered_extent *entry; + const u8 ivsize = fscrypt_mode_ivsize(&inode->vfs_inode); int ret; if (flags & @@ -199,6 +201,13 @@ 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; + if (ivsize) { + if (iv == NULL) + fscrypt_generate_random_iv(&inode->vfs_inode, + entry->iv); + else + memcpy(entry->iv, iv, ivsize); + } entry->bytes_left = num_bytes; entry->inode = igrab(&inode->vfs_inode); entry->compress_type = compress_type; @@ -1080,7 +1089,7 @@ 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->iv); } 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 87792f85e2c4..e738ecb70a89 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; + u8 iv[FSCRYPT_MAX_IV_SIZE]; /* number of bytes that still need writing */ u64 bytes_left; @@ -194,7 +195,7 @@ 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, u8 *iv); 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 d22086e1cbc8..466dfccba094 100644 --- a/fs/btrfs/reflink.c +++ b/fs/btrfs/reflink.c @@ -495,6 +495,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 05f404c7498e..e6b3f92ab56f 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -204,6 +204,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, @@ -255,10 +256,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) { @@ -287,12 +290,29 @@ 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 ivsize = btrfs_file_extent_encryption_ivsize(leaf, fi); + if (unlikely(item_size != sizeof(*fi) + ivsize)) { + file_extent_err(leaf, slot, + "invalid item size for encrypted file extent, have %u expect = %zu + iv of size %u", + item_size, sizeof(*fi), ivsize); + 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 ee7b80d953e9..09b02c3dc77d 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -4622,6 +4622,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 ivsize = fscrypt_mode_ivsize(&inode->vfs_inode); + u8 encryption = btrfs_pack_encryption(IS_ENCRYPTED(&inode->vfs_inode) ? + BTRFS_ENCRYPTION_FSCRYPT : 0, ivsize); btrfs_set_stack_file_extent_generation(&fi, trans->transid); if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) @@ -4643,6 +4646,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) @@ -4682,6 +4686,11 @@ 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)); + if (ivsize) + write_extent_buffer(leaf, em->iv, + btrfs_item_ptr_offset(leaf, path->slots[0]) + + sizeof(fi), ivsize); + 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 da44d3355385..6a2a37edd326 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -820,6 +820,10 @@ struct btrfs_file_extent_item { * but not for stat. */ __u8 compression; + /* + * This field contains 4 bits of encryption type in the lower bits, + * 4 bits of ivsize in the upper bits. The unencrypted value is 0. + */ __u8 encryption; __le16 other_encoding; /* spare for later use */ @@ -848,6 +852,11 @@ struct btrfs_file_extent_item { */ __le64 num_bytes; + /* + * Encryption initialization vector. Only present if extent is + * encrypted (stored in the encryption field). + */ + __u8 iv[0]; } __attribute__ ((__packed__)); struct btrfs_csum_item { From patchwork Wed Aug 17 14:50: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: 12946032 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 4FEECC32774 for ; Wed, 17 Aug 2022 14:50:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240075AbiHQOu4 (ORCPT ); Wed, 17 Aug 2022 10:50:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36746 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240437AbiHQOuy (ORCPT ); Wed, 17 Aug 2022 10:50:54 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 415C83E749; Wed, 17 Aug 2022 07:50:53 -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 9F12281140; Wed, 17 Aug 2022 10:50:52 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1660747852; bh=ORZrkqsA05BXOYu2otqHJ6RtbItHF/27zv6HhRn6/JA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=aIzq7OZ/vIJ6siSv4LsIptpRxUMfIdCJucppaSOK1tCGakV1Uw9eIqWnUdJ4njCjy +OANC7d61TU1v3VXkjlx3c97TGfEhEnf6c4Qjn2bZzklTryvFUBcGcYeFdZVIVboIz HJaAZKdnJa75yhyXD2iXxsn4y06vIicnmFGBQ92DAHredlRV8rubaSxGMBbDFt6Zzw NRmU3lj6MwMyJHnHEDIKcNZTEpU+qtP2QYGm8wbPBz+OUc2F0+zAM68oirmCZ0X2Ve xGLGi2Syq+Uix3bpxEzTIo/5iazp3AwfYP6tV8kXYHrB2tqRpJqDgPEIOlZpkPyvV+ J+8HPIuJ8XBtA== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o " , Jaegeuk Kim , Eric Biggers , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@fb.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH 17/21] btrfs: Add new FEATURE_INCOMPAT_FSCRYPT feature flag. Date: Wed, 17 Aug 2022 10:50:01 -0400 Message-Id: <2f7e98905d9275ad6f5515678c081a085380999b.1660744500.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Omar Sandoval As fscrypt files will be incompatible with older filesystem versions, new filesystems should be created with an incompat flag for fscrypt. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/ctree.h | 6 ++++-- include/uapi/linux/btrfs.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 40ceaef462ac..be4a3458b4d6 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -334,7 +334,8 @@ static_assert(sizeof(struct btrfs_super_block) == BTRFS_SUPER_INFO_SIZE); 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_FSCRYPT) #else #define BTRFS_FEATURE_INCOMPAT_SUPP \ (BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF | \ @@ -349,7 +350,8 @@ static_assert(sizeof(struct btrfs_super_block) == BTRFS_SUPER_INFO_SIZE); BTRFS_FEATURE_INCOMPAT_NO_HOLES | \ BTRFS_FEATURE_INCOMPAT_METADATA_UUID | \ BTRFS_FEATURE_INCOMPAT_RAID1C34 | \ - BTRFS_FEATURE_INCOMPAT_ZONED) + BTRFS_FEATURE_INCOMPAT_ZONED | \ + BTRFS_FEATURE_INCOMPAT_FSCRYPT) #endif #define BTRFS_FEATURE_INCOMPAT_SAFE_SET \ diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index f54dc91e4025..b19fc3725769 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -310,6 +310,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_FSCRYPT (1ULL << 14) struct btrfs_ioctl_feature_flags { __u64 compat_flags; From patchwork Wed Aug 17 14:50: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: 12946033 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 EACCFC32792 for ; Wed, 17 Aug 2022 14:50:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240465AbiHQOu5 (ORCPT ); Wed, 17 Aug 2022 10:50:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36764 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240464AbiHQOu4 (ORCPT ); Wed, 17 Aug 2022 10:50:56 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D83C43E749; Wed, 17 Aug 2022 07:50:55 -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 8A1198042B; Wed, 17 Aug 2022 10:50:54 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1660747854; bh=Wj5zQSc6tWfXdik16XxEGRmNwVcIzILAIatwxRAZUI8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=XBFSfzUBSoWiLXeZVmridWYJSdJnutH2sPux8OYMiY4afKe/dF1HlfHA+ueYbEgyP QC5jK7cWYi1llZLloozWRDy/WIFGFIDfVP3tZwzuukp/DZn7AmzoTS6w0NWBiFzPYF /RSqVTGPaXUDShqbbP042H9sA5fWOwo8+vQAraeTLkbDCznJd8hUhjyvJS/Llbvxxg bw7rYcoErk+2pu1kYAMj2Z26/Rp6JAmOQwFOkjbOHem2jdMlOfLnnaIG0JItvxISqD Fp3NyWpx3VQkmNMu/F24WATv/d/nIx5WN5/XPz7vp2XyOoo7sQ8YyAwbAv+WI4YyWD 7+X2nDcAmcDBA== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o " , Jaegeuk Kim , Eric Biggers , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@fb.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH 18/21] btrfs: reuse encrypted filename hash when possible. Date: Wed, 17 Aug 2022 10:50:02 -0400 Message-Id: <00906e96b2da0184dfac91b34814d8aa6177da4f.1660744500.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Omar Sandoval For encrypted fscrypt_names, we can reuse fscrypt's precomputed hash of the encrypted name to generate our own hash, instead of rehashing the unencrypted name (which may not be possible if it's a nokey name). Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/ctree.h | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index be4a3458b4d6..12a9341bb13b 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -2861,7 +2861,10 @@ static inline void btrfs_crc32c_final(u32 crc, u8 *result) static inline u64 btrfs_name_hash(const struct fscrypt_name *name) { - return crc32c((u32)~1, fname_name(name), fname_len(name)); + if (fname_name(name)) + return crc32c((u32)~1, fname_name(name), fname_len(name)); + else + return name->hash | ((u64)name->minor_hash << 32); } /* @@ -2870,8 +2873,20 @@ static inline u64 btrfs_name_hash(const struct fscrypt_name *name) static inline u64 btrfs_extref_hash(u64 parent_objectid, const struct fscrypt_name *name) { - return (u64) crc32c(parent_objectid, fname_name(name), - fname_len(name)); + /* + * If the name is encrypted and we don't have the key, we can use the + * fscrypt-provided hash instead of the normal name, and do the steps + * of crc32c() manually. Else, just hash the name, parent objectid, + * and name length. + */ + if (fname_name(name)) + return (u64) crc32c(parent_objectid, fname_name(name), + fname_len(name)); + else + return (__crc32c_le_combine(parent_objectid, + name->hash, + fname_len(name)) ^ + __crc32c_le_shift(~1, fname_len(name))); } static inline gfp_t btrfs_alloc_write_mask(struct address_space *mapping) From patchwork Wed Aug 17 14:50: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: 12946034 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 62C44C32774 for ; Wed, 17 Aug 2022 14:51:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240401AbiHQOu7 (ORCPT ); Wed, 17 Aug 2022 10:50:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36784 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240466AbiHQOu6 (ORCPT ); Wed, 17 Aug 2022 10:50:58 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F148976749; Wed, 17 Aug 2022 07:50:56 -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 3DA7480A9D; Wed, 17 Aug 2022 10:50:56 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1660747856; bh=AYZX7xfETmvB8D/8x6n4nbc0pzKjNysWuSFawmGP/jY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=vJXAOgtjZ6Hx2yF/Ypglz5s6oYRs4nP27hRRoZ0uZ3Zg0OqXZsz0d5fCtMnLTl2BV qpDUOdj41ImVp3MYyh22/fTW6l+J+SY8jB8JC4JQyBGhq9EE/BDCxzd3os9tgfkDqc x8CY0anI+CbkkLfNgd18tbXsFxsl0A/Fyo6CW/Mcf96Kkgbm3vMEfvZgK8YqS3RCRk FDfzGRP7byS4Bqio3XrCRELWS14JsnKYNV5/y7MQcP6OPQvmabT/j+IVwMxmMwyY0d HsyC8BUiRgkWsi+wQH4rjvaRmEMz0Q6WLyE3qrKfTZWO3ox0EQMZKgwi5/G+AanbJw AA91coaEb01Fw== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o " , Jaegeuk Kim , Eric Biggers , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@fb.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH 19/21] btrfs: adapt directory read and lookup to potentially encrypted filenames Date: Wed, 17 Aug 2022 10:50:03 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Omar Sandoval When filenames can be encrypted, if we don't initially match a desired filename, we have to try decrypting it with the directory key to see if it matches in plaintext. Similarly, for readdir, we need to read encrypted directory items as well as unencrypted. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy --- fs/btrfs/delayed-inode.c | 32 +++++++++++--- fs/btrfs/delayed-inode.h | 4 +- fs/btrfs/dir-item.c | 23 ++++++++++ fs/btrfs/inode.c | 90 ++++++++++++++++++++++++++++++++++++---- 4 files changed, 134 insertions(+), 15 deletions(-) diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index 9a4882960455..b468f9cadeb6 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -1524,9 +1524,9 @@ 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)", - fname_len(fname), fname_name(fname), + "err add delayed dir index item into the insertion tree of the delayed node (root id: %llu, inode id: %llu, errno: %d)", delayed_node->root->root_key.objectid, delayed_node->inode_id, ret); BUG(); @@ -1759,7 +1759,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; @@ -1769,6 +1771,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; @@ -1796,8 +1799,27 @@ 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_FSCRYPT_NAME) { + 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 968461b3c350..aa3c67d572e4 100644 --- a/fs/btrfs/delayed-inode.h +++ b/fs/btrfs/delayed-inode.h @@ -142,7 +142,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); /* for init */ diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c index 8d7c3c32ed8e..6b1ea32419fb 100644 --- a/fs/btrfs/dir-item.c +++ b/fs/btrfs/dir-item.c @@ -120,6 +120,9 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_disk_key disk_key; u32 data_size; + if (fname->crypto_buf.name) + type |= BTRFS_FT_FSCRYPT_NAME; + key.objectid = btrfs_ino(dir); key.type = BTRFS_DIR_ITEM_KEY; key.offset = btrfs_name_hash(fname); @@ -385,6 +388,18 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info, u32 cur = 0; u32 this_len; struct extent_buffer *leaf; + bool encrypted = (fname->crypto_buf.name != NULL); + struct fscrypt_name unencrypted_fname; + + if (encrypted) { + unencrypted_fname = (struct fscrypt_name){ + .usr_fname = fname->usr_fname, + .disk_name = { + .name = (unsigned char *)fname->usr_fname->name, + .len = fname->usr_fname->len, + }, + }; + } leaf = path->nodes[0]; dir_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item); @@ -401,6 +416,14 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info, return dir_item; } + if (encrypted && + btrfs_dir_name_len(leaf, dir_item) == fname_len(&unencrypted_fname) && + btrfs_fscrypt_match_name(&unencrypted_fname, leaf, + (unsigned long)(dir_item + 1), + dir_name_len)) { + return dir_item; + } + cur += this_len; dir_item = (struct btrfs_dir_item *)((char *)dir_item + this_len); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 836b6f25c283..d0fe956c0927 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4294,6 +4294,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans, u64 index; u64 ino = btrfs_ino(inode); u64 dir_ino = btrfs_ino(dir); + u64 di_name_len; path = btrfs_alloc_path(); if (!path) { @@ -4306,6 +4307,13 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans, ret = di ? PTR_ERR(di) : -ENOENT; goto err; } + + /* + * We have to read the actual name length off disk -- the fname + * provided may have been a nokey_name with uncertain length. + */ + di_name_len = btrfs_dir_name_len(path->nodes[0], di); + ret = btrfs_delete_one_dir_name(trans, root, path, di); if (ret) goto err; @@ -4373,7 +4381,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 - fname_len(fname) * 2); + btrfs_i_size_write(dir, dir->vfs_inode.i_size - di_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); @@ -5884,12 +5892,25 @@ 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); + if (BTRFS_I(dir)->flags & BTRFS_INODE_FSCRYPT_CONTEXT) { + ret = fscrypt_prepare_lookup(dir, dentry, &fname); + if (ret) + return ERR_PTR(ret); + } else { + fname = (struct fscrypt_name) { + .usr_fname = &dentry->d_name, + .disk_name = FSTR_INIT((char *) dentry->d_name.name, + dentry->d_name.len), + }; + } + ret = btrfs_inode_by_name(dir, dentry, &location, &di_type); if (ret < 0) return ERR_PTR(ret); @@ -6031,18 +6052,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; @@ -6060,6 +6095,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 di_flags; + u32 nokey_len; if (found_key.objectid != key.objectid) break; @@ -6071,8 +6107,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) @@ -6086,8 +6127,36 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) di_flags = 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 (di_flags & BTRFS_FT_FSCRYPT_NAME) { + 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. + */ + if (!fscrypt_has_encryption_key(inode)) { + struct fscrypt_name fname = { + .disk_name = fstr, + }; + u64 name_hash = btrfs_name_hash(&fname); + 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(btrfs_dir_flags_to_ftype(di_flags)), @@ -6109,7 +6178,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; @@ -6140,6 +6210,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; } From patchwork Wed Aug 17 14:50: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: 12946035 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 B9923C25B08 for ; Wed, 17 Aug 2022 14:51:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236443AbiHQOv0 (ORCPT ); Wed, 17 Aug 2022 10:51:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37040 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236240AbiHQOvZ (ORCPT ); Wed, 17 Aug 2022 10:51:25 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9D49A2A42B; Wed, 17 Aug 2022 07:51:24 -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 65DEC8042B; Wed, 17 Aug 2022 10:50:58 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1660747884; bh=OlozKYfOnwxc30qMXzB0M/kFw+zUFBtMZOWMgXEidbM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nLy+la3yUHZ3OhlGYugI+Lm0kydP1S1Fh15eBHzEmHdLNBywk3SdbSfQRrBgAOYK3 B2jyHM1Qpjf+nAZAUHXNE/d9OFcuI8cRAMu1Yy8R+wDrqlfoBAASLE056yb+W/3AUB 5nS7iDxrJooEVAGggXIWkIaZBzXvsg4geDjpncVO1GtWRhQE+v4MYi2DhqXzcwfa3H i99tJf0wT277gImin7O8yKfAUAEsW8uVAHqi75axNcbz9IK/mPeUOXqwRQWtanJDYA eePGRQgymy3ug8joyL+W/IB9vZq96sQY0pt0KAvJLllTySUHIDZKsav3ApUqpYSAcs 7u1YFN8vX9ThQ== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o " , Jaegeuk Kim , Eric Biggers , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@fb.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH 20/21] btrfs: encrypt normal file extent data if appropriate Date: Wed, 17 Aug 2022 10:50:04 -0400 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Omar Sandoval Add in the necessary calls to encrypt and decrypt data to achieve encryption of normal data. 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 | 27 ++++++++++++++++---- fs/btrfs/inode.c | 19 +++++++------- fs/btrfs/tree-checker.c | 11 +++++--- 5 files changed, 97 insertions(+), 25 deletions(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index ff2b995ded6e..440f8b41469b 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -184,6 +184,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; @@ -192,13 +193,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); @@ -2836,9 +2841,19 @@ static void end_bio_extent_writepage(struct bio *bio) 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)) @@ -2859,7 +2874,7 @@ static void end_bio_extent_writepage(struct bio *bio) } end_extent_writepage(page, error, start, end); - + fscrypt_free_bounce_page(bounce_page); btrfs_page_clear_writeback(fs_info, page, start, bvec->bv_len); } @@ -3058,6 +3073,17 @@ static void end_bio_extent_readpage(struct bio *bio) } } + 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; @@ -3415,11 +3441,29 @@ static int submit_extent_page(unsigned int 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); if (force_bio_submit) diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index 066d59707408..c3780eacdd35 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -663,8 +663,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 95a84d426d06..df19c7690b12 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -190,11 +190,28 @@ static bool btrfs_fscrypt_empty_dir(struct inode *inode) static void btrfs_fscrypt_get_iv(u8 *iv, int ivsize, struct inode *inode, u64 lblk_num) { - /* - * For encryption that doesn't involve extent data, juse use the - * nonce already loaded into the iv buffer. - */ - return; + u64 offset = lblk_num << inode->i_blkbits; + struct extent_map *em; + + if (lblk_num == 0) { + /* Must be a filename or a symlink. Just use the nonce. */ + return; + } + + /* Since IO must be in progress on this extent, this must succeed */ + em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, PAGE_SIZE); + ASSERT(!IS_ERR(em) && em); + if (em) { + __le64 *iv_64 = (__le64 *)iv; + memcpy(iv, em->iv, ivsize); + /* + * Add the lblk_num to the low bits of the IV to ensure + * the IV changes for every page + */ + *iv_64 = cpu_to_le64(le64_to_cpu(*iv_64) + lblk_num); + free_extent_map(em); + return; + } } const struct fscrypt_operations btrfs_fscrypt_ops = { diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index d0fe956c0927..8e07d4f2a1b7 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1016,7 +1016,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 */ @@ -1025,7 +1024,8 @@ 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, NULL); + async_extent->compress_type, em->iv); + free_extent_map(em); if (ret) { btrfs_drop_extent_cache(inode, start, end, 0); goto out_free_reserve; @@ -1298,12 +1298,12 @@ 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, NULL); + BTRFS_COMPRESS_NONE, em->iv); + free_extent_map(em); if (ret) goto out_drop_extent_cache; @@ -2094,14 +2094,14 @@ 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, NULL); + BTRFS_COMPRESS_NONE, em->iv); + free_extent_map(em); if (ret) { btrfs_drop_extent_cache(inode, cur_offset, nocow_end, 0); @@ -7435,7 +7435,7 @@ static struct extent_map *btrfs_create_dio_extent(struct btrfs_inode *inode, block_len, 0, (1 << type) | (1 << BTRFS_ORDERED_DIRECT), - BTRFS_COMPRESS_NONE, NULL); + BTRFS_COMPRESS_NONE, em->iv); if (ret) { if (em) { free_extent_map(em); @@ -7709,6 +7709,7 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start, struct extent_map_tree *em_tree; struct extent_map *em; int ret; + const u8 ivsize = fscrypt_mode_ivsize(&inode->vfs_inode); ASSERT(type == BTRFS_ORDERED_PREALLOC || type == BTRFS_ORDERED_COMPRESSED || @@ -11195,14 +11196,14 @@ 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, NULL); + compression, em->iv); + free_extent_map(em); if (ret) { btrfs_drop_extent_cache(inode, start, end, 0); goto out_free_reserved; diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index e6b3f92ab56f..1ccfaa138f55 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -273,9 +273,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 Aug 17 14:50: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: 12946036 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 80442C32772 for ; Wed, 17 Aug 2022 14:51:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240006AbiHQOv2 (ORCPT ); Wed, 17 Aug 2022 10:51:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37070 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236240AbiHQOv1 (ORCPT ); Wed, 17 Aug 2022 10:51:27 -0400 Received: from box.fidei.email (box.fidei.email [71.19.144.250]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AB40476749; Wed, 17 Aug 2022 07:51: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 143B080B43; Wed, 17 Aug 2022 10:51:25 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=dorminy.me; s=mail; t=1660747886; bh=1njMgtLMCKXOazQmPtODjiDRbsI7pkHmvUqzqv2QlzQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=t+GgFQEknqzOEOIaMBv0Jba/q7Gq3IeEREQhcecd8byYMyRmB96dRChM8J6m4MTuW vbtTKevs3LvlBx2ty7qsIHxwFK+ZVRcRP4OkEhEt/p0pqdwDVIIwkVwSXdf45uxhZh L3i1joKdPZUUFzP6XMEaQRFqrrGi/7Hq3mA7B55NL450zEonsPp7BjumE7G0sfzvdA xfGWB0kXxvWbD/aAt+VpUC/JmlEAq5C6yjHseLJzxGn59iX0G8z35z5nTl6/frngCM vj4oljXzKU9JHwca6cTqAi7NgW5WpRJvZXHyVYeDd5LjouOZ0ZQDc5SqBerRJ5RIEG KvJqAjKta9h3Q== From: Sweet Tea Dorminy To: Chris Mason , Josef Bacik , David Sterba , "Theodore Y . Ts'o " , Jaegeuk Kim , Eric Biggers , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, kernel-team@fb.com Cc: Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH 21/21] btrfs: implement fscrypt ioctls Date: Wed, 17 Aug 2022 10:50:05 -0400 Message-Id: <84f78bb6c3d95a9b1fbd51abe4451c02f234c775.1660744500.git.sweettea-kernel@dorminy.me> In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Omar Sandoval These ioctls allow encryption to be set up. 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 708e514aca25..ea1c14b26206 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -5457,6 +5457,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, FSCRYPT); + 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: