From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4D02F426D11 for ; Fri, 6 Feb 2026 18:23:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402236; cv=none; b=miaamSjudmo9lTYMBRhOiZ9farIW1HE0W8zj5MVL8cZZRf/w3nzVUK/x75+IGLiHNolZyA8X0CZY8gUWyujHLHGJiVqGomeBHwOGStuZ5RTOnGhhmxvKO+q8bOJ8zFIU77FECHzBmF5YNJ+zhoo5VbbIARcmP2YvdPELRnpAKbs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402236; c=relaxed/simple; bh=kqgvlO6eUzLn2d85mw7XQwIpiVD9orgONQSTd1aAjtQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=FvgEci+gS/vpxPk7GA0WYrQjFvyPccgenrzhyXmQCqQATMxLjHtzsV08iRZAtT8EMOwk/fPKttGGvH61JFpd7oDT0LbNFRPUlF5fuhqRT3WHzfDxhUsYMf3ULna6KSehVnZTTVehllllE8aXe0srzhZ/Cj7EU78qX88PfdxlcFE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=GHLSpuHB; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=GHLSpuHB; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="GHLSpuHB"; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="GHLSpuHB" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id A4A843E72D; Fri, 6 Feb 2026 18:23:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402234; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=pfIQDeUmEf5ccatb2Jkls8D0bbgJ78aaEFsNSXvLjrY=; b=GHLSpuHBIYwAJb9BxFzsPqPrkA6XxfzDII0cyq4GYHdMIqAQQAFANLCK57IvdrcRYlNYQn BhAzgrpLC2gdyFXkPMrSlrwR/fYhhw3D5yJEhKHZ1WjYIyewyKqcQ7srl8q2IMCobvwowG Yxe+TgeMGHaJP1hHu5L0wLO/+Sccwz0= Authentication-Results: smtp-out1.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402234; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=pfIQDeUmEf5ccatb2Jkls8D0bbgJ78aaEFsNSXvLjrY=; b=GHLSpuHBIYwAJb9BxFzsPqPrkA6XxfzDII0cyq4GYHdMIqAQQAFANLCK57IvdrcRYlNYQn BhAzgrpLC2gdyFXkPMrSlrwR/fYhhw3D5yJEhKHZ1WjYIyewyKqcQ7srl8q2IMCobvwowG Yxe+TgeMGHaJP1hHu5L0wLO/+Sccwz0= Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 787D33EA63; Fri, 6 Feb 2026 18:23:54 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id 8FvOHLoxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:23:54 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 01/43] fscrypt: add per-extent encryption support Date: Fri, 6 Feb 2026 19:22:33 +0100 Message-ID: <20260206182336.1397715-2-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spamd-Result: default: False [-6.80 / 50.00]; REPLY(-4.00)[]; BAYES_HAM(-3.00)[100.00%]; NEURAL_HAM_LONG(-1.00)[-1.000]; MID_CONTAINS_FROM(1.00)[]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; RCVD_COUNT_TWO(0.00)[2]; FROM_HAS_DN(0.00)[]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; RCPT_COUNT_TWELVE(0.00)[12]; TO_MATCH_ENVRCPT_ALL(0.00)[]; RCVD_VIA_SMTP_AUTH(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; DKIM_SIGNED(0.00)[suse.com:s=susede1]; FUZZY_RATELIMITED(0.00)[rspamd.com]; TO_DN_SOME(0.00)[]; DBL_BLOCKED_OPENRESOLVER(0.00)[suse.com:mid,suse.com:email]; RCVD_TLS_ALL(0.00)[] X-Spam-Flag: NO X-Spam-Score: -6.80 X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik This adds the code necessary for per-extent encryption. We will store a nonce for every extent we create, and then use the inode's policy and the extents nonce to derive a per-extent key. This is meant to be flexible, if we choose to expand the on-disk extent information in the future we have a version number we can use to change what exists on disk. The file system indicates it wants to use per-extent encryption by setting s_cop->has_per_extent_encryption. This also requires the use of inline block encryption. The support is relatively straightforward, the only "extra" bit is we're deriving a per-extent key to use for the encryption, the inode still controls the policy and access to the master key. Since extent based encryption uses a lot of keys, we're requiring the use of inline block crypto if you use extent-based encryption. This enables us to take advantage of the built in pooling and reclamation of the crypto structures that underpin all of the encryption. The different encryption related options for fscrypt are too numerous to support for extent based encryption. Support for a few of these options could possibly be added, but since they're niche options simply reject them for file systems using extent based encryption. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/37c2237bf485e44b0c813716f9506413026= ce6dc.1706116485.git.josef@toxicpanda.com/ * Fixed a merge collision with HW wrapped keydefinition. * Adapt to fscrypt changes. - Key derivation is void now instead of returning err. - Crypt info structure was split from VFS inode into FS specific inode structure. --- fs/crypto/crypto.c | 10 ++- fs/crypto/fscrypt_private.h | 42 +++++++++ fs/crypto/inline_crypt.c | 74 ++++++++++++++++ fs/crypto/keysetup.c | 165 ++++++++++++++++++++++++++++++++++++ fs/crypto/policy.c | 47 ++++++++++ include/linux/fscrypt.h | 67 +++++++++++++++ 6 files changed, 404 insertions(+), 1 deletion(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 07f9cbfe3ea4..8845de2d5af0 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -42,6 +42,7 @@ static struct workqueue_struct *fscrypt_read_workqueue; static DEFINE_MUTEX(fscrypt_init_mutex); =20 struct kmem_cache *fscrypt_inode_info_cachep; +struct kmem_cache *fscrypt_extent_info_cachep; =20 void fscrypt_enqueue_decrypt_work(struct work_struct *work) { @@ -402,12 +403,19 @@ static int __init fscrypt_init(void) if (!fscrypt_inode_info_cachep) goto fail_free_queue; =20 + fscrypt_extent_info_cachep =3D KMEM_CACHE(fscrypt_extent_info, + SLAB_RECLAIM_ACCOUNT); + if (!fscrypt_extent_info_cachep) + goto fail_free_inode_info; + err =3D fscrypt_init_keyring(); if (err) - goto fail_free_inode_info; + goto fail_free_extent_info; =20 return 0; =20 +fail_free_extent_info: + kmem_cache_destroy(fscrypt_extent_info_cachep); fail_free_inode_info: kmem_cache_destroy(fscrypt_inode_info_cachep); fail_free_queue: diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 4e8e82a9ccf9..409e84851fb0 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -66,6 +66,8 @@ #define FSCRYPT_CONTEXT_V1 1 #define FSCRYPT_CONTEXT_V2 2 =20 +#define FSCRYPT_EXTENT_CONTEXT_V1 1 + /* Keep this in sync with include/uapi/linux/fscrypt.h */ #define FSCRYPT_MODE_MAX FSCRYPT_MODE_AES_256_HCTR2 =20 @@ -89,6 +91,25 @@ struct fscrypt_context_v2 { u8 nonce[FSCRYPT_FILE_NONCE_SIZE]; }; =20 +/* + * fscrypt_extent_context - the encryption context of an extent + * + * This is the on-disk information stored for an extent. The nonce is use= d as a + * KDF input in conjuction with the inode context to derive a per-extent k= ey for + * encryption. + * + * With the current implementation, master_key_identifier and encryption m= ode + * must match the inode context. These are here for future expansion wher= e we + * may want the option of mixing different keys and encryption modes for t= he + * same file. + */ +struct fscrypt_extent_context { + u8 version; /* FSCRYPT_EXTENT_CONTEXT_V1 */ + u8 encryption_mode; + u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; + u8 nonce[FSCRYPT_FILE_NONCE_SIZE]; +}; + /* * fscrypt_context - the encryption context of an inode * @@ -326,6 +347,25 @@ struct fscrypt_inode_info { u8 ci_nonce[FSCRYPT_FILE_NONCE_SIZE]; }; =20 +/* + * fscrypt_extent_info - the "encryption key" for an extent. + * + * This contains the derived key for the given extent and the nonce for the + * extent. + */ +struct fscrypt_extent_info { + refcount_t refs; + + /* The derived key for this extent. */ + struct fscrypt_prepared_key prep_key; + + /* The super block that this extent belongs to. */ + struct super_block *sb; + + /* This is the extent's nonce, loaded from the fscrypt_extent_context */ + u8 nonce[FSCRYPT_FILE_NONCE_SIZE]; +}; + typedef enum { FS_DECRYPT =3D 0, FS_ENCRYPT, @@ -333,6 +373,7 @@ typedef enum { =20 /* crypto.c */ extern struct kmem_cache *fscrypt_inode_info_cachep; +extern struct kmem_cache *fscrypt_extent_info_cachep; int fscrypt_initialize(struct super_block *sb); int fscrypt_crypt_data_unit(const struct fscrypt_inode_info *ci, fscrypt_direction_t rw, u64 index, @@ -400,6 +441,7 @@ void fscrypt_init_hkdf(struct hmac_sha512_key *hkdf, co= nst u8 *master_key, #define HKDF_CONTEXT_INODE_HASH_KEY 7 /* info=3D */ #define HKDF_CONTEXT_KEY_IDENTIFIER_FOR_HW_WRAPPED_KEY \ 8 /* info=3D */ +#define HKDF_CONTEXT_PER_EXTENT_ENC_KEY 9 /* info=3Dextent_nonce */ =20 void fscrypt_hkdf_expand(const struct hmac_sha512_key *hkdf, u8 context, const u8 *info, unsigned int infolen, diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index ed6e926226b5..0d62bce2114a 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -314,6 +314,34 @@ void fscrypt_set_bio_crypt_ctx(struct bio *bio, const = struct inode *inode, } EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx); =20 +/** + * fscrypt_set_bio_crypt_ctx_from_extent() - prepare a file contents bio f= or + * inline crypto with extent + * encryption + * @bio: a bio which will eventually be submitted to the file + * @ei: the extent's crypto info + * @first_lblk: the first file logical block number in the I/O + * @gfp_mask: memory allocation flags - these must be a waiting mask so th= at + * bio_crypt_set_ctx can't fail. + * + * If the contents of the file should be encrypted (or decrypted) with inl= ine + * encryption, then assign the appropriate encryption context to the bio. + * + * Normally the bio should be newly allocated (i.e. no pages added yet), as + * otherwise fscrypt_mergeable_bio() won't work as intended. + * + * The encryption context will be freed automatically when the bio is free= d. + */ +void fscrypt_set_bio_crypt_ctx_from_extent(struct bio *bio, + const struct fscrypt_extent_info *ei, + u64 first_lblk, gfp_t gfp_mask) +{ + u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE] =3D { first_lblk }; + + bio_crypt_set_ctx(bio, ei->prep_key.blk_key, dun, gfp_mask); +} +EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx_from_extent); + /* Extract the inode and logical block number from a buffer_head. */ static bool bh_get_inode_and_lblk_num(const struct buffer_head *bh, const struct inode **inode_ret, @@ -406,6 +434,52 @@ bool fscrypt_mergeable_bio(struct bio *bio, const stru= ct inode *inode, } EXPORT_SYMBOL_GPL(fscrypt_mergeable_bio); =20 +/** + * fscrypt_mergeable_extent_bio() - test whether data can be added to a bio + * @bio: the bio being built up + * @ei: the fscrypt_extent_info for this extent + * @next_lblk: the next file logical block number in the I/O + * + * When building a bio which may contain data which should undergo inline + * encryption (or decryption) via fscrypt, filesystems should call this fu= nction + * to ensure that the resulting bio contains only contiguous data unit num= bers. + * This will return false if the next part of the I/O cannot be merged wit= h the + * bio because either the encryption key would be different or the encrypt= ion + * data unit numbers would be discontiguous. + * + * fscrypt_set_bio_crypt_ctx_from_extent() must have already been called o= n the + * bio. + * + * This function isn't required in cases where crypto-mergeability is ensu= red in + * another way, such as I/O targeting only a single file (and thus a singl= e key) + * combined with fscrypt_limit_io_blocks() to ensure DUN contiguity. + * + * Return: true iff the I/O is mergeable + */ +bool fscrypt_mergeable_extent_bio(struct bio *bio, + const struct fscrypt_extent_info *ei, + u64 next_lblk) +{ + const struct bio_crypt_ctx *bc =3D bio->bi_crypt_context; + u64 next_dun[BLK_CRYPTO_DUN_ARRAY_SIZE] =3D { next_lblk }; + + if (!ei) + return true; + if (!bc) + return true; + + /* + * Comparing the key pointers is good enough, as all I/O for each key + * uses the same pointer. I.e., there's currently no need to support + * merging requests where the keys are the same but the pointers differ. + */ + if (bc->bc_key !=3D ei->prep_key.blk_key) + return false; + + return bio_crypt_dun_is_contiguous(bc, bio->bi_iter.bi_size, next_dun); +} +EXPORT_SYMBOL_GPL(fscrypt_mergeable_extent_bio); + /** * fscrypt_mergeable_bio_bh() - test whether data can be added to a bio * @bio: the bio being built up diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index 40fa05688d3a..44be08988065 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -849,3 +849,168 @@ int fscrypt_drop_inode(struct inode *inode) return !READ_ONCE(ci->ci_master_key->mk_present); } EXPORT_SYMBOL_GPL(fscrypt_drop_inode); + +static struct fscrypt_extent_info * +setup_extent_info(struct inode *inode, const u8 nonce[FSCRYPT_FILE_NONCE_S= IZE]) +{ + struct fscrypt_extent_info *ei; + struct fscrypt_inode_info *ci; + struct fscrypt_master_key *mk; + u8 derived_key[FSCRYPT_MAX_RAW_KEY_SIZE]; + int err; + + ci =3D *fscrypt_inode_info_addr(inode); + mk =3D ci->ci_master_key; + if (WARN_ON_ONCE(!mk)) + return ERR_PTR(-ENOKEY); + + ei =3D kmem_cache_zalloc(fscrypt_extent_info_cachep, GFP_KERNEL); + if (!ei) + return ERR_PTR(-ENOMEM); + + refcount_set(&ei->refs, 1); + memcpy(ei->nonce, nonce, FSCRYPT_FILE_NONCE_SIZE); + ei->sb =3D inode->i_sb; + + down_read(&mk->mk_sem); + /* + * We specifically don't check ->mk_present here because if the inode is + * open and has a reference on the master key then it should be + * available for us to use. + */ + fscrypt_hkdf_expand(&mk->mk_secret.hkdf, + HKDF_CONTEXT_PER_EXTENT_ENC_KEY, ei->nonce, + FSCRYPT_FILE_NONCE_SIZE, derived_key, + ci->ci_mode->keysize); + + err =3D fscrypt_prepare_inline_crypt_key(&ei->prep_key, derived_key, + ci->ci_mode->keysize, false, ci); + memzero_explicit(derived_key, ci->ci_mode->keysize); + if (err) + goto out_free; + up_read(&mk->mk_sem); + return ei; +out_free: + up_read(&mk->mk_sem); + memzero_explicit(ei, sizeof(*ei)); + kmem_cache_free(fscrypt_extent_info_cachep, ei); + return ERR_PTR(err); +} + +/** + * fscrypt_prepare_new_extent() - prepare to create a new extent for a file + * @inode: the possibly-encrypted inode + * + * If the inode is encrypted, setup the fscrypt_extent_info for a new exte= nt. + * This will include the nonce and the derived key necessary for the exten= t to + * be encrypted. This is only meant to be used with inline crypto and on = inodes + * that need their contents encrypted. + * + * This doesn't persist the new extents encryption context, this is done l= ater + * by calling fscrypt_set_extent_context(). + * + * Return: The newly allocated fscrypt_extent_info on success, -EOPNOTSUPP= if + * we're not encrypted, or another -errno code + */ +struct fscrypt_extent_info *fscrypt_prepare_new_extent(struct inode *inode) +{ + u8 nonce[FSCRYPT_FILE_NONCE_SIZE]; + + if (WARN_ON_ONCE(!*fscrypt_inode_info_addr(inode))) + return ERR_PTR(-EOPNOTSUPP); + if (WARN_ON_ONCE(!fscrypt_inode_uses_inline_crypto(inode))) + return ERR_PTR(-EOPNOTSUPP); + + get_random_bytes(nonce, FSCRYPT_FILE_NONCE_SIZE); + return setup_extent_info(inode, nonce); +} +EXPORT_SYMBOL_GPL(fscrypt_prepare_new_extent); + +/** + * fscrypt_load_extent_info() - create an fscrypt_extent_info from the con= text + * @inode: the inode + * @ctx: the context buffer + * @ctx_size: the size of the context buffer + * + * Create the fscrypt_extent_info and derive the key based on the + * fscrypt_extent_context buffer that is provided. + * + * Return: The newly allocated fscrypt_extent_info on success, -EOPNOTSUPP= if + * we're not encrypted, or another -errno code + */ +struct fscrypt_extent_info *fscrypt_load_extent_info(struct inode *inode, + u8 *ctx, size_t ctx_size) +{ + struct fscrypt_extent_context extent_ctx; + const struct fscrypt_inode_info *ci =3D *fscrypt_inode_info_addr(inode); + const struct fscrypt_policy_v2 *policy =3D &ci->ci_policy.v2; + + if (WARN_ON_ONCE(!ci)) + return ERR_PTR(-EOPNOTSUPP); + if (WARN_ON_ONCE(!fscrypt_inode_uses_inline_crypto(inode))) + return ERR_PTR(-EOPNOTSUPP); + if (ctx_size < sizeof(extent_ctx)) + return ERR_PTR(-EINVAL); + + memcpy(&extent_ctx, ctx, sizeof(extent_ctx)); + + if (extent_ctx.version !=3D FSCRYPT_EXTENT_CONTEXT_V1) { + fscrypt_warn(inode, "Invalid extent encryption context version"); + return ERR_PTR(-EINVAL); + } + + /* + * For now we need to validate that the master key and the encryption + * mode matches what is in the inode. + */ + if (memcmp(extent_ctx.master_key_identifier, + policy->master_key_identifier, + sizeof(extent_ctx.master_key_identifier))) { + fscrypt_warn(inode, "Mismatching master key identifier"); + return ERR_PTR(-EINVAL); + } + + if (extent_ctx.encryption_mode !=3D policy->contents_encryption_mode) { + fscrypt_warn(inode, "Mismatching encryption mode"); + return ERR_PTR(-EINVAL); + } + + return setup_extent_info(inode, extent_ctx.nonce); +} +EXPORT_SYMBOL_GPL(fscrypt_load_extent_info); + +/** + * fscrypt_put_extent_info() - put a reference to a fscrypt_extent_info + * @ei: the fscrypt_extent_info being put or NULL. + * + * Drop a reference and possibly free the fscrypt_extent_info. + * + * Might sleep, since this may call blk_crypto_evict_key() which can sleep. + */ +void fscrypt_put_extent_info(struct fscrypt_extent_info *ei) +{ + if (ei && refcount_dec_and_test(&ei->refs)) { + fscrypt_destroy_prepared_key(ei->sb, &ei->prep_key); + memzero_explicit(ei, sizeof(*ei)); + kmem_cache_free(fscrypt_extent_info_cachep, ei); + } +} +EXPORT_SYMBOL_GPL(fscrypt_put_extent_info); + +/** + * fscrypt_get_extent_info() - get a reference to a fscrypt_extent_info + * @ei: the extent_info to get. + * + * Get a reference on the fscrypt_extent_info. This is useful for file sys= tems + * that need to pass the fscrypt_extent_info through various other structu= res to + * make lifetime tracking simpler. + * + * Return: the ei with an extra ref, NULL if ei was NULL. + */ +struct fscrypt_extent_info *fscrypt_get_extent_info(struct fscrypt_extent_= info *ei) +{ + if (ei) + refcount_inc(&ei->refs); + return ei; +} +EXPORT_SYMBOL_GPL(fscrypt_get_extent_info); diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index bbb2f5ced988..b37162fedd92 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -211,6 +211,12 @@ static bool fscrypt_supported_v1_policy(const struct f= scrypt_policy_v1 *policy, return false; } =20 + if (inode->i_sb->s_cop->has_per_extent_encryption) { + fscrypt_warn(inode, + "v1 policies aren't supported on file systems that use extent encr= yption"); + return false; + } + return true; } =20 @@ -240,6 +246,11 @@ static bool fscrypt_supported_v2_policy(const struct f= scrypt_policy_v2 *policy, count +=3D !!(policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY); count +=3D !!(policy->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64); count +=3D !!(policy->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32); + if (count > 0 && inode->i_sb->s_cop->has_per_extent_encryption) { + fscrypt_warn(inode, + "Encryption flags aren't supported on file systems that use extent= encryption"); + return false; + } if (count > 1) { fscrypt_warn(inode, "Mutually exclusive encryption flags (0x%02x)", policy->flags); @@ -792,6 +803,42 @@ int fscrypt_set_context(struct inode *inode, void *fs_= data) } EXPORT_SYMBOL_GPL(fscrypt_set_context); =20 +/** + * fscrypt_set_extent_context() - Set the fscrypt extent context of a new = extent + * @inode: the inode this extent belongs to + * @ei: the fscrypt_extent_info for the given extent + * @buf: the buffer to copy the fscrypt extent context into + * + * This should be called after fscrypt_prepare_new_extent(), using the + * fscrypt_extent_info that was created at that point. + * + * buf must be at most FSCRYPT_SET_CONTEXT_MAX_SIZE. + * + * Return: the size of the fscrypt_extent_context, errno if the inode has = the + * wrong policy version. + */ +ssize_t fscrypt_context_for_new_extent(struct inode *inode, + struct fscrypt_extent_info *ei, u8 *buf) +{ + struct fscrypt_extent_context *ctx =3D (struct fscrypt_extent_context *)b= uf; + const struct fscrypt_inode_info *ci =3D *fscrypt_inode_info_addr(inode); + + BUILD_BUG_ON(sizeof(struct fscrypt_extent_context) > + FSCRYPT_SET_CONTEXT_MAX_SIZE); + + if (WARN_ON_ONCE(ci->ci_policy.version !=3D 2)) + return -EINVAL; + + ctx->version =3D FSCRYPT_EXTENT_CONTEXT_V1; + ctx->encryption_mode =3D ci->ci_policy.v2.contents_encryption_mode; + memcpy(ctx->master_key_identifier, + ci->ci_policy.v2.master_key_identifier, + sizeof(ctx->master_key_identifier)); + memcpy(ctx->nonce, ei->nonce, FSCRYPT_FILE_NONCE_SIZE); + return sizeof(struct fscrypt_extent_context); +} +EXPORT_SYMBOL_GPL(fscrypt_context_for_new_extent); + /** * fscrypt_parse_test_dummy_encryption() - parse the test_dummy_encryption= mount option * @param: the mount option diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 516aba5b858b..5a17e4975b06 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -32,6 +32,7 @@ =20 union fscrypt_policy; struct fscrypt_inode_info; +struct fscrypt_extent_info; struct fs_parameter; struct seq_file; =20 @@ -103,6 +104,14 @@ struct fscrypt_operations { */ unsigned int supports_subblock_data_units : 1; =20 + /* + * If set then extent based encryption will be used for this file + * system, and fs/crypto/ will enforce limits on the policies that are + * allowed to be chosen. Currently this means only plain v2 policies + * are supported. + */ + unsigned int has_per_extent_encryption : 1; + /* * This field exists only for backwards compatibility reasons and should * only be set by the filesystems that are setting it already. It @@ -387,6 +396,8 @@ int fscrypt_ioctl_get_nonce(struct file *filp, void __u= ser *arg); int fscrypt_has_permitted_context(struct inode *parent, struct inode *chil= d); int fscrypt_context_for_new_inode(void *ctx, struct inode *inode); int fscrypt_set_context(struct inode *inode, void *fs_data); +ssize_t fscrypt_context_for_new_extent(struct inode *inode, + struct fscrypt_extent_info *ei, u8 *buf); =20 struct fscrypt_dummy_policy { const union fscrypt_policy *policy; @@ -423,6 +434,11 @@ int fscrypt_prepare_new_inode(struct inode *dir, struc= t inode *inode, void fscrypt_put_encryption_info(struct inode *inode); void fscrypt_free_inode(struct inode *inode); int fscrypt_drop_inode(struct inode *inode); +struct fscrypt_extent_info *fscrypt_prepare_new_extent(struct inode *inode= ); +void fscrypt_put_extent_info(struct fscrypt_extent_info *ei); +struct fscrypt_extent_info *fscrypt_get_extent_info(struct fscrypt_extent_= info *ei); +struct fscrypt_extent_info *fscrypt_load_extent_info(struct inode *inode, + u8 *ctx, size_t ctx_size); =20 /* fname.c */ int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *in= ame, @@ -636,6 +652,24 @@ fscrypt_free_dummy_policy(struct fscrypt_dummy_policy = *dummy_policy) { } =20 +static inline ssize_t +fscrypt_context_for_new_extent(struct inode *inode, struct fscrypt_extent_= info *ei, + u8 *buf) +{ + return -EOPNOTSUPP; +} + +static inline struct fscrypt_extent_info * +fscrypt_load_extent_info(struct inode *inode, u8 *ctx, size_t ctx_size) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline size_t fscrypt_extent_context_size(struct inode *inode) +{ + return 0; +} + /* keyring.c */ static inline void fscrypt_destroy_keyring(struct super_block *sb) { @@ -688,6 +722,20 @@ static inline int fscrypt_drop_inode(struct inode *ino= de) return 0; } =20 +static inline struct fscrypt_extent_info * +fscrypt_prepare_new_extent(struct inode *inode) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline void fscrypt_put_extent_info(struct fscrypt_extent_info *ei)= { } + +static inline struct fscrypt_extent_info * +fscrypt_get_extent_info(struct fscrypt_extent_info *ei) +{ + return ei; +} + /* fname.c */ static inline int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, @@ -869,6 +917,10 @@ void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode, u64 first_lblk, gfp_t gfp_mask); =20 +void fscrypt_set_bio_crypt_ctx_from_extent(struct bio *bio, + const struct fscrypt_extent_info *ei, + u64 first_lblk, gfp_t gfp_mask); + void fscrypt_set_bio_crypt_ctx_bh(struct bio *bio, const struct buffer_head *first_bh, gfp_t gfp_mask); @@ -879,6 +931,10 @@ bool fscrypt_mergeable_bio(struct bio *bio, const stru= ct inode *inode, bool fscrypt_mergeable_bio_bh(struct bio *bio, const struct buffer_head *next_bh); =20 +bool fscrypt_mergeable_extent_bio(struct bio *bio, + const struct fscrypt_extent_info *ei, + u64 next_lblk); + bool fscrypt_dio_supported(struct inode *inode); =20 u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_bl= ocks); @@ -894,6 +950,10 @@ static inline void fscrypt_set_bio_crypt_ctx(struct bi= o *bio, const struct inode *inode, u64 first_lblk, gfp_t gfp_mask) { } =20 +static inline void fscrypt_set_bio_crypt_ctx_from_extent(struct bio *bio, + const struct fscrypt_extent_info *ei, + u64 first_lblk, gfp_t gfp_mask) { } + static inline void fscrypt_set_bio_crypt_ctx_bh( struct bio *bio, const struct buffer_head *first_bh, @@ -906,6 +966,13 @@ static inline bool fscrypt_mergeable_bio(struct bio *b= io, return true; } =20 +static inline bool fscrypt_mergeable_extent_bio(struct bio *bio, + const struct fscrypt_extent_info *ei, + u64 next_lblk) +{ + return true; +} + static inline bool fscrypt_mergeable_bio_bh(struct bio *bio, const struct buffer_head *next_bh) { --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7698035F8D9 for ; Fri, 6 Feb 2026 18:24:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402241; cv=none; b=OPEBWbuOe6DM4hD9wDL+VP98c4nJWkQhVXKQn0o9P3bVp/mN8XU9ycsLzHhF+064KLs/tr/EWDO/8U+WwGLOo5U4XDZmXbNsIECZ0ERafsmGPvoH/e7xJnEn9KtKhHqkRgpw+OfiIjThaa5kieTV5wJIHuxELMDfZnupNmEcmfU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402241; c=relaxed/simple; bh=rIJCalTOBjrp9Aa9W7ors/hXzQdyQ6RUvbzFdOLTmXo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZMt78WTgS549J9KtJi64oYyFdBavvEDTu+/eIcS5pkLbQTdKCxwwYVJH0lbDlW+f9oKR3LucUBpZrT+1Xa/91mA1jqweDZcYbzo7TLll1mgQtBPozizIeq6FGanv7opPBFJi4Chq7yb9zlnAxI2EP39vnxGLbSdSok+Pc2RKdCQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 115373E742; Fri, 6 Feb 2026 18:23:55 +0000 (UTC) Authentication-Results: smtp-out1.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id DA34C3EA63; Fri, 6 Feb 2026 18:23:54 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id SHbQNLoxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:23:54 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 02/43] fscrypt: allow inline encryption for extent based encryption Date: Fri, 6 Feb 2026 19:22:34 +0100 Message-ID: <20260206182336.1397715-3-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO X-Spam-Score: -4.00 X-Rspamd-Queue-Id: 115373E742 X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Server: rspamd2.dmz-prg2.suse.org X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik Instead of requiring -o inlinecrypt to enable inline encryption, allow having s_cop->has_per_extent_encryption to indicate that this file system supports inline encryption. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/ba0289bf103653d5d98ef576756c9a2a661= 92865.1706116485.git.josef@toxicpanda.com/ * No changes since. --- fs/crypto/inline_crypt.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 0d62bce2114a..38a729700552 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -108,8 +108,11 @@ int fscrypt_select_encryption_impl(struct fscrypt_inod= e_info *ci, if (ci->ci_mode->blk_crypto_mode =3D=3D BLK_ENCRYPTION_MODE_INVALID) return 0; =20 - /* The filesystem must be mounted with -o inlinecrypt */ - if (!(sb->s_flags & SB_INLINECRYPT)) + /* + * The filesystem must be mounted with -o inlinecrypt or have + * has_per_extent_encryption enabled. + */ + if (!(sb->s_flags & SB_INLINECRYPT) && !sb->s_cop->has_per_extent_encrypt= ion) return 0; =20 /* --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CC264425CCF for ; Fri, 6 Feb 2026 18:24:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402249; cv=none; b=FCwdH4BTOSad3uAtQHFM+u7e/xzqmSuGVUWm8t91KxaOgDx1HE+8+QblnFkaGjrHR3onT7U/8WA6ESWBrlCM7HRqJA/UhmK0ffljFicXCQUvngrpKDriv/x+gS45d5K71ljgxNf0I4u/P7BHTmc9w1dbImbqkacfYmmRZfk6n3M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402249; c=relaxed/simple; bh=V3qkz5KR+/fbjcLmGLO6jWlKLTc0sUhs1UdnMQPC+Mc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Xkc+NKTuB/4rdZoz55HxM+o10fNF5q0QinDQSV/vMOCsW0Bnm65hnDpfDox784hjwxXUdPtcpoO/xkJR3axuKX1CaiwESfSNVJupxz77aIB+UZ8i3d/1EHdo/D9z/Vuo20Ysa2xt1LyKr3JV1inqorLPrnZ30LrkNiuL7pZ6Ra8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 8E7693E743; Fri, 6 Feb 2026 18:23:55 +0000 (UTC) Authentication-Results: smtp-out1.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 638BB3EA63; Fri, 6 Feb 2026 18:23:55 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id wM/aF7sxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:23:55 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 03/43] fscrypt: add a __fscrypt_file_open helper Date: Fri, 6 Feb 2026 19:22:35 +0100 Message-ID: <20260206182336.1397715-4-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO X-Spam-Score: -4.00 X-Rspamd-Queue-Id: 8E7693E743 X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Server: rspamd2.dmz-prg2.suse.org X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik We have fscrypt_file_open() which is meant to be called on files being opened so that their key is loaded when we start reading data from them. However for btrfs send we are opening the inode directly without a filp, so we need a different helper to make sure we can load the fscrypt context for the inode before reading its contents. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/4a372419c3fe6ad425e1b124c342a054e9d= 6db23.1706116485.git.josef@toxicpanda.com/ * Adapted to fscrypt changes. --- fs/crypto/hooks.c | 38 ++++++++++++++++++++++++++++++++------ include/linux/fscrypt.h | 8 ++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index b97de0d1430f..17eb2e844f30 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -9,6 +9,37 @@ =20 #include "fscrypt_private.h" =20 +/** + * __fscrypt_file_open() - prepare for filesystem-internal access to a + * possibly-encrypted regular file + * @dir: the inode for the directory via which the file is being accessed + * @inode: the inode being "opened" + * + * This is like fscrypt_file_open(), but instead of taking the 'struct fil= e' + * being opened it takes the parent directory explicitly. This is intende= d for + * use cases such as "send/receive" which involve the filesystem accessing= file + * contents without setting up a 'struct file'. + * + * Return: 0 on success, -ENOKEY if the key is missing, or another -errno = code + */ +int __fscrypt_file_open(struct inode *dir, struct inode *inode) +{ + int err; + + err =3D fscrypt_require_key(inode); + if (err) + return err; + + if (!fscrypt_has_permitted_context(dir, inode)) { + fscrypt_warn(inode, + "Inconsistent encryption context (parent directory: %lu)", + dir->i_ino); + return -EPERM; + } + return 0; +} +EXPORT_SYMBOL_GPL(__fscrypt_file_open); + /** * fscrypt_file_open() - prepare to open a possibly-encrypted regular file * @inode: the inode being opened @@ -60,12 +91,7 @@ int fscrypt_file_open(struct inode *inode, struct file *= filp) rcu_read_unlock(); =20 dentry_parent =3D dget_parent(dentry); - if (!fscrypt_has_permitted_context(d_inode(dentry_parent), inode)) { - fscrypt_warn(inode, - "Inconsistent encryption context (parent directory: %lu)", - d_inode(dentry_parent)->i_ino); - err =3D -EPERM; - } + err =3D __fscrypt_file_open(d_inode(dentry_parent), inode); dput(dentry_parent); return err; } diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 5a17e4975b06..dba5ca122775 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -471,6 +471,7 @@ int fscrypt_zeroout_range(const struct inode *inode, pg= off_t lblk, =20 /* hooks.c */ int fscrypt_file_open(struct inode *inode, struct file *filp); +int __fscrypt_file_open(struct inode *dir, struct inode *inode); int __fscrypt_prepare_link(struct inode *inode, struct inode *dir, struct dentry *dentry); int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_den= try, @@ -818,6 +819,13 @@ static inline int fscrypt_file_open(struct inode *inod= e, struct file *filp) return 0; } =20 +static inline int __fscrypt_file_open(struct inode *dir, struct inode *ino= de) +{ + if (IS_ENCRYPTED(inode)) + return -EOPNOTSUPP; + return 0; +} + static inline int __fscrypt_prepare_link(struct inode *inode, struct inode= *dir, struct dentry *dentry) { --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 69A83425CCF for ; Fri, 6 Feb 2026 18:23:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402241; cv=none; b=pTOWRp4JnZ3t+wBKPI0vMTEMkggg065PUDi3IChV15DphbIA9PuoDP39VNU4l04XNpD+4YJWGpzkCAigYchzTXqTZhHRDZS7xpsE26ztiiAHnjgboj4p/WqdvhBRYFHO9xMnIOCKC08PUsvVb2ImfYHuaG5pEM1ZPAX5NyYGu/A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402241; c=relaxed/simple; bh=Bl9NNyMFA+wH+8Z3rKl870aGK0RPvwizp/Q1YDkqEgM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eth+ShjqVN/0+EyigVNlV2vMAXz+HYZwnrcntr4BbYixV1rSymtwtd7SN55ZCoiZDU3qOsH++ThkNuV44uOZLY3ret+zUvjWeJZQ8Qt/iAv6KXWfk7tJ3MEzFb8Lzh9p0Qs9JgnWrl/sVdgKlKw9nGx2ZXspaWS8WfRPrmNCfms= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id F3A565BCE4; Fri, 6 Feb 2026 18:23:55 +0000 (UTC) Authentication-Results: smtp-out2.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id C82A73EA63; Fri, 6 Feb 2026 18:23:55 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id wIZlMLsxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:23:55 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 04/43] fscrypt: conditionally don't wipe mk secret until the last active user is done Date: Fri, 6 Feb 2026 19:22:36 +0100 Message-ID: <20260206182336.1397715-5-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO X-Spam-Score: -4.00 X-Rspamd-Queue-Id: F3A565BCE4 X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Server: rspamd2.dmz-prg2.suse.org X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik Previously we were wiping the master key secret when we do FS_IOC_REMOVE_ENCRYPTION_KEY, and then using the fact that it was cleared as the mechanism from keeping new users from being setup. This works with inode based encryption, as the per-inode key is derived at setup time, so the secret disappearing doesn't affect any currently open files from being able to continue working. However for extent based encryption we do our key derivation at page writeout and readpage time, which means we need the master key secret to be available while we still have our file open. Since the master key lifetime is controlled by a flag, move the clearing of the secret to the mk_active_users cleanup stage if we have extent based encryption enabled on this super block. This counter represents the actively open files that still exist on the file system, and thus should still be able to operate normally. Once the last user is closed we can clear the secret. Until then no new users are allowed, and this allows currently open files to continue to operate until they're closed. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/5f28e46ce99d918a16f5bf4d8190870d0ff= fefc4.1706116485.git.josef@toxicpanda.com/ * No changes since. --- fs/crypto/keyring.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index 5e939ea3ac28..7ccec27e4e4a 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -110,6 +110,14 @@ void fscrypt_put_master_key_activeref(struct super_blo= ck *sb, WARN_ON_ONCE(mk->mk_present); WARN_ON_ONCE(!list_empty(&mk->mk_decrypted_inodes)); =20 + /* We can't wipe the master key secret until the last activeref is + * dropped on the master key with per-extent encryption since the key + * derivation continues to happen as long as there are active refs. + * Wipe it here now that we're done using it. + */ + if (sb->s_cop->has_per_extent_encryption) + wipe_master_key_secret(&mk->mk_secret); + for (i =3D 0; i <=3D FSCRYPT_MODE_MAX; i++) { fscrypt_destroy_prepared_key( sb, &mk->mk_direct_keys[i]); @@ -134,7 +142,15 @@ static void fscrypt_initiate_key_removal(struct super_= block *sb, struct fscrypt_master_key *mk) { WRITE_ONCE(mk->mk_present, false); - wipe_master_key_secret(&mk->mk_secret); + + /* + * Per-extent encryption requires the master key to stick around until + * writeout has completed as we derive the per-extent keys at writeout + * time. Once the activeref drops to 0 we'll wipe the master secret + * key. + */ + if (!sb->s_cop->has_per_extent_encryption) + wipe_master_key_secret(&mk->mk_secret); fscrypt_put_master_key_activeref(sb, mk); } =20 --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D2D64426D1F for ; Fri, 6 Feb 2026 18:24:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402246; cv=none; b=VFTJ3o91lFI/q/BzAMdHXUidu18ZrA4BB+/T1wQ6+bK/kYhl5lLPG4QKjSdyG4yi7zNL0Nkvnc/I2kyZvVpg1HHw/JwXejlDBa1otAVe5ucK7LPAo+SBbOdL/1aUMPsQz18t0cGE2uFDY304Zv/WntphbCxm6lWQOvfm7oocCWY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402246; c=relaxed/simple; bh=u61vO7jsE5NYmS5zINbNxCfbfvNmM2phS/DGmwZ1RnY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DAVierJYPdvsbNFY6lP7Vl2mrwZY/pPJNvRqeE/VZmZv7j1uoChY7zhBdajdDMcRBkEFWA8b8VBwsy+6oo8ONXqN+g1p7ppFpiWTwj1yu4LCBEmN0jEj5ewwjjR9qcsrYNtwtNIXUGBvgDfcQOiyGUGjrOHAqwVvVxuEesslqrY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=FVJ4K0C7; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=FVJ4K0C7; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="FVJ4K0C7"; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="FVJ4K0C7" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id CF8A25BD1F; Fri, 6 Feb 2026 18:23:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402236; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Bdp12iChfRBUwX0bGREqDSlf5SU0oy2SyWcALVhmFUc=; b=FVJ4K0C7Hn5PFYIj71DoDUf6eM6ppScwb/kegHXu/0NHlGLfbpiWGix2j7/LVto27lJyBN gE4ROuBJxiSzoMKzk0c6XCVJZ7Kbhv96le2U3SdkPG7U3NTq6ahsphvP0hCxzFPmbRzGxA 6r2BAVd1Ff9JB03Jf5feyJZPbNEDsgQ= Authentication-Results: smtp-out2.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402236; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Bdp12iChfRBUwX0bGREqDSlf5SU0oy2SyWcALVhmFUc=; b=FVJ4K0C7Hn5PFYIj71DoDUf6eM6ppScwb/kegHXu/0NHlGLfbpiWGix2j7/LVto27lJyBN gE4ROuBJxiSzoMKzk0c6XCVJZ7Kbhv96le2U3SdkPG7U3NTq6ahsphvP0hCxzFPmbRzGxA 6r2BAVd1Ff9JB03Jf5feyJZPbNEDsgQ= Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id A2EDD3EA63; Fri, 6 Feb 2026 18:23:56 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id YOdPJ7wxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:23:56 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 05/43] blk-crypto: add a process_bio callback Date: Fri, 6 Feb 2026 19:22:37 +0100 Message-ID: <20260206182336.1397715-6-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spamd-Result: default: False [-6.80 / 50.00]; REPLY(-4.00)[]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; DBL_BLOCKED_OPENRESOLVER(0.00)[suse.com:mid,suse.com:email]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; TO_DN_SOME(0.00)[]; RCPT_COUNT_TWELVE(0.00)[12]; RCVD_VIA_SMTP_AUTH(0.00)[]; FUZZY_RATELIMITED(0.00)[rspamd.com]; FROM_HAS_DN(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; R_RATELIMIT(0.00)[to_ip_from(RLdafymbgddseoxkryad1wgu8n)]; DKIM_SIGNED(0.00)[suse.com:s=susede1]; RCVD_TLS_ALL(0.00)[] X-Spam-Flag: NO X-Spam-Score: -6.80 X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik Btrfs does checksumming, and the checksums need to match the bytes on disk. In order to facilitate this add a process bio callback for the blk-crypto layer. This allows the file system to specify a callback and then can process the encrypted bio as necessary. For btrfs, writes will have the checksums calculated and saved into our relevant data structures for storage once the write completes. For reads we will validate the checksums match what is on disk and error out if there is a mismatch. This is incompatible with native encryption obviously, so make sure we don't use native encryption if this callback is set. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/66c781f3b2afdf2c558efdf33a7bba8bcfe= 47ce7.1706116485.git.josef@toxicpanda.com/ * No changes since. --- block/blk-crypto-fallback.c | 43 +++++++++++++++++++++++++++++++++++++ block/blk-crypto-internal.h | 8 +++++++ block/blk-crypto-profile.c | 2 ++ block/blk-crypto.c | 6 +++++- fs/crypto/inline_crypt.c | 3 ++- include/linux/blk-crypto.h | 15 +++++++++++-- 6 files changed, 73 insertions(+), 4 deletions(-) diff --git a/block/blk-crypto-fallback.c b/block/blk-crypto-fallback.c index 86b27f96051a..7af6424a19ec 100644 --- a/block/blk-crypto-fallback.c +++ b/block/blk-crypto-fallback.c @@ -210,12 +210,15 @@ blk_crypto_fallback_alloc_cipher_req(struct blk_crypt= o_keyslot *slot, =20 static bool blk_crypto_fallback_split_bio_if_needed(struct bio **bio_ptr) { + struct bio_crypt_ctx *bc; struct bio *bio =3D *bio_ptr; unsigned int i =3D 0; unsigned int num_sectors =3D 0; struct bio_vec bv; struct bvec_iter iter; =20 + bc =3D bio->bi_crypt_context; + bio_for_each_segment(bv, bio, iter) { num_sectors +=3D bv.bv_len >> SECTOR_SHIFT; if (++i =3D=3D BIO_MAX_VECS) @@ -223,6 +226,16 @@ static bool blk_crypto_fallback_split_bio_if_needed(st= ruct bio **bio_ptr) } =20 if (num_sectors < bio_sectors(bio)) { + /* + * We cannot split bio's that have process_bio, as they require + * the original bio. The upper layer must make sure to limit + * the submitted bio's appropriately. + */ + if (bc->bc_key->crypto_cfg.process_bio) { + bio->bi_status =3D BLK_STS_RESOURCE; + return false; + } + bio =3D bio_submit_split_bioset(bio, num_sectors, &crypto_bio_split); if (!bio) @@ -343,6 +356,15 @@ static bool blk_crypto_fallback_encrypt_bio(struct bio= **bio_ptr) } } =20 + /* Process the encrypted bio before we submit it. */ + if (bc->bc_key->crypto_cfg.process_bio) { + blk_st =3D bc->bc_key->crypto_cfg.process_bio(src_bio, enc_bio); + if (blk_st !=3D BLK_STS_OK) { + src_bio->bi_status =3D blk_st; + goto out_free_bounce_pages; + } + } + enc_bio->bi_private =3D src_bio; enc_bio->bi_end_io =3D blk_crypto_fallback_encrypt_endio; *bio_ptr =3D enc_bio; @@ -388,6 +410,15 @@ static void blk_crypto_fallback_decrypt_bio(struct wor= k_struct *work) unsigned int i; blk_status_t blk_st; =20 + /* Process the bio first before trying to decrypt. */ + if (bc->bc_key->crypto_cfg.process_bio) { + blk_st =3D bc->bc_key->crypto_cfg.process_bio(bio, bio); + if (blk_st !=3D BLK_STS_OK) { + bio->bi_status =3D blk_st; + goto out_no_keyslot; + } + } + /* * Get a blk-crypto-fallback keyslot that contains a crypto_skcipher for * this bio's algorithm and key. @@ -437,6 +468,18 @@ static void blk_crypto_fallback_decrypt_bio(struct wor= k_struct *work) bio_endio(bio); } =20 +/** + * blk_crypto_profile_is_fallback - check if this profile is the fallback + * profile + * @profile: the profile we're checking + * + * This is just a quick check to make sure @profile is the fallback profil= e. + */ +bool blk_crypto_profile_is_fallback(struct blk_crypto_profile *profile) +{ + return profile =3D=3D blk_crypto_fallback_profile; +} + /** * blk_crypto_fallback_decrypt_endio - queue bio for fallback decryption * diff --git a/block/blk-crypto-internal.h b/block/blk-crypto-internal.h index ccf6dff6ff6b..82b512ca35f0 100644 --- a/block/blk-crypto-internal.h +++ b/block/blk-crypto-internal.h @@ -223,6 +223,8 @@ bool blk_crypto_fallback_bio_prep(struct bio **bio_ptr); =20 int blk_crypto_fallback_evict_key(const struct blk_crypto_key *key); =20 +bool blk_crypto_profile_is_fallback(struct blk_crypto_profile *profile); + #else /* CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK */ =20 static inline int @@ -245,6 +247,12 @@ blk_crypto_fallback_evict_key(const struct blk_crypto_= key *key) return 0; } =20 +static inline bool +blk_crypto_profile_is_fallback(struct blk_crypto_profile *profile) +{ + return false; +} + #endif /* CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK */ =20 #endif /* __LINUX_BLK_CRYPTO_INTERNAL_H */ diff --git a/block/blk-crypto-profile.c b/block/blk-crypto-profile.c index 81918f6e0cae..e57af3bb17dc 100644 --- a/block/blk-crypto-profile.c +++ b/block/blk-crypto-profile.c @@ -354,6 +354,8 @@ bool __blk_crypto_cfg_supported(struct blk_crypto_profi= le *profile, return false; if (!(profile->key_types_supported & cfg->key_type)) return false; + if (cfg->process_bio && !blk_crypto_profile_is_fallback(profile)) + return false; return true; } =20 diff --git a/block/blk-crypto.c b/block/blk-crypto.c index 3e7bf1974cbd..b0576e502d4d 100644 --- a/block/blk-crypto.c +++ b/block/blk-crypto.c @@ -332,6 +332,8 @@ int __blk_crypto_rq_bio_prep(struct request *rq, struct= bio *bio, * @dun_bytes: number of bytes that will be used to specify the DUN when t= his * key is used * @data_unit_size: the data unit size to use for en/decryption + * @process_bio: the call back if the upper layer needs to process the enc= rypted + * bio * * Return: 0 on success, -errno on failure. The caller is responsible for * zeroizing both blk_key and key_bytes when done with them. @@ -341,7 +343,8 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key, enum blk_crypto_key_type key_type, enum blk_crypto_mode_num crypto_mode, unsigned int dun_bytes, - unsigned int data_unit_size) + unsigned int data_unit_size, + blk_crypto_process_bio_t process_bio) { const struct blk_crypto_mode *mode; =20 @@ -375,6 +378,7 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key, blk_key->crypto_cfg.dun_bytes =3D dun_bytes; blk_key->crypto_cfg.data_unit_size =3D data_unit_size; blk_key->crypto_cfg.key_type =3D key_type; + blk_key->crypto_cfg.process_bio =3D process_bio; blk_key->data_unit_size_bits =3D ilog2(data_unit_size); blk_key->size =3D key_size; memcpy(blk_key->bytes, key_bytes, key_size); diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 38a729700552..d737fb6ff011 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -178,7 +178,8 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_pre= pared_key *prep_key, =20 err =3D blk_crypto_init_key(blk_key, key_bytes, key_size, key_type, crypto_mode, fscrypt_get_dun_bytes(ci), - 1U << ci->ci_data_unit_bits); + 1U << ci->ci_data_unit_bits, + NULL); if (err) { fscrypt_err(inode, "error %d initializing blk-crypto key", err); goto fail; diff --git a/include/linux/blk-crypto.h b/include/linux/blk-crypto.h index 58b0c5254a67..022b00004517 100644 --- a/include/linux/blk-crypto.h +++ b/include/linux/blk-crypto.h @@ -7,7 +7,7 @@ #define __LINUX_BLK_CRYPTO_H =20 #include -#include +#include #include =20 enum blk_crypto_mode_num { @@ -19,6 +19,14 @@ enum blk_crypto_mode_num { BLK_ENCRYPTION_MODE_MAX, }; =20 +/* + * orig_bio must be the bio that was submitted from the upper layer as the= upper + * layer could have used a specific bioset and expect the orig_bio to be f= rom + * its bioset. + */ +typedef blk_status_t (*blk_crypto_process_bio_t)(struct bio *orig_bio, + struct bio *enc_bio); + /* * Supported types of keys. Must be bitflags due to their use in * blk_crypto_profile::key_types_supported. @@ -77,12 +85,14 @@ enum blk_crypto_key_type { * filesystem block size or the disk sector size. * @dun_bytes: the maximum number of bytes of DUN used when using this key * @key_type: the type of this key -- either raw or hardware-wrapped + * @proces_bio: optional callback to process encrypted bios. */ struct blk_crypto_config { enum blk_crypto_mode_num crypto_mode; unsigned int data_unit_size; unsigned int dun_bytes; enum blk_crypto_key_type key_type; + blk_crypto_process_bio_t process_bio; }; =20 /** @@ -145,7 +155,8 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key, enum blk_crypto_key_type key_type, enum blk_crypto_mode_num crypto_mode, unsigned int dun_bytes, - unsigned int data_unit_size); + unsigned int data_unit_size, + blk_crypto_process_bio_t process_bio); =20 int blk_crypto_start_using_key(struct block_device *bdev, const struct blk_crypto_key *key); --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 65308428485 for ; Fri, 6 Feb 2026 18:24:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402253; cv=none; b=afNqUgAhLOuuNiEzemyvhKiTyOeDTZD6NBPa17yQBV1v3jyOLlQ7T6A3gYKzUga/OH8BV/U2Pi+NQGH5ZD9AP4tO9X79Ba24w2LQYW//oJFXoGGl6dqstSirgQqKcgXxVeRUPiGcJIzUS/DumiJs9SWT2M1LGHFzYfBSQnTsLEE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402253; c=relaxed/simple; bh=WWPyU3Cw5JFKguMvOW44poEbw0QYpQG6cCJf4kJzrok=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=l4MaVf02sUVzpviI0kXuVhJXetznx8Bm6Znlseu+WCHCmNm2cordLOpnXzyQ4d1Px4zLHNC1jkojJUQsQPleFcLQ9LHmvpVQAV9nBspMgFVjc8vFktGSiIfw70rmy6ci7NrcFwAIvOQjAD9pHR+lBnszkAygakuAZ339eGYr480= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 599833E745; Fri, 6 Feb 2026 18:23:57 +0000 (UTC) Authentication-Results: smtp-out1.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 2B1503EA63; Fri, 6 Feb 2026 18:23:57 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id wK0KCr0xhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:23:57 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 06/43] fscrypt: add a process_bio hook to fscrypt_operations Date: Fri, 6 Feb 2026 19:22:38 +0100 Message-ID: <20260206182336.1397715-7-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO X-Spam-Score: -4.00 X-Rspamd-Queue-Id: 599833E745 X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Server: rspamd2.dmz-prg2.suse.org X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik This will allow file systems to set a process_bio hook for inline encryption. This will be utilized by btrfs in order to make sure the checksumming work is done on the encrypted bio's. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/2c638e5fa1b7868dbf79d932b15364c3c30= ca9de.1706116485.git.josef@toxicpanda.com/ * No changes since. --- fs/crypto/inline_crypt.c | 2 +- include/linux/fscrypt.h | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index d737fb6ff011..fc1aa5b00af1 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -179,7 +179,7 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_pre= pared_key *prep_key, err =3D blk_crypto_init_key(blk_key, key_bytes, key_size, key_type, crypto_mode, fscrypt_get_dun_bytes(ci), 1U << ci->ci_data_unit_bits, - NULL); + sb->s_cop->process_bio); if (err) { fscrypt_err(inode, "error %d initializing blk-crypto key", err); goto fail; diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index dba5ca122775..e3bb9e3756e1 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -16,6 +16,7 @@ #include #include #include +#include #include =20 /* @@ -205,6 +206,19 @@ struct fscrypt_operations { */ struct block_device **(*get_devices)(struct super_block *sb, unsigned int *num_devs); + + /* + * A callback if the file system requires the ability to process the + * encrypted bio, used only with inline encryption. + * + * @orig_bio: the original bio submitted. + * @enc_bio: the encrypted bio. + * + * For writes the enc_bio will be different from the orig_bio, for reads + * they will be the same. For reads we get the bio before it is + * decrypted, for writes we get the bio before it is submitted. + */ + blk_crypto_process_bio_t process_bio; }; =20 int fscrypt_d_revalidate(struct inode *dir, const struct qstr *name, --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A3C4C428476 for ; Fri, 6 Feb 2026 18:24:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402253; cv=none; b=Qdaqv+zHaBX1RA4JCRNDh/WpuhF+fOn3RWsZJDibKxLosWN5HIJTByXMxEsG3PSapGgs7cgFEn2YrPg4aJdAPDOJJ9z6rYaoiD5TOAbWLH/hlyZUJoJD+YPcPbEBgpgjONqqBVSvzyPr6My06OlNdFv/LoNTXNj3s10xqWodGpc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402253; c=relaxed/simple; bh=EapV8U1miyXO0gY5JnhdhB+GrtVyUlRXkbekHb4fOa4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=pB3P9hkW91xT7OPU3rDQZO5KOjQhmDxOQgNdN+iEZ9XVvevF6mlpxUap8N3MyHhWA1C5sf5gtw2zA/uNvYKVIxPSQg2EgbMwukfXi5jXFVsjzC1pEhOumwYeNvapson6UWGeDw6yFY2t4+FCQDy0FrdJHJRCh3TXH+rK95um48k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=Rz+XSR9A; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=SXFLBIAh; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="Rz+XSR9A"; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="SXFLBIAh" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id DD2BA5BD25; Fri, 6 Feb 2026 18:23:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402238; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=aDxiEePKaHFlI5tO/p1XE2bOmMKz7ygZnuualgpXxeQ=; b=Rz+XSR9A/QjymWWwWstf+F0zAhUm09SGimogvLcNSFX/iG8NnFanC906Wew1e68KMfd+Mr xre3I8UKzAWhXufqXU4E+pY6azfp2uncYq1gNEjJFQRQzjYy9vatjIsk2oP78vfH5q7rcC 5Tymo5cIIZPncxDwaEjJ6QBLTWH2P1A= Authentication-Results: smtp-out2.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402237; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=aDxiEePKaHFlI5tO/p1XE2bOmMKz7ygZnuualgpXxeQ=; b=SXFLBIAhJ1jmnSvHBqXvDbAQjJYkHn7YmMuE/Xr4gAqElaT9hb0p+FWSdvyMCqANkIg8Vg qD5pr2sMLyAG7QVfUTYEIGENGM2us4Lczxhxv7CX+1jhOnC6YmVSPhNdLLprBwRBGVBAj1 414N/rMCK6lUAdMteaZOKHNpBKRYCQ4= Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id AE23E3EA63; Fri, 6 Feb 2026 18:23:57 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id kN3qKb0xhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:23:57 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org, Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v6 07/43] fscrypt: expose fscrypt_nokey_name Date: Fri, 6 Feb 2026 19:22:39 +0100 Message-ID: <20260206182336.1397715-8-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spamd-Result: default: False [-6.80 / 50.00]; REPLY(-4.00)[]; BAYES_HAM(-3.00)[100.00%]; NEURAL_HAM_LONG(-1.00)[-1.000]; MID_CONTAINS_FROM(1.00)[]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; RCVD_COUNT_TWO(0.00)[2]; FROM_HAS_DN(0.00)[]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; RCPT_COUNT_TWELVE(0.00)[14]; TO_MATCH_ENVRCPT_ALL(0.00)[]; RCVD_VIA_SMTP_AUTH(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; DKIM_SIGNED(0.00)[suse.com:s=susede1]; FUZZY_RATELIMITED(0.00)[rspamd.com]; TO_DN_SOME(0.00)[]; DBL_BLOCKED_OPENRESOLVER(0.00)[suse.com:mid,suse.com:email]; RCVD_TLS_ALL(0.00)[] X-Spam-Flag: NO X-Spam-Score: -6.80 X-Spam-Level: Content-Type: text/plain; charset="utf-8" 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 Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/132b64edf1e6b705995fb1a6dc2f194527f= 6be75.1706116485.git.josef@toxicpanda.com/ * No changes since. --- fs/crypto/fname.c | 36 ------------------------------------ include/linux/fscrypt.h | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index a9a4432d12ba..1e5e503686dd 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -27,42 +27,6 @@ */ #define FSCRYPT_FNAME_MIN_MSG_LEN 16 =20 -/* - * 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 a= llows - * it to find the directory entry again if requested. Naively, that would= just - * mean using the ciphertext filenames. However, since the ciphertext fil= enames - * 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 (2= 55 - * 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 qu= ickly - * find the directory entry. Since filesystems normally compute the dirha= sh - * over the on-disk filename (i.e. the ciphertext), it's not computable fr= om - * no-key names that abbreviate the ciphertext using the strong hash to fi= t 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 cas= es, - * 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 file= system - * 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 by= tes. - * - * This ensures that each no-key name contains everything needed to find t= he - * directory entry again, contains only legal characters, doesn't exceed - * NAME_MAX, is unambiguous unless there's a SHA-256 collision, and that w= e only - * take the performance hit of SHA-256 on very long filenames (which are r= are). - */ -struct fscrypt_nokey_name { - u32 dirhash[2]; - u8 bytes[149]; - u8 sha256[SHA256_DIGEST_SIZE]; -}; /* 189 bytes =3D> 252 bytes base64url-encoded, which is <=3D NAME_MAX (= 255) */ - /* * Decoded size of max-size no-key name, i.e. a name that was abbreviated = using * the strong hash and thus includes the 'sha256' field. This isn't simply diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index e3bb9e3756e1..5b4fc75c257c 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -56,6 +56,42 @@ struct fscrypt_name { #define fname_name(p) ((p)->disk_name.name) #define fname_len(p) ((p)->disk_name.len) =20 +/* + * 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 a= llows + * it to find the directory entry again if requested. Naively, that would= just + * mean using the ciphertext filenames. However, since the ciphertext fil= enames + * 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 (2= 55 + * 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 qu= ickly + * find the directory entry. Since filesystems normally compute the dirha= sh + * over the on-disk filename (i.e. the ciphertext), it's not computable fr= om + * no-key names that abbreviate the ciphertext using the strong hash to fi= t 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 cas= es, + * 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 file= system + * 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 by= tes. + * + * This ensures that each no-key name contains everything needed to find t= he + * directory entry again, contains only legal characters, doesn't exceed + * NAME_MAX, is unambiguous unless there's a SHA-256 collision, and that w= e only + * take the performance hit of SHA-256 on very long filenames (which are r= are). + */ +struct fscrypt_nokey_name { + u32 dirhash[2]; + u8 bytes[149]; + u8 sha256[32]; +}; /* 189 bytes =3D> 252 bytes base64url-encoded, which is <=3D NAME_MAX (= 255) */ + /* Maximum value for the third parameter of fscrypt_operations.set_context= (). */ #define FSCRYPT_SET_CONTEXT_MAX_SIZE 40 =20 --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9AFFB429818 for ; Fri, 6 Feb 2026 18:24:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402257; cv=none; b=GVzTIv2UZHoD/LbIPvs11Xxnd6eR9yc/EVo90VzLO+eF4lf0eG6bRdI8wCkH1ojarlZzSLftPHeYyZtkx8EAOFvLwlZ/Y3d/C6PgOsR0qGrMdxGT+gadZ7qpMsyKk+5JYbJL6iVzzz+iXLo+Iy8AIlDllr6KdvAKbGLypPISkA8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402257; c=relaxed/simple; bh=2+t/XBHJhNmnBBZvpZXK21VXE1q7IsC0yC80NgSLp2w=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jr5jfqfOzVvot289ceO+58xs6wRq3e9kSyv4PCBhKgnworGt7kF3uM6Cz3B4BPY6VK+k0/ODRelcTKICYpANt8N/loBRBgdvpUjYhitQHmq6oIMbogIfw+KmJGl6aJvypauTgiCT07SXV+diW6aURzT9BpY/o4DjRV2FVc94rwg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id 599525BCE0; Fri, 6 Feb 2026 18:23:58 +0000 (UTC) Authentication-Results: smtp-out2.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 27A833EA63; Fri, 6 Feb 2026 18:23:58 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id eNs0Cb4xhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:23:58 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba , Jonathan Corbet Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org Subject: [PATCH v6 08/43] fscrypt: add documentation about extent encryption Date: Fri, 6 Feb 2026 19:22:40 +0100 Message-ID: <20260206182336.1397715-9-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO X-Spam-Score: -4.00 X-Rspamd-Queue-Id: 599525BCE0 X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Server: rspamd2.dmz-prg2.suse.org X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik Add a couple of sections to the fscrypt documentation about per-extent encryption. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/7b2cc4dd423c3930e51b1ef5dd209164ff1= 1c05a.1706116485.git.josef@toxicpanda.com/ * No changes since. --- Documentation/filesystems/fscrypt.rst | 41 +++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesyst= ems/fscrypt.rst index 70af896822e1..8afec55dd913 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -283,6 +283,21 @@ alternative master keys or to support rotating master = keys. Instead, the master keys may be wrapped in userspace, e.g. as is done by the `fscrypt `_ tool. =20 +Per-extent encryption keys +-------------------------- + +For certain file systems, such as btrfs, it's desired to derive a +per-extent encryption key. This is to enable features such as snapshots +and reflink, where you could have different inodes pointing at the same +extent. When a new extent is created fscrypt randomly generates a +16-byte nonce and the file system stores it along side the extent. +Then, it uses a KDF (as described in `Key derivation function`_) to +derive the extent's key from the master key and nonce. + +Currently the inode's master key and encryption policy must match the +extent, so you cannot share extents between inodes that were encrypted +differently. + DIRECT_KEY policies ------------------- =20 @@ -1488,6 +1503,27 @@ by the kernel and is used as KDF input or as a tweak= to cause different files to be encrypted differently; see `Per-file encryption keys`_ and `DIRECT_KEY policies`_. =20 +Extent encryption context +------------------------- + +The extent encryption context mirrors the important parts of the above +`Encryption context`_, with a few ommisions. The struct is defined as +follows:: + + struct fscrypt_extent_context { + u8 version; + u8 encryption_mode; + u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; + u8 nonce[FSCRYPT_FILE_NONCE_SIZE]; + }; + +Currently all fields much match the containing inode's encryption +context, with the exception of the nonce. + +Additionally extent encryption is only supported with +FSCRYPT_EXTENT_CONTEXT_V2 using the standard policy, all other policies +are disallowed. + Data path changes ----------------- =20 @@ -1511,6 +1547,11 @@ buffer. Some filesystems, such as UBIFS, already us= e temporary buffers regardless of encryption. Other filesystems, such as ext4 and F2FS, have to allocate bounce pages specially for encryption. =20 +Inline encryption is not optional for extent encryption based file +systems, the amount of objects required to be kept around is too much. +Inline encryption handles the object lifetime details which results in a +cleaner implementation. + Filename hashing and encoding ----------------------------- =20 --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6EA8642E00C for ; Fri, 6 Feb 2026 18:24:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402261; cv=none; b=VRdJPO9kTUr+rKNfMXsVuaXlaSj+SFhlJbUw/MYRiuRsImn5hQVaVIcmw2ZeqxIFLxwMTWBO9sRSTuITT1VxkkV8YtJ5lF++T9ZQwhlLE/hYGCsVd7wyQX9ClC6m8RGvjPreldxu2mfrvc5hPNc6QeXy57aP/GuTAOg2QGhA7Ig= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402261; c=relaxed/simple; bh=XVhsTk+LYpWAXNJDQrvJq2UU4XDe6OSIWEwozraAeOc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nurk/8AhWD3R+06xT6UHZyatAsJYG9uSt/Siw6Pf3xAs2IcCsWXcrpz59aCTC4ySw7B5XnL3jNhVZ+yEJM7ZCrV7Jf3TeCLX2fIDUJnPJICSMEFQGYeTxTcJATdsf71KK1jAUS6dkvv60QZm72biRaQJk4uGVN5XYZSgtSYbVsE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=q827tzaI; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=q827tzaI; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="q827tzaI"; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="q827tzaI" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id F2F7F5BD27; Fri, 6 Feb 2026 18:23:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402239; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Ahxdoq0wAm++q/QMvFMTlIGeudeamFjihSebXBaXzxc=; b=q827tzaI998W3R1OaxrJEuODt4hhyA2sxStRK+nO8UgwIsr+6x8LhRCfV4zt9JXvi34KKh jRp7GVBngNSUGZUhEXk3igIekc0cNKAYuPIcJ/XoGM+e19/r+liIGM5k/SQcLS00ayiQZg up9jdfv+QGV73QtTgJFXBhi7BPy38qo= Authentication-Results: smtp-out2.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402239; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Ahxdoq0wAm++q/QMvFMTlIGeudeamFjihSebXBaXzxc=; b=q827tzaI998W3R1OaxrJEuODt4hhyA2sxStRK+nO8UgwIsr+6x8LhRCfV4zt9JXvi34KKh jRp7GVBngNSUGZUhEXk3igIekc0cNKAYuPIcJ/XoGM+e19/r+liIGM5k/SQcLS00ayiQZg up9jdfv+QGV73QtTgJFXBhi7BPy38qo= Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id C40DF3EA63; Fri, 6 Feb 2026 18:23:58 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id OMFeL74xhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:23:58 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org, Boris Burkov Subject: [PATCH v6 09/43] btrfs: add infrastructure for safe em freeing Date: Fri, 6 Feb 2026 19:22:41 +0100 Message-ID: <20260206182336.1397715-10-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spam-Score: -6.80 X-Spamd-Result: default: False [-6.80 / 50.00]; REPLY(-4.00)[]; BAYES_HAM(-3.00)[100.00%]; NEURAL_HAM_LONG(-1.00)[-1.000]; MID_CONTAINS_FROM(1.00)[]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; RCVD_COUNT_TWO(0.00)[2]; FROM_HAS_DN(0.00)[]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; RCPT_COUNT_TWELVE(0.00)[13]; TO_MATCH_ENVRCPT_ALL(0.00)[]; RCVD_VIA_SMTP_AUTH(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; DKIM_SIGNED(0.00)[suse.com:s=susede1]; FUZZY_RATELIMITED(0.00)[rspamd.com]; TO_DN_SOME(0.00)[]; DBL_BLOCKED_OPENRESOLVER(0.00)[suse.com:mid,suse.com:email]; RCVD_TLS_ALL(0.00)[] X-Spam-Level: X-Spam-Flag: NO Content-Type: text/plain; charset="utf-8" From: Josef Bacik When we add fscrypt support we're going to have fscrypt objects hanging off of extent_maps. This includes a block key, which if we're the last one freeing the key we may have to unregister it from the block layer. This requires taking a semaphore in the block layer, which means we can't free em's under the extent map tree lock. Thankfully we only do this in two places, one where we're dropping a range of extent maps, and when we're freeing logged extents. Add a free_extent_map_safe() which will add the em to a list in the em_tree if we free'd the object. Currently this is unconditional but will be changed to conditional on the fscrypt object we will add in a later patch. To process these delayed objects add a free_pending_extent_maps() that is called after the lock has been dropped on the em_tree. This will process the extent maps on the freed list and do the appropriate freeing work in a safe manner. Signed-off-by: Josef Bacik Reviewed-by: Boris Burkov Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/6cf44f7860e94de68df242e69f4c5250bd0= 61cff.1706116485.git.josef@toxicpanda.com/ * No changes since (other than simple function renames). --- fs/btrfs/extent_map.c | 76 +++++++++++++++++++++++++++++++++++++++++-- fs/btrfs/extent_map.h | 10 ++++++ fs/btrfs/tree-log.c | 6 ++-- 3 files changed, 87 insertions(+), 5 deletions(-) diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c index 095a561d733f..58589fc11802 100644 --- a/fs/btrfs/extent_map.c +++ b/fs/btrfs/extent_map.c @@ -34,7 +34,9 @@ void __cold btrfs_extent_map_exit(void) void btrfs_extent_map_tree_init(struct extent_map_tree *tree) { tree->root =3D RB_ROOT; + tree->flags =3D 0; INIT_LIST_HEAD(&tree->modified_extents); + INIT_LIST_HEAD(&tree->freed_extents); rwlock_init(&tree->lock); } =20 @@ -51,9 +53,15 @@ struct extent_map *btrfs_alloc_extent_map(void) RB_CLEAR_NODE(&em->rb_node); refcount_set(&em->refs, 1); INIT_LIST_HEAD(&em->list); + INIT_LIST_HEAD(&em->free_list); return em; } =20 +static void free_extent_map(struct extent_map *em) +{ + kmem_cache_free(extent_map_cache, em); +} + /* * Drop the reference out on @em by one and free the structure if the refe= rence * count hits zero. @@ -65,10 +73,69 @@ void btrfs_free_extent_map(struct extent_map *em) if (refcount_dec_and_test(&em->refs)) { WARN_ON(btrfs_extent_map_in_tree(em)); WARN_ON(!list_empty(&em->list)); - kmem_cache_free(extent_map_cache, em); + free_extent_map(em); + } +} + +/* + * Drop a ref for the extent map in the given tree. + * + * @tree: tree that the em is a part of. + * @em: the em to drop the reference to. + * + * Drop the reference count on @em by one, if the reference count hits 0 a= nd + * there is an object on the em that can't be safely freed in the current + * context (if we are holding the extent_map_tree->lock for example), then= add + * it to the freed_extents list on the extent_map_tree for later processin= g. + * + * This must be followed by a btrfs_free_pending_extent_maps() to clear + * the pending frees. + */ +void btrfs_free_extent_map_safe(struct extent_map_tree *tree, + struct extent_map *em) +{ + lockdep_assert_held_write(&tree->lock); + + if (!em) + return; + + if (refcount_dec_and_test(&em->refs)) { + WARN_ON(btrfs_extent_map_in_tree(em)); + WARN_ON(!list_empty(&em->list)); + list_add_tail(&em->free_list, &tree->freed_extents); + set_bit(EXTENT_MAP_TREE_PENDING_FREES, &tree->flags); } } =20 +/* + * Free the em objects that exist on the em tree + * + * @tree: the tree to free the objects from. + * + * If there are any objects on the em->freed_extents list go ahead and + * free them here in a safe way. This is to be coupled with any uses of + * btrfs_free_extent_map_safe(). + */ +void btrfs_free_pending_extent_maps(struct extent_map_tree *tree) +{ + struct extent_map *em; + + /* Avoid taking the write lock if we don't have any pending frees. */ + if (!test_and_clear_bit(EXTENT_MAP_TREE_PENDING_FREES, &tree->flags)) + return; + + write_lock(&tree->lock); + while ((em =3D list_first_entry_or_null(&tree->freed_extents, + struct extent_map, free_list))) { + list_del_init(&em->free_list); + write_unlock(&tree->lock); + free_extent_map(em); + cond_resched(); + write_lock(&tree->lock); + } + write_unlock(&tree->lock); +} + /* Do the math around the end of an extent, handling wrapping. */ static u64 range_end(u64 start, u64 len) { @@ -784,7 +851,7 @@ static void drop_all_extent_maps_fast(struct btrfs_inod= e *inode) em =3D rb_entry(node, struct extent_map, rb_node); em->flags &=3D ~(EXTENT_FLAG_PINNED | EXTENT_FLAG_LOGGING); btrfs_remove_extent_mapping(inode, em); - btrfs_free_extent_map(em); + btrfs_free_extent_map_safe(tree, em); =20 if (cond_resched_rwlock_write(&tree->lock)) node =3D rb_first(&tree->root); @@ -792,6 +859,8 @@ static void drop_all_extent_maps_fast(struct btrfs_inod= e *inode) node =3D next; } write_unlock(&tree->lock); + + btrfs_free_pending_extent_maps(tree); } =20 /* @@ -986,13 +1055,14 @@ void btrfs_drop_extent_map_range(struct btrfs_inode = *inode, u64 start, u64 end, btrfs_free_extent_map(em); next: /* Once for us (for our lookup reference). */ - btrfs_free_extent_map(em); + btrfs_free_extent_map_safe(em_tree, em); =20 em =3D next_em; } =20 write_unlock(&em_tree->lock); =20 + btrfs_free_pending_extent_maps(em_tree); btrfs_free_extent_map(split); btrfs_free_extent_map(split2); } diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h index 6f685f3c9327..a962012be1c3 100644 --- a/fs/btrfs/extent_map.h +++ b/fs/btrfs/extent_map.h @@ -97,11 +97,18 @@ struct extent_map { u32 flags; refcount_t refs; struct list_head list; + struct list_head free_list; +}; + +enum extent_map_flags { + EXTENT_MAP_TREE_PENDING_FREES, }; =20 struct extent_map_tree { struct rb_root root; + unsigned long flags; struct list_head modified_extents; + struct list_head freed_extents; rwlock_t lock; }; =20 @@ -175,6 +182,9 @@ int btrfs_split_extent_map(struct btrfs_inode *inode, u= 64 start, u64 len, u64 pr =20 struct extent_map *btrfs_alloc_extent_map(void); void btrfs_free_extent_map(struct extent_map *em); +void btrfs_free_extent_map_safe(struct extent_map_tree *tree, + struct extent_map *em); +void btrfs_free_pending_extent_maps(struct extent_map_tree *tree); int __init btrfs_extent_map_init(void); void __cold btrfs_extent_map_exit(void); int btrfs_unpin_extent_cache(struct btrfs_inode *inode, u64 start, u64 len= , u64 gen); diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index e1bd03ebfd98..4034c04d4d63 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -5383,7 +5383,7 @@ static int btrfs_log_changed_extents(struct btrfs_tra= ns_handle *trans, */ if (ret) { btrfs_clear_em_logging(inode, em); - btrfs_free_extent_map(em); + btrfs_free_extent_map_safe(tree, em); continue; } =20 @@ -5392,11 +5392,13 @@ static int btrfs_log_changed_extents(struct btrfs_t= rans_handle *trans, ret =3D log_one_extent(trans, inode, em, path, ctx); write_lock(&tree->lock); btrfs_clear_em_logging(inode, em); - btrfs_free_extent_map(em); + btrfs_free_extent_map_safe(tree, em); } WARN_ON(!list_empty(&extents)); write_unlock(&tree->lock); =20 + btrfs_free_pending_extent_maps(tree); + if (!ret) ret =3D btrfs_log_prealloc_extents(trans, inode, path, ctx); if (ret) --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A5C06429831 for ; Fri, 6 Feb 2026 18:24:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402259; cv=none; b=Ymm5cP4uNAQNHQz9MZeFlcWcbic5BZPaIywZOFNDcqHKifAbbeyDSzPMLik/n4isufni7TsuqNnoqqravw442M/blbLtGsN4yITppMYlszx7gjSyHEBWqJpSi/6yRfFzaeoeN5/HAccPbc2/N55Y37beVVMFZnza746Wr4WQojk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402259; c=relaxed/simple; bh=6eQ1M9/H/XuGaMD1MvhzaaFNWtUzgqxTRCVgXUGEjCw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=n9srlvd5Nc1Vf3wK+cg6CRGh0ifeE1XsUwlGq5OjAuz3F0shJcnOD/jUKGyvZ2sEBvmdqicqXKupuKfX34H6Gyv8i6XnKYL8yyx+ZfvobKIrUAOBzgJhw1remvwCTJW6tp3Lzu06GsUoTpU2AunF/R4jcfMNcomYMedb6dPnQw0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=vNsUsCXK; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=vNsUsCXK; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="vNsUsCXK"; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="vNsUsCXK" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 1406F3E74D; Fri, 6 Feb 2026 18:24:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402240; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ISdzY2BJK4ZBkWpAA57Mi26qixkjBLJ0zEsaqG3HRQ8=; b=vNsUsCXKlsFRP2PH6WLy/FOdwZxNqp0otxd20QdJSM7c90togl8xxmzrklH00p6xTujlGR F54hdiDomNQJFGGsnwxBI5OFyfPmYfoTR3vQAOZGaRsnpIwQ+gKNbUBBA2MWsKMw7CfJF+ CzIPyZM972sA48tmE8rsmiNE2/WLe90= Authentication-Results: smtp-out1.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402240; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ISdzY2BJK4ZBkWpAA57Mi26qixkjBLJ0zEsaqG3HRQ8=; b=vNsUsCXKlsFRP2PH6WLy/FOdwZxNqp0otxd20QdJSM7c90togl8xxmzrklH00p6xTujlGR F54hdiDomNQJFGGsnwxBI5OFyfPmYfoTR3vQAOZGaRsnpIwQ+gKNbUBBA2MWsKMw7CfJF+ CzIPyZM972sA48tmE8rsmiNE2/WLe90= Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id D51E03EA63; Fri, 6 Feb 2026 18:23:59 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id QJOFM78xhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:23:59 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org, Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v6 10/43] btrfs: start using fscrypt hooks Date: Fri, 6 Feb 2026 19:22:42 +0100 Message-ID: <20260206182336.1397715-11-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spam-Score: -6.80 X-Spamd-Result: default: False [-6.80 / 50.00]; REPLY(-4.00)[]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; DBL_BLOCKED_OPENRESOLVER(0.00)[suse.com:mid,suse.com:email]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; TO_DN_SOME(0.00)[]; RCPT_COUNT_TWELVE(0.00)[14]; RCVD_VIA_SMTP_AUTH(0.00)[]; FUZZY_RATELIMITED(0.00)[rspamd.com]; FROM_HAS_DN(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; R_RATELIMIT(0.00)[to_ip_from(RLjpbzrka5mc1bf4fb9cmz1e4f)]; DKIM_SIGNED(0.00)[suse.com:s=susede1]; RCVD_TLS_ALL(0.00)[] X-Spam-Level: X-Spam-Flag: NO Content-Type: text/plain; charset="utf-8" From: Omar Sandoval In order to appropriately encrypt, create, open, rename, and various symlink operations must call fscrypt hooks. These determine whether the inode should be encrypted and do other preparatory actions. The superblock must have fscrypt operations registered, so implement the minimal set also, and introduce the new fscrypt.[ch] files to hold the fscrypt-specific functionality. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/333de15efb2d7ec220293c4b5e3782fe856= 34c06.1706116485.git.josef@toxicpanda.com/ * FSCrypt info moved from VFS inode to FS specific inode structure. * Trivial renames. --- fs/btrfs/Makefile | 1 + fs/btrfs/btrfs_inode.h | 4 ++ fs/btrfs/file.c | 3 ++ fs/btrfs/fscrypt.c | 10 ++++ fs/btrfs/fscrypt.h | 10 ++++ fs/btrfs/inode.c | 109 +++++++++++++++++++++++++++++++++-------- fs/btrfs/super.c | 2 + 7 files changed, 118 insertions(+), 21 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 743d7677b175..715ba42f48dc 100644 --- a/fs/btrfs/Makefile +++ b/fs/btrfs/Makefile @@ -39,6 +39,7 @@ btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) +=3D acl.o btrfs-$(CONFIG_BTRFS_DEBUG) +=3D ref-verify.o btrfs-$(CONFIG_BLK_DEV_ZONED) +=3D zoned.o btrfs-$(CONFIG_FS_VERITY) +=3D verity.o +btrfs-$(CONFIG_FS_ENCRYPTION) +=3D fscrypt.o =20 btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) +=3D tests/free-space-tests.o \ tests/extent-buffer-tests.o tests/btrfs-tests.o \ diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 73602ee8de3f..bf638a6a0973 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -342,6 +342,9 @@ struct btrfs_inode { #ifdef CONFIG_FS_VERITY struct fsverity_info *i_verity_info; #endif +#ifdef CONFIG_FS_ENCRYPTION + struct fscrypt_inode_info *i_crypt_info; +#endif =20 struct inode vfs_inode; }; @@ -586,6 +589,7 @@ struct btrfs_new_inode_args { struct posix_acl *default_acl; struct posix_acl *acl; struct fscrypt_name fname; + bool encrypt; }; =20 int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args, diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 56ece1109832..3c0db279f592 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -3812,6 +3812,9 @@ static int btrfs_file_open(struct inode *inode, struc= t file *filp) return -EIO; =20 filp->f_mode |=3D FMODE_NOWAIT | FMODE_CAN_ODIRECT; + ret =3D fscrypt_file_open(inode, filp); + if (ret) + return ret; =20 ret =3D fsverity_file_open(inode, filp); if (ret) diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c new file mode 100644 index 000000000000..6cfba7d94e72 --- /dev/null +++ b/fs/btrfs/fscrypt.c @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "ctree.h" +#include "btrfs_inode.h" +#include "fscrypt.h" + +const struct fscrypt_operations btrfs_fscrypt_ops =3D { + .inode_info_offs =3D (int)offsetof(struct btrfs_inode, i_crypt_info) - + (int)offsetof(struct btrfs_inode, vfs_inode), +}; diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h new file mode 100644 index 000000000000..7f4e6888bd43 --- /dev/null +++ b/fs/btrfs/fscrypt.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef BTRFS_FSCRYPT_H +#define BTRFS_FSCRYPT_H + +#include + +extern const struct fscrypt_operations btrfs_fscrypt_ops; + +#endif /* BTRFS_FSCRYPT_H */ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 10609b8199a0..fdc07612af6e 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5446,6 +5446,10 @@ static int btrfs_setattr(struct mnt_idmap *idmap, st= ruct dentry *dentry, if (ret) return ret; =20 + ret =3D fscrypt_prepare_setattr(dentry, attr); + if (ret) + return ret; + if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) { ret =3D btrfs_setsize(inode, attr); if (ret) @@ -5600,11 +5604,8 @@ void btrfs_evict_inode(struct inode *inode) =20 trace_btrfs_inode_evict(inode); =20 - if (!root) { - fsverity_cleanup_inode(inode); - clear_inode(inode); - return; - } + if (!root) + goto cleanup; =20 fs_info =3D inode_to_fs_info(inode); evict_inode_truncate_pages(inode); @@ -5704,6 +5705,9 @@ void btrfs_evict_inode(struct inode *inode) * to retry these periodically in the future. */ btrfs_remove_delayed_node(BTRFS_I(inode)); + +cleanup: + fscrypt_put_encryption_info(inode); fsverity_cleanup_inode(inode); clear_inode(inode); } @@ -6482,6 +6486,12 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_a= rgs *args, return ret; } =20 + ret =3D fscrypt_prepare_new_inode(dir, inode, &args->encrypt); + if (ret) { + fscrypt_free_filename(&args->fname); + return ret; + } + /* 1 to add inode item */ *trans_num_items =3D 1; /* 1 to add compression property */ @@ -6974,9 +6984,13 @@ static int btrfs_link(struct dentry *old_dentry, str= uct inode *dir, if (inode->i_nlink >=3D BTRFS_LINK_MAX) return -EMLINK; =20 + ret =3D fscrypt_prepare_link(old_dentry, dir, dentry); + if (ret) + return ret; + ret =3D fscrypt_setup_filename(dir, &dentry->d_name, 0, &fname); if (ret) - goto fail; + return ret; =20 ret =3D btrfs_set_inode_index(BTRFS_I(dir), &index); if (ret) @@ -8035,6 +8049,9 @@ struct inode *btrfs_alloc_inode(struct super_block *s= b) INIT_LIST_HEAD(&ei->delayed_iput); init_rwsem(&ei->i_mmap_lock); =20 +#ifdef CONFIG_FS_ENCRYPTION + ei->i_crypt_info =3D NULL; +#endif return inode; } =20 @@ -8050,6 +8067,7 @@ void btrfs_test_destroy_inode(struct inode *inode) void btrfs_free_inode(struct inode *inode) { kfree(BTRFS_I(inode)->file_extent_tree); + fscrypt_free_inode(inode); kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode)); } =20 @@ -8121,8 +8139,7 @@ int btrfs_drop_inode(struct inode *inode) /* the snap/subvol tree is on deleting */ if (btrfs_root_refs(&root->root_item) =3D=3D 0) return 1; - else - return inode_generic_drop(inode); + return inode_generic_drop(inode) || fscrypt_drop_inode(inode); } =20 static void init_once(void *foo) @@ -8776,6 +8793,10 @@ static int btrfs_rename2(struct mnt_idmap *idmap, st= ruct inode *old_dir, if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT)) return -EINVAL; =20 + ret =3D fscrypt_prepare_rename(old_dir, old_dentry, new_dir, new_dentry, = flags); + if (ret) + return ret; + if (flags & RENAME_EXCHANGE) ret =3D btrfs_rename_exchange(old_dir, old_dentry, new_dir, new_dentry); @@ -8970,20 +8991,28 @@ static int btrfs_symlink(struct mnt_idmap *idmap, s= truct inode *dir, }; unsigned int trans_num_items; int ret; - int name_len; int datasize; unsigned long ptr; struct btrfs_file_extent_item *ei; struct extent_buffer *leaf; + struct fscrypt_str disk_link; + size_t max_len; + u32 name_len =3D strlen(symname); + + /* + * BTRFS_MAX_INLINE_DATA_SIZE() isn't actually telling the truth, we actu= ally + * limit inline data extents to min(BTRFS_MAX_INLINE_DATA_SIZE(), sectors= ize), + * so adjust max_len given this wonderful bit of inconsistency. + */ + max_len =3D min_t(size_t, BTRFS_MAX_INLINE_DATA_SIZE(fs_info), fs_info->s= ectorsize); =20 - name_len =3D strlen(symname); /* - * Symlinks utilize uncompressed inline extent data, which should not - * reach block size. + * fscrypt sets disk_link.len to be len + 1, including a NUL terminator, + * but we don't store that '\0' character. */ - if (name_len > BTRFS_MAX_INLINE_DATA_SIZE(fs_info) || - name_len >=3D fs_info->sectorsize) - return -ENAMETOOLONG; + ret =3D fscrypt_prepare_symlink(dir, symname, name_len, max_len + 1, &dis= k_link); + if (ret) + return ret; =20 inode =3D new_inode(dir->i_sb); if (!inode) @@ -8992,8 +9021,8 @@ static int btrfs_symlink(struct mnt_idmap *idmap, str= uct inode *dir, inode->i_op =3D &btrfs_symlink_inode_operations; inode_nohighmem(inode); inode->i_mapping->a_ops =3D &btrfs_aops; - btrfs_i_size_write(BTRFS_I(inode), name_len); - inode_set_bytes(inode, name_len); + btrfs_i_size_write(BTRFS_I(inode), disk_link.len - 1); + inode_set_bytes(inode, disk_link.len - 1); =20 new_inode_args.inode =3D inode; ret =3D btrfs_new_inode_prepare(&new_inode_args, &trans_num_items); @@ -9020,10 +9049,22 @@ static int btrfs_symlink(struct mnt_idmap *idmap, s= truct inode *dir, inode =3D NULL; goto out; } + + if (IS_ENCRYPTED(inode)) { + ret =3D fscrypt_encrypt_symlink(inode, symname, name_len, &disk_link); + if (ret) { + btrfs_abort_transaction(trans, ret); + btrfs_free_path(path); + discard_new_inode(inode); + inode =3D NULL; + goto out; + } + } + key.objectid =3D btrfs_ino(BTRFS_I(inode)); key.type =3D BTRFS_EXTENT_DATA_KEY; key.offset =3D 0; - datasize =3D btrfs_file_extent_calc_inline_size(name_len); + datasize =3D btrfs_file_extent_calc_inline_size(disk_link.len - 1); ret =3D btrfs_insert_empty_item(trans, root, path, &key, datasize); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); @@ -9041,10 +9082,10 @@ static int btrfs_symlink(struct mnt_idmap *idmap, s= truct inode *dir, btrfs_set_file_extent_encryption(leaf, ei, 0); btrfs_set_file_extent_compression(leaf, ei, 0); btrfs_set_file_extent_other_encoding(leaf, ei, 0); - btrfs_set_file_extent_ram_bytes(leaf, ei, name_len); + btrfs_set_file_extent_ram_bytes(leaf, ei, disk_link.len - 1); =20 ptr =3D 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_free_path(path); =20 d_instantiate_new(dentry, inode); @@ -9060,6 +9101,29 @@ static int btrfs_symlink(struct mnt_idmap *idmap, st= ruct inode *dir, return ret; } =20 +static const char *btrfs_get_link(struct dentry *dentry, struct inode *ino= de, + struct delayed_call *done) +{ + struct page *cpage; + const char *paddr; + struct btrfs_fs_info *fs_info =3D btrfs_sb(inode->i_sb); + + if (!IS_ENCRYPTED(inode)) + return page_get_link(dentry, inode, done); + + if (!dentry) + return ERR_PTR(-ECHILD); + + cpage =3D read_mapping_page(inode->i_mapping, 0, NULL); + if (IS_ERR(cpage)) + return ERR_CAST(cpage); + + paddr =3D 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, @@ -10684,7 +10748,7 @@ static const struct inode_operations btrfs_special_= inode_operations =3D { .update_time =3D btrfs_update_time, }; static const struct inode_operations btrfs_symlink_inode_operations =3D { - .get_link =3D page_get_link, + .get_link =3D btrfs_get_link, .getattr =3D btrfs_getattr, .setattr =3D btrfs_setattr, .permission =3D btrfs_permission, @@ -10694,4 +10758,7 @@ static const struct inode_operations btrfs_symlink_= inode_operations =3D { =20 const struct dentry_operations btrfs_dentry_operations =3D { .d_delete =3D btrfs_dentry_delete, +#ifdef CONFIG_FS_ENCRYPTION + .d_revalidate =3D fscrypt_d_revalidate, +#endif }; diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index d64d303b6edc..7eb367b1f6f6 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -49,6 +49,7 @@ #include "tests/btrfs-tests.h" #include "block-group.h" #include "discard.h" +#include "fscrypt.h" #include "qgroup.h" #include "raid56.h" #include "fs.h" @@ -969,6 +970,7 @@ static int btrfs_fill_super(struct super_block *sb, sb->s_vop =3D &btrfs_verityops; #endif sb->s_xattr =3D btrfs_xattr_handlers; + fscrypt_set_ops(sb, &btrfs_fscrypt_ops); sb->s_time_gran =3D 1; sb->s_iflags |=3D SB_I_CGROUPWB | SB_I_ALLOW_HSM; =20 --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C4C2143635E for ; Fri, 6 Feb 2026 18:24:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402264; cv=none; b=OxfrQwMgLfPauN6aiLugq0mfMuAdW9AccwmJQDVcfqhkk/hK/ftIdfJS/EnbJl3hE5YqwwEN33oJNrEOaZyimcdwLL27f45fSm4jnh+5wwXQYkNf9gcKB2B5RJy04/wS8Fmq8K1rWh0WsQTLPpEY2tpiw44vuY55RLgjgMvwvT8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402264; c=relaxed/simple; bh=pyvoI9kpidD1sgfEAw3bRdS1eNDuzNnC2qgbERKp0R0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZGJBsZ6OAJt8pVlcW3xCVF2mLUm7srHiESfM/tEexsG80N5/svlXZsDkbNE90EeL556Gyt/PCxmSU8594nayW1s0JmeFpIZGCQyHuZA+Z9Bhbwk6PYVp6LjaFUaG8MK9FfP/RvOUcVbstKfuVDQM9rdqVpQnPGETw9QA5Ki9KIY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=ManIxIWB; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=ManIxIWB; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="ManIxIWB"; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="ManIxIWB" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id E019B3E700; Fri, 6 Feb 2026 18:24:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402240; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=+65SWdv1RzImpYlFpkGn+W3DnH2iFIUZVW/vILN7NRw=; b=ManIxIWB7B8atNm4iqFEkZEV7szuEcVwmon6vHM4HeBOKWw8uQqqqKDcq2j9t0LSo9HfAS CC+vfpoS0BG4/yXEGPlVq0EF0ACMtMWWrvyJkrjqEPs4/9l8TPcQtp1/13lms1jejJ2yaM VEaDJ2IzOb4HUq+X0SaaCAqpOwR1yA4= Authentication-Results: smtp-out1.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402240; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=+65SWdv1RzImpYlFpkGn+W3DnH2iFIUZVW/vILN7NRw=; b=ManIxIWB7B8atNm4iqFEkZEV7szuEcVwmon6vHM4HeBOKWw8uQqqqKDcq2j9t0LSo9HfAS CC+vfpoS0BG4/yXEGPlVq0EF0ACMtMWWrvyJkrjqEPs4/9l8TPcQtp1/13lms1jejJ2yaM VEaDJ2IzOb4HUq+X0SaaCAqpOwR1yA4= Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id B283E3EA63; Fri, 6 Feb 2026 18:24:00 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id iCX1KsAxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:00 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org, Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v6 11/43] btrfs: add inode encryption contexts Date: Fri, 6 Feb 2026 19:22:43 +0100 Message-ID: <20260206182336.1397715-12-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spam-Score: -6.80 X-Spamd-Result: default: False [-6.80 / 50.00]; REPLY(-4.00)[]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; DBL_BLOCKED_OPENRESOLVER(0.00)[suse.com:mid,suse.com:email]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; TO_DN_SOME(0.00)[]; RCPT_COUNT_TWELVE(0.00)[14]; RCVD_VIA_SMTP_AUTH(0.00)[]; FUZZY_RATELIMITED(0.00)[rspamd.com]; FROM_HAS_DN(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; R_RATELIMIT(0.00)[to_ip_from(RLjpbzrka5mc1bf4fb9cmz1e4f)]; DKIM_SIGNED(0.00)[suse.com:s=susede1]; RCVD_TLS_ALL(0.00)[] X-Spam-Level: X-Spam-Flag: NO Content-Type: text/plain; charset="utf-8" From: Omar Sandoval fscrypt stores a context item with encrypted inodes that contains the related encryption information. fscrypt provides an arbitrary blob for the filesystem to store, and it does not clearly fit into an existing structure, so this goes in a new item type. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/5a88efb484b0874a7430b83bc6e5f6b9aa5= 858d5.1706116485.git.josef@toxicpanda.com/ * Shorten the inode context key macro name to BTRFS_FSCRYPT_INODE_CTX_KEY. --- fs/btrfs/fscrypt.c | 116 ++++++++++++++++++++++++++++++++ fs/btrfs/fscrypt.h | 2 + fs/btrfs/inode.c | 19 ++++++ fs/btrfs/ioctl.c | 8 ++- include/uapi/linux/btrfs_tree.h | 10 +++ 5 files changed, 153 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index 6cfba7d94e72..e9b024d671a2 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -1,10 +1,126 @@ // SPDX-License-Identifier: GPL-2.0 =20 +#include #include "ctree.h" +#include "accessors.h" #include "btrfs_inode.h" +#include "disk-io.h" +#include "fs.h" #include "fscrypt.h" +#include "ioctl.h" +#include "messages.h" +#include "transaction.h" +#include "xattr.h" + +static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_= t len) +{ + struct btrfs_key key =3D { + .objectid =3D btrfs_ino(BTRFS_I(inode)), + .type =3D BTRFS_FSCRYPT_INODE_CTX_KEY, + .offset =3D 0, + }; + struct btrfs_path *path; + struct extent_buffer *leaf; + unsigned long ptr; + int ret; + + + path =3D btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + ret =3D btrfs_search_slot(NULL, BTRFS_I(inode)->root, &key, path, 0, 0); + if (ret) { + len =3D -ENOENT; + goto out; + } + + leaf =3D path->nodes[0]; + ptr =3D btrfs_item_ptr_offset(leaf, path->slots[0]); + /* fscrypt provides max context length, but it could be less */ + len =3D min_t(size_t, len, btrfs_item_size(leaf, path->slots[0])); + read_extent_buffer(leaf, ctx, ptr, len); + +out: + btrfs_free_path(path); + return len; +} + +static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx, + size_t len, void *fs_data) +{ + struct btrfs_trans_handle *trans =3D fs_data; + struct btrfs_key key =3D { + .objectid =3D btrfs_ino(BTRFS_I(inode)), + .type =3D BTRFS_FSCRYPT_INODE_CTX_KEY, + .offset =3D 0, + }; + struct btrfs_path *path =3D NULL; + struct extent_buffer *leaf; + unsigned long ptr; + int ret; + + if (!trans) + trans =3D btrfs_start_transaction(BTRFS_I(inode)->root, 2); + if (IS_ERR(trans)) + return PTR_ERR(trans); + + path =3D btrfs_alloc_path(); + if (!path) { + ret =3D -ENOMEM; + goto out_err; + } + + ret =3D btrfs_search_slot(trans, BTRFS_I(inode)->root, &key, path, 0, 1); + if (ret < 0) + goto out_err; + + if (ret > 0) { + btrfs_release_path(path); + ret =3D btrfs_insert_empty_item(trans, BTRFS_I(inode)->root, path, &key,= len); + if (ret) + goto out_err; + } + + leaf =3D path->nodes[0]; + ptr =3D btrfs_item_ptr_offset(leaf, path->slots[0]); + + len =3D min_t(size_t, len, btrfs_item_size(leaf, path->slots[0])); + write_extent_buffer(leaf, ctx, ptr, len); + btrfs_mark_buffer_dirty(trans, leaf); + btrfs_release_path(path); + + if (fs_data) + return ret; + + BTRFS_I(inode)->flags |=3D BTRFS_INODE_ENCRYPT; + btrfs_sync_inode_flags_to_i_flags(BTRFS_I(inode)); + inode_inc_iversion(inode); + inode_set_ctime_current(inode); + ret =3D btrfs_update_inode(trans, BTRFS_I(inode)); + if (ret) + goto out_abort; + btrfs_free_path(path); + btrfs_end_transaction(trans); + return 0; +out_abort: + btrfs_abort_transaction(trans, ret); +out_err: + if (!fs_data) + btrfs_end_transaction(trans); + btrfs_free_path(path); + return ret; +} + +static bool btrfs_fscrypt_empty_dir(struct inode *inode) +{ + return inode->i_size =3D=3D BTRFS_EMPTY_DIR_SIZE; +} =20 const struct fscrypt_operations btrfs_fscrypt_ops =3D { .inode_info_offs =3D (int)offsetof(struct btrfs_inode, i_crypt_info) - (int)offsetof(struct btrfs_inode, vfs_inode), + .get_context =3D btrfs_fscrypt_get_context, + .set_context =3D btrfs_fscrypt_set_context, + .empty_dir =3D btrfs_fscrypt_empty_dir, }; diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h index 7f4e6888bd43..80adb7e56826 100644 --- a/fs/btrfs/fscrypt.h +++ b/fs/btrfs/fscrypt.h @@ -5,6 +5,8 @@ =20 #include =20 +#include "fs.h" + extern const struct fscrypt_operations btrfs_fscrypt_ops; =20 #endif /* BTRFS_FSCRYPT_H */ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index fdc07612af6e..c06d7cd45be5 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -60,6 +60,7 @@ #include "defrag.h" #include "dir-item.h" #include "file-item.h" +#include "fscrypt.h" #include "uuid-tree.h" #include "ioctl.h" #include "file.h" @@ -6473,6 +6474,9 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_ar= gs *args, struct inode *inode =3D args->inode; int ret; =20 + if (fscrypt_is_nokey_name(args->dentry)) + return -ENOKEY; + if (!args->orphan) { ret =3D fscrypt_setup_filename(dir, &args->dentry->d_name, 0, &args->fname); @@ -6508,6 +6512,9 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_ar= gs *args, if (dir->i_security) (*trans_num_items)++; #endif + /* 1 to add fscrypt item */ + if (args->encrypt) + (*trans_num_items)++; if (args->orphan) { /* 1 to add orphan item */ (*trans_num_items)++; @@ -6701,6 +6708,11 @@ int btrfs_create_new_inode(struct btrfs_trans_handle= *trans, BTRFS_I(inode)->i_otime_sec =3D ts.tv_sec; BTRFS_I(inode)->i_otime_nsec =3D ts.tv_nsec; =20 + if (args->encrypt) { + BTRFS_I(inode)->flags |=3D BTRFS_INODE_ENCRYPT; + btrfs_sync_inode_flags_to_i_flags(BTRFS_I(inode)); + } + /* * We're going to fill the inode item now, so at this point the inode * must be fully initialized. @@ -6774,6 +6786,13 @@ int btrfs_create_new_inode(struct btrfs_trans_handle= *trans, goto discard; } } + if (args->encrypt) { + ret =3D fscrypt_set_context(inode, trans); + if (ret) { + btrfs_abort_transaction(trans, ret); + goto discard; + } + } =20 ret =3D btrfs_add_inode_to_root(BTRFS_I(inode), false); if (WARN_ON(ret)) { diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index f1b56be6f8f4..9d0716498286 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -153,6 +153,8 @@ static unsigned int btrfs_inode_flags_to_fsflags(const = struct btrfs_inode *inode iflags |=3D FS_DIRSYNC_FL; if (flags & BTRFS_INODE_NODATACOW) iflags |=3D FS_NOCOW_FL; + if (flags & BTRFS_INODE_ENCRYPT) + iflags |=3D FS_ENCRYPT_FL; if (ro_flags & BTRFS_INODE_RO_VERITY) iflags |=3D FS_VERITY_FL; =20 @@ -181,12 +183,14 @@ void btrfs_sync_inode_flags_to_i_flags(struct btrfs_i= node *inode) new_fl |=3D S_NOATIME; if (inode->flags & BTRFS_INODE_DIRSYNC) new_fl |=3D S_DIRSYNC; + if (inode->flags & BTRFS_INODE_ENCRYPT) + new_fl |=3D S_ENCRYPTED; if (inode->ro_flags & BTRFS_INODE_RO_VERITY) new_fl |=3D S_VERITY; =20 set_mask_bits(&inode->vfs_inode.i_flags, S_SYNC | S_APPEND | S_IMMUTABLE | S_NOATIME | S_DIRSYNC | - S_VERITY, new_fl); + S_VERITY | S_ENCRYPTED, new_fl); } =20 /* @@ -199,7 +203,7 @@ static int check_fsflags(unsigned int old_flags, unsign= ed 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; =20 /* COMPR and NOCOMP on new/old are valid */ diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tre= e.h index f7843e6bb978..02b1ab1f7858 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -167,6 +167,8 @@ #define BTRFS_VERITY_DESC_ITEM_KEY 36 #define BTRFS_VERITY_MERKLE_ITEM_KEY 37 =20 +#define BTRFS_FSCRYPT_INODE_CTX_KEY 41 + #define BTRFS_ORPHAN_ITEM_KEY 48 /* reserve 2-15 close to the inode for later flexibility */ =20 @@ -431,6 +433,7 @@ static inline __u8 btrfs_dir_flags_to_ftype(__u8 flags) #define BTRFS_INODE_NOATIME (1U << 9) #define BTRFS_INODE_DIRSYNC (1U << 10) #define BTRFS_INODE_COMPRESS (1U << 11) +#define BTRFS_INODE_ENCRYPT (1U << 12) =20 #define BTRFS_INODE_ROOT_ITEM_INIT (1U << 31) =20 @@ -447,6 +450,7 @@ static inline __u8 btrfs_dir_flags_to_ftype(__u8 flags) BTRFS_INODE_NOATIME | \ BTRFS_INODE_DIRSYNC | \ BTRFS_INODE_COMPRESS | \ + BTRFS_INODE_ENCRYPT | \ BTRFS_INODE_ROOT_ITEM_INIT) =20 #define BTRFS_INODE_RO_VERITY (1U << 0) @@ -1075,6 +1079,12 @@ enum { BTRFS_NR_FILE_EXTENT_TYPES =3D 3, }; =20 +enum btrfs_encryption_type { + BTRFS_ENCRYPTION_NONE, + BTRFS_ENCRYPTION_FSCRYPT, + BTRFS_NR_ENCRYPTION_TYPES, +}; + struct btrfs_file_extent_item { /* * transaction id that created this extent --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7629F43CED6 for ; Fri, 6 Feb 2026 18:24:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402268; cv=none; b=kbUVUHBGJ1c9vsjSufTi/mBSDAoMzWJYYWBFvLM+iKeXIWqyZ4DuOwQWeANFT2aYHFRPt0ETg3+8Je2jmBkxsU7c2Z7GgEj5bvGcrZqAt4OMLskQVLrtIbKjjGKhzCVe1T0sFX6ThS/GD7l9szT3FRcCvzUBEjtOJbli4MSaN8Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402268; c=relaxed/simple; bh=1Ki8IVuhp5LIau9T8aLckRXW3rjNZE+2QwHBu0EN3yI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=EvVuKnKM7SQbWaWHa0nNd/bk7W777siwOA2Rd+NMAHum8hgUanGtDHWcH21xl66KnvckS0cMr3W6AFO6d9B6aN45E9Esb5fnzUzkkvomWW4cRJRrV8e4fg+eIRszhIEaHEr0HZ6iDSR4Ru6mOkXPrHWaAkDSWZg6z7r+fpUHLJY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=V5Jvo0Ua; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=V5Jvo0Ua; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="V5Jvo0Ua"; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="V5Jvo0Ua" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 95F073E72D; Fri, 6 Feb 2026 18:24:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402241; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=kxDptG4EymPM3tgwvRdPnDe2fYdObjgTVkmVYXHD3LQ=; b=V5Jvo0Ua//06MvdXODbRrmw+dgIMzMYeW3g4kG4gpMJN2uuwFP7qijRlQJ6Ne8iR1c/xux 6ZY3J7EISOsfPY8SqgeLhsUfz9al+QxZ/5zTrkqtGO7xrL06557jjqiKoaDfYVwtiQsiba LsLAGl58LmvvFSJPNm8RXB61jm2Z+mk= Authentication-Results: smtp-out1.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402241; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=kxDptG4EymPM3tgwvRdPnDe2fYdObjgTVkmVYXHD3LQ=; b=V5Jvo0Ua//06MvdXODbRrmw+dgIMzMYeW3g4kG4gpMJN2uuwFP7qijRlQJ6Ne8iR1c/xux 6ZY3J7EISOsfPY8SqgeLhsUfz9al+QxZ/5zTrkqtGO7xrL06557jjqiKoaDfYVwtiQsiba LsLAGl58LmvvFSJPNm8RXB61jm2Z+mk= Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 645D03EA63; Fri, 6 Feb 2026 18:24:01 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id 4LYAGMExhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:01 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org, Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v6 12/43] btrfs: add new FEATURE_INCOMPAT_ENCRYPT flag Date: Fri, 6 Feb 2026 19:22:44 +0100 Message-ID: <20260206182336.1397715-13-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spam-Score: -6.80 X-Spamd-Result: default: False [-6.80 / 50.00]; REPLY(-4.00)[]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; DBL_BLOCKED_OPENRESOLVER(0.00)[suse.com:mid,suse.com:email]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; TO_DN_SOME(0.00)[]; RCPT_COUNT_TWELVE(0.00)[14]; RCVD_VIA_SMTP_AUTH(0.00)[]; FUZZY_RATELIMITED(0.00)[rspamd.com]; FROM_HAS_DN(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; R_RATELIMIT(0.00)[to_ip_from(RLjpbzrka5mc1bf4fb9cmz1e4f)]; DKIM_SIGNED(0.00)[suse.com:s=susede1]; RCVD_TLS_ALL(0.00)[] X-Spam-Level: X-Spam-Flag: NO Content-Type: text/plain; charset="utf-8" From: Omar Sandoval As encrypted files will be incompatible with older filesystem versions, new filesystems should be created with an incompat flag for fscrypt, which will gate access to the encryption ioctls. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/ccbea52046c1dadbbef926bfc878cc23af9= 52729.1706116485.git.josef@toxicpanda.com/ * No changes since. --- fs/btrfs/fs.h | 3 ++- fs/btrfs/super.c | 5 +++++ fs/btrfs/sysfs.c | 6 ++++++ include/uapi/linux/btrfs.h | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h index d3762fbe7267..5077b7eed4b8 100644 --- a/fs/btrfs/fs.h +++ b/fs/btrfs/fs.h @@ -315,7 +315,8 @@ enum { (BTRFS_FEATURE_INCOMPAT_SUPP_STABLE | \ BTRFS_FEATURE_INCOMPAT_RAID_STRIPE_TREE | \ BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2 | \ - BTRFS_FEATURE_INCOMPAT_REMAP_TREE) + BTRFS_FEATURE_INCOMPAT_REMAP_TREE | \ + BTRFS_FEATURE_INCOMPAT_ENCRYPT) =20 #else =20 diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 7eb367b1f6f6..3914abec5b12 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -2567,6 +2567,11 @@ static int __init btrfs_print_mod_info(void) ", fsverity=3Dyes" #else ", fsverity=3Dno" +#endif +#ifdef CONFIG_FS_ENCRYPTION + ", fscrypt=3Dyes" +#else + ", fscrypt=3Dno" #endif ; =20 diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index 27bfb7b55ec4..98d6b1594d43 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -305,6 +305,9 @@ BTRFS_FEAT_ATTR_INCOMPAT(remap_tree, REMAP_TREE); #ifdef CONFIG_FS_VERITY BTRFS_FEAT_ATTR_COMPAT_RO(verity, VERITY); #endif +#ifdef CONFIG_FS_ENCRYPTION +BTRFS_FEAT_ATTR_INCOMPAT(encryption, ENCRYPT); +#endif /* CONFIG_FS_ENCRYPTION */ =20 /* * Features which depend on feature bits and may differ between each fs. @@ -338,6 +341,9 @@ static struct attribute *btrfs_supported_feature_attrs[= ] =3D { #ifdef CONFIG_FS_VERITY BTRFS_FEAT_ATTR_PTR(verity), #endif +#ifdef CONFIG_FS_ENCRYPTION + BTRFS_FEAT_ATTR_PTR(encryption), +#endif /* CONFIG_FS_ENCRYPTION */ NULL }; =20 diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index 9165154a274d..2f6a46e5f4ce 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -335,6 +335,7 @@ struct btrfs_ioctl_fs_info_args { #define BTRFS_FEATURE_INCOMPAT_ZONED (1ULL << 12) #define BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2 (1ULL << 13) #define BTRFS_FEATURE_INCOMPAT_RAID_STRIPE_TREE (1ULL << 14) +#define BTRFS_FEATURE_INCOMPAT_ENCRYPT (1ULL << 15) #define BTRFS_FEATURE_INCOMPAT_SIMPLE_QUOTA (1ULL << 16) #define BTRFS_FEATURE_INCOMPAT_REMAP_TREE (1ULL << 17) =20 --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6CAD043C072 for ; Fri, 6 Feb 2026 18:24:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402265; cv=none; b=jkNd7COEIvzkLPPv95zRHJ4Cqa31XHVkDqNE74QlK1MwZ1yPdvJpLpqIqkx8lPTFR1truhh5LXQxqIxNXTHlnUa1XSjZmkktJhAEOmkzc3VVoleXqRp50DIN+TcDFcz+4WNQqUj+nm9YrbWmBIExZOsTIGe0t6j1rQTf//eZjfQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402265; c=relaxed/simple; bh=SWc034qoUMF/g/Q9E6bvZ1Z4lORMnZtbZTgz0LEF9mk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=epsmESmU6zsghGMcYWLaHYBhpNGP+mrPB0womHFjULf+4CxxvG02cq+gNJUpxjrERbGLr16PLITzoZjSUqSTNb6oFR6opwZuE8aeuvD46ME/hGnrUeH2lQygVtQLFqft39Z/ujftbdxz8KhVhHBfanYFxIBzFzjSzVFjISAszgs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=Tqt5bnbb; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=Tqt5bnbb; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="Tqt5bnbb"; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="Tqt5bnbb" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id 411845BD0F; Fri, 6 Feb 2026 18:24:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402243; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=4daepTbAye7vSMUhmtsv60v/s875ehSxzDvgeoWS02k=; b=Tqt5bnbbDqD2F3rsxqZ8K5CG+A8ORiRrbQZXyff81yJ3ndwzIs8oR29qo6RHwp8UAENxoK D5N3AUXm3FTe/WgacIh1bLKqLjklPkEKjTLHP3NIxYaWd1yPDNeVVs7Lo7zV7YmPmIAZLt Ue4aljOjb9tG4D5DWF/OLtCR+QLUJnI= Authentication-Results: smtp-out2.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402243; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=4daepTbAye7vSMUhmtsv60v/s875ehSxzDvgeoWS02k=; b=Tqt5bnbbDqD2F3rsxqZ8K5CG+A8ORiRrbQZXyff81yJ3ndwzIs8oR29qo6RHwp8UAENxoK D5N3AUXm3FTe/WgacIh1bLKqLjklPkEKjTLHP3NIxYaWd1yPDNeVVs7Lo7zV7YmPmIAZLt Ue4aljOjb9tG4D5DWF/OLtCR+QLUJnI= Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 0DE283EA63; Fri, 6 Feb 2026 18:24:03 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id EIHfAsMxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:03 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org, Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v6 13/43] btrfs: adapt readdir for encrypted and nokey names Date: Fri, 6 Feb 2026 19:22:45 +0100 Message-ID: <20260206182336.1397715-14-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spam-Score: -6.80 X-Spamd-Result: default: False [-6.80 / 50.00]; REPLY(-4.00)[]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; DBL_BLOCKED_OPENRESOLVER(0.00)[suse.com:mid,suse.com:email]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; TO_DN_SOME(0.00)[]; RCPT_COUNT_TWELVE(0.00)[14]; RCVD_VIA_SMTP_AUTH(0.00)[]; FUZZY_RATELIMITED(0.00)[rspamd.com]; FROM_HAS_DN(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; R_RATELIMIT(0.00)[to_ip_from(RLjpbzrka5mc1bf4fb9cmz1e4f)]; DKIM_SIGNED(0.00)[suse.com:s=susede1]; RCVD_TLS_ALL(0.00)[] X-Spam-Level: X-Spam-Flag: NO Content-Type: text/plain; charset="utf-8" From: Omar Sandoval Deleting an encrypted file must always be permitted, even if the user does not have the appropriate key. Therefore, for listing an encrypted directory, so-called 'nokey' names are provided, and these nokey names must be sufficient to look up and delete the appropriate encrypted files. See 'struct fscrypt_nokey_name' for more information on the format of these names. The first part of supporting nokey names is allowing lookups by nokey name. Only a few entry points need to support these: deleting a directory, file, or subvolume -- each of these call fscrypt_setup_filename() with a '1' argument, indicating that the key is not required and therefore a nokey name may be provided. If a nokey name is provided, the fscrypt_name returned by fscrypt_setup_filename() will not have its disk_name field populated, but will have various other fields set. This change alters the relevant codepaths to pass a complete fscrypt_name anywhere that it might contain a nokey name. When it does contain a nokey name, the first time the name is successfully matched to a stored name populates the disk name field of the fscrypt_name, allowing the caller to use the normal disk name codepaths afterward. Otherwise, the matching functionality is in close analogue to the function fscrypt_match_name(). Functions where most callers are providing a fscrypt_str are duplicated and adapted for a fscrypt_name, and functions where most callers are providing a fscrypt_name are changed to so require at all callsites. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/52b31cd6e5c86f8fe3d08ce65bca5f40c3c= e5c5a.1706116485.git.josef@toxicpanda.com/ * Adapt to changed prototypes. * Return error directly instead of goto out and remove the label in btrfs_inode_by_name(). --- fs/btrfs/btrfs_inode.h | 2 +- fs/btrfs/delayed-inode.c | 25 ++++++- fs/btrfs/delayed-inode.h | 5 +- fs/btrfs/dir-item.c | 73 +++++++++++++++--- fs/btrfs/dir-item.h | 10 ++- fs/btrfs/extent_io.c | 41 +++++++++++ fs/btrfs/extent_io.h | 3 + fs/btrfs/fscrypt.c | 33 +++++++++ fs/btrfs/fscrypt.h | 18 +++++ fs/btrfs/inode.c | 155 ++++++++++++++++++++++++--------------- fs/btrfs/root-tree.c | 9 ++- fs/btrfs/root-tree.h | 2 +- fs/btrfs/tree-log.c | 3 +- 13 files changed, 301 insertions(+), 78 deletions(-) diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index bf638a6a0973..6a170eb159bd 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -563,7 +563,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, st= ruct dentry *dentry); int btrfs_set_inode_index(struct btrfs_inode *dir, u64 *index); int btrfs_unlink_inode(struct btrfs_trans_handle *trans, struct btrfs_inode *dir, struct btrfs_inode *inode, - const struct fscrypt_str *name); + struct fscrypt_name *name); int btrfs_add_link(struct btrfs_trans_handle *trans, struct btrfs_inode *parent_inode, struct btrfs_inode *inode, const struct fscrypt_str *name, bool add_backref, u64 index); diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index 1739a0b29c49..c19213fab3dd 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -1785,7 +1785,9 @@ bool btrfs_should_delete_dir_index(const struct list_= head *del_list, u64 index) /* * Read dir info stored in the delayed tree. */ -bool btrfs_readdir_delayed_dir_index(struct dir_context *ctx, +bool btrfs_readdir_delayed_dir_index(const struct inode *inode, + struct fscrypt_str *fstr, + struct dir_context *ctx, const struct list_head *ins_list) { struct btrfs_dir_item *di; @@ -1794,6 +1796,7 @@ bool btrfs_readdir_delayed_dir_index(struct dir_conte= xt *ctx, char *name; int name_len; unsigned char d_type; + size_t fstr_len =3D fstr->len; =20 /* * Changing the data of the delayed item is impossible. So @@ -1820,7 +1823,25 @@ bool btrfs_readdir_delayed_dir_index(struct dir_cont= ext *ctx, d_type =3D fs_ftype_to_dtype(btrfs_dir_flags_to_ftype(di->type)); btrfs_disk_key_to_cpu(&location, &di->location); =20 - over =3D !dir_emit(ctx, name, name_len, location.objectid, d_type); + if (di->type & BTRFS_FT_ENCRYPTED) { + int ret; + struct fscrypt_str iname =3D FSTR_INIT(name, name_len); + + fstr->len =3D 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 =3D fscrypt_fname_disk_to_usr(inode, 0, 0, &iname, fstr); + if (ret) + return ret; + over =3D !dir_emit(ctx, fstr->name, fstr->len, location.objectid, d_typ= e); + } else { + over =3D !dir_emit(ctx, name, name_len, location.objectid, d_type); + } =20 if (refcount_dec_and_test(&curr->refs)) kfree(curr); diff --git a/fs/btrfs/delayed-inode.h b/fs/btrfs/delayed-inode.h index fc752863f89b..9bff11478632 100644 --- a/fs/btrfs/delayed-inode.h +++ b/fs/btrfs/delayed-inode.h @@ -19,6 +19,7 @@ #include #include "ctree.h" =20 +struct fscrypt_str; struct btrfs_disk_key; struct btrfs_fs_info; struct btrfs_inode; @@ -159,7 +160,9 @@ void btrfs_readdir_put_delayed_items(struct btrfs_inode= *inode, struct list_head *ins_list, struct list_head *del_list); bool btrfs_should_delete_dir_index(const struct list_head *del_list, u64 i= ndex); -bool btrfs_readdir_delayed_dir_index(struct dir_context *ctx, +bool btrfs_readdir_delayed_dir_index(const struct inode *inode, + struct fscrypt_str *fstr, + struct dir_context *ctx, const struct list_head *ins_list); =20 /* Used during directory logging. */ diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c index 085a83ae9e62..6e10dd4a4e9e 100644 --- a/fs/btrfs/dir-item.c +++ b/fs/btrfs/dir-item.c @@ -6,6 +6,7 @@ #include "messages.h" #include "ctree.h" #include "disk-io.h" +#include "fscrypt.h" #include "transaction.h" #include "accessors.h" #include "dir-item.h" @@ -227,6 +228,47 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct bt= rfs_trans_handle *trans, return di; } =20 +/* + * Lookup for a directory item by fscrypt_name. + * + * @trans: The transaction handle to use. + * @root: The root of the target tree. + * @path: Path to use for the search. + * @dir: The inode number (objectid) of the directory. + * @name: The fscrypt_name associated to the directory entry + * @mod: Used to indicate if the tree search is meant for a read only + * lookup or for a deletion lookup, so its value should be 0 or + * -1, respectively. + * + * Returns: NULL if the dir item does not exists, an error pointer if an e= rror + * happened, or a pointer to a dir item if a dir item exists for the given= name. + */ +struct btrfs_dir_item *btrfs_lookup_dir_item_fname(struct btrfs_trans_hand= le *trans, + struct btrfs_root *root, + struct btrfs_path *path, u64 dir, + struct fscrypt_name *name, int mod) +{ + struct btrfs_key key; + struct btrfs_dir_item *di =3D NULL; + int ret =3D 0; + + key.objectid =3D dir; + key.type =3D BTRFS_DIR_ITEM_KEY; + key.offset =3D btrfs_name_hash(name->disk_name.name, name->disk_name.len); + /* XXX get the right hash for no-key names */ + + ret =3D btrfs_search_slot(trans, root, &key, path, mod, -mod); + if (ret =3D=3D 0) + di =3D btrfs_match_dir_item_fname(path, name); + + if (ret =3D=3D -ENOENT || (di && IS_ERR(di) && PTR_ERR(di) =3D=3D -ENOENT= )) + return NULL; + if (ret < 0) + di =3D ERR_PTR(ret); + + return di; +} + int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir_ino, const struct fscrypt_str *name) { @@ -278,9 +320,9 @@ int btrfs_check_dir_item_collision(struct btrfs_root *r= oot, u64 dir_ino, } =20 /* - * Lookup for a directory index item by name and index number. + * Lookup for a directory index item by fscrypt_name and index number. * - * @trans: The transaction handle to use. Can be NULL if @mod is 0. + * @trans: The transaction handle to use. * @root: The root of the target tree. * @path: Path to use for the search. * @dir: The inode number (objectid) of the directory. @@ -318,7 +360,7 @@ btrfs_lookup_dir_index_item(struct btrfs_trans_handle *= trans, =20 struct btrfs_dir_item * btrfs_search_dir_index_item(struct btrfs_root *root, struct btrfs_path *pa= th, - u64 dirid, const struct fscrypt_str *name) + u64 dirid, struct fscrypt_name *name) { struct btrfs_dir_item *di; struct btrfs_key key; @@ -331,8 +373,7 @@ btrfs_search_dir_index_item(struct btrfs_root *root, st= ruct btrfs_path *path, btrfs_for_each_slot(root, &key, &key, path, ret) { if (key.objectid !=3D dirid || key.type !=3D BTRFS_DIR_INDEX_KEY) break; - - di =3D btrfs_match_dir_item_name(path, name->name, name->len); + di =3D btrfs_match_dir_item_fname(path, name); if (di) return di; } @@ -368,8 +409,8 @@ struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_= trans_handle *trans, * this walks through all the entries in a dir item and finds one * for a specific name. */ -struct btrfs_dir_item *btrfs_match_dir_item_name(const struct btrfs_path *= path, - const char *name, int name_len) +struct btrfs_dir_item *btrfs_match_dir_item_fname(const struct btrfs_path = *path, + struct fscrypt_name *name) { struct btrfs_dir_item *dir_item; unsigned long name_ptr; @@ -388,8 +429,8 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(const = struct btrfs_path *path, btrfs_dir_data_len(leaf, dir_item); name_ptr =3D (unsigned long)(dir_item + 1); =20 - if (btrfs_dir_name_len(leaf, dir_item) =3D=3D name_len && - memcmp_extent_buffer(leaf, name, name_ptr, name_len) =3D=3D 0) + if (btrfs_fscrypt_match_name(name, leaf, name_ptr, + btrfs_dir_name_len(leaf, dir_item))) return dir_item; =20 cur +=3D this_len; @@ -399,6 +440,20 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(const= struct btrfs_path *path, return NULL; } =20 +/* + * helper function to look at the directory item pointed to by 'path' + * this walks through all the entries in a dir item and finds one + * for a specific name. + */ +struct btrfs_dir_item *btrfs_match_dir_item_name(const struct btrfs_path *= path, + const char *name, int name_len) +{ + struct fscrypt_name fname =3D { + .disk_name =3D FSTR_INIT((char *) name, name_len) + }; + return btrfs_match_dir_item_fname(path, &fname); +} + /* * given a pointer into a directory item, delete it. This * handles items that have more than one entry in them. diff --git a/fs/btrfs/dir-item.h b/fs/btrfs/dir-item.h index e52174a8baf9..f89d12ee28a7 100644 --- a/fs/btrfs/dir-item.h +++ b/fs/btrfs/dir-item.h @@ -7,6 +7,7 @@ #include =20 struct fscrypt_str; +struct fscrypt_name; struct btrfs_fs_info; struct btrfs_key; struct btrfs_path; @@ -23,6 +24,11 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrf= s_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, u64 dir, const struct fscrypt_str *name, int mod); +struct btrfs_dir_item *btrfs_lookup_dir_item_fname( + struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, u64 dir, + struct fscrypt_name *name, int mod); struct btrfs_dir_item *btrfs_lookup_dir_index_item( struct btrfs_trans_handle *trans, struct btrfs_root *root, @@ -30,7 +36,7 @@ struct btrfs_dir_item *btrfs_lookup_dir_index_item( u64 index, const struct fscrypt_str *name, int mod); struct btrfs_dir_item *btrfs_search_dir_index_item(struct btrfs_root *root, struct btrfs_path *path, u64 dirid, - const struct fscrypt_str *name); + struct fscrypt_name *name); int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, @@ -48,6 +54,8 @@ struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_tr= ans_handle *trans, struct btrfs_dir_item *btrfs_match_dir_item_name(const struct btrfs_path *= path, const char *name, int name_len); +struct btrfs_dir_item *btrfs_match_dir_item_fname(const struct btrfs_path = *path, + struct fscrypt_name *name); =20 static inline u64 btrfs_name_hash(const char *name, int len) { diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 3df399dc8856..a440cc550ece 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -4100,6 +4100,47 @@ static void assert_eb_folio_uptodate(const struct ex= tent_buffer *eb, int i) } } =20 +/* Take a sha256 of a portion of an extent buffer. */ +void extent_buffer_sha256(const struct extent_buffer *eb, + unsigned long start, + unsigned long len, u8 *out) +{ + size_t cur; + size_t offset; + char *kaddr; + unsigned long i =3D get_eb_folio_index(eb, start); + struct sha256_ctx sctx; + + if (check_eb_range(eb, start, len)) + return; + + if (eb->addr) { + sha256(eb->addr + start, len, out); + return; + } + + offset =3D get_eb_offset_in_folio(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) { + assert_eb_folio_uptodate(eb, i); + + cur =3D min(len, PAGE_SIZE - offset); + kaddr =3D folio_address(eb->folios[i]); + sha256_update(&sctx, (u8 *)(kaddr + offset), cur); + + len -=3D cur; + offset =3D 0; + i++; + } + sha256_final(&sctx, out); +} + static void __write_extent_buffer(const struct extent_buffer *eb, const void *srcv, unsigned long start, unsigned long len, bool use_memmove) diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index 73571d5d3d5a..21ec92f0a868 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -305,6 +305,9 @@ static inline int extent_buffer_uptodate(const struct e= xtent_buffer *eb) =20 int memcmp_extent_buffer(const struct extent_buffer *eb, const void *ptrv, unsigned long start, unsigned long len); +void extent_buffer_sha256(const struct extent_buffer *eb, + unsigned long start, + unsigned long len, u8 *out); void read_extent_buffer(const struct extent_buffer *eb, void *dst, unsigned long start, unsigned long len); diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index e9b024d671a2..99b87776ce51 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -1,17 +1,50 @@ // SPDX-License-Identifier: GPL-2.0 =20 #include +#include #include "ctree.h" #include "accessors.h" #include "btrfs_inode.h" #include "disk-io.h" +#include "ioctl.h" #include "fs.h" #include "fscrypt.h" #include "ioctl.h" #include "messages.h" +#include "root-tree.h" #include "transaction.h" #include "xattr.h" =20 +/* + * This function is extremely similar to fscrypt_match_name() but uses an + * extent_buffer. + */ +bool btrfs_fscrypt_match_name(struct fscrypt_name *fname, + struct extent_buffer *leaf, unsigned long de_name, + u32 de_name_len) +{ + const struct fscrypt_nokey_name *nokey_name =3D + (const struct fscrypt_nokey_name *)fname->crypto_buf.name; + u8 digest[SHA256_DIGEST_SIZE]; + + if (likely(fname->disk_name.name)) { + if (de_name_len !=3D fname->disk_name.len) + return false; + return !memcmp_extent_buffer(leaf, fname->disk_name.name, de_name, de_na= me_len); + } + + if (de_name_len <=3D sizeof(nokey_name->bytes)) + return false; + + if (memcmp_extent_buffer(leaf, nokey_name->bytes, de_name, sizeof(nokey_n= ame->bytes))) + return false; + + extent_buffer_sha256(leaf, de_name + sizeof(nokey_name->bytes), + de_name_len - sizeof(nokey_name->bytes), digest); + + return !memcmp(digest, nokey_name->sha256, sizeof(digest)); +} + static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_= t len) { struct btrfs_key key =3D { diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h index 80adb7e56826..4e9c87290158 100644 --- a/fs/btrfs/fscrypt.h +++ b/fs/btrfs/fscrypt.h @@ -4,9 +4,27 @@ #define BTRFS_FSCRYPT_H =20 #include +#include "extent_map.h" =20 #include "fs.h" =20 +#ifdef CONFIG_FS_ENCRYPTION +bool btrfs_fscrypt_match_name(struct fscrypt_name *fname, + struct extent_buffer *leaf, + unsigned long de_name, u32 de_name_len); + +#else +static inline bool btrfs_fscrypt_match_name(struct fscrypt_name *fname, + struct extent_buffer *leaf, + unsigned long de_name, + u32 de_name_len) +{ + if (de_name_len !=3D fname_len(fname)) + return false; + return !memcmp_extent_buffer(leaf, fname->disk_name.name, de_name, de_nam= e_len); +} +#endif /* CONFIG_FS_ENCRYPTION */ + extern const struct fscrypt_operations btrfs_fscrypt_ops; =20 #endif /* BTRFS_FSCRYPT_H */ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index c06d7cd45be5..79f0b67249de 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4362,7 +4362,7 @@ static void update_time_after_link_or_unlink(struct b= trfs_inode *dir) static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans, struct btrfs_inode *dir, struct btrfs_inode *inode, - const struct fscrypt_str *name, + struct fscrypt_name *name, struct btrfs_rename_ctx *rename_ctx) { struct btrfs_root *root =3D dir->root; @@ -4378,7 +4378,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_ha= ndle *trans, if (!path) return -ENOMEM; =20 - di =3D btrfs_lookup_dir_item(trans, root, path, dir_ino, name, -1); + di =3D btrfs_lookup_dir_item_fname(trans, root, path, dir_ino, name, -1); if (IS_ERR_OR_NULL(di)) { btrfs_free_path(path); return di ? PTR_ERR(di) : -ENOENT; @@ -4410,11 +4410,13 @@ static int __btrfs_unlink_inode(struct btrfs_trans_= handle *trans, } } =20 - ret =3D btrfs_del_inode_ref(trans, root, name, ino, dir_ino, &index); + ret =3D btrfs_del_inode_ref(trans, root, &name->disk_name, ino, dir_ino, = &index); if (unlikely(ret)) { + /* This should print a base-64 encoded name if relevant? */ btrfs_crit(fs_info, "failed to delete reference to %.*s, root %llu inode %llu parent %llu", - name->len, name->name, btrfs_root_id(root), ino, dir_ino); + name->disk_name.len, name->disk_name.name, + btrfs_root_id(root), ino, dir_ino); btrfs_abort_transaction(trans, ret); return ret; } @@ -4435,8 +4437,8 @@ static int __btrfs_unlink_inode(struct btrfs_trans_ha= ndle *trans, * operations on the log tree, increasing latency for applications. */ if (!rename_ctx) { - btrfs_del_inode_ref_in_log(trans, name, inode, dir); - btrfs_del_dir_entries_in_log(trans, name, dir, index); + btrfs_del_inode_ref_in_log(trans, &name->disk_name, inode, dir); + btrfs_del_dir_entries_in_log(trans, &name->disk_name, dir, index); } =20 /* @@ -4450,7 +4452,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_ha= ndle *trans, */ btrfs_run_delayed_iput(fs_info, inode); =20 - btrfs_i_size_write(dir, dir->vfs_inode.i_size - name->len * 2); + btrfs_i_size_write(dir, dir->vfs_inode.i_size - name->disk_name.len * 2); inode_inc_iversion(&inode->vfs_inode); inode_set_ctime_current(&inode->vfs_inode); inode_inc_iversion(&dir->vfs_inode); @@ -4461,7 +4463,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_ha= ndle *trans, =20 int btrfs_unlink_inode(struct btrfs_trans_handle *trans, struct btrfs_inode *dir, struct btrfs_inode *inode, - const struct fscrypt_str *name) + struct fscrypt_name *name) { int ret; =20 @@ -4508,11 +4510,9 @@ static int btrfs_unlink(struct inode *dir, struct de= ntry *dentry) goto fscrypt_free; } =20 - btrfs_record_unlink_dir(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)), - false); + btrfs_record_unlink_dir(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)), fa= lse); =20 - ret =3D btrfs_unlink_inode(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)), - &fname.disk_name); + ret =3D btrfs_unlink_inode(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)),= &fname); if (ret) goto end_trans; =20 @@ -4549,8 +4549,6 @@ static int btrfs_unlink_subvol(struct btrfs_trans_han= dle *trans, if (ret) return ret; =20 - /* This needs to handle no-key deletions later on */ - if (btrfs_ino(inode) =3D=3D BTRFS_FIRST_FREE_OBJECTID) { objectid =3D btrfs_root_id(inode->root); } else if (btrfs_ino(inode) =3D=3D BTRFS_EMPTY_SUBVOL_DIR_OBJECTID) { @@ -4567,8 +4565,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_han= dle *trans, goto out; } =20 - di =3D btrfs_lookup_dir_item(trans, root, path, dir_ino, - &fname.disk_name, -1); + di =3D btrfs_lookup_dir_item_fname(trans, root, path, dir_ino, &fname, -1= ); if (IS_ERR_OR_NULL(di)) { ret =3D di ? PTR_ERR(di) : -ENOENT; goto out; @@ -4594,7 +4591,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_han= dle *trans, * call btrfs_del_root_ref, and it _shouldn't_ fail. */ if (btrfs_ino(inode) =3D=3D BTRFS_EMPTY_SUBVOL_DIR_OBJECTID) { - di =3D btrfs_search_dir_index_item(root, path, dir_ino, &fname.disk_name= ); + di =3D btrfs_search_dir_index_item(root, path, dir_ino, &fname); if (IS_ERR(di)) { ret =3D PTR_ERR(di); btrfs_abort_transaction(trans, ret); @@ -4608,7 +4605,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_han= dle *trans, } else { ret =3D btrfs_del_root_ref(trans, objectid, btrfs_root_id(root), dir_ino, - &index, &fname.disk_name); + &index, &fname); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); goto out; @@ -4919,7 +4916,7 @@ static int btrfs_rmdir(struct inode *vfs_dir, struct = dentry *dentry) goto out; =20 /* now the directory is empty */ - ret =3D btrfs_unlink_inode(trans, dir, inode, &fname.disk_name); + ret =3D btrfs_unlink_inode(trans, dir, inode, &fname); if (!ret) btrfs_i_size_write(inode, 0); out: @@ -5720,36 +5717,23 @@ void btrfs_evict_inode(struct inode *inode) * If no dir entries were found, returns -ENOENT. * If found a corrupted location in dir entry, returns -EUCLEAN. */ -static int btrfs_inode_by_name(struct btrfs_inode *dir, struct dentry *den= try, +static int btrfs_inode_by_name(struct btrfs_inode *dir, struct fscrypt_nam= e *fname, struct btrfs_key *location, u8 *type) { struct btrfs_dir_item *di; BTRFS_PATH_AUTO_FREE(path); struct btrfs_root *root =3D dir->root; int ret =3D 0; - struct fscrypt_name fname; =20 path =3D btrfs_alloc_path(); if (!path) return -ENOMEM; =20 - ret =3D fscrypt_setup_filename(&dir->vfs_inode, &dentry->d_name, 1, &fnam= e); - if (ret < 0) - return ret; - /* - * fscrypt_setup_filename() should never return a positive value, but - * gcc on sparc/parisc thinks it can, so assert that doesn't happen. - */ - ASSERT(ret =3D=3D 0); - /* This needs to handle no-key deletions later on */ =20 - di =3D btrfs_lookup_dir_item(NULL, root, path, btrfs_ino(dir), - &fname.disk_name, 0); - if (IS_ERR_OR_NULL(di)) { - ret =3D di ? PTR_ERR(di) : -ENOENT; - goto out; - } + di =3D btrfs_lookup_dir_item_fname(NULL, root, path, btrfs_ino(dir), fnam= e, 0); + if (IS_ERR_OR_NULL(di)) + return di ? PTR_ERR(di) : -ENOENT; =20 btrfs_dir_item_key_to_cpu(path->nodes[0], di, location); if (unlikely(location->type !=3D BTRFS_INODE_ITEM_KEY && @@ -5757,13 +5741,11 @@ static int btrfs_inode_by_name(struct btrfs_inode *= dir, struct dentry *dentry, ret =3D -EUCLEAN; btrfs_warn(root->fs_info, "%s gets something invalid in DIR_ITEM (name %s, directory ino %llu, locat= ion " BTRFS_KEY_FMT ")", - __func__, fname.disk_name.name, btrfs_ino(dir), + __func__, fname->usr_fname->name, btrfs_ino(dir), BTRFS_KEY_FMT_VALUE(location)); } if (!ret) *type =3D btrfs_dir_ftype(path->nodes[0], di); -out: - fscrypt_free_filename(&fname); return ret; } =20 @@ -6030,20 +6012,27 @@ struct inode *btrfs_lookup_dentry(struct inode *dir= , struct dentry *dentry) struct btrfs_root *root =3D BTRFS_I(dir)->root; struct btrfs_root *sub_root =3D root; struct btrfs_key location =3D { 0 }; + struct fscrypt_name fname; u8 di_type =3D 0; int ret =3D 0; =20 if (dentry->d_name.len > BTRFS_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); =20 - ret =3D btrfs_inode_by_name(BTRFS_I(dir), dentry, &location, &di_type); - if (ret < 0) + ret =3D fscrypt_prepare_lookup(dir, dentry, &fname); + if (ret) return ERR_PTR(ret); =20 + ret =3D btrfs_inode_by_name(BTRFS_I(dir), &fname, &location, &di_type); + if (ret < 0) { + inode =3D ERR_PTR(ret); + goto cleanup; + } + if (location.type =3D=3D BTRFS_INODE_ITEM_KEY) { inode =3D btrfs_iget(location.objectid, root); if (IS_ERR(inode)) - return ERR_CAST(inode); + goto cleanup; =20 /* Do extra check against inode mode with di_type */ if (unlikely(btrfs_inode_type(inode) !=3D di_type)) { @@ -6052,9 +6041,10 @@ struct inode *btrfs_lookup_dentry(struct inode *dir,= struct dentry *dentry) inode->vfs_inode.i_mode, btrfs_inode_type(inode), di_type); iput(&inode->vfs_inode); - return ERR_PTR(-EUCLEAN); + inode =3D ERR_PTR(-EUCLEAN); + goto cleanup; } - return &inode->vfs_inode; + goto cleanup; } =20 ret =3D fixup_tree_root_location(fs_info, BTRFS_I(dir), dentry, @@ -6069,7 +6059,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, = struct dentry *dentry) btrfs_put_root(sub_root); =20 if (IS_ERR(inode)) - return ERR_CAST(inode); + goto cleanup; =20 down_read(&fs_info->cleanup_work_sem); if (!sb_rdonly(inode->vfs_inode.i_sb)) @@ -6081,6 +6071,9 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, = struct dentry *dentry) } } =20 +cleanup: + fscrypt_free_filename(&fname); + if (IS_ERR(inode)) return ERR_CAST(inode); =20 @@ -6270,18 +6263,32 @@ static int btrfs_real_readdir(struct file *file, st= ruct dir_context *ctx) LIST_HEAD(del_list); int ret; char *name_ptr; - int name_len; + u32 name_len; int entries =3D 0; int total_len =3D 0; bool put =3D false; struct btrfs_key location; + struct fscrypt_str fstr =3D FSTR_INIT(NULL, 0); + u32 fstr_len =3D 0; =20 if (!dir_emit_dots(file, ctx)) return 0; =20 + if (BTRFS_I(inode)->flags & BTRFS_INODE_ENCRYPT) { + ret =3D fscrypt_prepare_readdir(inode); + if (ret) + return ret; + ret =3D fscrypt_fname_alloc_buffer(BTRFS_NAME_LEN, &fstr); + if (ret) + return ret; + fstr_len =3D fstr.len; + } + path =3D btrfs_alloc_path(); - if (!path) - return -ENOMEM; + if (!path) { + ret =3D -ENOMEM; + goto err_fstr; + } =20 addr =3D private->filldir_buf; path->reada =3D READA_FORWARD; @@ -6298,6 +6305,7 @@ static int btrfs_real_readdir(struct file *file, stru= ct dir_context *ctx) struct dir_entry *entry; struct extent_buffer *leaf =3D path->nodes[0]; u8 ftype; + u32 nokey_len; =20 if (found_key.objectid !=3D key.objectid) break; @@ -6311,8 +6319,12 @@ static int btrfs_real_readdir(struct file *file, str= uct dir_context *ctx) continue; di =3D btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item); name_len =3D btrfs_dir_name_len(leaf, di); - if ((total_len + sizeof(struct dir_entry) + name_len) >=3D - PAGE_SIZE) { + nokey_len =3D 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) >=3D PAGE_SIZE) { btrfs_release_path(path); ret =3D btrfs_filldir(private->filldir_buf, entries, ctx); if (ret) @@ -6326,8 +6338,31 @@ static int btrfs_real_readdir(struct file *file, str= uct dir_context *ctx) ftype =3D btrfs_dir_flags_to_ftype(btrfs_dir_flags(leaf, di)); entry =3D addr; name_ptr =3D (char *)(entry + 1); - read_extent_buffer(leaf, name_ptr, - (unsigned long)(di + 1), name_len); + if (btrfs_dir_flags(leaf, di) & BTRFS_FT_ENCRYPTED) { + struct fscrypt_str oname =3D FSTR_INIT(name_ptr, nokey_len); + u32 hash =3D 0, minor_hash =3D 0; + + read_extent_buffer(leaf, fstr.name, (unsigned long)(di + 1), name_len); + fstr.len =3D name_len; + /* + * We're iterating through DIR_INDEX items, so we don't + * have the DIR_ITEM hash handy. Only compute it if + * we'll need it -- the nokey name stores it, so that + * we can look up the appropriate item by nokey name + * later on. + */ + if (!fscrypt_has_encryption_key(inode)) { + u64 name_hash =3D btrfs_name_hash(fstr.name, fstr.len); + hash =3D name_hash; + minor_hash =3D name_hash >> 32; + } + ret =3D fscrypt_fname_disk_to_usr(inode, hash, minor_hash, &fstr, &onam= e); + if (ret) + goto err; + name_len =3D oname.len; + } else { + read_extent_buffer(leaf, name_ptr, (unsigned long)(di + 1), name_len); + } put_unaligned(name_len, &entry->name_len); put_unaligned(fs_ftype_to_dtype(ftype), &entry->type); btrfs_dir_item_key_to_cpu(leaf, di, &location); @@ -6347,7 +6382,8 @@ static int btrfs_real_readdir(struct file *file, stru= ct dir_context *ctx) if (ret) goto nopos; =20 - if (btrfs_readdir_delayed_dir_index(ctx, &ins_list)) + fstr.len =3D fstr_len; + if (btrfs_readdir_delayed_dir_index(inode, &fstr, ctx, &ins_list)) goto nopos; =20 /* @@ -6376,6 +6412,8 @@ static int btrfs_real_readdir(struct file *file, stru= ct dir_context *ctx) err: if (put) btrfs_readdir_put_delayed_items(BTRFS_I(inode), &ins_list, &del_list); +err_fstr: + fscrypt_fname_free_buffer(&fstr); return ret; } =20 @@ -6853,6 +6891,7 @@ int btrfs_add_link(struct btrfs_trans_handle *trans, struct btrfs_root *root =3D parent_inode->root; u64 ino =3D btrfs_ino(inode); u64 parent_ino =3D btrfs_ino(parent_inode); + struct fscrypt_name fname =3D { .disk_name =3D *name }; =20 if (unlikely(ino =3D=3D BTRFS_FIRST_FREE_OBJECTID)) { memcpy(&key, &inode->root->root_key, sizeof(key)); @@ -6900,7 +6939,7 @@ int btrfs_add_link(struct btrfs_trans_handle *trans, int ret2; =20 ret2 =3D btrfs_del_root_ref(trans, key.objectid, btrfs_root_id(root), - parent_ino, &local_index, name); + parent_ino, &local_index, &fname); if (ret2) btrfs_abort_transaction(trans, ret2); } else if (add_backref) { @@ -8425,7 +8464,7 @@ static int btrfs_rename_exchange(struct inode *old_di= r, } else { /* src is an inode */ ret =3D __btrfs_unlink_inode(trans, BTRFS_I(old_dir), BTRFS_I(old_dentry->d_inode), - old_name, &old_rename_ctx); + &old_fname, &old_rename_ctx); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); goto out_fail; @@ -8447,7 +8486,7 @@ static int btrfs_rename_exchange(struct inode *old_di= r, } else { /* dest is an inode */ ret =3D __btrfs_unlink_inode(trans, BTRFS_I(new_dir), BTRFS_I(new_dentry->d_inode), - new_name, &new_rename_ctx); + &new_fname, &new_rename_ctx); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); goto out_fail; @@ -8716,7 +8755,7 @@ static int btrfs_rename(struct mnt_idmap *idmap, } else { ret =3D __btrfs_unlink_inode(trans, BTRFS_I(old_dir), BTRFS_I(d_inode(old_dentry)), - &old_fname.disk_name, &rename_ctx); + &old_fname, &rename_ctx); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); goto out_fail; @@ -8741,7 +8780,7 @@ static int btrfs_rename(struct mnt_idmap *idmap, } else { ret =3D btrfs_unlink_inode(trans, BTRFS_I(new_dir), BTRFS_I(d_inode(new_dentry)), - &new_fname.disk_name); + &new_fname); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); goto out_fail; diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c index 37a4173c0a0b..f56245b09951 100644 --- a/fs/btrfs/root-tree.c +++ b/fs/btrfs/root-tree.c @@ -10,6 +10,7 @@ #include "messages.h" #include "transaction.h" #include "disk-io.h" +#include "fscrypt.h" #include "qgroup.h" #include "space-info.h" #include "accessors.h" @@ -328,7 +329,7 @@ int btrfs_del_root(struct btrfs_trans_handle *trans, =20 int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id, u64 ref_id, u64 dirid, u64 *sequence, - const struct fscrypt_str *name) + struct fscrypt_name *name) { struct btrfs_root *tree_root =3D trans->fs_info->tree_root; BTRFS_PATH_AUTO_FREE(path); @@ -350,15 +351,15 @@ int btrfs_del_root_ref(struct btrfs_trans_handle *tra= ns, u64 root_id, if (ret < 0) { return ret; } else if (ret =3D=3D 0) { + u32 name_len; leaf =3D path->nodes[0]; ref =3D btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_ref); ptr =3D (unsigned long)(ref + 1); + name_len =3D btrfs_root_ref_name_len(leaf, ref); if ((btrfs_root_ref_dirid(leaf, ref) !=3D dirid) || - (btrfs_root_ref_name_len(leaf, ref) !=3D name->len) || - memcmp_extent_buffer(leaf, name->name, ptr, name->len)) + !btrfs_fscrypt_match_name(name, leaf, ptr, name_len)) return -ENOENT; - *sequence =3D btrfs_root_ref_sequence(leaf, ref); =20 ret =3D btrfs_del_item(trans, tree_root, path); diff --git a/fs/btrfs/root-tree.h b/fs/btrfs/root-tree.h index 8f5739e732b9..e85623dba952 100644 --- a/fs/btrfs/root-tree.h +++ b/fs/btrfs/root-tree.h @@ -23,7 +23,7 @@ int btrfs_add_root_ref(struct btrfs_trans_handle *trans, = u64 root_id, const struct fscrypt_str *name); int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id, u64 ref_id, u64 dirid, u64 *sequence, - const struct fscrypt_str *name); + struct fscrypt_name *name); int btrfs_del_root(struct btrfs_trans_handle *trans, const struct btrfs_ke= y *key); int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root = *root, const struct btrfs_key *key, diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 4034c04d4d63..d6d3ce41fc3e 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -1038,9 +1038,10 @@ static int unlink_inode_for_log_replay(struct walk_c= ontrol *wc, const struct fscrypt_str *name) { struct btrfs_trans_handle *trans =3D wc->trans; + struct fscrypt_name fname =3D { .disk_name =3D *name, }; int ret; =20 - ret =3D btrfs_unlink_inode(trans, dir, inode, name); + ret =3D btrfs_unlink_inode(trans, dir, inode, &fname); if (ret) { btrfs_abort_log_replay(wc, ret, "failed to unlink inode %llu parent dir %llu name %.*s root %llu", --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0248943D50C for ; Fri, 6 Feb 2026 18:24:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402271; cv=none; b=lvmgowzBNNRzEVoxybx3Pn91rmbWci+72bBPiu5oEVQt1Z9SNKAAfIggygFWpYDa7EFFcAPB+Y4snmosKGzJWDz4V7onwkkrzxmt+P8ljhX87pwwzAkboWWf2oP33QpUihwzXdpKfgw9Gi29199cR47+j+WesjNxAsACzrXZrK8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402271; c=relaxed/simple; bh=S88iX+pQLBnDUUEDjcXBBPJboVrjuvn/D1vOa8/e714=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=fBducY6+rCwQklfU48gUBejmg4fQwFvi9golPAu24HpN47+cCUl+X1G5h4tE6V6FBXS4s6J4404qg23CR3xpnOjNTAJI1K2v0UmqIKyYSXyLQwReWrufa9R1rRdqraNGRpbhgoPpdEDYMx3BPu4IY7AMJftpqqaeT7rIe4dbCN4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=EPlOWgnH; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=EPlOWgnH; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="EPlOWgnH"; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="EPlOWgnH" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id D55885BD24; Fri, 6 Feb 2026 18:24:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402243; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=PMDR3hJyN7VskU25nVZ2Nq4bl6y56eSfjjKv060wZho=; b=EPlOWgnHFdABA6aPE9m/SivtblsNvbmggbgLx/5/uUeFc/PuXBfeAjZ2gOoK2RryhoSIj2 utt7KOCusHGWskuMJOOLu1qx5wr/t3ZNcjRhxjglDRrrdYtsVRapSxyGNd7HFW7001LlMV WnVQwUabmYwUHAoLpSz4gBDT+AEBELY= Authentication-Results: smtp-out2.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402243; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=PMDR3hJyN7VskU25nVZ2Nq4bl6y56eSfjjKv060wZho=; b=EPlOWgnHFdABA6aPE9m/SivtblsNvbmggbgLx/5/uUeFc/PuXBfeAjZ2gOoK2RryhoSIj2 utt7KOCusHGWskuMJOOLu1qx5wr/t3ZNcjRhxjglDRrrdYtsVRapSxyGNd7HFW7001LlMV WnVQwUabmYwUHAoLpSz4gBDT+AEBELY= Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id A65FF3EA63; Fri, 6 Feb 2026 18:24:03 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id ECYfKMMxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:03 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org, Sweet Tea Dorminy Subject: [PATCH v6 14/43] btrfs: handle nokey names Date: Fri, 6 Feb 2026 19:22:46 +0100 Message-ID: <20260206182336.1397715-15-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spam-Score: -6.80 X-Spamd-Result: default: False [-6.80 / 50.00]; REPLY(-4.00)[]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; DBL_BLOCKED_OPENRESOLVER(0.00)[suse.com:mid,suse.com:email]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; TO_DN_SOME(0.00)[]; RCPT_COUNT_TWELVE(0.00)[13]; RCVD_VIA_SMTP_AUTH(0.00)[]; FUZZY_RATELIMITED(0.00)[rspamd.com]; FROM_HAS_DN(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; R_RATELIMIT(0.00)[to_ip_from(RLjpbzrka5mc1bf4fb9cmz1e4f)]; DKIM_SIGNED(0.00)[suse.com:s=susede1]; RCVD_TLS_ALL(0.00)[] X-Spam-Level: X-Spam-Flag: NO Content-Type: text/plain; charset="utf-8" From: Sweet Tea Dorminy For encrypted or unencrypted names, we calculate the offset for the dir item by hashing the name for the dir item. However, this doesn't work for a long nokey name, where we do not have the complete ciphertext. Instead, fscrypt stores the filesystem-provided hash in the nokey name, and we can extract it from the fscrypt_name structure in such a case. Additionally, for nokey names, if we find the nokey name on disk we can update the fscrypt_name with the disk name, so add that to searching for diritems. Signed-off-by: Sweet Tea Dorminy Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/a7e3aec8ec03814b4baa1d929ae8fb77f13= c17d2.1706116485.git.josef@toxicpanda.com/ * No changes since. --- fs/btrfs/dir-item.c | 35 +++++++++++++++++++++++++++++++++-- fs/btrfs/fscrypt.c | 27 +++++++++++++++++++++++++++ fs/btrfs/fscrypt.h | 11 +++++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c index 6e10dd4a4e9e..75c213bd826e 100644 --- a/fs/btrfs/dir-item.c +++ b/fs/btrfs/dir-item.c @@ -228,6 +228,27 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct bt= rfs_trans_handle *trans, return di; } =20 +/* + * If appropriate, populate the disk name for a fscrypt_name looked up wit= hout + * a key. + * + * @path: The path to the extent buffer in which the name was found. + * @di: The dir item corresponding. + * @fname: The fscrypt_name to perhaps populate. + * + * Returns: 0 if the name is already populated or the dir item doesn't exi= st + * or the name was successfully populated, else an error code. + */ +static int ensure_disk_name_from_dir_item(struct btrfs_path *path, + struct btrfs_dir_item *di, + struct fscrypt_name *name) +{ + if (name->disk_name.name || !di) + return 0; + + return btrfs_fscrypt_get_disk_name(path->nodes[0], di, &name->disk_name); +} + /* * Lookup for a directory item by fscrypt_name. * @@ -254,8 +275,11 @@ struct btrfs_dir_item *btrfs_lookup_dir_item_fname(str= uct btrfs_trans_handle *tr =20 key.objectid =3D dir; key.type =3D BTRFS_DIR_ITEM_KEY; - key.offset =3D btrfs_name_hash(name->disk_name.name, name->disk_name.len); - /* XXX get the right hash for no-key names */ + + if (!name->disk_name.name) + key.offset =3D name->hash | ((u64)name->minor_hash << 32); + else + key.offset =3D btrfs_name_hash(name->disk_name.name, name->disk_name.len= ); =20 ret =3D btrfs_search_slot(trans, root, &key, path, mod, -mod); if (ret =3D=3D 0) @@ -263,6 +287,8 @@ struct btrfs_dir_item *btrfs_lookup_dir_item_fname(stru= ct btrfs_trans_handle *tr =20 if (ret =3D=3D -ENOENT || (di && IS_ERR(di) && PTR_ERR(di) =3D=3D -ENOENT= )) return NULL; + if (ret =3D=3D 0) + ret =3D ensure_disk_name_from_dir_item(path, di, name); if (ret < 0) di =3D ERR_PTR(ret); =20 @@ -373,7 +399,12 @@ btrfs_search_dir_index_item(struct btrfs_root *root, s= truct btrfs_path *path, btrfs_for_each_slot(root, &key, &key, path, ret) { if (key.objectid !=3D dirid || key.type !=3D BTRFS_DIR_INDEX_KEY) break; + di =3D btrfs_match_dir_item_fname(path, name); + if (di) + ret =3D ensure_disk_name_from_dir_item(path, di, name); + if (ret) + break; if (di) return di; } diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index 99b87776ce51..078b8c9a0334 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -15,6 +15,33 @@ #include "transaction.h" #include "xattr.h" =20 +/* + * From a given location in a leaf, read a name into a qstr (usually a + * fscrypt_name's disk_name), allocating the required buffer. Used for + * nokey names. + */ +int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf, + struct btrfs_dir_item *dir_item, + struct fscrypt_str *name) +{ + unsigned long de_name_len =3D btrfs_dir_name_len(leaf, dir_item); + unsigned long de_name =3D (unsigned long)(dir_item + 1); + /* + * For no-key names, we use this opportunity to find the disk + * name, so future searches don't need to deal with nokey names + * and we know what the encrypted size is. + */ + name->name =3D kmalloc(de_name_len, GFP_NOFS); + + if (!name->name) + return -ENOMEM; + + read_extent_buffer(leaf, name->name, de_name, de_name_len); + + name->len =3D de_name_len; + return 0; +} + /* * This function is extremely similar to fscrypt_match_name() but uses an * extent_buffer. diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h index 4e9c87290158..c5cdc27f943c 100644 --- a/fs/btrfs/fscrypt.h +++ b/fs/btrfs/fscrypt.h @@ -9,11 +9,22 @@ #include "fs.h" =20 #ifdef CONFIG_FS_ENCRYPTION +int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf, + struct btrfs_dir_item *di, + struct fscrypt_str *qstr); + bool btrfs_fscrypt_match_name(struct fscrypt_name *fname, struct extent_buffer *leaf, unsigned long de_name, u32 de_name_len); =20 #else +static inline int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf, + struct btrfs_dir_item *di, + struct fscrypt_str *qstr) +{ + return 0; +} + static inline bool btrfs_fscrypt_match_name(struct fscrypt_name *fname, struct extent_buffer *leaf, unsigned long de_name, --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5FD9443DA28 for ; Fri, 6 Feb 2026 18:24:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402272; cv=none; b=G18GfmU3YcQJ2MgMGsCXG+VUNDr7hZA8zrOMKCDGyNMlClsGs/tISk2g9gUY37qLDv8e4HbnFwlz+/5ia4vlXybrknzt+meHfkmNcKCudkqTHmwpNhFEP9QGqvD88RAziI6rq1rFvGBl19of2gro+RvZkkGW40HcL4FeF78exks= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402272; c=relaxed/simple; bh=aBJFZhYabX99GGa2eECyKreqZIT3wiY6UB4onD+4v2Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=d8e/dzMB/EdhcNNpLrReEEWEHLoHUQEUJZ9gtZtcfw3A/kXOmHGlXAGpoRpdnSe2l+wTXMSFyug2YakuC64ZJ2eBBy5E7ar2rXUboytn7nRkA8jNJWaajucAIcUpfSFJIxjAinJcN19qXE+QsEwosPSq3PAqGqR0TZtRGwFFTVg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 4BD623E749; Fri, 6 Feb 2026 18:24:04 +0000 (UTC) Authentication-Results: smtp-out1.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 1C6993EA63; Fri, 6 Feb 2026 18:24:04 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id AO9GBsQxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:04 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org, Omar Sandoval , Sweet Tea Dorminy Subject: [PATCH v6 15/43] btrfs: implement fscrypt ioctls Date: Fri, 6 Feb 2026 19:22:47 +0100 Message-ID: <20260206182336.1397715-16-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO X-Spam-Score: -4.00 X-Rspamd-Queue-Id: 4BD623E749 X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Server: rspamd2.dmz-prg2.suse.org X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Omar Sandoval These ioctls allow encryption to actually be used. The set_encryption_policy ioctl is the thing which actually turns on encryption, and therefore sets the ENCRYPT flag in the superblock. This prevents the filesystem from being loaded on older kernels. fscrypt provides CONFIG_FS_ENCRYPTION-disabled versions of all these functions which just return -EOPNOTSUPP, so the ioctls don't need to be compiled out if CONFIG_FS_ENCRYPTION isn't enabled. We could instead gate this ioctl on the superblock having the flag set, if we wanted to require mkfs with the encrypt flag in order to have a filesystem with any encryption. Since this is a disk format change and is currently in development, gate the fscrypt ioctls for btrfs behind BTRFS_EXPERIMENTAL. Signed-off-by: Omar Sandoval Signed-off-by: Sweet Tea Dorminy Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/6182b77868d0c9f9447641f869658ef017c= 1f467.1706116485.git.josef@toxicpanda.com/ * No changes since. * But squash in https://lore.kernel.org/linux-btrfs/5134ab713b080f78b90eae= 46671596f72bbb1815.1706116485.git.josef@toxicpanda.com/ ("btrfs: gate encryption behind BTRFS_DEBUG") and change it to BTRFS_EXPERIMENTAL as suggested by Dave. --- fs/btrfs/ioctl.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 9d0716498286..a8adf99ad0a8 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -5192,6 +5192,35 @@ long btrfs_ioctl(struct file *file, unsigned int return btrfs_ioctl_get_fslabel(fs_info, argp); case FS_IOC_SETFSLABEL: return btrfs_ioctl_set_fslabel(file, argp); +#ifdef CONFIG_BTRFS_EXPERIMENTAL + case FS_IOC_SET_ENCRYPTION_POLICY: { + if (!IS_ENABLED(CONFIG_FS_ENCRYPTION)) + return -EOPNOTSUPP; + if (sb_rdonly(fs_info->sb)) + return -EROFS; + /* + * If we crash before we commit, nothing encrypted could have + * been written so it doesn't matter whether the encrypted + * state persists. + */ + btrfs_set_fs_incompat(fs_info, ENCRYPT); + return fscrypt_ioctl_set_policy(file, (const void __user *)arg); + } + case FS_IOC_GET_ENCRYPTION_POLICY: + return fscrypt_ioctl_get_policy(file, (void __user *)arg); + case FS_IOC_GET_ENCRYPTION_POLICY_EX: + return fscrypt_ioctl_get_policy_ex(file, (void __user *)arg); + case FS_IOC_ADD_ENCRYPTION_KEY: + return fscrypt_ioctl_add_key(file, (void __user *)arg); + case FS_IOC_REMOVE_ENCRYPTION_KEY: + return fscrypt_ioctl_remove_key(file, (void __user *)arg); + case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS: + return fscrypt_ioctl_remove_key_all_users(file, (void __user *)arg); + case FS_IOC_GET_ENCRYPTION_KEY_STATUS: + return fscrypt_ioctl_get_key_status(file, (void __user *)arg); + case FS_IOC_GET_ENCRYPTION_NONCE: + return fscrypt_ioctl_get_nonce(file, (void __user *)arg); +#endif /* CONFIG_BTRFS_EXPERIMENTAL */ case FITRIM: return btrfs_ioctl_fitrim(fs_info, argp); case BTRFS_IOC_SNAP_CREATE: --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 240D343E49C for ; Fri, 6 Feb 2026 18:24:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402276; cv=none; b=mYt6c/FWJq+Sw7MS/5iaaj1JUqOwj8SqsbE5lG0wdAPmXW4YgqHjtuJmJKa7EbB+x1wCd/eKaP4dHMk1ieobLDXz/UH8h/Fsau79Cwf4w1tWjOTp4Zd4zPY/MFLMHpijGVXMGdqAyK842lGOKbLsI+Ukxp855hgt1C5pP9F5kpM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402276; c=relaxed/simple; bh=8loaANaPFESANLrYnj8Wqx4yYtxkh/Uh+aihMl15zrc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=VEHD5Vqjb6K2qxp3MfZINkRniklnhICiL+M9pjsMkuKz/cyE0OnNuvEFYw7YzgLvdodkkchbJf7IuM+MxHBg7GoX8JNBEQMfs1ReX8drM1pUjUAjQHlSS1l7OU7IlTAvtRHAiIxjTfvVp/mWnElXwHWqDUfZ8Jdl3v1KUxJztts= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=ihQb7wKY; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=ihQb7wKY; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="ihQb7wKY"; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="ihQb7wKY" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id B23F35BCE4; Fri, 6 Feb 2026 18:24:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402244; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=NmyK3AesDBGZv034yJBs56keke+EnBt3klqGpQx8SQY=; b=ihQb7wKYYxZvJjVJZ7cCm+iivepKmOmCPf2LdHgt6qW3xLQMO0+wDXU5jq2NnrHQW3uyI6 9HelUOcdE45AbSngkEZxv8vaeZ6nBXnmB9tgbXH81nle7XPU0kb3+/5PB7q6JdPm0ut51R 71v8D5Nj+tjQsjHSg7UATnruCVR5jZQ= Authentication-Results: smtp-out2.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402244; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=NmyK3AesDBGZv034yJBs56keke+EnBt3klqGpQx8SQY=; b=ihQb7wKYYxZvJjVJZ7cCm+iivepKmOmCPf2LdHgt6qW3xLQMO0+wDXU5jq2NnrHQW3uyI6 9HelUOcdE45AbSngkEZxv8vaeZ6nBXnmB9tgbXH81nle7XPU0kb3+/5PB7q6JdPm0ut51R 71v8D5Nj+tjQsjHSg7UATnruCVR5jZQ= Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 85C1F3EA63; Fri, 6 Feb 2026 18:24:04 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id +PssIMQxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:04 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 16/43] btrfs: select encryption dependencies if FS_ENCRYPTION Date: Fri, 6 Feb 2026 19:22:48 +0100 Message-ID: <20260206182336.1397715-17-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spamd-Result: default: False [-6.80 / 50.00]; REPLY(-4.00)[]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; DBL_BLOCKED_OPENRESOLVER(0.00)[suse.com:mid,suse.com:email,toxicpanda.com:email]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; TO_DN_SOME(0.00)[]; RCPT_COUNT_TWELVE(0.00)[12]; RCVD_VIA_SMTP_AUTH(0.00)[]; FUZZY_RATELIMITED(0.00)[rspamd.com]; FROM_HAS_DN(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; R_RATELIMIT(0.00)[to_ip_from(RLdafymbgddseoxkryad1wgu8n)]; DKIM_SIGNED(0.00)[suse.com:s=susede1]; RCVD_TLS_ALL(0.00)[] X-Spam-Flag: NO X-Spam-Score: -6.80 X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik We need this to make sure the appropriate encryption algorithms are turned on in our config if we have FS_ENCRYPTION enabled, and additionally we only support inline encryption with the fallback block crypto, so we need to make sure we pull in those dependencies. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/396f5067b2551a9f2de4b439509e1a28598= 5d358.1706116485.git.josef@toxicpanda.com/ * No changes since. --- fs/btrfs/Kconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig index ede184b6eda1..216a5707b099 100644 --- a/fs/btrfs/Kconfig +++ b/fs/btrfs/Kconfig @@ -16,6 +16,9 @@ config BTRFS_FS select RAID6_PQ select XOR_BLOCKS select XXHASH + select FS_ENCRYPTION_ALGS if FS_ENCRYPTION + select FS_ENCRYPTION_INLINE_CRYPT if FS_ENCRYPTION + select BLK_INLINE_ENCRYPTION_FALLBACK if FS_ENCRYPTION depends on PAGE_SIZE_LESS_THAN_256KB =20 help --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5FC1943E9F3 for ; Fri, 6 Feb 2026 18:24:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402280; cv=none; b=VVQuTW1dVzAsLeLOcBUUklOpxPfVA2UFdStGO/EnqO/0cixyeB0PUVirkocCza13J49OSO7J36BXSHGYGZubaRj6E5CFtj0wJKSEptGddjW7gs5xoCopN8VDzkkx1VeRvjxWGrjoNuj0w7R0+e0Xm8q7Di/Js/AQT0ACQKm++3g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402280; c=relaxed/simple; bh=+NNKrKZBxf1YzCP43qrAbbVRt54PK87oJ/7BnhV/IO0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DBLWRptSmMJW0akKIYzNxNHhqjLDCU/0M8YGx/jJ7HYqtQjNYdftvh07PqeihVidzOBUH81P8r78xnqf9fMXh5WKicULpaHymeVXBzp5OwJG2+ccCXQvnvloyBZ4extlSzMf3ar+uTy8yHEMn0hONi5unK/ixeE8qJT/MtLvfHM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=OY7zAGUK; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=OY7zAGUK; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="OY7zAGUK"; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="OY7zAGUK" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id 279305BD29; Fri, 6 Feb 2026 18:24:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402245; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=1kkMxy8XmUk8cQiHlFLSrRmbG6vYEnV5u0ThGPeCzWw=; b=OY7zAGUKsb4fN15VAVW9MtuVeuOPOnPtn71qU40nQuZczR7EjxAFC7akQY0n9vJXHqWCPz iSzTXr59NEPcKD4U2pHY5sgJt+QY5mFknIPFWgiC2yCHtgP2WvFsMKSPK2lW2XMEs9h6fT qw0Twxd2zBP4bDpcm2j0flvNDjTC5mY= Authentication-Results: smtp-out2.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402245; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=1kkMxy8XmUk8cQiHlFLSrRmbG6vYEnV5u0ThGPeCzWw=; b=OY7zAGUKsb4fN15VAVW9MtuVeuOPOnPtn71qU40nQuZczR7EjxAFC7akQY0n9vJXHqWCPz iSzTXr59NEPcKD4U2pHY5sgJt+QY5mFknIPFWgiC2yCHtgP2WvFsMKSPK2lW2XMEs9h6fT qw0Twxd2zBP4bDpcm2j0flvNDjTC5mY= Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id ED0163EA63; Fri, 6 Feb 2026 18:24:04 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id AANfOcQxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:04 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org, Sweet Tea Dorminy Subject: [PATCH v6 17/43] btrfs: add get_devices hook for fscrypt Date: Fri, 6 Feb 2026 19:22:49 +0100 Message-ID: <20260206182336.1397715-18-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spamd-Result: default: False [-6.80 / 50.00]; REPLY(-4.00)[]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; DBL_BLOCKED_OPENRESOLVER(0.00)[toxicpanda.com:email,suse.com:mid,suse.com:email]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; TO_DN_SOME(0.00)[]; RCPT_COUNT_TWELVE(0.00)[13]; RCVD_VIA_SMTP_AUTH(0.00)[]; FUZZY_RATELIMITED(0.00)[rspamd.com]; FROM_HAS_DN(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; R_RATELIMIT(0.00)[to_ip_from(RLjpbzrka5mc1bf4fb9cmz1e4f)]; DKIM_SIGNED(0.00)[suse.com:s=susede1]; RCVD_TLS_ALL(0.00)[] X-Spam-Flag: NO X-Spam-Score: -6.80 X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Sweet Tea Dorminy Since extent encryption requires inline encryption, even though we expect to use the inlinecrypt software fallback most of the time, we need to enumerate all the devices in use by btrfs. Signed-off-by: Sweet Tea Dorminy Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/47ff044fae65cb2b06a6bb6ffc91027511c= 4d1f4.1706116485.git.josef@toxicpanda.com/ * No changes since. --- fs/btrfs/fscrypt.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index 078b8c9a0334..608f8797ea6f 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -12,7 +12,9 @@ #include "ioctl.h" #include "messages.h" #include "root-tree.h" +#include "super.h" #include "transaction.h" +#include "volumes.h" #include "xattr.h" =20 /* @@ -177,10 +179,44 @@ static bool btrfs_fscrypt_empty_dir(struct inode *ino= de) return inode->i_size =3D=3D BTRFS_EMPTY_DIR_SIZE; } =20 +static struct block_device **btrfs_fscrypt_get_devices(struct super_block = *sb, + unsigned int *num_devs) +{ + struct btrfs_fs_info *fs_info =3D btrfs_sb(sb); + struct btrfs_fs_devices *fs_devices =3D fs_info->fs_devices; + int nr_devices =3D fs_devices->open_devices; + struct block_device **devs; + struct btrfs_device *device; + int i =3D 0; + + devs =3D kmalloc_array(nr_devices, sizeof(*devs), GFP_NOFS | GFP_NOWAIT); + if (!devs) + return ERR_PTR(-ENOMEM); + + rcu_read_lock(); + list_for_each_entry_rcu(device, &fs_devices->devices, dev_list) { + if (!test_bit(BTRFS_DEV_STATE_IN_FS_METADATA, &device->dev_state) || + !device->bdev || + test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state)) + continue; + + devs[i++] =3D device->bdev; + + if (i >=3D nr_devices) + break; + + } + rcu_read_unlock(); + + *num_devs =3D i; + return devs; +} + const struct fscrypt_operations btrfs_fscrypt_ops =3D { .inode_info_offs =3D (int)offsetof(struct btrfs_inode, i_crypt_info) - (int)offsetof(struct btrfs_inode, vfs_inode), .get_context =3D btrfs_fscrypt_get_context, .set_context =3D btrfs_fscrypt_set_context, .empty_dir =3D btrfs_fscrypt_empty_dir, + .get_devices =3D btrfs_fscrypt_get_devices, }; --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8478F4418CB for ; Fri, 6 Feb 2026 18:24:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402284; cv=none; b=WSQJq8YPaLoG5Hz0U4ToeJEmUoIwsdXK34B7KVmheeUARvli1UStCPtDdYZVY+vUUV8zJpilKZoNvrflASVI0ZblN76wOs5cyrukSRYbNogl3stf2xktrlaj/zdUgFyXwrO234P5RR06wPRRAqeGx+r/l6GTWnkCVAfQzE/b344= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402284; c=relaxed/simple; bh=57zrBcpTNnZ9dSBcH18Ql37o6I66oeN+cim/q8qd3Bo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eA+mA04Tf2DhPtKJQ0Nb+I1UCVkU0csqN9nw11VBy5fLGIvgeq8mmkFx0KclUlzFEpgu842ZBzASvv+u7yk0ohhHtyYzc4LgwwWTMCtCcWF3rQ4x78Zk2LlVe6Xs018HyNZm98W+Wj8g1uIMxq8yTu/Fil7YhEWlkJ5eBUuTxdE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id E14D95BD2B; Fri, 6 Feb 2026 18:24:05 +0000 (UTC) Authentication-Results: smtp-out2.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id B22DC3EA63; Fri, 6 Feb 2026 18:24:05 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id mNQIK8UxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:05 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org, Sweet Tea Dorminy Subject: [PATCH v6 18/43] btrfs: set file extent encryption excplicitly Date: Fri, 6 Feb 2026 19:22:50 +0100 Message-ID: <20260206182336.1397715-19-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spam-Score: -4.00 X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Queue-Id: E14D95BD2B X-Rspamd-Server: rspamd1.dmz-prg2.suse.org X-Spam-Level: X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO Content-Type: text/plain; charset="utf-8" From: Sweet Tea Dorminy This puts the long-preserved 1-byte encryption field to work, storing whether the extent is encrypted. Update the tree-checker to allow for the encryption bit to be set to our valid types. Signed-off-by: Sweet Tea Dorminy Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/1bf1cb768bc8bc54b3855e271412077c60f= 23c50.1706116485.git.josef@toxicpanda.com/ * No changes since. --- fs/btrfs/accessors.h | 2 ++ fs/btrfs/inode.c | 6 ++++-- fs/btrfs/tree-checker.c | 8 +++++--- fs/btrfs/tree-log.c | 2 ++ include/uapi/linux/btrfs_tree.h | 8 +++++++- 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/accessors.h b/fs/btrfs/accessors.h index 8938357fcb40..09ed68fa2041 100644 --- a/fs/btrfs/accessors.h +++ b/fs/btrfs/accessors.h @@ -907,6 +907,8 @@ BTRFS_SETGET_STACK_FUNCS(stack_file_extent_disk_num_byt= es, 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); =20 =20 BTRFS_SETGET_FUNCS(file_extent_type, struct btrfs_file_extent_item, type, = 8); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 79f0b67249de..758dde148be6 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3123,7 +3123,8 @@ static int insert_ordered_extent_file_extent(struct b= trfs_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 */ + btrfs_set_stack_file_extent_encryption(&stack_fi, BTRFS_ENCRYPTION_NONE); + /* Other encoding is reserved and always 0 */ =20 /* * For delalloc, when completing an ordered extent we update the inode's @@ -9205,7 +9206,8 @@ static struct btrfs_trans_handle *insert_prealloc_fil= e_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 */ =20 ret =3D btrfs_qgroup_release_data(inode, file_offset, len, &qgroup_releas= ed); if (ret < 0) diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index 452394b34d01..9675dbcd78a3 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -213,6 +213,7 @@ static int check_extent_data_item(struct extent_buffer = *leaf, u32 sectorsize =3D fs_info->sectorsize; u32 item_size =3D btrfs_item_size(leaf, slot); u64 extent_end; + u8 policy; =20 if (unlikely(!IS_ALIGNED(key->offset, sectorsize))) { file_extent_err(leaf, slot, @@ -264,10 +265,11 @@ static int check_extent_data_item(struct extent_buffe= r *leaf, BTRFS_NR_COMPRESS_TYPES - 1); return -EUCLEAN; } - if (unlikely(btrfs_file_extent_encryption(leaf, fi))) { + policy =3D btrfs_file_extent_encryption(leaf, fi); + if (unlikely(policy >=3D 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) =3D=3D BTRFS_FILE_EXTENT_INLINE) { diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index d6d3ce41fc3e..118dd1888996 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -5123,6 +5123,7 @@ static int log_one_extent(struct btrfs_trans_handle *= trans, u64 block_start =3D btrfs_extent_map_block_start(em); u64 block_len; int ret; + u8 encryption =3D BTRFS_ENCRYPTION_NONE; =20 btrfs_set_stack_file_extent_generation(&fi, trans->transid); if (em->flags & EXTENT_FLAG_PREALLOC) @@ -5144,6 +5145,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, compress_type); + btrfs_set_stack_file_extent_encryption(&fi, encryption); =20 ret =3D log_extent_csums(trans, inode, log, em, ctx); if (ret) diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tre= e.h index 02b1ab1f7858..cb8dbcc612e9 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -1107,8 +1107,14 @@ struct btrfs_file_extent_item { * but not for stat. */ __u8 compression; + + /* + * Type of encryption in use. Unencrypted value is 0. + */ __u8 encryption; - __le16 other_encoding; /* spare for later use */ + + /* spare for later use */ + __le16 other_encoding; =20 /* are we inline data or a real extent? */ __u8 type; --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7FCC143E4A4 for ; Fri, 6 Feb 2026 18:24:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402276; cv=none; b=riep3lGN3CYUW/apokAsRtDFgpF6xAb0BK5dvpXSeGOlPX+oPSF7dlx7lW67K1NX0zTsA0BTz/f132pH+YURClni1zSvBkVtpzS/niZ5k6Epzi4nLarFVIj1l3kOk7cBqOovEroSN1gFCBW1DOEhPGmr0BuQBpZl322CulT9krs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402276; c=relaxed/simple; bh=gvoi/0nQGPPiJP3k8zgHGQ32lRhLX5hgreRW21At+Y8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DKI+SSF8wdYuDJCDT9U14U2ylw+kWOqNZRQ2A+y7fpEFnn2k0wQ60FGsxFz5FhJBtYOgsRDg+bB9uVpkJe3zaPE3o3r5sZKRpAxmCrsEcUszYs1NycA5tW3T24UN11jWWgDFuQ2fJgNkj/ctWTKeyqrrd+vCaBEq5xzD5fY8TZU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 98D793E742; Fri, 6 Feb 2026 18:24:06 +0000 (UTC) Authentication-Results: smtp-out1.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 69C773EA63; Fri, 6 Feb 2026 18:24:06 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id KPpVGcYxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:06 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org, Sweet Tea Dorminy Subject: [PATCH v6 19/43] btrfs: add fscrypt_info and encryption_type to extent_map Date: Fri, 6 Feb 2026 19:22:51 +0100 Message-ID: <20260206182336.1397715-20-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO X-Spam-Score: -4.00 X-Rspamd-Queue-Id: 98D793E742 X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Server: rspamd2.dmz-prg2.suse.org X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Sweet Tea Dorminy Each extent_map will end up with a pointer to its associated fscrypt_info if any, which should have the same lifetime as the extent_map. We are also going to need to track the encryption_type for the file extent items. Add the fscrypt_info to the extent_map and the subsequent code for transferring it in the split and merge cases as well as the code necessary to free them. A future patch will add the code to load them as appropriate. Signed-off-by: Sweet Tea Dorminy Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/9818cd4134d048d2d641b9b8ae10be6c6af= 51956.1706116485.git.josef@toxicpanda.com/ * No significant changes since. Just add some btrfs_ prefixes even to static functions in extent map header file to match the style. --- fs/btrfs/extent_map.c | 32 +++++++++++++++++++++++++++++--- fs/btrfs/extent_map.h | 16 ++++++++++++++++ fs/btrfs/file-item.c | 1 + fs/btrfs/inode.c | 1 + 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c index 58589fc11802..f6b58a5c1151 100644 --- a/fs/btrfs/extent_map.c +++ b/fs/btrfs/extent_map.c @@ -59,6 +59,7 @@ struct extent_map *btrfs_alloc_extent_map(void) =20 static void free_extent_map(struct extent_map *em) { + fscrypt_put_extent_info(em->fscrypt_info); kmem_cache_free(extent_map_cache, em); } =20 @@ -99,12 +100,24 @@ void btrfs_free_extent_map_safe(struct extent_map_tree= *tree, if (!em) return; =20 - if (refcount_dec_and_test(&em->refs)) { - WARN_ON(btrfs_extent_map_in_tree(em)); - WARN_ON(!list_empty(&em->list)); + if (!refcount_dec_and_test(&em->refs)) + return; + + WARN_ON(btrfs_extent_map_in_tree(em)); + WARN_ON(!list_empty(&em->list)); + + /* + * We could take a lock freeing the fscrypt_info, so add this to the + * list of freed_extents to be freed later. + */ + if (em->fscrypt_info) { list_add_tail(&em->free_list, &tree->freed_extents); set_bit(EXTENT_MAP_TREE_PENDING_FREES, &tree->flags); + return; } + + /* Nothing scary here, just free the object. */ + free_extent_map(em); } =20 /* @@ -291,6 +304,10 @@ static bool can_merge_extent_map(const struct extent_m= ap *em) if (!list_empty(&em->list)) return false; =20 + /* We can't merge encrypted extents. */ + if (em->fscrypt_info) + return false; + return true; } =20 @@ -311,6 +328,10 @@ static bool mergeable_maps(const struct extent_map *pr= ev, const struct extent_ma if (next->disk_bytenr < EXTENT_MAP_LAST_BYTE - 1) return btrfs_extent_map_block_start(next) =3D=3D extent_map_block_end(pr= ev); =20 + /* Don't merge adjacent encrypted maps. */ + if (prev->fscrypt_info || next->fscrypt_info) + return false; + /* HOLES and INLINE extents. */ return next->disk_bytenr =3D=3D prev->disk_bytenr; } @@ -977,6 +998,7 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *in= ode, u64 start, u64 end, =20 split->generation =3D gen; split->flags =3D flags; + split->fscrypt_info =3D fscrypt_get_extent_info(em->fscrypt_info); replace_extent_mapping(inode, em, split, modified); btrfs_free_extent_map(split); split =3D split2; @@ -1005,6 +1027,7 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *= inode, u64 start, u64 end, split->ram_bytes =3D split->len; } =20 + split->fscrypt_info =3D fscrypt_get_extent_info(em->fscrypt_info); if (btrfs_extent_map_in_tree(em)) { replace_extent_mapping(inode, em, split, modified); } else { @@ -1163,6 +1186,7 @@ int btrfs_split_extent_map(struct btrfs_inode *inode,= u64 start, u64 len, u64 pr split_pre->ram_bytes =3D split_pre->len; split_pre->flags =3D flags; split_pre->generation =3D em->generation; + split_pre->fscrypt_info =3D fscrypt_get_extent_info(em->fscrypt_info); =20 replace_extent_mapping(inode, em, split_pre, true); =20 @@ -1180,6 +1204,8 @@ int btrfs_split_extent_map(struct btrfs_inode *inode,= u64 start, u64 len, u64 pr split_mid->ram_bytes =3D split_mid->len; split_mid->flags =3D flags; split_mid->generation =3D em->generation; + split_mid->fscrypt_info =3D fscrypt_get_extent_info(em->fscrypt_info); + add_extent_mapping(inode, split_mid, true); =20 /* Once for us */ diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h index a962012be1c3..a0d5be758e7e 100644 --- a/fs/btrfs/extent_map.h +++ b/fs/btrfs/extent_map.h @@ -24,6 +24,7 @@ enum { ENUM_BIT(EXTENT_FLAG_COMPRESS_ZLIB), ENUM_BIT(EXTENT_FLAG_COMPRESS_LZO), ENUM_BIT(EXTENT_FLAG_COMPRESS_ZSTD), + ENUM_BIT(EXTENT_FLAG_ENCRYPT_FSCRYPT), /* pre-allocated extent */ ENUM_BIT(EXTENT_FLAG_PREALLOC), /* Logging this extent */ @@ -96,6 +97,7 @@ struct extent_map { u64 generation; u32 flags; refcount_t refs; + struct fscrypt_extent_info *fscrypt_info; struct list_head list; struct list_head free_list; }; @@ -114,6 +116,20 @@ struct extent_map_tree { =20 struct btrfs_inode; =20 +static inline void btrfs_extent_map_set_encryption(struct extent_map *em, + enum btrfs_encryption_type type) +{ + if (type =3D=3D BTRFS_ENCRYPTION_FSCRYPT) + em->flags |=3D EXTENT_FLAG_ENCRYPT_FSCRYPT; +} + +static inline enum btrfs_encryption_type btrfs_extent_map_encryption(const= struct extent_map *em) +{ + if (em->flags & EXTENT_FLAG_ENCRYPT_FSCRYPT) + return BTRFS_ENCRYPTION_FSCRYPT; + return BTRFS_ENCRYPTION_NONE; +} + static inline void btrfs_extent_map_set_compression(struct extent_map *em, enum btrfs_compression_type type) { diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index 7bd715442f3e..08dc78295707 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -1357,6 +1357,7 @@ void btrfs_extent_item_to_extent_map(struct btrfs_ino= de *inode, if (type =3D=3D BTRFS_FILE_EXTENT_PREALLOC) em->flags |=3D EXTENT_FLAG_PREALLOC; } + btrfs_extent_map_set_encryption(em, btrfs_file_extent_encryption(leaf, f= i)); } else if (type =3D=3D BTRFS_FILE_EXTENT_INLINE) { /* Tree-checker has ensured this. */ ASSERT(extent_start =3D=3D 0); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 758dde148be6..b425047f77c7 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7580,6 +7580,7 @@ struct extent_map *btrfs_create_io_em(struct btrfs_in= ode *inode, u64 start, em->flags |=3D EXTENT_FLAG_PINNED; if (type =3D=3D BTRFS_ORDERED_COMPRESSED) btrfs_extent_map_set_compression(em, file_extent->compression); + btrfs_extent_map_set_encryption(em, BTRFS_ENCRYPTION_NONE); =20 ret =3D btrfs_replace_extent_map_range(inode, em, true); if (ret) { --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6800043E9F6 for ; Fri, 6 Feb 2026 18:24:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402280; cv=none; b=Ypp8TCsBNp3lkUpHxWUxBHpcbPNjJcBJw4UdXF/vijr1KELAg0G2wOTx0dMuqTyxwAv1o2KPjvc49nWqHq+LT8iiYVyz7pmpPdS7yvAdGyJiSNew0ULRXvoqG1UXzdD3Lxutf+lI/yH6ZSTEu3bhQjHyU4u7QUzIz6wFbh4uO24= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402280; c=relaxed/simple; bh=ILW5MjqFNyY2StUj24TeI6krWd5DbLhdgiXmKM46mD0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=FvCMgQMPDkg+5e202yxlSZR/QQ8BuhtUR5cjr1pTk6+FcufXPJNTor2lr5rGDVjqArX/x9eRIigXpPKLOrshbW24PGG7Tpos8N7LvRLiARNpfGt5ulCcciKE2giSt2wwdj7MUsT0EeHDq33O+T/flHc7GI2E47ZXM16/qeRxAdc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 1DD1C3E750; Fri, 6 Feb 2026 18:24:07 +0000 (UTC) Authentication-Results: smtp-out1.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id E69543EA63; Fri, 6 Feb 2026 18:24:06 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id aMq9N8YxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:06 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 20/43] btrfs: add fscrypt_info and encryption_type to ordered_extent Date: Fri, 6 Feb 2026 19:22:52 +0100 Message-ID: <20260206182336.1397715-21-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO X-Spam-Score: -4.00 X-Rspamd-Queue-Id: 1DD1C3E750 X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Server: rspamd2.dmz-prg2.suse.org X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik We're going to need these to update the file extent items once the writes are complete. Add them and add the pieces necessary to assign them and free everything. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/de9ff13d1dc042b764c224d039fbb2a0894= 6e004.1706116485.git.josef@toxicpanda.com/ * Changed encryption_type member to u8. --- fs/btrfs/ordered-data.c | 2 ++ fs/btrfs/ordered-data.h | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 5df02c707aee..7a5701937184 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -192,6 +192,7 @@ static struct btrfs_ordered_extent *alloc_ordered_exten= t( } entry->inode =3D inode; entry->compress_type =3D compress_type; + entry->encryption_type =3D BTRFS_ENCRYPTION_NONE; entry->truncated_len =3D (u64)-1; entry->qgroup_rsv =3D qgroup_rsv; entry->flags =3D flags; @@ -630,6 +631,7 @@ void btrfs_put_ordered_extent(struct btrfs_ordered_exte= nt *entry) btrfs_add_delayed_iput(entry->inode); list_for_each_entry_safe(sum, tmp, &entry->list, list) kvfree(sum); + fscrypt_put_extent_info(entry->fscrypt_info); kmem_cache_free(btrfs_ordered_extent_cache, entry); } } diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h index 1e6b0b182b29..ce19198e7f84 100644 --- a/fs/btrfs/ordered-data.h +++ b/fs/btrfs/ordered-data.h @@ -124,6 +124,9 @@ struct btrfs_ordered_extent { /* compression algorithm */ int compress_type; =20 + /* encryption mode */ + u8 encryption_type; + /* Qgroup reserved space */ int qgroup_rsv; =20 @@ -133,6 +136,9 @@ struct btrfs_ordered_extent { /* the inode we belong to */ struct btrfs_inode *inode; =20 + /* the fscrypt_info for this extent, if necessary */ + struct fscrypt_extent_info *fscrypt_info; + /* list of checksums for insertion when the extent io is done */ struct list_head list; =20 --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 907A3428489 for ; Fri, 6 Feb 2026 18:24:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402288; cv=none; b=qZFEfFGTMYj4BqSmtg0a0XHzTNmZ6km90V6KoPqhQkTDzBvlW9pt/2tgu1DxHMp6nKmQugtbyxLURyJuAjb9LuoTzBZjcWcwdpd5nBMJ1giTStCsdE86Jje+T4ge+NCNaeuCVfY6E3azXwCPGLqAa4mM8omUpaCJn5Ct8FQ00hQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402288; c=relaxed/simple; bh=eKEMXMgvwbEHaLDnuSziobkSPY1jAcWctjgqwLL2RHQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eoK8aIGMbpgmJYryf4X722N33HNeAhx3FszkUtw892nWj0G82lHGjKTnGRvO9rpUK4nTQAN+C/FTebJoGK38LE67lsW7ZcpdUWpHGoJlZoZLxZ9nL/BAq8aSrhfSqVY4fz7wx96yzMp2V1hTD0b532NnDjJqOSOUrW9RmInu2ZQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=VR2/SUri; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=VR2/SUri; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="VR2/SUri"; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="VR2/SUri" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id C70505BD04; Fri, 6 Feb 2026 18:24:07 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402247; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=T09jMFM1tmsRBBdjulk6aVd+RtanVhjVM/1FNw+dLLY=; b=VR2/SUri3Xy7havY1dhm0wpyffcmALF5c0cVXIqS5LP46++Rz34dVsX8sPMg45Rf7MyFi7 cZ6qiguUbrJsXhIExfxoX1lqepkl3Iz3Vj6gQdYYTPKc6z7S4UBybZui0hDgbfMsZXPMZy PT72vMqCqZsJtXSAhcs5zDN3SRFFPGs= Authentication-Results: smtp-out2.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402247; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=T09jMFM1tmsRBBdjulk6aVd+RtanVhjVM/1FNw+dLLY=; b=VR2/SUri3Xy7havY1dhm0wpyffcmALF5c0cVXIqS5LP46++Rz34dVsX8sPMg45Rf7MyFi7 cZ6qiguUbrJsXhIExfxoX1lqepkl3Iz3Vj6gQdYYTPKc6z7S4UBybZui0hDgbfMsZXPMZy PT72vMqCqZsJtXSAhcs5zDN3SRFFPGs= Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 9764F3EA63; Fri, 6 Feb 2026 18:24:07 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id +CZ2JMcxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:07 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 21/43] btrfs: plumb through setting the fscrypt_info for ordered extents Date: Fri, 6 Feb 2026 19:22:53 +0100 Message-ID: <20260206182336.1397715-22-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spamd-Result: default: False [-6.80 / 50.00]; REPLY(-4.00)[]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-0.999]; MIME_GOOD(-0.10)[text/plain]; DBL_BLOCKED_OPENRESOLVER(0.00)[toxicpanda.com:email,suse.com:mid,suse.com:email]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; TO_DN_SOME(0.00)[]; RCPT_COUNT_TWELVE(0.00)[12]; RCVD_VIA_SMTP_AUTH(0.00)[]; FUZZY_RATELIMITED(0.00)[rspamd.com]; FROM_HAS_DN(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; R_RATELIMIT(0.00)[to_ip_from(RLdafymbgddseoxkryad1wgu8n)]; DKIM_SIGNED(0.00)[suse.com:s=susede1]; RCVD_TLS_ALL(0.00)[] X-Spam-Flag: NO X-Spam-Score: -6.80 X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik We're going to be getting fscrypt_info from the extent maps. Pass the fscrypt_info to helpers using the file_extent structure and use that to set the encryption type on the ordered extent. For prealloc extents we create an em, since we already have the context loaded from the original prealloc extent creation we need to pre- populate the extent map fscrypt info so it can be read properly later if the pages are evicted. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/80c5dabfe190b84e31a95160021da64ebcf= 7ecf7.1706116485.git.josef@toxicpanda.com/ * Added fscrypt_info to file_extent structure to clean up and simplify the code following upstream changes since. * Squashed in https://lore.kernel.org/linux-btrfs/f2b402eac1963296b6b8db3c= b59cdf24a8121b97.1706116485.git.josef@toxicpanda.com/ ("btrfs: plumb the fscrypt extent context through create_io_em"). * Moved fscrypt_info to be the last argument of alloc_ordered_extent(). --- fs/btrfs/direct-io.c | 1 + fs/btrfs/inode.c | 3 +++ fs/btrfs/ordered-data.c | 18 ++++++++++++------ fs/btrfs/ordered-data.h | 1 + 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c index 9a63200d7a53..f3efc451d9a5 100644 --- a/fs/btrfs/direct-io.c +++ b/fs/btrfs/direct-io.c @@ -202,6 +202,7 @@ static struct extent_map *btrfs_new_extent_direct(struc= t btrfs_inode *inode, file_extent.ram_bytes =3D ins.offset; file_extent.offset =3D 0; file_extent.compression =3D BTRFS_COMPRESS_NONE; + file_extent.fscrypt_info =3D NULL; em =3D btrfs_create_dio_extent(inode, dio_data, start, &file_extent, BTRFS_ORDERED_REGULAR); btrfs_dec_block_group_reservations(fs_info, ins.objectid); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index b425047f77c7..aef95d6e02bf 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1183,6 +1183,7 @@ static void submit_one_async_extent(struct async_chun= k *async_chunk, file_extent.num_bytes =3D async_extent->ram_size; file_extent.offset =3D 0; file_extent.compression =3D async_extent->compress_type; + file_extent.fscrypt_info =3D NULL; =20 em =3D btrfs_create_io_em(inode, start, &file_extent, BTRFS_ORDERED_COMPR= ESSED); if (IS_ERR(em)) { @@ -1319,6 +1320,7 @@ static int cow_one_range(struct btrfs_inode *inode, s= truct folio *locked_folio, file_extent.ram_bytes =3D ins->offset; file_extent.offset =3D 0; file_extent.compression =3D BTRFS_COMPRESS_NONE; + file_extent.fscrypt_info =3D NULL; =20 /* * Locked range will be released either during error clean up (inside @@ -10117,6 +10119,7 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, = struct iov_iter *from, file_extent.ram_bytes =3D ram_bytes; file_extent.offset =3D encoded->unencoded_offset; file_extent.compression =3D compression; + file_extent.fscrypt_info =3D NULL; em =3D btrfs_create_io_em(inode, start, &file_extent, BTRFS_ORDERED_COMPR= ESSED); if (IS_ERR(em)) { ret =3D PTR_ERR(em); diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 7a5701937184..1b4d20a2f983 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -148,7 +148,8 @@ static inline struct rb_node *ordered_tree_search(struc= t btrfs_inode *inode, static struct btrfs_ordered_extent *alloc_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 long flags, int compress_type) + u64 offset, unsigned long flags, int compress_type, + struct fscrypt_extent_info *fscrypt_info) { struct btrfs_ordered_extent *entry; int ret; @@ -192,10 +193,12 @@ static struct btrfs_ordered_extent *alloc_ordered_ext= ent( } entry->inode =3D inode; entry->compress_type =3D compress_type; - entry->encryption_type =3D BTRFS_ENCRYPTION_NONE; entry->truncated_len =3D (u64)-1; entry->qgroup_rsv =3D qgroup_rsv; entry->flags =3D flags; + entry->fscrypt_info =3D fscrypt_get_extent_info(fscrypt_info); + entry->encryption_type =3D entry->fscrypt_info ? + BTRFS_ENCRYPTION_FSCRYPT : BTRFS_ENCRYPTION_NONE; refcount_set(&entry->refs, 1); init_waitqueue_head(&entry->wait); INIT_LIST_HEAD(&entry->list); @@ -272,6 +275,7 @@ static void insert_ordered_extent(struct btrfs_ordered_= extent *entry) * @offset: Offset into unencoded data where file data starts. * @flags: Flags specifying type of extent (1U << BTRFS_ORDERED_= *). * @compress_type: Compression algorithm used for data. + * @fscrypt_info: The fscrypt_extent_info for this extent, if necessary. * * Most of these parameters correspond to &struct btrfs_file_extent_item. = The * tree is given a single reference on the ordered extent that was inserte= d, and @@ -305,7 +309,8 @@ struct btrfs_ordered_extent *btrfs_alloc_ordered_extent( file_extent->num_bytes, file_extent->disk_bytenr + file_extent->offset, file_extent->num_bytes, 0, flags, - file_extent->compression); + file_extent->compression, + file_extent->fscrypt_info); else entry =3D alloc_ordered_extent(inode, file_offset, file_extent->num_bytes, @@ -313,7 +318,8 @@ struct btrfs_ordered_extent *btrfs_alloc_ordered_extent( file_extent->disk_bytenr, file_extent->disk_num_bytes, file_extent->offset, flags, - file_extent->compression); + file_extent->compression, + file_extent->fscrypt_info); if (!IS_ERR(entry)) insert_ordered_extent(entry); return entry; @@ -1271,8 +1277,8 @@ struct btrfs_ordered_extent *btrfs_split_ordered_exte= nt( if (WARN_ON_ONCE(ordered->disk_num_bytes !=3D ordered->num_bytes)) return ERR_PTR(-EINVAL); =20 - new =3D alloc_ordered_extent(inode, file_offset, len, len, disk_bytenr, - len, 0, flags, ordered->compress_type); + new =3D alloc_ordered_extent(inode, file_offset, len, len, disk_bytenr, l= en, 0, + flags, ordered->compress_type, ordered->fscrypt_info); if (IS_ERR(new)) return new; =20 diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h index ce19198e7f84..51c795865fe6 100644 --- a/fs/btrfs/ordered-data.h +++ b/fs/btrfs/ordered-data.h @@ -188,6 +188,7 @@ struct btrfs_file_extent { u64 num_bytes; u64 ram_bytes; u64 offset; + struct fscrypt_extent_info *fscrypt_info; u8 compression; }; =20 --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 77D0B4418C8 for ; Fri, 6 Feb 2026 18:24:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402284; cv=none; b=bgSDQQsAXeILA85lXYURRtCt2S+9kJCW2yaBR9CPA/ij/TG94ABR20oabo7viD7jrMXVZcwznTda21Ynv2iLxKooVXukHO/xfw59PLlXIiPqyB1sCMT8DHlmxZlTO36iFAQcsKEDVLYg3MvLsdKWjHO3XfxzFX32KNBQM8a5HeE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402284; c=relaxed/simple; bh=4gsIL/puGcm2Z7MT/G9xezZ4P8I3WruwNiNQf72J8HQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=sXkyLla2YchLpG8SoVTR88NAoDqWSJFnFWo5RlFhxuFUtiisg62/THMPmSKg6GyXsq6JyDfBWf8LeP+3EqrZI2UofjE9UKFbEFqJtquA9ggy2d7PGRELzzLG1pp3H54pU0MEHFwC/3SuuIryKfIGHHj4y3rquh1ewkGt/N8U7PY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 525F23E758; Fri, 6 Feb 2026 18:24:08 +0000 (UTC) Authentication-Results: smtp-out1.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 24AF53EA63; Fri, 6 Feb 2026 18:24:08 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id IBhWCMgxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:08 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 22/43] btrfs: populate the ordered_extent with the fscrypt context Date: Fri, 6 Feb 2026 19:22:54 +0100 Message-ID: <20260206182336.1397715-23-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spam-Score: -4.00 X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Queue-Id: 525F23E758 X-Rspamd-Server: rspamd1.dmz-prg2.suse.org X-Spam-Level: X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO Content-Type: text/plain; charset="utf-8" From: Josef Bacik The fscrypt_extent_info will be tied to the extent_map lifetime, so it will be created when we create the IO em, or it'll already exist in the NOCOW case. Use this fscrypt_info when creating the ordered extent to make sure everything is passed through properly. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/33e650f3e91ed0318211301beb27fa61338= 2f28e.1706116485.git.josef@toxicpanda.com/ * Splitted the dio-related hunks from inode.c to direct-io.c as upstream refactored in the meantime. * Pass fscrypt_info using the file_extent structure to follow the upstream cleanup. --- fs/btrfs/direct-io.c | 4 +++- fs/btrfs/inode.c | 36 ++++++++++++++++++++++++++++++------ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c index f3efc451d9a5..c95b4e768043 100644 --- a/fs/btrfs/direct-io.c +++ b/fs/btrfs/direct-io.c @@ -140,7 +140,7 @@ static int lock_extent_direct(struct inode *inode, u64 = lockstart, u64 lockend, static struct extent_map *btrfs_create_dio_extent(struct btrfs_inode *inod= e, struct btrfs_dio_data *dio_data, const u64 start, - const struct btrfs_file_extent *file_extent, + struct btrfs_file_extent *file_extent, const int type) { struct extent_map *em =3D NULL; @@ -150,6 +150,7 @@ static struct extent_map *btrfs_create_dio_extent(struc= t btrfs_inode *inode, em =3D btrfs_create_io_em(inode, start, file_extent, type); if (IS_ERR(em)) goto out; + file_extent->fscrypt_info =3D em->fscrypt_info; } =20 ordered =3D btrfs_alloc_ordered_extent(inode, start, file_extent, @@ -276,6 +277,7 @@ static int btrfs_get_blocks_direct_write(struct extent_= map **map, } space_reserved =3D true; =20 + file_extent.fscrypt_info =3D em->fscrypt_info; em2 =3D btrfs_create_dio_extent(BTRFS_I(inode), dio_data, start, &file_extent, type); btrfs_dec_nocow_writers(bg); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index aef95d6e02bf..f81bdb97b212 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1190,10 +1190,11 @@ static void submit_one_async_extent(struct async_ch= unk *async_chunk, ret =3D PTR_ERR(em); goto out_free_reserve; } - btrfs_free_extent_map(em); =20 + file_extent.fscrypt_info =3D em->fscrypt_info; ordered =3D btrfs_alloc_ordered_extent(inode, start, &file_extent, 1U << BTRFS_ORDERED_COMPRESSED); + btrfs_free_extent_map(em); if (IS_ERR(ordered)) { btrfs_drop_extent_map_range(inode, start, end, false); ret =3D PTR_ERR(ordered); @@ -1333,10 +1334,11 @@ static int cow_one_range(struct btrfs_inode *inode,= struct folio *locked_folio, ret =3D PTR_ERR(em); goto free_reserved; } - btrfs_free_extent_map(em); =20 + file_extent.fscrypt_info =3D em->fscrypt_info; ordered =3D btrfs_alloc_ordered_extent(inode, file_offset, &file_extent, 1U << BTRFS_ORDERED_REGULAR); + btrfs_free_extent_map(em); if (IS_ERR(ordered)) { btrfs_drop_extent_map_range(inode, file_offset, cur_end, false); ret =3D PTR_ERR(ordered); @@ -2006,18 +2008,32 @@ static int nocow_one_range(struct btrfs_inode *inod= e, struct folio *locked_folio u64 file_pos, bool is_prealloc) { struct btrfs_ordered_extent *ordered; + struct extent_map *em; const u64 len =3D nocow_args->file_extent.num_bytes; const u64 end =3D file_pos + len - 1; int ret =3D 0; =20 btrfs_lock_extent(&inode->io_tree, file_pos, end, cached); =20 - if (is_prealloc) { - struct extent_map *em; + /* + * We only want to do this lookup if we're encrypted, otherwise + * fsrypt_info will be null and we can avoid this lookup. + */ + if (IS_ENCRYPTED(&inode->vfs_inode)) { + em =3D btrfs_get_extent(inode, NULL, file_pos, len); + if (IS_ERR(em)) { + btrfs_unlock_extent(&inode->io_tree, file_pos, end, cached); + return PTR_ERR(em); + } + nocow_args->file_extent.fscrypt_info =3D fscrypt_get_extent_info(em->fsc= rypt_info); + btrfs_free_extent_map(em); + } =20 + if (is_prealloc) { em =3D btrfs_create_io_em(inode, file_pos, &nocow_args->file_extent, BTRFS_ORDERED_PREALLOC); if (IS_ERR(em)) { + fscrypt_put_extent_info(nocow_args->file_extent.fscrypt_info); ret =3D PTR_ERR(em); goto error; } @@ -2028,6 +2044,7 @@ static int nocow_one_range(struct btrfs_inode *inode,= struct folio *locked_folio is_prealloc ? (1U << BTRFS_ORDERED_PREALLOC) : (1U << BTRFS_ORDERED_NOCOW)); + fscrypt_put_extent_info(nocow_args->file_extent.fscrypt_info); if (IS_ERR(ordered)) { if (is_prealloc) btrfs_drop_extent_map_range(inode, file_pos, end, false); @@ -7582,7 +7599,13 @@ struct extent_map *btrfs_create_io_em(struct btrfs_i= node *inode, u64 start, em->flags |=3D EXTENT_FLAG_PINNED; if (type =3D=3D BTRFS_ORDERED_COMPRESSED) btrfs_extent_map_set_compression(em, file_extent->compression); - btrfs_extent_map_set_encryption(em, BTRFS_ENCRYPTION_NONE); + + if (file_extent->fscrypt_info) { + btrfs_extent_map_set_encryption(em, BTRFS_ENCRYPTION_FSCRYPT); + em->fscrypt_info =3D fscrypt_get_extent_info(file_extent->fscrypt_info); + } else { + btrfs_extent_map_set_encryption(em, BTRFS_ENCRYPTION_NONE); + } =20 ret =3D btrfs_replace_extent_map_range(inode, em, true); if (ret) { @@ -10125,11 +10148,12 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb= , struct iov_iter *from, ret =3D PTR_ERR(em); goto out_free_reserved; } - btrfs_free_extent_map(em); =20 + file_extent.fscrypt_info =3D em->fscrypt_info; ordered =3D btrfs_alloc_ordered_extent(inode, start, &file_extent, (1U << BTRFS_ORDERED_ENCODED) | (1U << BTRFS_ORDERED_COMPRESSED)); + btrfs_free_extent_map(em); if (IS_ERR(ordered)) { btrfs_drop_extent_map_range(inode, start, end, false); ret =3D PTR_ERR(ordered); --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7B632428485 for ; Fri, 6 Feb 2026 18:24:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402288; cv=none; b=p4Y6oVsZ1D8azdVCDnuQ7ZZGAwjh1J35DEMPvIcri9RgQZXqcDuGS4FNEBy+JwECU+z2IWcvrJ/DzVcVByRxLfq7H+5KDCchZ1NQ+dLgePWaeZSIiiVYiBKKBfW33taSLDpfTnj7KgZkvoZCAM5o+K8vqPJpIamc/HClPcsHSek= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402288; c=relaxed/simple; bh=YnH1HnTod1Q2da9rrj4LriQGSR/+/7gxFU8409P7IKk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=T21v91hq+TGI7tPMOE1FcsZ6F3tMLEEG/BucdxIh2Dd51cOyMzCjxcFJKm5+WzgvquiaMEDzO4xqbZy+1rV9seQ/86s0S/pHRCkh0pCrbtQ9pudj0xxyCQj1zA9H96nYdBP9Z7tgOjcVI/p+CkTWMiTQOmExB5HVpeamHZswyFw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=A+0mPrRF; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=A+0mPrRF; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="A+0mPrRF"; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="A+0mPrRF" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id BAFBC3E6E5; Fri, 6 Feb 2026 18:24:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402248; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=btJMz5miTBcJgRZuwePXkRB+sZ9KG4pk++LUH6QvOIM=; b=A+0mPrRF8G7YLXshbBXOAI2DFOgSUL61mCdoUorpfv9vzJKhzXJruReI0JNLlkoHQrCoO1 tedtN9VrwEvkQAsSpzSgp325h5F93x9iuFxWFAyHZuyzq2X8ygSLqR90fBNLPk2bE2F3Xa NGqqJoA7JfgGapTD701mhEGwLmRmMmg= Authentication-Results: smtp-out1.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402248; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=btJMz5miTBcJgRZuwePXkRB+sZ9KG4pk++LUH6QvOIM=; b=A+0mPrRF8G7YLXshbBXOAI2DFOgSUL61mCdoUorpfv9vzJKhzXJruReI0JNLlkoHQrCoO1 tedtN9VrwEvkQAsSpzSgp325h5F93x9iuFxWFAyHZuyzq2X8ygSLqR90fBNLPk2bE2F3Xa NGqqJoA7JfgGapTD701mhEGwLmRmMmg= Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 8EE023EA63; Fri, 6 Feb 2026 18:24:08 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id QKItIsgxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:08 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 23/43] btrfs: keep track of fscrypt info and orig_start for dio reads Date: Fri, 6 Feb 2026 19:22:55 +0100 Message-ID: <20260206182336.1397715-24-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spam-Score: -6.80 X-Spamd-Result: default: False [-6.80 / 50.00]; REPLY(-4.00)[]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; DBL_BLOCKED_OPENRESOLVER(0.00)[suse.com:mid,suse.com:email,toxicpanda.com:email]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; TO_DN_SOME(0.00)[]; RCPT_COUNT_TWELVE(0.00)[12]; RCVD_VIA_SMTP_AUTH(0.00)[]; FUZZY_RATELIMITED(0.00)[rspamd.com]; FROM_HAS_DN(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; R_RATELIMIT(0.00)[to_ip_from(RLdafymbgddseoxkryad1wgu8n)]; DKIM_SIGNED(0.00)[suse.com:s=susede1]; RCVD_TLS_ALL(0.00)[] X-Spam-Level: X-Spam-Flag: NO Content-Type: text/plain; charset="utf-8" From: Josef Bacik We keep track of this information in the ordered extent for writes, but we need it for reads as well. Add fscrypt_extent_info and orig_start to the dio_data so we can populate this on reads. This will be used later when we attach the fscrypt context to the bios. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/acf3d79bcd72a4447b2993e78d9742fa5a0= 5397f.1706116485.git.josef@toxicpanda.com/ * Open-code orig_start to start - offset. --- fs/btrfs/direct-io.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c index c95b4e768043..d3789109ca85 100644 --- a/fs/btrfs/direct-io.c +++ b/fs/btrfs/direct-io.c @@ -17,6 +17,8 @@ struct btrfs_dio_data { ssize_t submitted; struct extent_changeset *data_reserved; struct btrfs_ordered_extent *ordered; + struct fscrypt_extent_info *fscrypt_info; + u64 orig_start; bool data_space_reserved; bool nocow_done; }; @@ -550,6 +552,9 @@ static int btrfs_dio_iomap_begin(struct inode *inode, l= off_t start, release_offset, release_len); } + } else { + dio_data->fscrypt_info =3D fscrypt_get_extent_info(em->fscrypt_info); + dio_data->orig_start =3D em->start - em->offset; } =20 /* @@ -640,6 +645,11 @@ static int btrfs_dio_iomap_end(struct inode *inode, lo= ff_t pos, loff_t length, dio_data->ordered =3D NULL; } =20 + if (dio_data->fscrypt_info) { + fscrypt_put_extent_info(dio_data->fscrypt_info); + dio_data->fscrypt_info =3D NULL; + } + if (write) extent_changeset_free(dio_data->data_reserved); return ret; --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9E766429817 for ; Fri, 6 Feb 2026 18:24:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402293; cv=none; b=lhrtcpmcGvVR9FmpqHb8dwWydryyJyvTmMqz/Le1IUv4uAQ9z+PrHRxb9NqaLpuTppmhxHFhTIDv6ywyVID368UCQJEBezv0Bfk4L9/1lkn8gVa99BqLiMe5e/DSRshWXAy645NqWGcTrMbLf90Ox8A4IEAibF7pwg/kcqqhXXw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402293; c=relaxed/simple; bh=+q0IPKISyxE5BrbDrdgwvnzOvu7RKcrDM0nVMz926ZU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=KrZCpLnrUN8bnKtI5QldxkW8+y9mZ5G2BwzfAi+mfCOUx/tgg+p9IvRUzF9HsyJwvPdqBa9lGf15wCjeogOc2DLiBMOwYZ4eucWlnEmVgx/15qClL2oGHZRrszAJrz7IKXpTsXF4r40uKxbU3w3SF8q+eSJOKgzusG18QzxYv1k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=uI4hxJ+4; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=uI4hxJ+4; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="uI4hxJ+4"; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="uI4hxJ+4" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id 620825BCE6; Fri, 6 Feb 2026 18:24:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402249; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=OmG9N4ohjHh0cmkqi9rJ6UL4YN030xTE9nSNlJN0anI=; b=uI4hxJ+4VB4OVQ/JZcHRqR0R1O9Ay9BIGOcWSAUBJLmVbF4TUPhURBzlzb6HLGUVUs6uWN 1Ef/v8yHIoU2AmT1YFA2C/u1cJyRD8DEWA26xUg/YbwnXkdLRAre/pKFLbN2I5mBNuNvYk sm8ittN1/n4sC3qr2g98PKAIbewARnU= Authentication-Results: smtp-out2.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402249; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=OmG9N4ohjHh0cmkqi9rJ6UL4YN030xTE9nSNlJN0anI=; b=uI4hxJ+4VB4OVQ/JZcHRqR0R1O9Ay9BIGOcWSAUBJLmVbF4TUPhURBzlzb6HLGUVUs6uWN 1Ef/v8yHIoU2AmT1YFA2C/u1cJyRD8DEWA26xUg/YbwnXkdLRAre/pKFLbN2I5mBNuNvYk sm8ittN1/n4sC3qr2g98PKAIbewARnU= Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 36DE73EA63; Fri, 6 Feb 2026 18:24:09 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id yNfVDMkxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:09 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 24/43] btrfs: add extent encryption context tree item type Date: Fri, 6 Feb 2026 19:22:56 +0100 Message-ID: <20260206182336.1397715-25-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spamd-Result: default: False [-6.80 / 50.00]; REPLY(-4.00)[]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-0.999]; MIME_GOOD(-0.10)[text/plain]; DBL_BLOCKED_OPENRESOLVER(0.00)[suse.com:mid,suse.com:email,toxicpanda.com:email]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; TO_DN_SOME(0.00)[]; RCPT_COUNT_TWELVE(0.00)[12]; RCVD_VIA_SMTP_AUTH(0.00)[]; FUZZY_RATELIMITED(0.00)[rspamd.com]; FROM_HAS_DN(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; R_RATELIMIT(0.00)[to_ip_from(RLdafymbgddseoxkryad1wgu8n)]; DKIM_SIGNED(0.00)[suse.com:s=susede1]; RCVD_TLS_ALL(0.00)[] X-Spam-Flag: NO X-Spam-Score: -6.80 X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik The fscrypt encryption context will be stored as a new tree item type. This gives us flexibility to include different things in the future. Also update the tree-checker to validate the new item type. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/7ee9171262857336011bf0e121846617c51= 81fa4.1706116485.git.josef@toxicpanda.com/ ("btrfs: add an optional encryption context to the end of file extents") * Not much left from the original commit. - This was reworked so that the encryption context is now a separate tree item with it's unique key. - It is tightly related to the file extent item but still optional and only used for encrypted extents. - The content (and hence the size as well) comes from the fscrrypt subsystem and it is not touched by btrfs at all. - It's handled as a raw binary data (u8 *). - This patch makes sure it is correctly removed when the related extent is dropped. * As a result, the following patch https://lore.kernel.org/linux-btrfs/f0d= 9b2d3a40b7a963a977d3dfb62793ff7b065d1.1706116485.git.josef@toxicpanda.com/ ("btrfs: explicitly track file extent length for replace and drop") was dropped as not applicable. There's no need to track the size anymore as it just matches the size of the stored item. [RFC]: Should I have kept the structure with __u8 type? Like: | struct btrfs_encryption_info { | __u8 context[0]; | }; I did remove it as it was only used to extend the file extent item structure and hence no longer needed. --- fs/btrfs/file.c | 61 +++++++++++++++++++++++++++++++++ fs/btrfs/tree-checker.c | 59 ++++++++++++++++++++++++++++--- include/uapi/linux/btrfs_tree.h | 8 +++++ 3 files changed, 124 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 3c0db279f592..639462164d08 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -150,6 +150,7 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans, u64 extent_offset =3D 0; u64 extent_end =3D 0; u64 last_end =3D args->start; + u64 first_ctx =3D 1, last_ctx =3D 0; int del_nr =3D 0; int del_slot =3D 0; int extent_type; @@ -407,6 +408,12 @@ int btrfs_drop_extents(struct btrfs_trans_handle *tran= s, del_nr++; } =20 + if (btrfs_file_extent_encryption(leaf, fi) =3D=3D BTRFS_ENCRYPTION_FSCR= YPT) { + if (first_ctx > last_ctx) + first_ctx =3D key.offset; + last_ctx =3D key.offset; + } + if (update_refs && extent_type =3D=3D BTRFS_FILE_EXTENT_INLINE) { args->bytes_found +=3D extent_end - key.offset; @@ -496,6 +503,60 @@ int btrfs_drop_extents(struct btrfs_trans_handle *tran= s, args->extent_inserted =3D true; } =20 + if (first_ctx <=3D last_ctx) { + int slot, nritems; + + btrfs_release_path(path); + + key.objectid =3D ino; + key.type =3D BTRFS_FSCRYPT_CTX_KEY; + key.offset =3D first_ctx; + + ret =3D btrfs_search_slot(trans, root, &key, path, modify_tree, !!modify= _tree); + if (ret < 0) + goto out_ctx; +next_leaf: + leaf =3D path->nodes[0]; + slot =3D path->slots[0]; + + del_slot =3D slot; + del_nr =3D 0; + nritems =3D btrfs_header_nritems(leaf); + while (slot < nritems) { + btrfs_item_key_to_cpu(leaf, &key, slot); + if (key.objectid > ino || + key.type > BTRFS_FSCRYPT_CTX_KEY || + key.offset > last_ctx) + break; + del_nr++; + slot++; + } + if (del_nr) { + ret =3D btrfs_del_items(trans, root, path, del_slot, del_nr); + if (unlikely(ret)) { + btrfs_abort_transaction(trans, ret); + goto out_ctx; + } + + if (slot =3D=3D nritems) { + ret =3D btrfs_next_leaf(root, path); + if (!ret) + goto next_leaf; + if (ret > 0) + ret =3D 0; + } + } +out_ctx: + if (args->path && args->extent_inserted) { + btrfs_release_path(path); + + key.objectid =3D ino; + key.type =3D BTRFS_EXTENT_DATA_KEY; + key.offset =3D args->start; + ret =3D btrfs_search_slot(trans, root, &key, path, 0, 0); + } + } + if (!args->path) btrfs_free_path(path); else if (!args->extent_inserted) diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index 9675dbcd78a3..776901f297fe 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -186,6 +186,7 @@ static bool check_prev_ino(struct extent_buffer *leaf, key->type =3D=3D BTRFS_INODE_EXTREF_KEY || key->type =3D=3D BTRFS_DIR_INDEX_KEY || key->type =3D=3D BTRFS_DIR_ITEM_KEY || + key->type =3D=3D BTRFS_FSCRYPT_CTX_KEY || key->type =3D=3D BTRFS_EXTENT_DATA_KEY, "key->type=3D%u", key->typ= e); =20 /* @@ -204,6 +205,39 @@ static bool check_prev_ino(struct extent_buffer *leaf, prev_key->objectid, key->objectid); return false; } +static int check_fscrypt_context(struct extent_buffer *leaf, + struct btrfs_key *key, int slot, + struct btrfs_key *prev_key) +{ + u32 sectorsize =3D leaf->fs_info->sectorsize; + u32 item_size =3D btrfs_item_size(leaf, slot); + + if (unlikely(!IS_ALIGNED(key->offset, sectorsize))) { + file_extent_err(leaf, slot, +"unaligned file_offset for encryption context, have %llu should be aligned= to %u", + key->offset, sectorsize); + return -EUCLEAN; + } + + /* + * Previous key must have the same key->objectid (ino). + * It can be XATTR_ITEM, INODE_ITEM or just another EXTENT_DATA. + * But if objectids mismatch, it means we have a missing + * INODE_ITEM. + */ + if (unlikely(!check_prev_ino(leaf, key, slot, prev_key))) + return -EUCLEAN; + + if (unlikely(item_size > BTRFS_MAX_EXTENT_CTX_SIZE)) { + file_extent_err(leaf, slot, + "invalid encryption context size, have %u expect a maximum of %u", + item_size, BTRFS_MAX_EXTENT_CTX_SIZE); + return -EUCLEAN; + } + + return 0; +} + static int check_extent_data_item(struct extent_buffer *leaf, struct btrfs_key *key, int slot, struct btrfs_key *prev_key) @@ -214,6 +248,7 @@ static int check_extent_data_item(struct extent_buffer = *leaf, u32 item_size =3D btrfs_item_size(leaf, slot); u64 extent_end; u8 policy; + u8 fe_type; =20 if (unlikely(!IS_ALIGNED(key->offset, sectorsize))) { file_extent_err(leaf, slot, @@ -244,12 +279,12 @@ static int check_extent_data_item(struct extent_buffe= r *leaf, SZ_4K); return -EUCLEAN; } - if (unlikely(btrfs_file_extent_type(leaf, fi) >=3D - BTRFS_NR_FILE_EXTENT_TYPES)) { + + fe_type =3D btrfs_file_extent_type(leaf, fi); + if (unlikely(fe_type >=3D BTRFS_NR_FILE_EXTENT_TYPES)) { file_extent_err(leaf, slot, "invalid type for file extent, have %u expect range [0, %u]", - btrfs_file_extent_type(leaf, fi), - BTRFS_NR_FILE_EXTENT_TYPES - 1); + fe_type, BTRFS_NR_FILE_EXTENT_TYPES - 1); return -EUCLEAN; } =20 @@ -298,6 +333,19 @@ static int check_extent_data_item(struct extent_buffer= *leaf, return 0; } =20 + if (policy =3D=3D BTRFS_ENCRYPTION_FSCRYPT) { + /* + * Only regular and prealloc extents should have an encryption + * context. + */ + if (unlikely(fe_type !=3D BTRFS_FILE_EXTENT_REG && + fe_type !=3D BTRFS_FILE_EXTENT_PREALLOC)) { + file_extent_err(leaf, slot, + "invalid type for encrypted file extent, have %u", fe_type); + return -EUCLEAN; + } + } + /* Regular or preallocated extent has fixed item size */ if (unlikely(item_size !=3D sizeof(*fi))) { file_extent_err(leaf, slot, @@ -1948,6 +1996,9 @@ static enum btrfs_tree_block_status check_leaf_item(s= truct extent_buffer *leaf, case BTRFS_EXTENT_CSUM_KEY: ret =3D check_csum_item(leaf, key, slot, prev_key); break; + case BTRFS_FSCRYPT_CTX_KEY: + ret =3D check_fscrypt_context(leaf, key, slot, prev_key); + break; case BTRFS_DIR_ITEM_KEY: case BTRFS_DIR_INDEX_KEY: case BTRFS_XATTR_ITEM_KEY: diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tre= e.h index cb8dbcc612e9..76937d66b5dd 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -168,6 +168,7 @@ #define BTRFS_VERITY_MERKLE_ITEM_KEY 37 =20 #define BTRFS_FSCRYPT_INODE_CTX_KEY 41 +#define BTRFS_FSCRYPT_CTX_KEY 42 =20 #define BTRFS_ORPHAN_ITEM_KEY 48 /* reserve 2-15 close to the inode for later flexibility */ @@ -1079,6 +1080,13 @@ enum { BTRFS_NR_FILE_EXTENT_TYPES =3D 3, }; =20 +/* + * Currently just the FSCRYPT_SET_CONTEXT_MAX_SIZE, which is larger than t= he + * current extent context size from fscrypt, so this should give us plenty= of + * breathing room for expansion later. + */ +#define BTRFS_MAX_EXTENT_CTX_SIZE 40 + enum btrfs_encryption_type { BTRFS_ENCRYPTION_NONE, BTRFS_ENCRYPTION_FSCRYPT, --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A3559429819 for ; Fri, 6 Feb 2026 18:24:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402292; cv=none; b=MtT9A4t4JOpnKWzxD3QRfo9xPO8ZmTUX8Rd1xYEshNd73+sYuaElwn8oN+NJoeyzE5tX1CKOiKLs5aj1lIaddqUxfISjDO5Tcj/5yEqSNxTdvyq1HuO98NDytZYyd/LnGDuhMkNtCAOgsjNDJDAT7JG2vx27oYOLsWCxpqyVTvU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402292; c=relaxed/simple; bh=rypYO6gORnw4ieKxJ3St2Aeyn2HaW7fwz1NPjO8+wSY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TsTOWqOy626DvLdJa9YLKBwlmcOBn8zZ+e2POd2soit/jkvAUOcdbESGSWJ8tp5eXZKCSPOToAxRaFbqEUV3CRa+b3/EXTi9+Mx2GzJRfPHKpKZqteI8CdbYsDpK3L72GmFQ67nmKdGIofoCVnLlLIpZT1YEGIJIM1PDreP78pc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id E0F083E754; Fri, 6 Feb 2026 18:24:09 +0000 (UTC) Authentication-Results: smtp-out1.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id B51883EA63; Fri, 6 Feb 2026 18:24:09 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id 4AG7K8kxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:09 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 25/43] btrfs: pass through fscrypt_extent_info to the file extent helpers Date: Fri, 6 Feb 2026 19:22:57 +0100 Message-ID: <20260206182336.1397715-26-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO X-Spam-Score: -4.00 X-Rspamd-Queue-Id: E0F083E754 X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Server: rspamd2.dmz-prg2.suse.org X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik Now that we have the fscrypt_extnet_info in all of the supporting structures, pass this through and set the file extent encryption bit accordingly from the supporting structures. In subsequent patches code will be added to populate these appropriately. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/a8b9dd312a4504f209e861cca5289a528b3= 0ff95.1706116485.git.josef@toxicpanda.com/ * No changes since. --- fs/btrfs/inode.c | 16 ++++++++++------ fs/btrfs/tree-log.c | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index f81bdb97b212..f449839d6d84 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3018,7 +3018,9 @@ int btrfs_writepage_cow_fixup(struct folio *folio) } =20 static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, - struct btrfs_inode *inode, u64 file_pos, + struct btrfs_inode *inode, + struct fscrypt_extent_info *fscrypt_info, + u64 file_pos, struct btrfs_file_extent_item *stack_fi, const bool update_inode_bytes, u64 qgroup_reserved) @@ -3142,7 +3144,7 @@ static int insert_ordered_extent_file_extent(struct b= trfs_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); - btrfs_set_stack_file_extent_encryption(&stack_fi, BTRFS_ENCRYPTION_NONE); + btrfs_set_stack_file_extent_encryption(&stack_fi, oe->encryption_type); /* Other encoding is reserved and always 0 */ =20 /* @@ -3155,7 +3157,7 @@ static int insert_ordered_extent_file_extent(struct b= trfs_trans_handle *trans, test_bit(BTRFS_ORDERED_ENCODED, &oe->flags) || test_bit(BTRFS_ORDERED_TRUNCATED, &oe->flags); =20 - return insert_reserved_file_extent(trans, oe->inode, + return insert_reserved_file_extent(trans, oe->inode, oe->fscrypt_info, oe->file_offset, &stack_fi, update_inode_bytes, oe->qgroup_rsv); } @@ -9213,6 +9215,7 @@ static struct btrfs_trans_handle *insert_prealloc_fil= e_extent( struct btrfs_trans_handle *trans_in, struct btrfs_inode *inode, struct btrfs_key *ins, + struct fscrypt_extent_info *fscrypt_info, u64 file_offset) { struct btrfs_file_extent_item stack_fi; @@ -9232,7 +9235,8 @@ static struct btrfs_trans_handle *insert_prealloc_fil= e_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); - btrfs_set_stack_file_extent_encryption(&stack_fi, BTRFS_ENCRYPTION_NONE); + btrfs_set_stack_file_extent_encryption(&stack_fi, fscrypt_info? BTRFS_ENC= RYPTION_FSCRYPT: + BTRFS_ENCRYPTION_NONE); /* Other encoding is reserved and always 0 */ =20 ret =3D btrfs_qgroup_release_data(inode, file_offset, len, &qgroup_releas= ed); @@ -9240,7 +9244,7 @@ static struct btrfs_trans_handle *insert_prealloc_fil= e_extent( return ERR_PTR(ret); =20 if (trans) { - ret =3D insert_reserved_file_extent(trans, inode, + ret =3D insert_reserved_file_extent(trans, inode, fscrypt_info, file_offset, &stack_fi, true, qgroup_released); if (ret) @@ -9333,7 +9337,7 @@ static int __btrfs_prealloc_file_range(struct inode *= inode, int mode, =20 last_alloc =3D ins.offset; trans =3D insert_prealloc_file_extent(trans, BTRFS_I(inode), - &ins, cur_offset); + &ins, NULL, cur_offset); /* * Now that we inserted the prealloc extent we can finally * decrement the number of reservations in the block group. diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 118dd1888996..43b1470ebf71 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -5123,7 +5123,7 @@ static int log_one_extent(struct btrfs_trans_handle *= trans, u64 block_start =3D btrfs_extent_map_block_start(em); u64 block_len; int ret; - u8 encryption =3D BTRFS_ENCRYPTION_NONE; + u8 encryption =3D btrfs_extent_map_encryption(em); =20 btrfs_set_stack_file_extent_generation(&fi, trans->transid); if (em->flags & EXTENT_FLAG_PREALLOC) --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4660E42EEB8 for ; Fri, 6 Feb 2026 18:24:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402298; cv=none; b=RvTtuqT3Qms/O/0XQ8ew5nG18j0AbAQt8a6BcvGzYNPtEtyuIin08wqmgWfH3vs1BQvGXKHOdddlrsZiNLVFdRK3U0oXrRo0wiP0wd8tWzXu+e7c8ZTTuBrvoCbryZB2bC3X2INrk5sU6abbudjq5I07qqm+UFq/wPNwlI7xs4w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402298; c=relaxed/simple; bh=FfvPlXAyWoUxGB8f+0UO9dIt1mwkYNum1bSIDzve2Ic=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Czr1CmHIYM3JoZECTEtblp2Mdk61Hl4+QGYeCar6BKfvS6lx6dJYNuUNxbu27hOsFwU8tcLpdidRo52r6v0crAuEn0Utqd+ZlWnY+DH/ksnhyvpOT1WJjGGDmCwhHHs8PYoT2iLJCy4qXslOzWHD4uWFg302vrOzM3eD5fLPH2g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id 043805BD2E; Fri, 6 Feb 2026 18:24:11 +0000 (UTC) Authentication-Results: smtp-out2.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id CBA723EA63; Fri, 6 Feb 2026 18:24:10 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id wGH9MMoxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:10 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 26/43] btrfs: implement the fscrypt extent encryption hooks Date: Fri, 6 Feb 2026 19:22:58 +0100 Message-ID: <20260206182336.1397715-27-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO X-Spam-Score: -4.00 X-Rspamd-Queue-Id: 043805BD2E X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Server: rspamd2.dmz-prg2.suse.org X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik This patch implements the necessary hooks from fscrypt to support per-extent encryption. There's two main entry points btrfs_fscrypt_load_extent_info btrfs_fscrypt_save_extent_info btrfs_fscrypt_load_extent_info gets called when we create the extent maps from the file extent item at btrfs_get_extent() time. We read the extent context, and pass it into fscrypt to create the appropriate fscrypt_extent_info structure. This is then used on the bio's to make sure the encryption is done properly. btrfs_fscrypt_save_extent_info is used to generate the fscrypt context from fscrypt and save it into the tree item when we create a new file extent item. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/30eaad31964c88c3497a0c5bc8f2c727c1d= c763a.1706116485.git.josef@toxicpanda.com/ * Also significantly reworked due to the changes in previous commit [24/43= ]. --- fs/btrfs/ctree.h | 3 ++ fs/btrfs/defrag.c | 6 ++++ fs/btrfs/file.c | 11 ++++++ fs/btrfs/fscrypt.c | 84 +++++++++++++++++++++++++++++++++++++++++++++ fs/btrfs/fscrypt.h | 29 ++++++++++++++++ fs/btrfs/inode.c | 36 +++++++++++++++++++ fs/btrfs/reflink.c | 43 ++++++++++++++++++++++- fs/btrfs/tree-log.c | 19 ++++++++++ 8 files changed, 230 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 6de7ad191e04..89d3c3137786 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -400,6 +400,9 @@ struct btrfs_replace_extent_info { u64 file_offset; /* Pointer to a file extent item of type regular or prealloc. */ char *extent_buf; + /* The fscrypt_extent_info for a new extent. */ + u8 *fscrypt_ctx; + u32 fscrypt_context_size; /* * Set to true when attempting to replace a file range with a new extent * described by this structure, set to false when attempting to clone an diff --git a/fs/btrfs/defrag.c b/fs/btrfs/defrag.c index ecf05cd64696..f64c0502cef9 100644 --- a/fs/btrfs/defrag.c +++ b/fs/btrfs/defrag.c @@ -16,6 +16,7 @@ #include "file-item.h" #include "super.h" #include "compression.h" +#include "fscrypt.h" =20 static struct kmem_cache *btrfs_inode_defrag_cachep; =20 @@ -720,6 +721,11 @@ static struct extent_map *defrag_get_extent(struct btr= fs_inode *inode, if (ret > 0) goto not_found; } + btrfs_release_path(&path); + + ret =3D btrfs_fscrypt_load_extent_info(inode, &path, &key, em); + if (ret) + goto err; return em; =20 not_found: diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 639462164d08..89cd4f49e84b 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -37,6 +37,7 @@ #include "file.h" #include "super.h" #include "print-tree.h" +#include "fscrypt.h" =20 /* * Unlock folio after btrfs_file_write() is done with it. @@ -2401,6 +2402,16 @@ static int btrfs_insert_replace_extent(struct btrfs_= trans_handle *trans, if (extent_info->is_new_extent) btrfs_set_file_extent_generation(leaf, extent, trans->transid); btrfs_release_path(path); + if (extent_info->fscrypt_context_size) { + key.type =3D BTRFS_FSCRYPT_CTX_KEY; + ret =3D btrfs_insert_empty_item(trans, root, path, &key, + extent_info->fscrypt_context_size); + if (ret) + return ret; + btrfs_fscrypt_save_extent_info(path, + extent_info->fscrypt_ctx, + extent_info->fscrypt_context_size); + } =20 ret =3D btrfs_inode_set_file_extent_range(inode, extent_info->file_offset, replace_len); diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index 608f8797ea6f..26060f3e50de 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -212,9 +212,93 @@ static struct block_device **btrfs_fscrypt_get_devices= (struct super_block *sb, return devs; } =20 +int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode, + struct btrfs_path *path, + struct btrfs_key *key, + struct extent_map *em) +{ + struct extent_buffer *leaf; + int slot; + unsigned long offset; + u8 ctx[BTRFS_MAX_EXTENT_CTX_SIZE]; + unsigned long size; + struct fscrypt_extent_info *info; + unsigned long nofs_flag; + int ret; + + if (em->disk_bytenr =3D=3D EXTENT_MAP_HOLE) + return 0; + if (btrfs_extent_map_encryption(em) !=3D BTRFS_ENCRYPTION_FSCRYPT) + return 0; + + key->type =3D BTRFS_FSCRYPT_CTX_KEY; + ret =3D btrfs_search_slot(NULL, inode->root, key, path, 0, 0); + leaf =3D path->nodes[0]; + slot =3D path->slots[0]; + if (ret) { + btrfs_err(leaf->fs_info, + "missing or error searching encryption context item in leaf: root=3D%llu = block=3D%llu slot=3D%d ino=3D%llu file_offset=3D%llu, err %i", + btrfs_header_owner(leaf), btrfs_header_bytenr(leaf), slot, + key->objectid, key->offset, ret); + btrfs_release_path(path); + return ret; + } + + size =3D btrfs_item_size(leaf, slot); + if (size > FSCRYPT_SET_CONTEXT_MAX_SIZE) { + btrfs_err(leaf->fs_info, + "unexpected or corrupted encryption context size in leaf: root=3D%llu blo= ck=3D%llu slot=3D%d ino=3D%llu file_offset=3D%llu, size %lu (too big)", + btrfs_header_owner(leaf), btrfs_header_bytenr(leaf), slot, + key->objectid, key->offset, size); + btrfs_release_path(path); + return -EIO; + } + + offset =3D btrfs_item_ptr_offset(leaf, slot), + read_extent_buffer(leaf, ctx, offset, size); + btrfs_release_path(path); + + nofs_flag =3D memalloc_nofs_save(); + info =3D fscrypt_load_extent_info(&inode->vfs_inode, ctx, size); + memalloc_nofs_restore(nofs_flag); + if (IS_ERR(info)) + return PTR_ERR(info); + em->fscrypt_info =3D info; + return 0; +} + +void btrfs_fscrypt_save_extent_info(struct btrfs_path *path, u8 *ctx, unsi= gned long size) +{ + struct extent_buffer *leaf =3D path->nodes[0]; + unsigned long offset =3D btrfs_item_ptr_offset(leaf, path->slots[0]); + + ASSERT(size <=3D FSCRYPT_SET_CONTEXT_MAX_SIZE); + + write_extent_buffer(leaf, ctx, offset, size); + btrfs_release_path(path); +} + +ssize_t btrfs_fscrypt_context_for_new_extent(struct btrfs_inode *inode, + struct fscrypt_extent_info *info, + u8 *ctx) +{ + ssize_t ret; + + if (!info) + return 0; + + ret =3D fscrypt_context_for_new_extent(&inode->vfs_inode, info, ctx); + if (ret < 0) { + btrfs_err_rl(inode->root->fs_info, "invalid encrypt context"); + return ret; + } + return ret; +} + const struct fscrypt_operations btrfs_fscrypt_ops =3D { .inode_info_offs =3D (int)offsetof(struct btrfs_inode, i_crypt_info) - (int)offsetof(struct btrfs_inode, vfs_inode), + .has_per_extent_encryption =3D 1, .get_context =3D btrfs_fscrypt_get_context, .set_context =3D btrfs_fscrypt_set_context, .empty_dir =3D btrfs_fscrypt_empty_dir, diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h index c5cdc27f943c..68eab4606935 100644 --- a/fs/btrfs/fscrypt.h +++ b/fs/btrfs/fscrypt.h @@ -16,8 +16,27 @@ int btrfs_fscrypt_get_disk_name(struct extent_buffer *le= af, bool btrfs_fscrypt_match_name(struct fscrypt_name *fname, struct extent_buffer *leaf, unsigned long de_name, u32 de_name_len); +int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode, + struct btrfs_path *path, + struct btrfs_key *key, + struct extent_map *em); +void btrfs_fscrypt_save_extent_info(struct btrfs_path *path, u8 *ctx, unsi= gned long size); +ssize_t btrfs_fscrypt_context_for_new_extent(struct btrfs_inode *inode, + struct fscrypt_extent_info *info, + u8 *ctx); =20 #else +static inline void btrfs_fscrypt_save_extent_info(struct btrfs_path *path, + u8 *ctx, unsigned long size) { } + +static inline int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode, + struct btrfs_path *path, + struct btrfs_key *key, + struct extent_map *em) +{ + return 0; +} + static inline int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf, struct btrfs_dir_item *di, struct fscrypt_str *qstr) @@ -34,6 +53,16 @@ static inline bool btrfs_fscrypt_match_name(struct fscry= pt_name *fname, return false; return !memcmp_extent_buffer(leaf, fname->disk_name.name, de_name, de_nam= e_len); } + +static inline ssize_t btrfs_fscrypt_context_for_new_extent(struct btrfs_in= ode *inode, + struct fscrypt_extent_info *info, + u8 *ctx) +{ + if (!info) + return 0; + return -EINVAL; +} + #endif /* CONFIG_FS_ENCRYPTION */ =20 extern const struct fscrypt_operations btrfs_fscrypt_ops; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index f449839d6d84..15191dffa354 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3036,8 +3036,16 @@ static int insert_reserved_file_extent(struct btrfs_= trans_handle *trans, u64 num_bytes =3D btrfs_stack_file_extent_num_bytes(stack_fi); u64 ram_bytes =3D btrfs_stack_file_extent_ram_bytes(stack_fi); struct btrfs_drop_extents_args drop_args =3D { 0 }; + u8 fscrypt_ctx[FSCRYPT_SET_CONTEXT_MAX_SIZE]; + ssize_t fscrypt_context_size; int ret; =20 + fscrypt_context_size =3D btrfs_fscrypt_context_for_new_extent(inode, + fscrypt_info, + fscrypt_ctx); + if (fscrypt_context_size < 0) + return (int)fscrypt_context_size; + path =3D btrfs_alloc_path(); if (!path) return -ENOMEM; @@ -3078,6 +3086,16 @@ static int insert_reserved_file_extent(struct btrfs_= trans_handle *trans, =20 btrfs_release_path(path); =20 + if (fscrypt_context_size) { + ins.objectid =3D btrfs_ino(inode); + ins.type =3D BTRFS_FSCRYPT_CTX_KEY; + ins.offset =3D file_pos; + ret =3D btrfs_insert_empty_item(trans, root, path, &ins, fscrypt_context= _size); + if (ret) + return ret; + btrfs_fscrypt_save_extent_info(path, fscrypt_ctx, fscrypt_context_size); + } + /* * If we dropped an inline extent here, we know the range where it is * was not marked with the EXTENT_DELALLOC_NEW bit, so we update the @@ -7406,6 +7424,12 @@ struct extent_map *btrfs_get_extent(struct btrfs_ino= de *inode, goto out; } =20 + ret =3D btrfs_fscrypt_load_extent_info(inode, path, &found_key, em); + if (ret > 0) + ret =3D -EIO; + if (ret < 0) + goto out; + write_lock(&em_tree->lock); ret =3D btrfs_add_extent_mapping(inode, &em, start, len); write_unlock(&em_tree->lock); @@ -9219,6 +9243,8 @@ static struct btrfs_trans_handle *insert_prealloc_fil= e_extent( u64 file_offset) { struct btrfs_file_extent_item stack_fi; + u8 fscrypt_ctx[FSCRYPT_SET_CONTEXT_MAX_SIZE]; + ssize_t fscrypt_context_size; struct btrfs_replace_extent_info extent_info; struct btrfs_trans_handle *trans =3D trans_in; struct btrfs_path *path; @@ -9252,6 +9278,14 @@ static struct btrfs_trans_handle *insert_prealloc_fi= le_extent( return trans; } =20 + fscrypt_context_size =3D btrfs_fscrypt_context_for_new_extent(inode, + fscrypt_info, + fscrypt_ctx); + if (fscrypt_context_size < 0) { + ret =3D (int)fscrypt_context_size; + goto free_qgroup; + } + extent_info.disk_offset =3D start; extent_info.disk_len =3D len; extent_info.data_offset =3D 0; @@ -9262,6 +9296,8 @@ static struct btrfs_trans_handle *insert_prealloc_fil= e_extent( extent_info.update_times =3D true; extent_info.qgroup_reserved =3D qgroup_released; extent_info.insertions =3D 0; + extent_info.fscrypt_ctx =3D fscrypt_ctx; + extent_info.fscrypt_context_size =3D fscrypt_context_size; =20 path =3D btrfs_alloc_path(); if (!path) { diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c index 314cb95ba846..8e8d2f3c4c0a 100644 --- a/fs/btrfs/reflink.c +++ b/fs/btrfs/reflink.c @@ -376,7 +376,7 @@ static int btrfs_clone(struct inode *src, struct inode = *inode, struct btrfs_key new_key; u64 disko =3D 0, diskl =3D 0; u64 datao =3D 0, datal =3D 0; - u8 comp; + u8 comp, encryption; u64 drop_start; =20 /* Note the key will change type as we walk through the tree */ @@ -419,6 +419,7 @@ static int btrfs_clone(struct inode *src, struct inode = *inode, extent =3D btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item); extent_gen =3D btrfs_file_extent_generation(leaf, extent); + encryption =3D btrfs_file_extent_encryption(leaf, extent); comp =3D btrfs_file_extent_compression(leaf, extent); type =3D btrfs_file_extent_type(leaf, extent); if (type =3D=3D BTRFS_FILE_EXTENT_REG || @@ -478,6 +479,7 @@ static int btrfs_clone(struct inode *src, struct inode = *inode, if (type =3D=3D BTRFS_FILE_EXTENT_REG || type =3D=3D BTRFS_FILE_EXTENT_PREALLOC) { struct btrfs_replace_extent_info clone_info; + u8 fscrypt_ctx[FSCRYPT_SET_CONTEXT_MAX_SIZE]; =20 /* * a | --- range to clone ---| b @@ -494,6 +496,43 @@ static int btrfs_clone(struct inode *src, struct inode= *inode, datal -=3D off - key.offset; } =20 + if (encryption =3D=3D BTRFS_ENCRYPTION_FSCRYPT) { + unsigned long offset; + + key.type =3D BTRFS_FSCRYPT_CTX_KEY; + ret =3D btrfs_search_slot(NULL, BTRFS_I(src)->root, &key, path, 0, 0); + if (ret < 0) + goto out; + leaf =3D path->nodes[0]; + slot =3D path->slots[0]; + if (ret) { + btrfs_err(leaf->fs_info, + "missing or error searching encryption context item in leaf: root=3D%llu = block=3D%llu slot=3D%d ino=3D%llu file_offset=3D%llu, err %i", + btrfs_header_owner(leaf), + btrfs_header_bytenr(leaf), slot, + key.objectid, key.offset, ret); + goto out; + } + + size =3D btrfs_item_size(leaf, slot); + if (size > FSCRYPT_SET_CONTEXT_MAX_SIZE) { + btrfs_err(leaf->fs_info, + "unexpected or corrupted encryption context size in leaf: root=3D%llu blo= ck=3D%llu slot=3D%d ino=3D%llu file_offset=3D%llu, size %u (too big)", + btrfs_header_owner(leaf), + btrfs_header_bytenr(leaf), slot, + key.objectid, key.offset, size); + ret =3D -EIO; + goto out; + } + + offset =3D btrfs_item_ptr_offset(leaf, slot), + read_extent_buffer(leaf, fscrypt_ctx, offset, size); + btrfs_release_path(path); + key.type =3D BTRFS_EXTENT_DATA_KEY; + } else { + size =3D 0; + } + clone_info.disk_offset =3D disko; clone_info.disk_len =3D diskl; clone_info.data_offset =3D datao; @@ -502,6 +541,8 @@ static int btrfs_clone(struct inode *src, struct inode = *inode, clone_info.extent_buf =3D buf; clone_info.is_new_extent =3D false; clone_info.update_times =3D !no_time_update; + clone_info.fscrypt_ctx =3D fscrypt_ctx; + clone_info.fscrypt_context_size =3D size; ret =3D btrfs_replace_file_extents(BTRFS_I(inode), path, drop_start, new_key.offset + datal - 1, &clone_info, &trans); diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 43b1470ebf71..0d01e31a4592 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -30,6 +30,7 @@ #include "print-tree.h" #include "tree-checker.h" #include "delayed-inode.h" +#include "fscrypt.h" =20 #define MAX_CONFLICT_INODES 10 =20 @@ -5118,6 +5119,8 @@ static int log_one_extent(struct btrfs_trans_handle *= trans, struct btrfs_file_extent_item fi =3D { 0 }; struct extent_buffer *leaf; struct btrfs_key key; + u8 fscrypt_ctx[FSCRYPT_SET_CONTEXT_MAX_SIZE]; + ssize_t fscrypt_context_size; enum btrfs_compression_type compress_type; u64 extent_offset =3D em->offset; u64 block_start =3D btrfs_extent_map_block_start(em); @@ -5125,6 +5128,12 @@ static int log_one_extent(struct btrfs_trans_handle = *trans, int ret; u8 encryption =3D btrfs_extent_map_encryption(em); =20 + fscrypt_context_size =3D btrfs_fscrypt_context_for_new_extent(inode, + em->fscrypt_info, + fscrypt_ctx); + if (fscrypt_context_size < 0) + return (int)fscrypt_context_size; + btrfs_set_stack_file_extent_generation(&fi, trans->transid); if (em->flags & EXTENT_FLAG_PREALLOC) btrfs_set_stack_file_extent_type(&fi, BTRFS_FILE_EXTENT_PREALLOC); @@ -5188,6 +5197,16 @@ static int log_one_extent(struct btrfs_trans_handle = *trans, =20 btrfs_release_path(path); =20 + if (fscrypt_context_size) { + key.objectid =3D btrfs_ino(inode); + key.type =3D BTRFS_FSCRYPT_CTX_KEY; + key.offset =3D em->start; + ret =3D btrfs_insert_empty_item(trans, log, path, &key, fscrypt_context_= size); + if (ret) + return ret; + btrfs_fscrypt_save_extent_info(path, fscrypt_ctx, fscrypt_context_size); + } + return ret; } =20 --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 63AD142E011 for ; Fri, 6 Feb 2026 18:24:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402297; cv=none; b=dP5OmH0eF8jd2Sk+0VGYze/34Fuzotn55jD+BxXHC2g8Hfpa+TSutGWlKWu3NccZ5+RKz/RP8KCJ/OnuVi9++ZH5q7N4GjCcazNzdqqnbbqSHmD5TvwJDq7cfPujUKlM8Pa3hlVvq3jvcUk1SlX1+y1zOPE0evPt6fgfmgkaz+s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402297; c=relaxed/simple; bh=1N45I4EjeGu3+jI4xIHB8eeIS3TKCNO30iA+sKwN9Eg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=CiV4A7aH9VYONWUdhmSKIW5bxxu85mHRQHmer3MB1qklTn6Rs5Yh8diug0p0hImyCtrQa2RN99ugUsB/cAG2G2T+Ri6vr/4joS2MNByOrTg3BjRcuDouF7NJeH1298os3+5haPUffvD3AeKoaFSRb5RMUUCU7f5OJm9lEvwu4iI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 6EAF63E743; Fri, 6 Feb 2026 18:24:11 +0000 (UTC) Authentication-Results: smtp-out1.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 4390A3EA63; Fri, 6 Feb 2026 18:24:11 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id cJHfD8sxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:11 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 27/43] btrfs: setup fscrypt_extent_info for new extents Date: Fri, 6 Feb 2026 19:22:59 +0100 Message-ID: <20260206182336.1397715-28-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO X-Spam-Score: -4.00 X-Rspamd-Queue-Id: 6EAF63E743 X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Server: rspamd2.dmz-prg2.suse.org X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik New extents for encrypted inodes must have a fscrypt_extent_info, which has the necessary keys and does all the registration at the block layer for them. This is passed through all of the infrastructure we've previously added to make sure the context gets saved properly with the file extents. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/d8ab016d25f70c9365f508af1d8e0b9ab7c= 09d76.1706116485.git.josef@toxicpanda.com/ * No changes since. --- fs/btrfs/inode.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 15191dffa354..de1989edffc1 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7629,6 +7629,16 @@ struct extent_map *btrfs_create_io_em(struct btrfs_i= node *inode, u64 start, if (file_extent->fscrypt_info) { btrfs_extent_map_set_encryption(em, BTRFS_ENCRYPTION_FSCRYPT); em->fscrypt_info =3D fscrypt_get_extent_info(file_extent->fscrypt_info); + } else if (IS_ENCRYPTED(&inode->vfs_inode)) { + struct fscrypt_extent_info *fscrypt_info; + + btrfs_extent_map_set_encryption(em, BTRFS_ENCRYPTION_FSCRYPT); + fscrypt_info =3D fscrypt_prepare_new_extent(&inode->vfs_inode); + if (IS_ERR(fscrypt_info)) { + btrfs_free_extent_map(em); + return ERR_CAST(fscrypt_info); + } + em->fscrypt_info =3D fscrypt_info; } else { btrfs_extent_map_set_encryption(em, BTRFS_ENCRYPTION_NONE); } @@ -9348,6 +9358,9 @@ static int __btrfs_prealloc_file_range(struct inode *= inode, int mode, if (trans) own_trans =3D false; while (num_bytes > 0) { + struct fscrypt_extent_info *fscrypt_info =3D NULL; + int encryption_type =3D BTRFS_ENCRYPTION_NONE; + cur_bytes =3D min_t(u64, num_bytes, SZ_256M); cur_bytes =3D max(cur_bytes, min_size); /* @@ -9362,6 +9375,17 @@ static int __btrfs_prealloc_file_range(struct inode = *inode, int mode, if (ret) break; =20 + if (IS_ENCRYPTED(inode)) { + fscrypt_info =3D fscrypt_prepare_new_extent(inode); + if (IS_ERR(fscrypt_info)) { + btrfs_dec_block_group_reservations(fs_info, ins.objectid); + btrfs_free_reserved_extent(fs_info, ins.objectid, ins.offset, 0); + ret =3D PTR_ERR(fscrypt_info); + break; + } + encryption_type =3D BTRFS_ENCRYPTION_FSCRYPT; + } + /* * We've reserved this space, and thus converted it from * ->bytes_may_use to ->bytes_reserved. Any error that happens @@ -9373,7 +9397,7 @@ static int __btrfs_prealloc_file_range(struct inode *= inode, int mode, =20 last_alloc =3D ins.offset; trans =3D insert_prealloc_file_extent(trans, BTRFS_I(inode), - &ins, NULL, cur_offset); + &ins, fscrypt_info, cur_offset); /* * Now that we inserted the prealloc extent we can finally * decrement the number of reservations in the block group. @@ -9383,6 +9407,7 @@ static int __btrfs_prealloc_file_range(struct inode *= inode, int mode, btrfs_dec_block_group_reservations(fs_info, ins.objectid); if (IS_ERR(trans)) { ret =3D PTR_ERR(trans); + fscrypt_put_extent_info(fscrypt_info); btrfs_free_reserved_extent(fs_info, ins.objectid, ins.offset, false); break; @@ -9390,6 +9415,7 @@ static int __btrfs_prealloc_file_range(struct inode *= inode, int mode, =20 em =3D btrfs_alloc_extent_map(); if (!em) { + fscrypt_put_extent_info(fscrypt_info); btrfs_drop_extent_map_range(BTRFS_I(inode), cur_offset, cur_offset + ins.offset - 1, false); btrfs_set_inode_full_sync(BTRFS_I(inode)); @@ -9404,6 +9430,8 @@ static int __btrfs_prealloc_file_range(struct inode *= inode, int mode, em->ram_bytes =3D ins.offset; em->flags |=3D EXTENT_FLAG_PREALLOC; em->generation =3D trans->transid; + em->fscrypt_info =3D fscrypt_info; + btrfs_extent_map_set_encryption(em, encryption_type); =20 ret =3D btrfs_replace_extent_map_range(BTRFS_I(inode), em, true); btrfs_free_extent_map(em); --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4F20543C059 for ; Fri, 6 Feb 2026 18:25:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402303; cv=none; b=TmOy3Qt16qVPR4n02jPndgDW3TIQausJFnKS3v0HIJ+H5kIB4bcJTg6aZyIutelglvTbHiPZQhD8UzKSWVlxvgAfKIOc4pON3Q1J/BnpBFi9p/sEcPm8hdQFZYxq/NBc5OxZQfcZloGkcSaxgs0FdBmMr/y/+LMtAH+9Eo1pbHM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402303; c=relaxed/simple; bh=blFKUpi3xvJKZR3+nYjzSJqvyUo+6IFCcloG9ZycAU0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iJ4iubZ2V9fReOIMDle1WfG4rowHj8gk7agQ+bFYcV6t4Hmivkh6sOjMxse+6GzXT6q1o1ffbHkZ9ZlHL/wPRL0+WlDqPXQMKwWKy0HHHLtQP1KzVuCWd2SdE/PZFZMosqKOvY0EM7NAbHXgyeccdtzfjP88Ew9BTRSZWiTC0uk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id 1FD0F5BD2C; Fri, 6 Feb 2026 18:24:12 +0000 (UTC) Authentication-Results: smtp-out2.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id E74633EA63; Fri, 6 Feb 2026 18:24:11 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id WBf+N8sxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:11 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 28/43] btrfs: populate ordered_extent with the orig offset Date: Fri, 6 Feb 2026 19:23:00 +0100 Message-ID: <20260206182336.1397715-29-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO X-Spam-Score: -4.00 X-Rspamd-Queue-Id: 1FD0F5BD2C X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Server: rspamd2.dmz-prg2.suse.org X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik For extent encryption we have to use a logical block nr as input for the IV. For btrfs we're using the offset into the extent we're operating on. For most ordered extents this is the same as the file_offset, however for prealloc and NOCOW we have to use the original offset. Add this as an argument and plumb it through everywhere, this will be used when setting up the bio. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/fe06053fe2973c424dd539fecfee8cc171b= dd22d.1706116485.git.josef@toxicpanda.com/ * Splitted the dio-related hunks from inode.c to direct-io.c as upstream refactored in the meantime. * Open-code orig_start to start - offset. --- fs/btrfs/direct-io.c | 1 + fs/btrfs/inode.c | 5 +++++ fs/btrfs/ordered-data.c | 21 ++++++++++++++++++--- fs/btrfs/ordered-data.h | 7 +++++++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c index d3789109ca85..2d89ac05b1b3 100644 --- a/fs/btrfs/direct-io.c +++ b/fs/btrfs/direct-io.c @@ -155,6 +155,7 @@ static struct extent_map *btrfs_create_dio_extent(struc= t btrfs_inode *inode, file_extent->fscrypt_info =3D em->fscrypt_info; } =20 + file_extent->orig_offset =3D start - file_extent->offset; ordered =3D btrfs_alloc_ordered_extent(inode, start, file_extent, (1U << type) | (1U << BTRFS_ORDERED_DIRECT)); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index de1989edffc1..b28e1b7497b8 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1192,6 +1192,7 @@ static void submit_one_async_extent(struct async_chun= k *async_chunk, } =20 file_extent.fscrypt_info =3D em->fscrypt_info; + file_extent.orig_offset =3D start; ordered =3D btrfs_alloc_ordered_extent(inode, start, &file_extent, 1U << BTRFS_ORDERED_COMPRESSED); btrfs_free_extent_map(em); @@ -1336,6 +1337,7 @@ static int cow_one_range(struct btrfs_inode *inode, s= truct folio *locked_folio, } =20 file_extent.fscrypt_info =3D em->fscrypt_info; + file_extent.orig_offset =3D file_offset; ordered =3D btrfs_alloc_ordered_extent(inode, file_offset, &file_extent, 1U << BTRFS_ORDERED_REGULAR); btrfs_free_extent_map(em); @@ -2281,6 +2283,8 @@ static noinline int run_delalloc_nocow(struct btrfs_i= node *inode, cow_start =3D (u64)-1; } =20 + nocow_args.file_extent.orig_offset =3D + found_key.offset - nocow_args.file_extent.offset; ret =3D nocow_one_range(inode, locked_folio, &cached_state, &nocow_args, cur_offset, extent_type =3D=3D BTRFS_FILE_EXTENT_PREALLOC); @@ -10218,6 +10222,7 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, = struct iov_iter *from, } =20 file_extent.fscrypt_info =3D em->fscrypt_info; + file_extent.orig_offset =3D start - encoded->unencoded_offset; ordered =3D btrfs_alloc_ordered_extent(inode, start, &file_extent, (1U << BTRFS_ORDERED_ENCODED) | (1U << BTRFS_ORDERED_COMPRESSED)); diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 1b4d20a2f983..bb5477ce58db 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -146,7 +146,8 @@ static inline struct rb_node *ordered_tree_search(struc= t btrfs_inode *inode, } =20 static struct btrfs_ordered_extent *alloc_ordered_extent( - struct btrfs_inode *inode, u64 file_offset, u64 num_bytes, + struct btrfs_inode *inode, + u64 file_offset, u64 orig_offset, u64 num_bytes, u64 ram_bytes, u64 disk_bytenr, u64 disk_num_bytes, u64 offset, unsigned long flags, int compress_type, struct fscrypt_extent_info *fscrypt_info) @@ -180,6 +181,7 @@ static struct btrfs_ordered_extent *alloc_ordered_exten= t( } =20 entry->file_offset =3D file_offset; + entry->orig_offset =3D orig_offset; entry->num_bytes =3D num_bytes; entry->ram_bytes =3D ram_bytes; entry->disk_bytenr =3D disk_bytenr; @@ -268,6 +270,7 @@ static void insert_ordered_extent(struct btrfs_ordered_= extent *entry) * * @inode: Inode that this extent is for. * @file_offset: Logical offset in file where the extent starts. + * @orig_offset: Logical offset of the original extent (PREALLOC or NO= COW) * @num_bytes: Logical length of extent in file. * @ram_bytes: Full length of unencoded data. * @disk_bytenr: Offset of extent on disk. @@ -305,6 +308,7 @@ struct btrfs_ordered_extent *btrfs_alloc_ordered_extent( */ if (flags & ((1U << BTRFS_ORDERED_NOCOW) | (1U << BTRFS_ORDERED_PREALLOC)= )) entry =3D alloc_ordered_extent(inode, file_offset, + file_extent->orig_offset, file_extent->num_bytes, file_extent->num_bytes, file_extent->disk_bytenr + file_extent->offset, @@ -313,6 +317,7 @@ struct btrfs_ordered_extent *btrfs_alloc_ordered_extent( file_extent->fscrypt_info); else entry =3D alloc_ordered_extent(inode, file_offset, + file_extent->orig_offset, file_extent->num_bytes, file_extent->ram_bytes, file_extent->disk_bytenr, @@ -1277,8 +1282,8 @@ struct btrfs_ordered_extent *btrfs_split_ordered_exte= nt( if (WARN_ON_ONCE(ordered->disk_num_bytes !=3D ordered->num_bytes)) return ERR_PTR(-EINVAL); =20 - new =3D alloc_ordered_extent(inode, file_offset, len, len, disk_bytenr, l= en, 0, - flags, ordered->compress_type, ordered->fscrypt_info); + new =3D alloc_ordered_extent(inode, file_offset, ordered->orig_offset, le= n, len, disk_bytenr, + len, 0, flags, ordered->compress_type, ordered->fscrypt_info); if (IS_ERR(new)) return new; =20 @@ -1315,6 +1320,16 @@ struct btrfs_ordered_extent *btrfs_split_ordered_ext= ent( ordered->disk_num_bytes -=3D len; ordered->ram_bytes -=3D len; =20 + /* + * ->orig_offset is the original offset of the original extent, which + * for PREALLOC or NOCOW stays the same, but if we're a regular extent + * that means this is a new extent and thus ->orig_offset must equal + * ->file_offset. This is only important for encryption as we only use + * it for setting the offset for the bio encryption context. + */ + if (test_bit(BTRFS_ORDERED_REGULAR, &ordered->flags)) + ordered->orig_offset =3D ordered->file_offset; + if (test_bit(BTRFS_ORDERED_IO_DONE, &ordered->flags)) { ASSERT(ordered->bytes_left =3D=3D 0); new->bytes_left =3D 0; diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h index 51c795865fe6..58cc1713eb4d 100644 --- a/fs/btrfs/ordered-data.h +++ b/fs/btrfs/ordered-data.h @@ -99,6 +99,12 @@ struct btrfs_ordered_extent { /* logical offset in the file */ u64 file_offset; =20 + /* + * The original logical offset of the extent, this is for NOCOW and + * PREALLOC extents, otherwise it'll be the same as file_offset. + */ + u64 orig_offset; + /* * These fields directly correspond to the same fields in * btrfs_file_extent_item. @@ -188,6 +194,7 @@ struct btrfs_file_extent { u64 num_bytes; u64 ram_bytes; u64 offset; + u64 orig_offset; struct fscrypt_extent_info *fscrypt_info; u8 compression; }; --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 770FF436363 for ; Fri, 6 Feb 2026 18:25:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402301; cv=none; b=OjhN40umMyb1qj4CkmJyFaSf9GcgiYeIl6Yvi9EH7xxlxu03AbyOW6CipQliS6bJKIMrqh3Qgqh2R5u5AIFMlBKXcQBwSw4rgSo5wCb+z04smFKvzHzwRwahBN9mHMgqCLKXjHcEwRIRuYu9beAk8rQFhpuK+ifOlhjCh2vCA+w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402301; c=relaxed/simple; bh=qffIPQg1d8ufVNyNSiBteACKOtofx3HUKppGtoXGvFg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=fW6IGMRqbqxy9VfopR/TSGAjqbJCVORqXTlv+4nhRsHkqCELgyT5KS5fn/CnRhTBHMM5VJxQ8aGbVYoSPwSd4BrEoEKE134fJAP/lx6QWwHRiYBG7aH0ijk5in7ychlJmk5tEsfL1TmkJ25F6TNv/0gWReJWyQywFmoHzqy7gkM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id E7D913E759; Fri, 6 Feb 2026 18:24:12 +0000 (UTC) Authentication-Results: smtp-out1.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id BAEC33EA63; Fri, 6 Feb 2026 18:24:12 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id kHonLcwxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:12 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 29/43] btrfs: set the bio fscrypt context when applicable Date: Fri, 6 Feb 2026 19:23:01 +0100 Message-ID: <20260206182336.1397715-30-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO X-Spam-Score: -4.00 X-Rspamd-Queue-Id: E7D913E759 X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Server: rspamd2.dmz-prg2.suse.org X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik Now that we have the fscrypt_info plumbed through everywhere, add the code to setup the bio encryption context from the extent context. We use the per-extent fscrypt_extent_info for encryption/decryption. We use the offset into the extent as the lblk for fscrypt. So the start of the extent has the lblk of 0, 4k into the extent has the lblk of 4k, etc. This is done to allow things like relocation to continue to work properly. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/9ca883acc746b5aba980d1d19317168a749= 1aba6.1706116485.git.josef@toxicpanda.com/ * The dio-related changes in inode.c were moved to direct-io.c as upstream refactored in the meantime. * Open-code orig_start to start - offset. * Get the inode from bbio instead of from page mapping as we no longer have the page in btrfs_bio_is_contig(). * Directly use the file_offset passed to btrfs_bio_is_contig(), no need to compute it locally. We even no longer have the page and offset. --- fs/btrfs/compression.c | 4 +++ fs/btrfs/direct-io.c | 10 ++++++ fs/btrfs/extent_io.c | 72 +++++++++++++++++++++++++++++++++++++++++- fs/btrfs/fscrypt.c | 33 +++++++++++++++++++ fs/btrfs/fscrypt.h | 22 +++++++++++++ 5 files changed, 140 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index 4c6298cf01b2..cd7b245bedb8 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -33,6 +33,7 @@ #include "subpage.h" #include "messages.h" #include "super.h" +#include "fscrypt.h" =20 static struct bio_set btrfs_compressed_bioset; =20 @@ -391,6 +392,8 @@ void btrfs_submit_compressed_write(struct btrfs_ordered= _extent *ordered, cb->bbio.ordered =3D ordered; btrfs_add_compressed_bio_folios(cb); =20 + btrfs_set_bio_crypt_ctx_from_extent(&cb->bbio.bio, inode, ordered->fscryp= t_info, 0); + btrfs_submit_bbio(&cb->bbio, 0); } =20 @@ -604,6 +607,7 @@ void btrfs_submit_compressed_read(struct btrfs_bio *bbi= o) cb->orig_bbio =3D bbio; cb->bbio.csum_search_commit_root =3D bbio->csum_search_commit_root; =20 + btrfs_set_bio_crypt_ctx_from_extent(&cb->bbio.bio, inode, em->fscrypt_inf= o, 0); btrfs_free_extent_map(em); =20 cb->nr_folios =3D DIV_ROUND_UP(compressed_len, btrfs_min_folio_size(fs_in= fo)); diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c index 2d89ac05b1b3..d20a5b99bdde 100644 --- a/fs/btrfs/direct-io.c +++ b/fs/btrfs/direct-io.c @@ -12,6 +12,7 @@ #include "volumes.h" #include "bio.h" #include "ordered-data.h" +#include "fscrypt.h" =20 struct btrfs_dio_data { ssize_t submitted; @@ -728,6 +729,8 @@ static void btrfs_dio_submit_io(const struct iomap_iter= *iter, struct bio *bio, struct btrfs_dio_private *dip =3D container_of(bbio, struct btrfs_dio_private, bbio); struct btrfs_dio_data *dio_data =3D iter->private; + struct fscrypt_extent_info *fscrypt_info =3D NULL; + u64 offset =3D 0; =20 btrfs_bio_init(bbio, BTRFS_I(iter->inode), file_offset, btrfs_dio_end_io, bio->bi_private); @@ -747,6 +750,9 @@ static void btrfs_dio_submit_io(const struct iomap_iter= *iter, struct bio *bio, if (iter->flags & IOMAP_WRITE) { int ret; =20 + offset =3D file_offset - dio_data->ordered->orig_offset; + fscrypt_info =3D dio_data->ordered->fscrypt_info; + ret =3D btrfs_extract_ordered_extent(bbio, dio_data->ordered); if (ret) { btrfs_finish_ordered_extent(dio_data->ordered, NULL, @@ -756,8 +762,12 @@ static void btrfs_dio_submit_io(const struct iomap_ite= r *iter, struct bio *bio, iomap_dio_bio_end_io(bio); return; } + } else { + fscrypt_info =3D dio_data->fscrypt_info; + offset =3D file_offset - dio_data->orig_start; } =20 + btrfs_set_bio_crypt_ctx_from_extent(&bbio->bio, bbio->inode, fscrypt_info= , offset); btrfs_submit_bbio(bbio, 0); } =20 diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index a440cc550ece..3273b7e3b4b0 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -34,6 +34,7 @@ #include "dev-replace.h" #include "super.h" #include "transaction.h" +#include "fscrypt.h" =20 static struct kmem_cache *extent_buffer_cache; =20 @@ -149,6 +150,10 @@ struct btrfs_bio_ctrl { * inside the same inode. */ u64 last_em_start; + + /* This is set for reads and we have encryption. */ + struct fscrypt_extent_info *fscrypt_info; + u64 orig_start; }; =20 /* @@ -711,9 +716,28 @@ static int alloc_eb_folio_array(struct extent_buffer *= eb, bool nofail) static bool btrfs_bio_is_contig(struct btrfs_bio_ctrl *bio_ctrl, u64 disk_bytenr, loff_t file_offset) { - struct bio *bio =3D &bio_ctrl->bbio->bio; + struct btrfs_bio *bbio =3D bio_ctrl->bbio; + struct bio *bio =3D &bbio->bio; + struct inode *inode =3D &bbio->inode->vfs_inode; const sector_t sector =3D disk_bytenr >> SECTOR_SHIFT; =20 + if (IS_ENCRYPTED(inode)) { + u64 offset =3D 0; + struct fscrypt_extent_info *fscrypt_info =3D NULL; + + /* bio_ctrl->fscrypt_info is only set in the READ case. */ + if (bio_ctrl->fscrypt_info) { + offset =3D file_offset - bio_ctrl->orig_start; + fscrypt_info =3D bio_ctrl->fscrypt_info; + } else if (bbio->ordered) { + fscrypt_info =3D bbio->ordered->fscrypt_info; + offset =3D file_offset - bbio->ordered->orig_offset; + } + + if (!btrfs_mergeable_encrypted_bio(bio, inode, fscrypt_info, offset)) + return false; + } + if (bio_ctrl->compress_type !=3D BTRFS_COMPRESS_NONE) { /* * For compression, all IO should have its logical bytenr set @@ -736,6 +760,8 @@ static void alloc_new_bio(struct btrfs_inode *inode, { struct btrfs_fs_info *fs_info =3D inode->root->fs_info; struct btrfs_bio *bbio; + struct fscrypt_extent_info *fscrypt_info =3D NULL; + u64 offset =3D 0; =20 bbio =3D btrfs_bio_alloc(BIO_MAX_VECS, bio_ctrl->opf, inode, file_offset, bio_ctrl->end_io_func, NULL); @@ -755,6 +781,8 @@ static void alloc_new_bio(struct btrfs_inode *inode, ordered->file_offset + ordered->disk_num_bytes - file_offset); bbio->ordered =3D ordered; + fscrypt_info =3D ordered->fscrypt_info; + offset =3D file_offset - ordered->orig_offset; } =20 /* @@ -765,7 +793,12 @@ static void alloc_new_bio(struct btrfs_inode *inode, */ bio_set_dev(&bbio->bio, fs_info->fs_devices->latest_dev->bdev); wbc_init_bio(bio_ctrl->wbc, &bbio->bio); + } else { + fscrypt_info =3D bio_ctrl->fscrypt_info; + offset =3D file_offset - bio_ctrl->orig_start; } + + btrfs_set_bio_crypt_ctx_from_extent(&bbio->bio, inode, fscrypt_info, offs= et); } =20 /* @@ -811,6 +844,19 @@ static void submit_extent_folio(struct btrfs_bio_ctrl = *bio_ctrl, len =3D bio_ctrl->len_to_oe_boundary; } =20 + /* + * Encryption has to allocate bounce buffers to encrypt the bio, + * and we need to make sure that it doesn't split the bio so we + * retain all of our special info in the btrfs_bio, so submit + * any bio that gets up to BIO_MAX_VECS worth of segments. + */ + if (IS_ENCRYPTED(&inode->vfs_inode) && + bio_data_dir(&bio_ctrl->bbio->bio) =3D=3D WRITE && + bio_segments(&bio_ctrl->bbio->bio) =3D=3D BIO_MAX_VECS) { + submit_one_bio(bio_ctrl); + continue; + } + if (!bio_add_folio(&bio_ctrl->bbio->bio, folio, len, pg_offset)) { /* bio full: move on to a new one */ submit_one_bio(bio_ctrl); @@ -1031,6 +1077,8 @@ static int btrfs_do_readpage(struct folio *folio, str= uct extent_map **em_cached, u64 block_start; u64 em_gen; =20 + bio_ctrl->fscrypt_info =3D NULL; + ASSERT(IS_ALIGNED(cur, fs_info->sectorsize)); if (cur >=3D last_byte) { folio_zero_range(folio, pg_offset, end - cur + 1); @@ -1120,6 +1168,21 @@ static int btrfs_do_readpage(struct folio *folio, st= ruct extent_map **em_cached, =20 bio_ctrl->last_em_start =3D em->start; =20 + /* + * We use the extent offset for the IV when decrypting the page, + * so we have to set the extent_offset based on the orig_start + * for this extent. Also save the fscrypt_info so the bio ctx + * can be set properly. If this inode isn't encrypted this + * won't do anything. + * + * If we're compressed we'll handle all of this in + * btrfs_submit_compressed_read. + */ + if (compress_type =3D=3D BTRFS_COMPRESS_NONE) { + bio_ctrl->orig_start =3D em->start - em->offset; + bio_ctrl->fscrypt_info =3D fscrypt_get_extent_info(em->fscrypt_info); + } + em_gen =3D em->generation; btrfs_free_extent_map(em); em =3D NULL; @@ -1128,11 +1191,17 @@ static int btrfs_do_readpage(struct folio *folio, s= truct extent_map **em_cached, if (block_start =3D=3D EXTENT_MAP_HOLE) { folio_zero_range(folio, pg_offset, blocksize); end_folio_read(folio, true, cur, blocksize); + + /* This shouldn't be set, but clear it just in case. */ + fscrypt_put_extent_info(bio_ctrl->fscrypt_info); continue; } /* the get_extent function already copied into the folio */ if (block_start =3D=3D EXTENT_MAP_INLINE) { end_folio_read(folio, true, cur, blocksize); + + /* This shouldn't be set, but clear it just in case. */ + fscrypt_put_extent_info(bio_ctrl->fscrypt_info); continue; } =20 @@ -1145,6 +1214,7 @@ static int btrfs_do_readpage(struct folio *folio, str= uct extent_map **em_cached, submit_one_bio(bio_ctrl); submit_extent_folio(bio_ctrl, disk_bytenr, folio, blocksize, pg_offset, em_gen); + fscrypt_put_extent_info(bio_ctrl->fscrypt_info); } return 0; } diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index 26060f3e50de..f4d6979a581b 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -295,6 +295,39 @@ ssize_t btrfs_fscrypt_context_for_new_extent(struct bt= rfs_inode *inode, return ret; } =20 +void btrfs_set_bio_crypt_ctx_from_extent(struct bio *bio, + struct btrfs_inode *inode, + struct fscrypt_extent_info *fi, + u64 logical_offset) +{ + if (!fi) + return; + + /* + * fscrypt uses bytes >> s_blocksize_bits for the block numbers, so we + * have to adjust everything based on our sectorsize so that the DUN + * calculations are correct. + */ + logical_offset =3D div64_u64(logical_offset, inode->root->fs_info->sector= size); + fscrypt_set_bio_crypt_ctx_from_extent(bio, fi, logical_offset, GFP_NOFS); +} + +bool btrfs_mergeable_encrypted_bio(struct bio *bio, struct inode *inode, + struct fscrypt_extent_info *fi, + u64 logical_offset) +{ + if (!fi) + return true; + + /* + * fscrypt uses bytes >> s_blocksize_bits for the block numbers, so we + * have to adjust everything based on our sectorsize so that the DUN + * calculations are correct. + */ + logical_offset =3D div64_u64(logical_offset, BTRFS_I(inode)->root->fs_inf= o->sectorsize); + return fscrypt_mergeable_extent_bio(bio, fi, logical_offset); +} + const struct fscrypt_operations btrfs_fscrypt_ops =3D { .inode_info_offs =3D (int)offsetof(struct btrfs_inode, i_crypt_info) - (int)offsetof(struct btrfs_inode, vfs_inode), diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h index 68eab4606935..1a2a4ffee383 100644 --- a/fs/btrfs/fscrypt.h +++ b/fs/btrfs/fscrypt.h @@ -24,6 +24,13 @@ void btrfs_fscrypt_save_extent_info(struct btrfs_path *p= ath, u8 *ctx, unsigned l ssize_t btrfs_fscrypt_context_for_new_extent(struct btrfs_inode *inode, struct fscrypt_extent_info *info, u8 *ctx); +void btrfs_set_bio_crypt_ctx_from_extent(struct bio *bio, + struct btrfs_inode *inode, + struct fscrypt_extent_info *fi, + u64 logical_offset); +bool btrfs_mergeable_encrypted_bio(struct bio *bio, struct inode *inode, + struct fscrypt_extent_info *fi, + u64 logical_offset); =20 #else static inline void btrfs_fscrypt_save_extent_info(struct btrfs_path *path, @@ -63,6 +70,21 @@ static inline ssize_t btrfs_fscrypt_context_for_new_exte= nt(struct btrfs_inode *i return -EINVAL; } =20 +static inline void btrfs_set_bio_crypt_ctx_from_extent(struct bio *bio, + struct btrfs_inode *inode, + struct fscrypt_extent_info *fi, + u64 logical_offset) +{ +} + +static inline bool btrfs_mergeable_encrypted_bio(struct bio *bio, + struct inode *inode, + struct fscrypt_extent_info *fi, + u64 logical_offset) +{ + return true; +} + #endif /* CONFIG_FS_ENCRYPTION */ =20 extern const struct fscrypt_operations btrfs_fscrypt_ops; --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A1CAF4418F0 for ; Fri, 6 Feb 2026 18:25:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402306; cv=none; b=putuQITv0QLImQebuSV7Q1/6l4ndp0Aap7VfjX5RwLRwbEqMucmZVafVZ5t9EWbxhdSOlJ0EKFeFiAXpdtox7W0UlpeQH2Nvt97fZOH5CMcID9G6w6UfX9gwl0NLBzkSZBGLvkNFz+Kf9Mj27PmUvCUYky/fXtRoptjsurybx48= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402306; c=relaxed/simple; bh=FBy99Pni9ZqUQJCWFn5hJAPFNmk7nOaxxWnDwn8nPjA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=utABwe9b9GquayTnC+7zNZoPtqNIXmVLrzOoWSHqWccsHJh+N/P+HguLL5j4gTFQeDhFtabggJS4rCnEGCUU55KzKToCUkha9XUuYjlzB6mhWZ2JW1QPA2EnPoMFI7onT3oEmHdt5XwrOzs55stgd3V917hFYc88gsK5KIBbQAs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=dgnhT86q; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=dgnhT86q; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="dgnhT86q"; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="dgnhT86q" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 95C6C3E74C; Fri, 6 Feb 2026 18:24:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402253; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=HiR3m+n+8ALmEnGsv/bKdMO09KbAjSFNZ63G9sqGBN0=; b=dgnhT86qpk1BBbJ6q5dy6D/4ZkkJHYXxV3PLkgBqWX4UmuDUlm2DpPFzNGcax/QzCwibmV dRNlHH5U34MGyGsm4n0aj+soJxXwdxqjki6oMot27IsaBYyIYCYPyKd6FHYVlyyYe8kBiy Xob5nXnLwfMXiMjS1tnVP+R/vR5VYZQ= Authentication-Results: smtp-out1.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402253; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=HiR3m+n+8ALmEnGsv/bKdMO09KbAjSFNZ63G9sqGBN0=; b=dgnhT86qpk1BBbJ6q5dy6D/4ZkkJHYXxV3PLkgBqWX4UmuDUlm2DpPFzNGcax/QzCwibmV dRNlHH5U34MGyGsm4n0aj+soJxXwdxqjki6oMot27IsaBYyIYCYPyKd6FHYVlyyYe8kBiy Xob5nXnLwfMXiMjS1tnVP+R/vR5VYZQ= Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 6B23C3EA63; Fri, 6 Feb 2026 18:24:13 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id +N+uGc0xhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:13 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 30/43] btrfs: add a bio argument to btrfs_csum_one_bio Date: Fri, 6 Feb 2026 19:23:02 +0100 Message-ID: <20260206182336.1397715-31-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spam-Score: -6.80 X-Spamd-Result: default: False [-6.80 / 50.00]; REPLY(-4.00)[]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; DBL_BLOCKED_OPENRESOLVER(0.00)[suse.com:mid,suse.com:email,toxicpanda.com:email]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; TO_DN_SOME(0.00)[]; RCPT_COUNT_TWELVE(0.00)[12]; RCVD_VIA_SMTP_AUTH(0.00)[]; FUZZY_RATELIMITED(0.00)[rspamd.com]; FROM_HAS_DN(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; R_RATELIMIT(0.00)[to_ip_from(RLdafymbgddseoxkryad1wgu8n)]; DKIM_SIGNED(0.00)[suse.com:s=susede1]; RCVD_TLS_ALL(0.00)[] X-Spam-Level: X-Spam-Flag: NO Content-Type: text/plain; charset="utf-8" From: Josef Bacik We only ever needed the bbio in btrfs_csum_one_bio, since that has the bio embedded in it. However with encryption we'll have a different bio with the encrypted data in it, and the original bbio. Update btrfs_csum_one_bio to take the bio we're going to csum as an argument, which will allow us to csum the encrypted bio and stuff the csums into the corresponding bbio to be used later when the IO completes. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/595a2e46dae3d18bceb5dfa60ac3ae23760= fc800.1706116485.git.josef@toxicpanda.com/ * Adapted for async csums. For writes we have to stick with sync csums. Otherwise fscrypt would have to hand us the bounced bio instead of releasing it. Though that is not implemented yet. --- fs/btrfs/bio.c | 4 ++-- fs/btrfs/bio.h | 1 + fs/btrfs/file-item.c | 13 ++++++------- fs/btrfs/file-item.h | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c index 0a69e09bfe28..487dd9267fd7 100644 --- a/fs/btrfs/bio.c +++ b/fs/btrfs/bio.c @@ -592,9 +592,9 @@ static int btrfs_bio_csum(struct btrfs_bio *bbio) if (bbio->bio.bi_opf & REQ_META) return btree_csum_one_bio(bbio); #ifdef CONFIG_BTRFS_EXPERIMENTAL - return btrfs_csum_one_bio(bbio, true); + return btrfs_csum_one_bio(bbio, &bbio->bio, true); #else - return btrfs_csum_one_bio(bbio, false); + return btrfs_csum_one_bio(bbio, &bbio->bio, false); #endif } =20 diff --git a/fs/btrfs/bio.h b/fs/btrfs/bio.h index 303ed6c7103d..43f7544029ac 100644 --- a/fs/btrfs/bio.h +++ b/fs/btrfs/bio.h @@ -59,6 +59,7 @@ struct btrfs_bio { struct btrfs_ordered_sum *sums; struct work_struct csum_work; struct completion csum_done; + struct bio *csum_bio; struct bvec_iter csum_saved_iter; u64 orig_physical; u64 orig_logical; diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index 08dc78295707..ef0b6faf3de0 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -764,11 +764,10 @@ int btrfs_lookup_csums_bitmap(struct btrfs_root *root= , struct btrfs_path *path, return ret; } =20 -static void csum_one_bio(struct btrfs_bio *bbio, struct bvec_iter *src) +static void csum_one_bio(struct btrfs_bio *bbio, struct bio *bio, struct b= vec_iter *src) { struct btrfs_inode *inode =3D bbio->inode; struct btrfs_fs_info *fs_info =3D inode->root->fs_info; - struct bio *bio =3D &bbio->bio; struct btrfs_ordered_sum *sums =3D bbio->sums; struct bvec_iter iter =3D *src; phys_addr_t paddr; @@ -796,19 +795,18 @@ static void csum_one_bio_work(struct work_struct *wor= k) =20 ASSERT(btrfs_op(&bbio->bio) =3D=3D BTRFS_MAP_WRITE); ASSERT(bbio->async_csum =3D=3D true); - csum_one_bio(bbio, &bbio->csum_saved_iter); + csum_one_bio(bbio, bbio->csum_bio, &bbio->csum_saved_iter); complete(&bbio->csum_done); } =20 /* * Calculate checksums of the data contained inside a bio. */ -int btrfs_csum_one_bio(struct btrfs_bio *bbio, bool async) +int btrfs_csum_one_bio(struct btrfs_bio *bbio, struct bio *bio, bool async) { struct btrfs_ordered_extent *ordered =3D bbio->ordered; struct btrfs_inode *inode =3D bbio->inode; struct btrfs_fs_info *fs_info =3D inode->root->fs_info; - struct bio *bio =3D &bbio->bio; struct btrfs_ordered_sum *sums; unsigned nofs_flag; =20 @@ -827,12 +825,13 @@ int btrfs_csum_one_bio(struct btrfs_bio *bbio, bool a= sync) btrfs_add_ordered_sum(ordered, sums); =20 if (!async) { - csum_one_bio(bbio, &bbio->bio.bi_iter); + csum_one_bio(bbio, bio, &bio->bi_iter); return 0; } init_completion(&bbio->csum_done); bbio->async_csum =3D true; - bbio->csum_saved_iter =3D bbio->bio.bi_iter; + bbio->csum_bio =3D bio; + bbio->csum_saved_iter =3D bio->bi_iter; INIT_WORK(&bbio->csum_work, csum_one_bio_work); schedule_work(&bbio->csum_work); return 0; diff --git a/fs/btrfs/file-item.h b/fs/btrfs/file-item.h index 5645c5e3abdb..d16fd2144552 100644 --- a/fs/btrfs/file-item.h +++ b/fs/btrfs/file-item.h @@ -64,7 +64,7 @@ int btrfs_lookup_file_extent(struct btrfs_trans_handle *t= rans, int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_ordered_sum *sums); -int btrfs_csum_one_bio(struct btrfs_bio *bbio, bool async); +int btrfs_csum_one_bio(struct btrfs_bio *bbio, struct bio *bio, bool async= ); int btrfs_alloc_dummy_sum(struct btrfs_bio *bbio); int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end, struct list_head *list, int search_commit, --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A72C744A70A for ; Fri, 6 Feb 2026 18:25:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402311; cv=none; b=DkBMOwPeSSwobHtXNnPIDkFPfiHsT/3J7wvQJ0OCzFDsoWjUmVDy2NGh0FqDA9C8W47XflfYrYUvk2kJ3IYLePJXxWBvmZC6jTHMheyMs0ZDLwg44qN+rgao2pVQsdzBHMZJ3GnR2zVTUJ/aRs3XFUWQTWIWLttiahl2USSXgCs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402311; c=relaxed/simple; bh=01Qr6eo0ySyzsJAE+aRqlzlEGMAUXrRO3vLfBmO34jk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eryBg8zeN/lU00+/fZ7X+vc1f9dnKQJq4ycMxLW23tu/rZhkCcis3uD4uWGPZMh4IHe1muJAXV/LrEk2ik3YdacKHc/V7W9ak1JhQnM0CdKfciJdrzqBWFbm8tye5Tw4sH8mpMpz7VHTJVIfxugBGkCOcjXdvjdmnKkPM9e5Pew= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=dOl9FZg0; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=dOl9FZg0; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="dOl9FZg0"; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="dOl9FZg0" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 3434A3E757; Fri, 6 Feb 2026 18:24:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402254; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=TlRycN/r/hzMtCufsix9uWAe9WmB5Kqoq1qghJz4TLY=; b=dOl9FZg0+Rg6TRLur29O2zhigULec92c2Ovjjr/QRjk/Zpjctzpt2pAqv52mMGFA1PRE3H 2+EQrhMVOazJYnfkZTmglTQPYYDbEzj2a0RVAT5FtlCy+hOyFRMDhRKwXpFF2y2L2oS5On I+DtK93/qbZs19kDxFr1TGlgQFiEWW4= Authentication-Results: smtp-out1.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402254; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=TlRycN/r/hzMtCufsix9uWAe9WmB5Kqoq1qghJz4TLY=; b=dOl9FZg0+Rg6TRLur29O2zhigULec92c2Ovjjr/QRjk/Zpjctzpt2pAqv52mMGFA1PRE3H 2+EQrhMVOazJYnfkZTmglTQPYYDbEzj2a0RVAT5FtlCy+hOyFRMDhRKwXpFF2y2L2oS5On I+DtK93/qbZs19kDxFr1TGlgQFiEWW4= Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 0771C3EA63; Fri, 6 Feb 2026 18:24:14 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id WOFWAc4xhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:14 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 31/43] btrfs: limit encrypted writes to 256 segments Date: Fri, 6 Feb 2026 19:23:03 +0100 Message-ID: <20260206182336.1397715-32-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spamd-Result: default: False [-6.80 / 50.00]; REPLY(-4.00)[]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; DBL_BLOCKED_OPENRESOLVER(0.00)[toxicpanda.com:email,suse.com:mid,suse.com:email]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; TO_DN_SOME(0.00)[]; RCPT_COUNT_TWELVE(0.00)[12]; RCVD_VIA_SMTP_AUTH(0.00)[]; FUZZY_RATELIMITED(0.00)[rspamd.com]; FROM_HAS_DN(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; R_RATELIMIT(0.00)[to_ip_from(RLdafymbgddseoxkryad1wgu8n)]; DKIM_SIGNED(0.00)[suse.com:s=susede1]; RCVD_TLS_ALL(0.00)[] X-Spam-Flag: NO X-Spam-Score: -6.80 X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik For the fallback encrypted writes it allocates a bounce buffer to encrypt, and if the bio is larger than 256 segments it splits the bio we send down. This wreaks havoc on us because we need our actual original bio to be passed into the process_cb callback in order to get at our ordered extent. With the split we'll get some cloned bio that has none of our information. Handle this by returning the length we need to be truncated to and then splitting ourselves, which will handle giving us the correct btrfs_bio that we need. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/1fc07b885453495bccea5a37e13d7d26333= bd2af.1706116485.git.josef@toxicpanda.com/ * No changes since. --- fs/btrfs/bio.c | 29 ++++++++++++++++++++++++++++- fs/btrfs/fscrypt.c | 24 ++++++++++++++++++++++++ fs/btrfs/fscrypt.h | 6 ++++++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c index 487dd9267fd7..1ae81997fb2d 100644 --- a/fs/btrfs/bio.c +++ b/fs/btrfs/bio.c @@ -14,6 +14,7 @@ #include "zoned.h" #include "file-item.h" #include "raid-stripe-tree.h" +#include "fscrypt.h" =20 static struct bio_set btrfs_bioset; static struct bio_set btrfs_clone_bioset; @@ -751,6 +752,7 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, = int mirror_num) u64 logical =3D bio->bi_iter.bi_sector << SECTOR_SHIFT; u64 length =3D bio->bi_iter.bi_size; u64 map_length =3D length; + u64 max_bio_len =3D length; struct btrfs_io_context *bioc =3D NULL; struct btrfs_io_stripe smap; blk_status_t status; @@ -762,6 +764,31 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio,= int mirror_num) smap.rst_search_commit_root =3D false; =20 btrfs_bio_counter_inc_blocked(fs_info); + + /* + * The blk-crypto-fallback limits bio sizes to 256 segments, because it + * has no way of controlling the pages it gets for the bounce bio it + * submits. + * + * If we don't pre-split our bio blk-crypto-fallback will do it for us, + * and then call into our checksum callback with a random cloned bio + * that isn't a btrfs_bio. + * + * To account for this we must truncate the bio ourselves, so we need to + * get our length at 256 segments and return that. We must do this + * before btrfs_map_block because the RAID5/6 code relies on having + * properly stripe aligned things, so we return the truncated length + * here so that btrfs_map_block() does the correct thing. Further down + * we actually truncate map_length to map_bio_len because map_length + * will be set to whatever the mapping length is for the underlying + * geometry. This will work properly with RAID5/6 as it will have + * already setup everything for the expected length, and everything else + * can handle with a truncated map_length. + */ + if (bio_has_crypt_ctx(bio)) + max_bio_len =3D btrfs_fscrypt_bio_length(bio, map_length); + + map_length =3D max_bio_len; ret =3D btrfs_map_block(fs_info, btrfs_op(bio), logical, &map_length, &bioc, &smap, &mirror_num); if (ret) { @@ -780,7 +807,7 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, = int mirror_num) =20 bbio->can_use_append =3D btrfs_use_zone_append(bbio); =20 - map_length =3D min(map_length, length); + map_length =3D min(map_length, max_bio_len); if (bbio->can_use_append) map_length =3D btrfs_append_map_length(bbio, map_length); =20 diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index f4d6979a581b..b6350b043994 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -328,6 +328,30 @@ bool btrfs_mergeable_encrypted_bio(struct bio *bio, st= ruct inode *inode, return fscrypt_mergeable_extent_bio(bio, fi, logical_offset); } =20 +/* + * The block crypto stuff allocates bounce buffers for encryption, so spli= ts at + * BIO_MAX_VECS worth of segments. If we are larger than that number of + * segments then we need to limit the size to the size that BIO_MAX_VECS c= overs. + */ +int btrfs_fscrypt_bio_length(struct bio *bio, u64 map_length) +{ + unsigned int i =3D 0; + struct bio_vec bv; + struct bvec_iter iter; + u64 segments_length =3D 0; + + if (bio_op(bio) !=3D REQ_OP_WRITE) + return map_length; + + bio_for_each_segment(bv, bio, iter) { + segments_length +=3D bv.bv_len; + if (++i =3D=3D BIO_MAX_VECS) + return segments_length; + } + + return map_length; +} + const struct fscrypt_operations btrfs_fscrypt_ops =3D { .inode_info_offs =3D (int)offsetof(struct btrfs_inode, i_crypt_info) - (int)offsetof(struct btrfs_inode, vfs_inode), diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h index 1a2a4ffee383..347b34f45715 100644 --- a/fs/btrfs/fscrypt.h +++ b/fs/btrfs/fscrypt.h @@ -31,6 +31,7 @@ void btrfs_set_bio_crypt_ctx_from_extent(struct bio *bio, bool btrfs_mergeable_encrypted_bio(struct bio *bio, struct inode *inode, struct fscrypt_extent_info *fi, u64 logical_offset); +int btrfs_fscrypt_bio_length(struct bio *bio, u64 map_length); =20 #else static inline void btrfs_fscrypt_save_extent_info(struct btrfs_path *path, @@ -85,6 +86,11 @@ static inline bool btrfs_mergeable_encrypted_bio(struct = bio *bio, return true; } =20 +static inline u64 btrfs_fscrypt_bio_length(struct bio *bio, u64 map_length) +{ + return map_length; +} + #endif /* CONFIG_FS_ENCRYPTION */ =20 extern const struct fscrypt_operations btrfs_fscrypt_ops; --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 970AD449EAF for ; Fri, 6 Feb 2026 18:25:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402307; cv=none; b=ShqBAetr7jbKASkxTHuumTjNiGpszq5GgsYvAtdePEqgh/GGvjB7qzvhfbI7sZtfm3IADw3c+fF2TFz1AWHDvJ3g+6STIriOKitZBGUUkkrHIAch+1Tjg0AtH6ok+dCC0QP7kqkMj3eov4V7MpTdPL6y0cbR5EIOERKTRTfskX0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402307; c=relaxed/simple; bh=/fJqgQjZ/DCnSZEMQWs3+DMllQ5f35E6cTh4k19I5AQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eLLqpVYqR5NEAPispP/BHGXDWtyMNArklSq1zjfDa7iVn3+NyGMKa+KLowZ75LijxKTWZRXVRLL6dVFLVh25TvFJD0meyN8822/aIzo5pBQmiBhWnfQ/eDnWZHqeDQQNFAM+I/WNy9ISNvbagLrJ4ovF5ymUrvBe10HqIKwX8+0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id DA3A35BD2D; Fri, 6 Feb 2026 18:24:14 +0000 (UTC) Authentication-Results: smtp-out2.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id AFFB93EA63; Fri, 6 Feb 2026 18:24:14 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id gJeBKs4xhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:14 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 32/43] btrfs: implement process_bio cb for fscrypt Date: Fri, 6 Feb 2026 19:23:04 +0100 Message-ID: <20260206182336.1397715-33-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spam-Score: -4.00 X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Queue-Id: DA3A35BD2D X-Rspamd-Server: rspamd1.dmz-prg2.suse.org X-Spam-Level: X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO Content-Type: text/plain; charset="utf-8" From: Josef Bacik We are going to be checksumming the encrypted data, so we have to implement the ->process_bio fscrypt callback. This will provide us with the original bio and the encrypted bio to do work on. For WRITE's this will happen after the encrypted bio has been encrypted. For READ's this will happen after the read has completed and before the decryption step is done. For write's this is straightforward, we can just pass in the encrypted bio to btrfs_csum_one_bio and then the csums will be added to the bbio as normal. For read's this is relatively straightforward, but requires some care. We assume (because that's how it works currently) that the encrypted bio match the original bio, this is important because we save the iter of the bio before we submit. If this changes in the future we'll need a hook to give us the bi_iter of the decryption bio before it's submitted. We check the csums before decryption. If it doesn't match we simply error out and we let the normal path handle the repair work. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/ca32684b01ff8c252be515509137e0a4a0e= 5db7a.1706116485.git.josef@toxicpanda.com/ * Adapt to btrfs_data_csum_ok() changes for bs > ps. Mostly follow what was done in 052fd7a5cace ("btrfs: make read verification handle bs > ps cases without large folios"). * Rename bbio::csum_done to csum_ok due to name collision. With upstream, member name csum_done was used for async csums. --- fs/btrfs/bio.c | 38 +++++++++++++++++++++++++++++++++++++- fs/btrfs/bio.h | 3 +++ fs/btrfs/file-item.c | 14 ++++++++++++-- fs/btrfs/fscrypt.c | 29 +++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c index 1ae81997fb2d..69dc32cb4ed6 100644 --- a/fs/btrfs/bio.c +++ b/fs/btrfs/bio.c @@ -300,6 +300,34 @@ static struct btrfs_failed_bio *repair_one_sector(stru= ct btrfs_bio *failed_bbio, return fbio; } =20 +blk_status_t btrfs_check_encrypted_read_bio(struct btrfs_bio *bbio, struct= bio *enc_bio) +{ + struct btrfs_inode *inode =3D bbio->inode; + struct btrfs_fs_info *fs_info =3D inode->root->fs_info; + struct bvec_iter iter =3D bbio->saved_iter; + struct btrfs_device *dev =3D bbio->bio.bi_private; + const u32 step =3D min(fs_info->sectorsize, PAGE_SIZE); + const u32 nr_steps =3D iter.bi_size / step; + phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE]; + phys_addr_t paddr; + unsigned int slot =3D 0; + + /* + * We have to use a copy of iter in case there's an error, + * btrfs_check_read_bio will handle submitting the repair bios. + */ + btrfs_bio_for_each_block(paddr, enc_bio, &iter, step) { + ASSERT(slot < nr_steps); + paddrs[slot] =3D paddr; + slot++; + } + if (!btrfs_data_csum_ok(bbio, dev, 0, paddrs)) + return BLK_STS_IOERR; + + bbio->csum_ok =3D true; + return BLK_STS_OK; +} + static void btrfs_check_read_bio(struct btrfs_bio *bbio, struct btrfs_devi= ce *dev) { struct btrfs_inode *inode =3D bbio->inode; @@ -329,6 +357,10 @@ static void btrfs_check_read_bio(struct btrfs_bio *bbi= o, struct btrfs_device *de /* Clear the I/O error. A failed repair will reset it. */ bbio->bio.bi_status =3D BLK_STS_OK; =20 + /* This was an encrypted bio and we've already done the csum check. */ + if (status =3D=3D BLK_STS_OK && bbio->csum_ok) + goto out; + btrfs_bio_for_each_block(paddr, &bbio->bio, iter, step) { paddrs[(offset / step) % nr_steps] =3D paddr; offset +=3D step; @@ -340,6 +372,7 @@ static void btrfs_check_read_bio(struct btrfs_bio *bbio= , struct btrfs_device *de paddrs, fbio); } } +out: if (bbio->csum !=3D bbio->csum_inline) kvfree(bbio->csum); =20 @@ -851,10 +884,13 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio= , int mirror_num) /* * Csum items for reloc roots have already been cloned at this * point, so they are handled as part of the no-checksum case. + * + * Encrypted inodes are csum'ed via the ->process_bio callback. */ if (!(inode->flags & BTRFS_INODE_NODATASUM) && !test_bit(BTRFS_FS_STATE_NO_DATA_CSUMS, &fs_info->fs_state) && - !btrfs_is_data_reloc_root(inode->root) && !bbio->is_remap) { + !btrfs_is_data_reloc_root(inode->root) && !bbio->is_remap && + !IS_ENCRYPTED(&inode->vfs_inode)) { if (should_async_write(bbio) && btrfs_wq_submit_bio(bbio, bioc, &smap, mirror_num)) goto done; diff --git a/fs/btrfs/bio.h b/fs/btrfs/bio.h index 43f7544029ac..456d32db9e9e 100644 --- a/fs/btrfs/bio.h +++ b/fs/btrfs/bio.h @@ -43,6 +43,7 @@ struct btrfs_bio { struct { u8 *csum; u8 csum_inline[BTRFS_BIO_INLINE_CSUM_SIZE]; + bool csum_ok; struct bvec_iter saved_iter; }; =20 @@ -130,5 +131,7 @@ void btrfs_submit_repair_write(struct btrfs_bio *bbio, = int mirror_num, bool dev_ int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 fi= leoff, u32 length, u64 logical, const phys_addr_t paddrs[], unsigned int step, int mirror_num); +blk_status_t btrfs_check_encrypted_read_bio(struct btrfs_bio *bbio, + struct bio *enc_bio); =20 #endif diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index ef0b6faf3de0..cee57a2f241b 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -331,6 +331,14 @@ static int search_csum_tree(struct btrfs_fs_info *fs_i= nfo, return ret; } =20 +static inline bool inode_skip_csum(struct btrfs_inode *inode) +{ + struct btrfs_fs_info *fs_info =3D inode->root->fs_info; + + return (inode->flags & BTRFS_INODE_NODATASUM) || + test_bit(BTRFS_FS_STATE_NO_DATA_CSUMS, &fs_info->fs_state); +} + /* * Lookup the checksum for the read bio in csum tree. * @@ -350,8 +358,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio) int ret =3D 0; u32 bio_offset =3D 0; =20 - if ((inode->flags & BTRFS_INODE_NODATASUM) || - test_bit(BTRFS_FS_STATE_NO_DATA_CSUMS, &fs_info->fs_state)) + if (inode_skip_csum(inode)) return 0; =20 /* @@ -810,6 +817,9 @@ int btrfs_csum_one_bio(struct btrfs_bio *bbio, struct b= io *bio, bool async) struct btrfs_ordered_sum *sums; unsigned nofs_flag; =20 + if (inode_skip_csum(inode)) + return 0; + nofs_flag =3D memalloc_nofs_save(); sums =3D kvzalloc(btrfs_ordered_sum_size(fs_info, bio->bi_iter.bi_size), GFP_KERNEL); diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index b6350b043994..f74404bdd89e 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -16,6 +16,7 @@ #include "transaction.h" #include "volumes.h" #include "xattr.h" +#include "file-item.h" =20 /* * From a given location in a leaf, read a name into a qstr (usually a @@ -212,6 +213,33 @@ static struct block_device **btrfs_fscrypt_get_devices= (struct super_block *sb, return devs; } =20 +static blk_status_t btrfs_process_encrypted_bio(struct bio *orig_bio, + struct bio *enc_bio) +{ + struct btrfs_bio *bbio; + + /* + * If our bio is from the normal fs_bio_set then we know this is a + * mirror split and we can skip it, we'll get the real bio on the last + * mirror and we can process that one. + */ + if (orig_bio->bi_pool =3D=3D &fs_bio_set) + return BLK_STS_OK; + + bbio =3D btrfs_bio(orig_bio); + + if (bio_op(orig_bio) =3D=3D REQ_OP_READ) { + /* + * We have ->saved_iter based on the orig_bio, so if the block + * layer changes we need to notice this asap so we can update + * our code to handle the new world order. + */ + ASSERT(orig_bio =3D=3D enc_bio); + return btrfs_check_encrypted_read_bio(bbio, enc_bio); + } + return btrfs_csum_one_bio(bbio, enc_bio, false); +} + int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode, struct btrfs_path *path, struct btrfs_key *key, @@ -360,4 +388,5 @@ const struct fscrypt_operations btrfs_fscrypt_ops =3D { .set_context =3D btrfs_fscrypt_set_context, .empty_dir =3D btrfs_fscrypt_empty_dir, .get_devices =3D btrfs_fscrypt_get_devices, + .process_bio =3D btrfs_process_encrypted_bio, }; --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D776143D4EC for ; Fri, 6 Feb 2026 18:25:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402317; cv=none; b=VXNlz2zXezjibka3qW5N/qm9UkAye3jDp+dHew7Ixpwe+F6OcsSYAzyzwHxUurCWJag2otuE1R056kTkfiuHcvW+4LXw0vOL4Rd1Gm0bbNc/+1Xvp1sqwpzHp0SLpva/xSzh7Nyl4Gvu5FVpI7OiSMnG+yZF71GrY7ThHiFTFfM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402317; c=relaxed/simple; bh=sn0axnhEpIJaP96cuQggy2tofkrcxgC82ZQ/5vn9dQE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RMc6BIl5M5H4zHEAVZsnY+BJ25mHJJZSsjk/kIEzMoKTaMDfA2kbWKtU8uvHIiQMhc+b909JISlxZoEkW4kaRsOZ4mjlXu81h3YKZh5TfVOz+WMBbKUYv45//mZBSdmAT2iES40CT5bgFBn9Rmp3nTmIHd29vAxfS/ImWvENAVk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id AB4AB3E75A; Fri, 6 Feb 2026 18:24:15 +0000 (UTC) Authentication-Results: smtp-out1.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 7B54A3EA63; Fri, 6 Feb 2026 18:24:15 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id GNqkHc8xhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:15 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 33/43] btrfs: implement read repair for encryption Date: Fri, 6 Feb 2026 19:23:05 +0100 Message-ID: <20260206182336.1397715-34-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO X-Spam-Score: -4.00 X-Rspamd-Queue-Id: AB4AB3E75A X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Server: rspamd2.dmz-prg2.suse.org X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik In order to do read repair we will allocate sectorsize bio's and read them one at a time, repairing any sectors that don't match their csum. In order to do this we re-submit the IO's after it's failed, and at this point we still need the fscrypt_extent_info for these new bio's. Add the fscrypt_extent_info to the read part of the union in the btrfs_bio, and then pass this through all the places where we do reads. Additionally add the orig_start, because we need to be able to put the correct extent offset for the encryption context. With these in place we can utilize the normal read repair path. The only exception is that the actual repair of the bad copies has to be triggered from the ->process_bio callback, because this is the encrypted data. If we waited until the end_io we would have the decrypted data and we don't want to write that to the disk. This is the only change to the normal read repair path, we trigger the fixup of the broken sectors in ->process_bio, and then we skip that part if we successfully repair the sector in ->process_bio once we get to the endio. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/310c0ebdc78613b6f379595e160206013f7= 5b6dc.1706116485.git.josef@toxicpanda.com/ * Fixed UAF bug with !ordered case doing bbio->end_io(bbio) >>> fscrypt_put_extent_info(bbio->fscrypt_info). - We can simply put the fscrypt_info first and then end the bio. - Also no need to clear the bbio->fscrypt_info pointer as bbio is just going to be freed. That cleans up the code a bit. * Adapted to bs > ps changes. * Updated and re-wrap the comments. * Moved the dio-related changes from inode.c to direct-io.c as upstream refactored in the meantime. --- fs/btrfs/bio.c | 75 +++++++++++++++++++++++++++++++++++++----- fs/btrfs/bio.h | 10 +++++- fs/btrfs/compression.c | 2 ++ fs/btrfs/direct-io.c | 2 ++ fs/btrfs/extent_io.c | 2 ++ 5 files changed, 82 insertions(+), 9 deletions(-) diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c index 69dc32cb4ed6..a89f49dac0f2 100644 --- a/fs/btrfs/bio.c +++ b/fs/btrfs/bio.c @@ -97,6 +97,9 @@ static struct btrfs_bio *btrfs_split_bio(struct btrfs_fs_= info *fs_info, bbio->ordered =3D orig_bbio->ordered; bbio->orig_logical =3D orig_bbio->orig_logical; orig_bbio->orig_logical +=3D map_length; + } else if (is_data_bbio(bbio)) { + bbio->fscrypt_info =3D fscrypt_get_extent_info(orig_bbio->fscrypt_info); + bbio->orig_start =3D orig_bbio->orig_start; } =20 bbio->csum_search_commit_root =3D orig_bbio->csum_search_commit_root; @@ -124,6 +127,8 @@ void btrfs_bio_end_io(struct btrfs_bio *bbio, blk_statu= s_t status) /* Free bio that was never submitted to the underlying device. */ if (bbio_has_ordered_extent(bbio)) btrfs_put_ordered_extent(bbio->ordered); + else if (is_data_bbio(bbio)) + fscrypt_put_extent_info(bbio->fscrypt_info); bio_put(&bbio->bio); =20 bbio =3D orig_bbio; @@ -147,6 +152,8 @@ void btrfs_bio_end_io(struct btrfs_bio *bbio, blk_statu= s_t status) bbio->end_io(bbio); btrfs_put_ordered_extent(ordered); } else { + if (is_data_bbio(bbio)) + fscrypt_put_extent_info(bbio->fscrypt_info); bbio->end_io(bbio); } } @@ -174,6 +181,23 @@ static void btrfs_repair_done(struct btrfs_failed_bio = *fbio) } } =20 +static void handle_repair(struct btrfs_bio *repair_bbio, phys_addr_t *padd= rs) +{ + struct btrfs_failed_bio *fbio =3D repair_bbio->private; + struct btrfs_inode *inode =3D repair_bbio->inode; + struct btrfs_fs_info *fs_info =3D inode->root->fs_info; + const u32 step =3D min(fs_info->sectorsize, PAGE_SIZE); + const u64 logical =3D repair_bbio->saved_iter.bi_sector << SECTOR_SHIFT; + int mirror =3D repair_bbio->mirror_num; + + do { + mirror =3D prev_repair_mirror(fbio, mirror); + btrfs_repair_io_failure(fs_info, btrfs_ino(inode), + repair_bbio->file_offset, fs_info->sectorsize, + logical, paddrs, step, mirror); + } while (mirror !=3D fbio->bbio->mirror_num); +} + static void btrfs_end_repair_bio(struct btrfs_bio *repair_bbio, struct btrfs_device *dev) { @@ -186,7 +210,6 @@ static void btrfs_end_repair_bio(struct btrfs_bio *repa= ir_bbio, */ struct bvec_iter saved_iter =3D repair_bbio->saved_iter; const u32 step =3D min(fs_info->sectorsize, PAGE_SIZE); - const u64 logical =3D repair_bbio->saved_iter.bi_sector << SECTOR_SHIFT; const u32 nr_steps =3D repair_bbio->saved_iter.bi_size / step; int mirror =3D repair_bbio->mirror_num; phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE]; @@ -202,6 +225,13 @@ static void btrfs_end_repair_bio(struct btrfs_bio *rep= air_bbio, slot++; } =20 + /* + * If we got here from the encrypted path with ->csum_ok set then + * we've already csumed and repaired this sector, we're all done. + */ + if (repair_bbio->csum_ok) + goto done; + if (repair_bbio->bio.bi_status || !btrfs_data_csum_ok(repair_bbio, dev, 0, paddrs)) { bio_reset(&repair_bbio->bio, NULL, REQ_OP_READ); @@ -214,17 +244,17 @@ static void btrfs_end_repair_bio(struct btrfs_bio *re= pair_bbio, goto done; } =20 + btrfs_set_bio_crypt_ctx_from_extent(&repair_bbio->bio, + repair_bbio->inode, + repair_bbio->fscrypt_info, + repair_bbio->file_offset - + repair_bbio->orig_start); + btrfs_submit_bbio(repair_bbio, mirror); return; } =20 - do { - mirror =3D prev_repair_mirror(fbio, mirror); - btrfs_repair_io_failure(fs_info, btrfs_ino(inode), - repair_bbio->file_offset, fs_info->sectorsize, - logical, paddrs, step, mirror); - } while (mirror !=3D fbio->bbio->mirror_num); - + handle_repair(repair_bbio, paddrs); done: btrfs_repair_done(fbio); bio_put(&repair_bbio->bio); @@ -293,6 +323,13 @@ static struct btrfs_failed_bio *repair_one_sector(stru= ct btrfs_bio *failed_bbio, repair_bbio =3D btrfs_bio(repair_bio); btrfs_bio_init(repair_bbio, failed_bbio->inode, failed_bbio->file_offset = + bio_offset, NULL, fbio); + repair_bbio->fscrypt_info =3D fscrypt_get_extent_info(failed_bbio->fscryp= t_info); + repair_bbio->orig_start =3D failed_bbio->orig_start; + + btrfs_set_bio_crypt_ctx_from_extent(repair_bio, repair_bbio->inode, + failed_bbio->fscrypt_info, + repair_bbio->file_offset - + failed_bbio->orig_start); =20 mirror =3D next_repair_mirror(fbio, failed_bbio->mirror_num); btrfs_debug(fs_info, "submitting repair read to mirror %d", mirror); @@ -324,7 +361,29 @@ blk_status_t btrfs_check_encrypted_read_bio(struct btr= fs_bio *bbio, struct bio * if (!btrfs_data_csum_ok(bbio, dev, 0, paddrs)) return BLK_STS_IOERR; =20 + /* + * Read repair is slightly different for encrypted bio's. This + * callback is before we decrypt the bio in the block crypto layer, + * we're not actually in the endio handler. + * + * We don't trigger the repair process here either, that is handled + * in the actual endio path because we don't want to create another + * pseudo endio path through this callback. This is because when we + * call btrfs_repair_done() we want to call the endio for the original + * bbio. Short circuiting that for the encrypted case would be ugly. + * We really want to the repair case to be handled generically. + * + * However for the actual repair part we need to use this page + * pre-decrypted, which is why we call the btrfs_repair_io_failure() + * code from this path. The repair path is synchronous so we are + * safe there. Then we simply mark the repair bbio as completed so + * the actual btrfs_end_repair_bio() code can skip the repair part. + */ + if (bbio->bio.bi_pool =3D=3D &btrfs_repair_bioset) + handle_repair(bbio, paddrs); bbio->csum_ok =3D true; + fscrypt_put_extent_info(bbio->fscrypt_info); + bbio->fscrypt_info =3D NULL; return BLK_STS_OK; } =20 diff --git a/fs/btrfs/bio.h b/fs/btrfs/bio.h index 456d32db9e9e..7a8ff4378cba 100644 --- a/fs/btrfs/bio.h +++ b/fs/btrfs/bio.h @@ -15,6 +15,7 @@ struct btrfs_bio; struct btrfs_fs_info; struct btrfs_inode; +struct fscrypt_extent_info; =20 #define BTRFS_BIO_INLINE_CSUM_SIZE 64 =20 @@ -38,13 +39,20 @@ struct btrfs_bio { union { /* * For data reads: checksumming and original I/O information. - * (for internal use in the btrfs_submit_bbio() machinery only) + * (for internal use in the btrfs_submit_bbio() machinery only). + * + * The fscrypt context is used for read repair, this is the + * only thing not internal to btrfs_submit_bbio() machinery. */ struct { u8 *csum; u8 csum_inline[BTRFS_BIO_INLINE_CSUM_SIZE]; bool csum_ok; struct bvec_iter saved_iter; + + /* Used for read repair. */ + struct fscrypt_extent_info *fscrypt_info; + u64 orig_start; }; =20 /* diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index cd7b245bedb8..15bf03be3fd5 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -606,6 +606,8 @@ void btrfs_submit_compressed_read(struct btrfs_bio *bbi= o) cb->compress_type =3D btrfs_extent_map_compression(em); cb->orig_bbio =3D bbio; cb->bbio.csum_search_commit_root =3D bbio->csum_search_commit_root; + cb->bbio.fscrypt_info =3D fscrypt_get_extent_info(em->fscrypt_info); + cb->bbio.orig_start =3D 0; =20 btrfs_set_bio_crypt_ctx_from_extent(&cb->bbio.bio, inode, em->fscrypt_inf= o, 0); btrfs_free_extent_map(em); diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c index d20a5b99bdde..c59e13fce764 100644 --- a/fs/btrfs/direct-io.c +++ b/fs/btrfs/direct-io.c @@ -765,6 +765,8 @@ static void btrfs_dio_submit_io(const struct iomap_iter= *iter, struct bio *bio, } else { fscrypt_info =3D dio_data->fscrypt_info; offset =3D file_offset - dio_data->orig_start; + bbio->fscrypt_info =3D fscrypt_get_extent_info(fscrypt_info); + bbio->orig_start =3D dio_data->orig_start; } =20 btrfs_set_bio_crypt_ctx_from_extent(&bbio->bio, bbio->inode, fscrypt_info= , offset); diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 3273b7e3b4b0..094855b77768 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -796,6 +796,8 @@ static void alloc_new_bio(struct btrfs_inode *inode, } else { fscrypt_info =3D bio_ctrl->fscrypt_info; offset =3D file_offset - bio_ctrl->orig_start; + bbio->fscrypt_info =3D fscrypt_get_extent_info(fscrypt_info); + bbio->orig_start =3D bio_ctrl->orig_start; } =20 btrfs_set_bio_crypt_ctx_from_extent(&bbio->bio, inode, fscrypt_info, offs= et); --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C224944B66F for ; Fri, 6 Feb 2026 18:25:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402322; cv=none; b=h5ExRfjzM6dtuexn55aLnHbqhibkChzhBbV7AeXagr/w7+iubv+nFTGRoN825ItqIkSYqroKk8Sorp8tCyl0EeIsjqhB8B+qbKDiclubtxbcArSK1OKgxm7KYjK7xbd+x4xizDdWTLVQtymaJP0IIkR3rzBxWhsFkepBOoj8WA8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402322; c=relaxed/simple; bh=U0l18ou8F/MGcFM1MDGVGTmRLR8+PcY/gyt+caSfplE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nESDgtqaa2fB3PfO4Rfv7mU5K8NCWaGnR8GZq+stE0YFlIA723CGgn1S5anIx4aqlcE0JJzu6GbaGwnPr5rB5xgoL+WAd99BLktc3xfs1M0+VH0Y/58PKDYHMWBvkXGtDv2kgFjLI08vY7XtpyF0Xvrb8zW209AFnvRGD2o0mto= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 67DD23E75E; Fri, 6 Feb 2026 18:24:16 +0000 (UTC) Authentication-Results: smtp-out1.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 3C6803EA63; Fri, 6 Feb 2026 18:24:16 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id oElJDtAxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:16 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 34/43] btrfs: add test_dummy_encryption support Date: Fri, 6 Feb 2026 19:23:06 +0100 Message-ID: <20260206182336.1397715-35-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO X-Spam-Score: -4.00 X-Rspamd-Queue-Id: 67DD23E75E X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Server: rspamd2.dmz-prg2.suse.org X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik In order to enable more thorough testing of fscrypt enable the test_dummy_encryption mount option. This is used by fscrypt users to easily enable fscrypt on the file system for testing without needing to do the key setup and everything. The only deviation from other file systems we make is we only support the fsparam_flag version of this mount option, as it defaults to v2. We don't want to have to bother with rejecting v1 related mount options. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/77449ee5a882db2945429946c74ea7e7961= 22328.1706116485.git.josef@toxicpanda.com/ * No changes since. Just re-wrapping. We now accept longer lines. --- fs/btrfs/disk-io.c | 1 + fs/btrfs/fs.h | 3 +++ fs/btrfs/fscrypt.c | 6 +++++ fs/btrfs/super.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 20c405a4789d..f47a20976b53 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1224,6 +1224,7 @@ void btrfs_free_fs_info(struct btrfs_fs_info *fs_info) btrfs_extent_buffer_leak_debug_check(fs_info); kfree(fs_info->super_copy); kfree(fs_info->super_for_commit); + fscrypt_free_dummy_policy(&fs_info->dummy_enc_policy); kvfree(fs_info); } =20 diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h index 5077b7eed4b8..13cfb6887d89 100644 --- a/fs/btrfs/fs.h +++ b/fs/btrfs/fs.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include "extent-io-tree.h" @@ -263,6 +264,7 @@ enum { BTRFS_MOUNT_IGNOREMETACSUMS =3D (1ULL << 31), BTRFS_MOUNT_IGNORESUPERFLAGS =3D (1ULL << 32), BTRFS_MOUNT_REF_TRACKER =3D (1ULL << 33), + BTRFS_MOUNT_TEST_DUMMY_ENCRYPTION =3D (1ULL << 34), }; =20 /* These mount options require a full read-only fs, no new transaction is = allowed. */ @@ -951,6 +953,7 @@ struct btrfs_fs_info { spinlock_t eb_leak_lock; struct list_head allocated_ebs; #endif + struct fscrypt_dummy_policy dummy_enc_policy; }; =20 #define folio_to_inode(_folio) (BTRFS_I(_Generic((_folio), \ diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index f74404bdd89e..d1a4cbb990d4 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -240,6 +240,11 @@ static blk_status_t btrfs_process_encrypted_bio(struct= bio *orig_bio, return btrfs_csum_one_bio(bbio, enc_bio, false); } =20 +static const union fscrypt_policy *btrfs_get_dummy_policy(struct super_blo= ck *sb) +{ + return btrfs_sb(sb)->dummy_enc_policy.policy; +} + int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode, struct btrfs_path *path, struct btrfs_key *key, @@ -389,4 +394,5 @@ const struct fscrypt_operations btrfs_fscrypt_ops =3D { .empty_dir =3D btrfs_fscrypt_empty_dir, .get_devices =3D btrfs_fscrypt_get_devices, .process_bio =3D btrfs_process_encrypted_bio, + .get_dummy_policy =3D btrfs_get_dummy_policy, }; diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 3914abec5b12..06788b27a870 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -87,6 +87,7 @@ struct btrfs_fs_context { unsigned long compress_type:4; int compress_level; refcount_t refs; + struct fscrypt_dummy_policy dummy_enc_policy; }; =20 static void btrfs_emit_options(struct btrfs_fs_info *info, @@ -125,6 +126,7 @@ enum { Opt_treelog, Opt_user_subvol_rm_allowed, Opt_norecovery, + Opt_test_dummy_encryption, =20 /* Rescue options */ Opt_rescue, @@ -259,6 +261,8 @@ static const struct fs_parameter_spec btrfs_fs_paramete= rs[] =3D { fsparam_enum("fragment", Opt_fragment, btrfs_parameter_fragment), fsparam_flag("ref_tracker", Opt_ref_tracker), fsparam_flag("ref_verify", Opt_ref_verify), + fsparam_flag("test_dummy_encryption", Opt_test_dummy_encryption), + fsparam_string("test_dummy_encryption", Opt_test_dummy_encryption), #endif {} }; @@ -651,6 +655,23 @@ static int btrfs_parse_param(struct fs_context *fc, st= ruct fs_parameter *param) case Opt_ref_tracker: btrfs_set_opt(ctx->mount_opt, REF_TRACKER); break; + case Opt_test_dummy_encryption: + int ret; + + /* + * We only support v2, so reject any v1 policies. + */ + if (param->type =3D=3D fs_value_is_string && *param->string && + !strcmp(param->string, "v1")) { + btrfs_info(NULL, "v1 encryption isn't supported"); + return -EINVAL; + } + + btrfs_set_opt(ctx->mount_opt, TEST_DUMMY_ENCRYPTION); + ret =3D fscrypt_parse_test_dummy_encryption(param, &ctx->dummy_enc_polic= y); + if (ret) + return ret; + break; #endif default: btrfs_err(NULL, "unrecognized mount option '%s'", param->key); @@ -986,6 +1007,9 @@ static int btrfs_fill_super(struct super_block *sb, return ret; } =20 + if (fscrypt_is_dummy_policy_set(&fs_info->dummy_enc_policy)) + btrfs_set_fs_incompat(fs_info, ENCRYPT); + btrfs_emit_options(fs_info, NULL); =20 inode =3D btrfs_iget(BTRFS_FIRST_FREE_OBJECTID, fs_info->fs_root); @@ -1150,6 +1174,8 @@ static int btrfs_show_options(struct seq_file *seq, s= truct dentry *dentry) seq_puts(seq, ",ref_verify"); if (btrfs_test_opt(info, REF_TRACKER)) seq_puts(seq, ",ref_tracker"); + if (btrfs_test_opt(info, TEST_DUMMY_ENCRYPTION)) + fscrypt_show_test_dummy_encryption(seq, ',', dentry->d_sb); seq_printf(seq, ",subvolid=3D%llu", btrfs_root_id(BTRFS_I(d_inode(dentry)= )->root)); subvol_name =3D btrfs_get_subvol_name_from_objectid(info, btrfs_root_id(BTRFS_I(d_inode(dentry))->root)); @@ -1415,6 +1441,18 @@ static void btrfs_ctx_to_info(struct btrfs_fs_info *= fs_info, struct btrfs_fs_con fs_info->mount_opt =3D ctx->mount_opt; fs_info->compress_type =3D ctx->compress_type; fs_info->compress_level =3D ctx->compress_level; + + /* + * If there's nothing set, or if the fs_info already has one set, don't + * do anything. If the fs_info is set we'll free the dummy one when we + * free the ctx. + */ + if (!fscrypt_is_dummy_policy_set(&ctx->dummy_enc_policy) || + fscrypt_is_dummy_policy_set(&fs_info->dummy_enc_policy)) + return; + + fs_info->dummy_enc_policy =3D ctx->dummy_enc_policy; + memset(&ctx->dummy_enc_policy, 0, sizeof(ctx->dummy_enc_policy)); } =20 static void btrfs_info_to_ctx(struct btrfs_fs_info *fs_info, struct btrfs_= fs_context *ctx) @@ -1468,6 +1506,7 @@ static void btrfs_emit_options(struct btrfs_fs_info *= info, btrfs_info_if_set(info, old, IGNOREDATACSUMS, "ignoring data csums"); btrfs_info_if_set(info, old, IGNOREMETACSUMS, "ignoring meta csums"); btrfs_info_if_set(info, old, IGNORESUPERFLAGS, "ignoring unknown super bl= ock flags"); + btrfs_info_if_set(info, old, TEST_DUMMY_ENCRYPTION, "test dummy encryptio= n mode enabled"); =20 btrfs_info_if_unset(info, old, NODATASUM, "setting datasum"); btrfs_info_if_unset(info, old, NODATACOW, "setting datacow"); @@ -1498,6 +1537,21 @@ static void btrfs_emit_options(struct btrfs_fs_info = *info, btrfs_info(info, "max_inline set to %llu", info->max_inline); } =20 +static bool btrfs_check_test_dummy_encryption(struct fs_context *fc) +{ + struct btrfs_fs_context *ctx =3D fc->fs_private; + struct btrfs_fs_info *fs_info =3D btrfs_sb(fc->root->d_sb); + + if (!fscrypt_is_dummy_policy_set(&ctx->dummy_enc_policy)) + return true; + + if (fscrypt_dummy_policies_equal(&fs_info->dummy_enc_policy, &ctx->dummy_= enc_policy)) + return true; + + btrfs_warn(fs_info, "Can't set or change test_dummy_encryption on remount= "); + return false; +} + static int btrfs_reconfigure(struct fs_context *fc) { struct super_block *sb =3D fc->root->d_sb; @@ -1523,6 +1577,9 @@ static int btrfs_reconfigure(struct fs_context *fc) if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags)) return -EINVAL; =20 + if (!mount_reconfigure && !btrfs_check_test_dummy_encryption(fc)) + return -EINVAL; + ret =3D btrfs_check_features(fs_info, !(fc->sb_flags & SB_RDONLY)); if (ret < 0) return ret; @@ -2139,6 +2196,7 @@ static void btrfs_free_fs_context(struct fs_context *= fc) btrfs_free_fs_info(fs_info); =20 if (ctx && refcount_dec_and_test(&ctx->refs)) { + fscrypt_free_dummy_policy(&ctx->dummy_enc_policy); kfree(ctx->subvol_name); kfree(ctx); } --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 654CB44BC9F for ; Fri, 6 Feb 2026 18:25:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402327; cv=none; b=sx7dWthPhA84d3+BClYKZD3JLV1qBBeimip4BrutkJBEUAg/7PLyMaZYbezTomPJl4VIicJIRRA2MblQB/ACYvsnD92Znu4MP/11OvirrWBG2qzCbEcn1MPYaGHUQ9/oQATAiee4YDMJzQUIPPCy5qfejLubvgNN+znq1IPsHys= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402327; c=relaxed/simple; bh=9LsGDAhN6yaS4eVreOGRrVmOuVBkuDvHtHFMAJyoGyQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=X5D9Bcct9W7Xf/2G7HGzKX3axpv73ReXcUEvTza1mjNSeAg+/JfAxVL0aNgUSbF9C+WmF4+joZMU0WF/dI1wmx/lqEWANNumQmCliULeML/ceiCdV/j2SZWdar+3yo+nfVBHbcuBDT5hSI5VXC63FT2Jjx16A0zcCgDmyKDdu3Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 102F53E737; Fri, 6 Feb 2026 18:24:17 +0000 (UTC) Authentication-Results: smtp-out1.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id D5F513EA63; Fri, 6 Feb 2026 18:24:16 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id uOKzM9AxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:16 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 35/43] btrfs: make btrfs_ref_to_path handle encrypted filenames Date: Fri, 6 Feb 2026 19:23:07 +0100 Message-ID: <20260206182336.1397715-36-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spam-Score: -4.00 X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Queue-Id: 102F53E737 X-Rspamd-Server: rspamd1.dmz-prg2.suse.org X-Spam-Level: X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO Content-Type: text/plain; charset="utf-8" From: Josef Bacik We use this helper for inode-resolve and path resolution in send, so update this helper to properly decrypt any encrypted names it finds. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/365d4f820f70b7cf69b1b9cae9b949a15c3= 350b0.1706116485.git.josef@toxicpanda.com/ * Adapted to btrfs_iget() now returning binode instead of vfs inode as before. * Adapted to crypt info being moved from vfs inode to FS specific inode. --- fs/btrfs/backref.c | 42 +++++++++++++++++++++++++++++++++++++---- fs/btrfs/fscrypt.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++ fs/btrfs/fscrypt.h | 10 ++++++++++ 3 files changed, 95 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index 9bb406f7dd30..577c3ef87791 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -20,6 +20,7 @@ #include "extent-tree.h" #include "relocation.h" #include "tree-checker.h" +#include "fscrypt.h" =20 /* Just arbitrary numbers so we can be sure one of these happened. */ #define BACKREF_FOUND_SHARED 6 @@ -2107,6 +2108,39 @@ int btrfs_find_one_extref(struct btrfs_root *root, u= 64 inode_objectid, return ret; } =20 +static int copy_resolved_iref_to_buf(struct btrfs_root *fs_root, + struct extent_buffer *eb, char *dest, + u64 parent, unsigned long name_off, + u32 name_len, s64 *bytes_left) +{ + struct btrfs_fs_info *fs_info =3D fs_root->fs_info; + struct fscrypt_str fname =3D FSTR_INIT(NULL, 0); + int ret; + + /* No encryption, just copy the name in. */ + if (!btrfs_fs_incompat(fs_info, ENCRYPT)) { + *bytes_left -=3D name_len; + if (*bytes_left >=3D 0) + read_extent_buffer(eb, dest + *bytes_left, name_off, name_len); + return 0; + } + + ret =3D fscrypt_fname_alloc_buffer(BTRFS_NAME_LEN, &fname); + if (ret) + return ret; + + ret =3D btrfs_decrypt_name(fs_root, eb, name_off, name_len, parent, &fnam= e); + if (ret) + goto out; + + *bytes_left -=3D fname.len; + if (*bytes_left >=3D 0) + memcpy(dest + *bytes_left, fname.name, fname.len); +out: + fscrypt_fname_free_buffer(&fname); + return ret; +} + /* * this iterates to turn a name (from iref/extref) into a full filesystem = path. * Elements of the path are separated by '/' and the path is guaranteed to= be @@ -2138,10 +2172,10 @@ char *btrfs_ref_to_path(struct btrfs_root *fs_root,= struct btrfs_path *path, dest[bytes_left] =3D '\0'; =20 while (1) { - bytes_left -=3D name_len; - if (bytes_left >=3D 0) - read_extent_buffer(eb, dest + bytes_left, - name_off, name_len); + ret =3D copy_resolved_iref_to_buf(fs_root, eb, dest, parent, + name_off, name_len, &bytes_left); + if (ret) + break; if (eb !=3D eb_in) { if (!path->skip_locking) btrfs_tree_read_unlock(eb); diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index d1a4cbb990d4..bcb86cbaa171 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -385,6 +385,53 @@ int btrfs_fscrypt_bio_length(struct bio *bio, u64 map_= length) return map_length; } =20 +int btrfs_decrypt_name(struct btrfs_root *root, struct extent_buffer *eb, + unsigned long name_off, u32 name_len, + u64 parent_ino, struct fscrypt_str *name) +{ + struct btrfs_inode *inode; + struct inode *dir; + struct fscrypt_str iname =3D FSTR_INIT(NULL, 0); + int ret; + + ASSERT(name_len <=3D BTRFS_NAME_LEN); + + ret =3D fscrypt_fname_alloc_buffer(name_len, &iname); + if (ret) + return ret; + + inode =3D btrfs_iget(parent_ino, root); + if (IS_ERR(inode)) { + ret =3D PTR_ERR(inode); + goto out; + } + dir =3D &inode->vfs_inode; + + /* + * Directory isn't encrypted, the name isn't encrypted, we can just copy + * it into the buffer. + */ + if (!IS_ENCRYPTED(dir)) { + read_extent_buffer(eb, name->name, name_off, name_len); + name->len =3D name_len; + goto out_inode; + } + + read_extent_buffer(eb, iname.name, name_off, name_len); + + ret =3D fscrypt_prepare_readdir(dir); + if (ret) + goto out_inode; + + ASSERT(inode->i_crypt_info); + ret =3D fscrypt_fname_disk_to_usr(dir, 0, 0, &iname, name); +out_inode: + iput(dir); +out: + fscrypt_fname_free_buffer(&iname); + return ret; +} + const struct fscrypt_operations btrfs_fscrypt_ops =3D { .inode_info_offs =3D (int)offsetof(struct btrfs_inode, i_crypt_info) - (int)offsetof(struct btrfs_inode, vfs_inode), diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h index 347b34f45715..4f49ed6176d4 100644 --- a/fs/btrfs/fscrypt.h +++ b/fs/btrfs/fscrypt.h @@ -32,6 +32,9 @@ bool btrfs_mergeable_encrypted_bio(struct bio *bio, struc= t inode *inode, struct fscrypt_extent_info *fi, u64 logical_offset); int btrfs_fscrypt_bio_length(struct bio *bio, u64 map_length); +int btrfs_decrypt_name(struct btrfs_root *root, struct extent_buffer *eb, + unsigned long name_off, u32 name_len, + u64 parent_ino, struct fscrypt_str *name); =20 #else static inline void btrfs_fscrypt_save_extent_info(struct btrfs_path *path, @@ -91,6 +94,13 @@ static inline u64 btrfs_fscrypt_bio_length(struct bio *b= io, u64 map_length) return map_length; } =20 +static inline int btrfs_decrypt_name(struct btrfs_root *root, struct exten= t_buffer *eb, + unsigned long name_off, u32 name_len, + u64 parent_ino, struct fscrypt_str *name) +{ + return -EINVAL; +} + #endif /* CONFIG_FS_ENCRYPTION */ =20 extern const struct fscrypt_operations btrfs_fscrypt_ops; --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6BF0343DA48 for ; Fri, 6 Feb 2026 18:25:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402331; cv=none; b=GTdN7ZE8bR+RNKpSh2dPr3wg0NquqysupVCgFebv7LSH7aVOERjCOGFHvjcBqhxskS9P8//CDZDcKye0idjjJ+XG6wXi5JdkYuIwJn7nv9cl4frHQbMnDqNB2YIcx4lhts5+xMHbCz4ps3LVxHUeUGFJgwUZt9yXSK35kkcPjr8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402331; c=relaxed/simple; bh=Tfn+Z9xauLtsg6WSNT0FytGznv6AKi3dk0uKZsElWiM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Smvd8eZu2NpC5dOHflvSYFLtgxgeHqf7Uo7Jr7vLB7fxfkXk4niKnGwV/zdhJv795i4eachhQCn9vDMkEtFQALt5r34fgPu2s3VYH76pLXdhn23wHTPXsE07VZKI2cxXQFbfDF9Rw87SXLFCv5TPoL+GyP+DvMKCN4tRd3UnCxk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 7B99D3E75F; Fri, 6 Feb 2026 18:24:17 +0000 (UTC) Authentication-Results: smtp-out1.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 505653EA63; Fri, 6 Feb 2026 18:24:17 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id IFK3EtExhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:17 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 36/43] btrfs: deal with encrypted symlinks in send Date: Fri, 6 Feb 2026 19:23:08 +0100 Message-ID: <20260206182336.1397715-37-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spam-Score: -4.00 X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Queue-Id: 7B99D3E75F X-Rspamd-Server: rspamd1.dmz-prg2.suse.org X-Spam-Level: X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO Content-Type: text/plain; charset="utf-8" From: Josef Bacik Send needs to send the decrypted value of the symlinks, handle the case where the inode is encrypted and decrypt the symlink name into a buffer and copy this buffer into our fs_path struct. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/4d97f35d6f85ff041b09bed33b63446a92b= 7a20c.1706116485.git.josef@toxicpanda.com/ * read_symlink_encrypted() reworked from using pages to using folios. --- fs/btrfs/send.c | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index 3dcfdba018b5..b77f96ae2fea 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -1701,9 +1701,7 @@ static int find_extent_clone(struct send_ctx *sctx, return ret; } =20 -static int read_symlink(struct btrfs_root *root, - u64 ino, - struct fs_path *dest) +static int read_symlink_unencrypted(struct btrfs_root *root, u64 ino, stru= ct fs_path *dest) { int ret; BTRFS_PATH_AUTO_FREE(path); @@ -1764,6 +1762,47 @@ static int read_symlink(struct btrfs_root *root, return fs_path_add_from_extent_buffer(dest, path->nodes[0], off, len); } =20 +static int read_symlink_encrypted(struct btrfs_root *root, u64 ino, struct= fs_path *dest) +{ + DEFINE_DELAYED_CALL(done); + const char *buf; + struct folio *folio; + struct btrfs_inode *inode; + int ret =3D 0; + + inode =3D btrfs_iget(ino, root); + if (IS_ERR(inode)) + return PTR_ERR(inode); + + folio =3D read_mapping_folio(inode->vfs_inode.i_mapping, 0, NULL); + if (IS_ERR(folio)) { + iput(&inode->vfs_inode); + return PTR_ERR(folio); + } + + buf =3D fscrypt_get_symlink(&inode->vfs_inode, folio_address(folio), + BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info), + &done); + folio_put(folio); + iput(&inode->vfs_inode); + + if (IS_ERR(buf)) + return PTR_ERR(buf); + + ret =3D fs_path_add(dest, buf, strlen(buf)); + do_delayed_call(&done); + return ret; +} + + +static int read_symlink(struct btrfs_root *root, u64 ino, + struct fs_path *dest) +{ + if (btrfs_fs_incompat(root->fs_info, ENCRYPT)) + return read_symlink_encrypted(root, ino, dest); + return read_symlink_unencrypted(root, ino, dest); +} + /* * Helper function to generate a file name that is unique in the root of * send_root and parent_root. This is used to generate names for orphan in= odes. --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A83DD44A710 for ; Fri, 6 Feb 2026 18:25:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402311; cv=none; b=oor7LolbUSAfn35anvtRQiE5c64cJfe4dWsyEZ2h57KiPec+PIqZxLzXNJKWrtA40CgK4/mzBuZOqyqu/5Tuq4dF6GCaQAZKCQIXlf3MtYmXEi5SmGLQ4VmQxgW6g9K1myNYQevNfXfEPuiYkAtVEoUPqnv/uXJo5C+ptn4Ysqw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402311; c=relaxed/simple; bh=jxsfwWOkFu6y3aSWsnhI7vrlS53sGNFWe3N9q0oyctE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=mGxGY6vVejbT13ZWk/fFYwybfQPiwz0/e77H283wpllq1TA4A1oif5Vv+ZneI8QRq868fsN4PU4nGow+gYpcDfOY1n6SkoVx6dud1vfwhF+VC2V7+TnCHcsUZg4uT6qjMp2ZJya6/9ToE5yZDsGA2YVMgM/I2m45YXaKIQEba3Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id E5CDF5BD03; Fri, 6 Feb 2026 18:24:17 +0000 (UTC) Authentication-Results: smtp-out2.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id B97A53EA63; Fri, 6 Feb 2026 18:24:17 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id gEDCLNExhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:17 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 37/43] btrfs: decrypt file names for send Date: Fri, 6 Feb 2026 19:23:09 +0100 Message-ID: <20260206182336.1397715-38-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO X-Spam-Score: -4.00 X-Rspamd-Queue-Id: E5CDF5BD03 X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Server: rspamd2.dmz-prg2.suse.org X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik In send we're going to be looking up file names from back references and putting them into the send stream. If we are encrypted use the helper for decrypting names and copy the decrypted name into the buffer. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/fd8b1d5f395a890dbdf8281a52fbaaa920c= 7b726.1706116485.git.josef@toxicpanda.com/ * Simplified the error return logic in fs_path_add_from_encrypted(). --- fs/btrfs/send.c | 47 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index b77f96ae2fea..faaade4c35dc 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -32,6 +32,7 @@ #include "ioctl.h" #include "verity.h" #include "lru_cache.h" +#include "fscrypt.h" =20 /* * Maximum number of references an extent can have in order for us to atte= mpt to @@ -579,13 +580,39 @@ static inline int fs_path_add_path(struct fs_path *p,= const struct fs_path *p2) return fs_path_add(p, p2->start, fs_path_len(p2)); } =20 -static int fs_path_add_from_extent_buffer(struct fs_path *p, +static int fs_path_add_from_encrypted(struct btrfs_root *root, + struct fs_path *p, + struct extent_buffer *eb, + unsigned long off, int len, + u64 parent_ino) +{ + struct fscrypt_str fname =3D FSTR_INIT(NULL, 0); + int ret; + + ret =3D fscrypt_fname_alloc_buffer(BTRFS_NAME_LEN, &fname); + if (ret) + return ret; + + ret =3D btrfs_decrypt_name(root, eb, off, len, parent_ino, &fname); + if (!ret) + ret =3D fs_path_add(p, fname.name, fname.len); + + fscrypt_fname_free_buffer(&fname); + return ret; +} + +static int fs_path_add_from_extent_buffer(struct btrfs_root *root, + struct fs_path *p, struct extent_buffer *eb, - unsigned long off, int len) + unsigned long off, int len, + u64 parent_ino) { int ret; char *prepared; =20 + if (root && btrfs_fs_incompat(root->fs_info, ENCRYPT)) + return fs_path_add_from_encrypted(root, p, eb, off, len, parent_ino); + ret =3D fs_path_prepare_for_add(p, len, &prepared); if (ret < 0) return ret; @@ -1062,8 +1089,7 @@ static int iterate_inode_ref(struct btrfs_root *root,= struct btrfs_path *path, } p->start =3D start; } else { - ret =3D fs_path_add_from_extent_buffer(p, eb, name_off, - name_len); + ret =3D fs_path_add_from_extent_buffer(root, p, eb, name_off, name_len,= dir); if (ret < 0) goto out; } @@ -1759,7 +1785,7 @@ static int read_symlink_unencrypted(struct btrfs_root= *root, u64 ino, struct fs_ off =3D btrfs_file_extent_inline_start(ei); len =3D btrfs_file_extent_ram_bytes(path->nodes[0], ei); =20 - return fs_path_add_from_extent_buffer(dest, path->nodes[0], off, len); + return fs_path_add_from_extent_buffer(NULL, dest, path->nodes[0], off, le= n, 0); } =20 static int read_symlink_encrypted(struct btrfs_root *root, u64 ino, struct= fs_path *dest) @@ -2034,18 +2060,19 @@ static int get_first_ref(struct btrfs_root *root, u= 64 ino, iref =3D btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_inode_ref); len =3D btrfs_inode_ref_name_len(path->nodes[0], iref); - ret =3D fs_path_add_from_extent_buffer(name, path->nodes[0], - (unsigned long)(iref + 1), - len); parent_dir =3D found_key.offset; + ret =3D fs_path_add_from_extent_buffer(root, name, path->nodes[0], + (unsigned long)(iref + 1), + len, parent_dir); } else { struct btrfs_inode_extref *extref; extref =3D btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_inode_extref); len =3D btrfs_inode_extref_name_len(path->nodes[0], extref); - ret =3D fs_path_add_from_extent_buffer(name, path->nodes[0], - (unsigned long)&extref->name, len); parent_dir =3D btrfs_inode_extref_parent(path->nodes[0], extref); + ret =3D fs_path_add_from_extent_buffer(root, name, path->nodes[0], + (unsigned long)&extref->name, len, + parent_dir); } if (ret < 0) return ret; --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2FBF343DA53 for ; Fri, 6 Feb 2026 18:25:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402336; cv=none; b=SibU+CndFDPbF26ewYREKPC/UQm+7ThIK0TEGXu6xwImSnxHFKYasf3ziIvz/lET5m/ROsmOTy7q9rTOw9BUIBDhxA/GFmFMRgJo1t8POnPEf/ECM40NIuTY+k61RHJOMVmR3a9vWXeJqZlvWYCw/MRlsiVnyJkuDWx/m8/WNPo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402336; c=relaxed/simple; bh=HDKQWiuOFYGyr6lPE2282Odn8MY1GFwXoZFWSi5il2k=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=drnC5x+XzM/irEj3YH5D478kCWCwGKpgTs8Gem22ZMxMNtcda3ujEJ+Q+1JgEuFLvFv7OhaFIwzdEfIhepWDQCknwTVUsn5ZyGAXV/OEnoJGwMHhN6Aak9HWRZrDW8vhGeZlslMWXW6dk6oBs1YRpOX+CiR9z1zEe/7b+sBLxW0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 565393E760; Fri, 6 Feb 2026 18:24:18 +0000 (UTC) Authentication-Results: smtp-out1.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 2A5E23EA63; Fri, 6 Feb 2026 18:24:18 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id 2L3dCdIxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:18 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 38/43] btrfs: load the inode context before sending writes Date: Fri, 6 Feb 2026 19:23:10 +0100 Message-ID: <20260206182336.1397715-39-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spam-Score: -4.00 X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Queue-Id: 565393E760 X-Rspamd-Server: rspamd1.dmz-prg2.suse.org X-Spam-Level: X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO Content-Type: text/plain; charset="utf-8" From: Josef Bacik For send we will read the pages and copy them into our buffer. Use the fscrypt_inode_open helper to make sure the key is loaded properly before trying to read from the inode so the contents are properly decrypted. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/a307def01bf28c3ed99398868a142180c60= 88527.1706116485.git.josef@toxicpanda.com/ * btrfs_iget() returns binode now. --- fs/btrfs/send.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index faaade4c35dc..bf9a99a9d24d 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -5331,6 +5331,38 @@ static int put_file_data(struct send_ctx *sctx, u64 = offset, u32 len) return ret; } =20 +static int load_fscrypt_context(struct send_ctx *sctx) +{ + struct btrfs_root *root =3D sctx->send_root; + struct name_cache_entry *nce; + struct btrfs_inode *dir; + int ret; + + if (!btrfs_fs_incompat(root->fs_info, ENCRYPT)) + return 0; + + /* + * If we're encrypted we need to load the parent inode in order to make + * sure the encryption context is loaded. We have to do this even if + * we're not encrypted, as we need to make sure that we don't violate + * the rule about encrypted children with non-encrypted parents, which + * is enforced by __fscrypt_file_open. + */ + nce =3D name_cache_search(sctx, sctx->cur_ino, sctx->cur_inode_gen); + if (!nce) { + ASSERT(nce); + return -EINVAL; + } + + dir =3D btrfs_iget(nce->parent_ino, root); + if (IS_ERR(dir)) + return PTR_ERR(dir); + + ret =3D __fscrypt_file_open(&dir->vfs_inode, sctx->cur_inode); + iput(&dir->vfs_inode); + return ret; +} + /* * Read some bytes from the current inode/file and send a write command to * user space. @@ -5348,6 +5380,10 @@ static int send_write(struct send_ctx *sctx, u64 off= set, u32 len) if (ret < 0) return ret; =20 + ret =3D load_fscrypt_context(sctx); + if (ret < 0) + return ret; + TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH, p); TLV_PUT_U64(sctx, BTRFS_SEND_A_FILE_OFFSET, offset); ret =3D put_file_data(sctx, offset, len); --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 62E7943D4EA for ; Fri, 6 Feb 2026 18:25:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402316; cv=none; b=hypOgY86WjxsSODBCxpD96/w7LibtqFOkgN2Wg859gh1V8Rj5btzPsY2gjtERhOshA0aatehaoAvj8Q7WNYXqyFnN6RFqDO0lZBhFBr0PbUKsHbLya9MSSq6ImvVxsmUT8UfJYO7IyOysXzEATx5Rchbzo5hgykK7/9sPbT8pWY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402316; c=relaxed/simple; bh=x51Cg5xsCzKVGWOmBJtih3DOztBpxfL6ZG35siCQ3ms=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rssTanBBGSHqQblax/D6NK1IUIDYn3CYjH3Td73NMzN+LNFRv+Lf3Axaxh3HBEtr1qFd4cB5wUROgrKjbkoDwC6KQvx9PvcrZQcJvpFm1rVGd1EnPHJSjWq5vqJGy66D8/YSgyep8JY76W2YglXV5sOKsje+tqjGGryZoi0nRcs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=D7eLlhRf; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=o3CIKfJw; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="D7eLlhRf"; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="o3CIKfJw" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id E70C05BD38; Fri, 6 Feb 2026 18:24:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402259; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=u9CaRWPO0XEEkcqklCFMGPi2Gz5Gl8+A3LyPbQLO5J0=; b=D7eLlhRfCnY9+hkf+p9KXeqIRvCUG/8VTOeBoBatxFEeHl/Xn0bMe5QwI88ijwFQ+EWRiH uvth8ETfOFWA1ozenxXnMFRMTrVEiI/qif9B64u8lnRZnzmxLfbBQmV88MX8fSKsMuXwcI VopFnAy38Gpat5BMEP9cRgdHwHI2HQc= Authentication-Results: smtp-out2.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402258; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=u9CaRWPO0XEEkcqklCFMGPi2Gz5Gl8+A3LyPbQLO5J0=; b=o3CIKfJwv0X6WkIAr2IiYMi8eN6avPef/zXeFxEXUE36h903nV+Owpb7u4etSigKcKohLD gdEgUE5gkC9KQaUnY11RyWswS+kgcFwLNuK7No7/ssnOEx6DuCszdIE0mPjwJE35N/GHJl 2U392xn0mh+Rg5g2lhcEdHOhoNKz4CY= Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id B9DC33EA63; Fri, 6 Feb 2026 18:24:18 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id ACrlLNIxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:18 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 39/43] btrfs: set the appropriate free space settings in reconfigure Date: Fri, 6 Feb 2026 19:23:11 +0100 Message-ID: <20260206182336.1397715-40-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spamd-Result: default: False [-6.80 / 50.00]; REPLY(-4.00)[]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; DBL_BLOCKED_OPENRESOLVER(0.00)[suse.com:mid,suse.com:email,toxicpanda.com:email]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; TO_DN_SOME(0.00)[]; RCPT_COUNT_TWELVE(0.00)[12]; RCVD_VIA_SMTP_AUTH(0.00)[]; FUZZY_RATELIMITED(0.00)[rspamd.com]; FROM_HAS_DN(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; R_RATELIMIT(0.00)[to_ip_from(RLdafymbgddseoxkryad1wgu8n)]; DKIM_SIGNED(0.00)[suse.com:s=susede1]; RCVD_TLS_ALL(0.00)[] X-Spam-Flag: NO X-Spam-Score: -6.80 X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik btrfs/330 uncovered a problem where we were accidentally turning off the free space tree when we do the transition from ro->rw. This happens because we don't update Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/325598eeb1d23f9ae04f675b4ee218f7e98= e3ff0.1706116485.git.josef@toxicpanda.com/ * Re-wrapped comments. * Fixed argument type. mount_opt is now ull. --- fs/btrfs/disk-io.c | 2 +- fs/btrfs/super.c | 28 +++++++++++++++------------- fs/btrfs/super.h | 3 ++- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index f47a20976b53..993d2080fcbf 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -3416,7 +3416,7 @@ int __cold open_ctree(struct super_block *sb, struct = btrfs_fs_devices *fs_device * Handle the space caching options appropriately now that we have the * super block loaded and validated. */ - btrfs_set_free_space_cache_settings(fs_info); + btrfs_set_free_space_cache_settings(fs_info, &fs_info->mount_opt); =20 if (!btrfs_check_options(fs_info, &fs_info->mount_opt, sb->s_flags)) { ret =3D -EINVAL; diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 06788b27a870..4a2887147ead 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -745,10 +745,10 @@ bool btrfs_check_options(const struct btrfs_fs_info *= info, } =20 /* - * This is subtle, we only call this during open_ctree(). We need to pre-= load - * the mount options with the on-disk settings. Before the new mount API = took - * effect we would do this on mount and remount. With the new mount API w= e'll - * only do this on the initial mount. + * Because we have an odd set of behavior with turning on and off the spac= e cache + * and free space tree we have to call this before we start the mount oper= ation + * after we load the super, or before we start remount. This is to make s= ure we + * have the proper free space settings in place if the user didn't specify= any. * * This isn't a change in behavior, because we're using the current state = of the * file system to set the current mount options. If you mounted with spec= ial @@ -756,21 +756,22 @@ bool btrfs_check_options(const struct btrfs_fs_info *= info, * settings, because mounting without these features cleared the on-disk * settings, so this being called on re-mount is not needed. */ -void btrfs_set_free_space_cache_settings(struct btrfs_fs_info *fs_info) +void btrfs_set_free_space_cache_settings(struct btrfs_fs_info *fs_info, + unsigned long long *mount_opt) { - if (fs_info->sectorsize !=3D PAGE_SIZE && btrfs_test_opt(fs_info, SPACE_C= ACHE)) { + if (fs_info->sectorsize !=3D PAGE_SIZE && btrfs_raw_test_opt(*mount_opt, = SPACE_CACHE)) { btrfs_info(fs_info, "forcing free space tree for sector size %u with page size %lu", fs_info->sectorsize, PAGE_SIZE); - btrfs_clear_opt(fs_info->mount_opt, SPACE_CACHE); - btrfs_set_opt(fs_info->mount_opt, FREE_SPACE_TREE); + btrfs_clear_opt(*mount_opt, SPACE_CACHE); + btrfs_set_opt(*mount_opt, FREE_SPACE_TREE); } =20 /* * At this point our mount options are populated, so we only mess with * these settings if we don't have any settings already. */ - if (btrfs_test_opt(fs_info, FREE_SPACE_TREE)) + if (btrfs_raw_test_opt(*mount_opt, FREE_SPACE_TREE)) return; =20 if (btrfs_is_zoned(fs_info) && @@ -780,10 +781,10 @@ void btrfs_set_free_space_cache_settings(struct btrfs= _fs_info *fs_info) return; } =20 - if (btrfs_test_opt(fs_info, SPACE_CACHE)) + if (btrfs_raw_test_opt(*mount_opt, SPACE_CACHE)) return; =20 - if (btrfs_test_opt(fs_info, NOSPACECACHE)) + if (btrfs_raw_test_opt(*mount_opt, NOSPACECACHE)) return; =20 /* @@ -791,9 +792,9 @@ void btrfs_set_free_space_cache_settings(struct btrfs_f= s_info *fs_info) * them ourselves based on the state of the file system. */ if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) - btrfs_set_opt(fs_info->mount_opt, FREE_SPACE_TREE); + btrfs_set_opt(*mount_opt, FREE_SPACE_TREE); else if (btrfs_free_space_cache_v1_active(fs_info)) - btrfs_set_opt(fs_info->mount_opt, SPACE_CACHE); + btrfs_set_opt(*mount_opt, SPACE_CACHE); } =20 static void set_device_specific_options(struct btrfs_fs_info *fs_info) @@ -1573,6 +1574,7 @@ static int btrfs_reconfigure(struct fs_context *fc) =20 sync_filesystem(sb); set_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state); + btrfs_set_free_space_cache_settings(fs_info, &ctx->mount_opt); =20 if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags)) return -EINVAL; diff --git a/fs/btrfs/super.h b/fs/btrfs/super.h index d80a86acfbbe..584f428d36e2 100644 --- a/fs/btrfs/super.h +++ b/fs/btrfs/super.h @@ -16,7 +16,8 @@ bool btrfs_check_options(const struct btrfs_fs_info *info, int btrfs_sync_fs(struct super_block *sb, int wait); char *btrfs_get_subvol_name_from_objectid(struct btrfs_fs_info *fs_info, u64 subvol_objectid); -void btrfs_set_free_space_cache_settings(struct btrfs_fs_info *fs_info); +void btrfs_set_free_space_cache_settings(struct btrfs_fs_info *fs_info, + unsigned long long *mount_opt); =20 static inline struct btrfs_fs_info *btrfs_sb(struct super_block *sb) { --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F1C3643E490 for ; Fri, 6 Feb 2026 18:25:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402341; cv=none; b=PAuiV18pL2wNqY7Nyu2MkjYVsMq8Z8YA9PVvmsu3AT5K4a+lGGoQcFdEIXhzja7Ue3f7pcH5PJ62j835qufsMu0PzO+spXjZLVNLgjRt+O66Rdg/uQC2Uq3tuTo79sYO2D+HcGIF+i1DjbM2EYsVZMm6r+O1t4OY+Xg7dqxALcw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402341; c=relaxed/simple; bh=fZxW8wexZZ7fyQehWlyzdfCe9GH5o7H4GiTaE8gAS1o=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=SOcxSr2TaYqK9VxwvkFdN4seOieoCLw7pynPYxX6RpTgCierPjJKOuZKVRnQbjWjLRVtTY3SHOfHuTE+tzOjFIMULvL7DRj/OtMyRAcHrCCXK5Ink5+Gx3F9mRd3d6UXz2EI1wGE4w963POZe8DYKGH/xkf+qWWvXObf82d+f+U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 69D0A3E745; Fri, 6 Feb 2026 18:24:19 +0000 (UTC) Authentication-Results: smtp-out1.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 3F3A63EA63; Fri, 6 Feb 2026 18:24:19 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id SJfzDtMxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:19 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 40/43] btrfs: support encryption with log replay Date: Fri, 6 Feb 2026 19:23:12 +0100 Message-ID: <20260206182336.1397715-41-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spam-Score: -4.00 X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Queue-Id: 69D0A3E745 X-Rspamd-Server: rspamd1.dmz-prg2.suse.org X-Spam-Level: X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO Content-Type: text/plain; charset="utf-8" From: Josef Bacik Log replay needs a few tweaks in order to make sure everything works with encryption. 1. Copy in the fscrypt context if we find one in the log. 2. Set NEEDS_FULL_SYNC when we update the inode context so all of the items are copied into the log at fsync time. This makes replay of encrypted files work properly. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/d1ada7ac632c2ab554a840c7ba29b53a93b= 9855f.1706116485.git.josef@toxicpanda.com/ * Adapted to the redesigned encryption context storing. - No need to specially handle the extent item now. It is not any different to before and the new context items are simply copied. --- fs/btrfs/fscrypt.c | 1 + fs/btrfs/tree-log.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c index bcb86cbaa171..cbdeaa75a868 100644 --- a/fs/btrfs/fscrypt.c +++ b/fs/btrfs/fscrypt.c @@ -145,6 +145,7 @@ static int btrfs_fscrypt_set_context(struct inode *inod= e, const void *ctx, goto out_err; } =20 + set_bit(BTRFS_INODE_NEEDS_FULL_SYNC, &BTRFS_I(inode)->runtime_flags); leaf =3D path->nodes[0]; ptr =3D btrfs_item_ptr_offset(leaf, path->slots[0]); =20 diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 0d01e31a4592..fa75531f9633 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -2922,7 +2922,9 @@ static int replay_one_buffer(struct extent_buffer *eb, continue; =20 /* these keys are simply copied */ - if (wc->log_key.type =3D=3D BTRFS_XATTR_ITEM_KEY) { + if (wc->log_key.type =3D=3D BTRFS_XATTR_ITEM_KEY || + wc->log_key.type =3D=3D BTRFS_FSCRYPT_INODE_CTX_KEY || + wc->log_key.type =3D=3D BTRFS_FSCRYPT_CTX_KEY) { ret =3D overwrite_item(wc); if (ret) break; --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6AAF544B665 for ; Fri, 6 Feb 2026 18:25:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402320; cv=none; b=Rjw8J8FQ7mZKfJGg3zw6L8QmjdnUi0Dl9A+YnOtwW48pTunLtPCRYAkqCyoLuD7xIbGqJtGfK3sSiAqL9sQhDIcTp3h2v8un9Sm7+TjNYHS/lp1X5ampHZOXOSEAfNO8u/F4Hop62/SoZhR1eD2nFsbtkuyP2/dPj/c3QxxOoq0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402320; c=relaxed/simple; bh=wBNEVAk30UYG4m43mdnD0/y+iUy42IO/9sgi8dADWHU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=lXCCKNrwqlH7fX14LK3TyYKy2/rii+u/XDvmA0JnB/2vCVoq6RoTa2mALJVrZvpwcshyZdKZzBinmfhrrAdI/nRwSTYnY1qQ8GHBWXWNXKyN0o937t7rZ2quQ9FjdqgFXSo4pYOWsIfmdjAlZL//kju6wfZ3EMOnZJkqjhyLaHg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=QvI3NU/7; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=QvI3NU/7; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="QvI3NU/7"; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="QvI3NU/7" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id D182C5BD30; Fri, 6 Feb 2026 18:24:19 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402259; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=naQJMp1+YtAXW7i9Wmu4TNf5csVeJ0Q0sjWmskdGN3Q=; b=QvI3NU/7hIF95rabCHRgR+X9m6CVlVvOttcPtm3aEGXNdbg2Wm2NTtNfy38JSZpmytrMLv tVP1G8tV4DHSmyYF0Kg1A2AaJCHIZHcbHLKVzXI+4Gh0F4BLwEyNsNIwA0O0LJMg0SMTF0 FRHZpFDDbOIFaMymXdQKRiSc9pqtPs8= Authentication-Results: smtp-out2.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402259; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=naQJMp1+YtAXW7i9Wmu4TNf5csVeJ0Q0sjWmskdGN3Q=; b=QvI3NU/7hIF95rabCHRgR+X9m6CVlVvOttcPtm3aEGXNdbg2Wm2NTtNfy38JSZpmytrMLv tVP1G8tV4DHSmyYF0Kg1A2AaJCHIZHcbHLKVzXI+4Gh0F4BLwEyNsNIwA0O0LJMg0SMTF0 FRHZpFDDbOIFaMymXdQKRiSc9pqtPs8= Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id A6BE23EA63; Fri, 6 Feb 2026 18:24:19 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id QKcdKNMxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:19 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 41/43] btrfs: disable auto defrag on encrypted files Date: Fri, 6 Feb 2026 19:23:13 +0100 Message-ID: <20260206182336.1397715-42-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spamd-Result: default: False [-6.80 / 50.00]; REPLY(-4.00)[]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; DBL_BLOCKED_OPENRESOLVER(0.00)[suse.com:mid,suse.com:email,toxicpanda.com:email]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; TO_DN_SOME(0.00)[]; RCPT_COUNT_TWELVE(0.00)[12]; RCVD_VIA_SMTP_AUTH(0.00)[]; FUZZY_RATELIMITED(0.00)[rspamd.com]; FROM_HAS_DN(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; R_RATELIMIT(0.00)[to_ip_from(RLdafymbgddseoxkryad1wgu8n)]; DKIM_SIGNED(0.00)[suse.com:s=susede1]; RCVD_TLS_ALL(0.00)[] X-Spam-Flag: NO X-Spam-Score: -6.80 X-Spam-Level: Content-Type: text/plain; charset="utf-8" From: Josef Bacik We will drop the inode and re-look it up to do defrag with auto defrag, which means we could lose the encryption policy. Auto defrag needs to be reworked to just hold onto the inode for scheduling later so we don't lose the context. For now just disable it if the file is encrypted. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/b717912bf88797b3044a3c2724b59b1ecc1= 7ea78.1706116485.git.josef@toxicpanda.com/ * No changes since. --- fs/btrfs/defrag.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/btrfs/defrag.c b/fs/btrfs/defrag.c index f64c0502cef9..cd6ea6e5d3ea 100644 --- a/fs/btrfs/defrag.c +++ b/fs/btrfs/defrag.c @@ -126,6 +126,14 @@ void btrfs_add_inode_defrag(struct btrfs_inode *inode,= u32 extent_thresh) if (!need_auto_defrag(fs_info)) return; =20 + /* + * Since we have to read the inode at defrag time disable auto defrag + * for encrypted inodes until we have code to read the parent and load + * the encryption context. + */ + if (IS_ENCRYPTED(&inode->vfs_inode)) + return; + if (test_bit(BTRFS_INODE_IN_DEFRAG, &inode->runtime_flags)) return; =20 --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5AAE444CF22 for ; Fri, 6 Feb 2026 18:25:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402345; cv=none; b=rIRanooA7HUU5Gt9K9K9YvBRd8DiPjQKsbeMbqdywoRjx4Ww+452BRX6TydttlXTgarhr2OpE1RECXRXI6FyIchXhS/G5JwOIZhn/DF+9kEYzVqk3mGQtZ+bvmqxmzrHxdOGjw7kFtWMUYXcofNZHN3kLKuYqAWBBdtzv4HQjLg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402345; c=relaxed/simple; bh=JHBmb8Tt6hvKekTkLiQSRIgJNkTIdVCMwN3m2x25jJQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=lhNOLPloUCP/gjS8fEhdzAi7DDV7srkkmlPMW1G7uNjlAS2owQ9ZmKjfKmeYibmTnWVTLkqivRLOkSaIfssa45qa+tlPaeCe1wjkxk/WClhZNl+X2otaJLmmW3DhoQTn431yVdojUKTFsz466oRAc1Y2SKfTnitPfjJf9VYq5Cg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 5B60F3E763; Fri, 6 Feb 2026 18:24:20 +0000 (UTC) Authentication-Results: smtp-out1.suse.de; none Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 2E7C83EA63; Fri, 6 Feb 2026 18:24:20 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id yPvbCtQxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:20 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v6 42/43] btrfs: disable encryption on RAID5/6 Date: Fri, 6 Feb 2026 19:23:14 +0100 Message-ID: <20260206182336.1397715-43-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Spam-Score: -4.00 X-Rspamd-Pre-Result: action=no action; module=replies; Message is reply to one we originated X-Rspamd-Action: no action X-Rspamd-Queue-Id: 5B60F3E763 X-Rspamd-Server: rspamd1.dmz-prg2.suse.org X-Spam-Level: X-Spamd-Result: default: False [-4.00 / 50.00]; REPLY(-4.00)[] X-Spam-Flag: NO Content-Type: text/plain; charset="utf-8" From: Josef Bacik The RAID5/6 code will re-arrange bios and submit them through a different mechanism. This is problematic with inline encryption as we have to get the bio and csum it after it's been encrypted, and the radi5/6 bio's don't have the btrfs_bio embedded, so we have no way to get the csums put on disk. This isn't an unsolvable problem, but would require a bit of reworking. Since we discourage users from using this code currently simply don't allow encryption on RAID5/6 setups. If there's sufficient demand in the future we can add the support for this. Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/941f02bb923edadae1aea4ae3e5aa6ba05d= 1215a.1706116485.git.josef@toxicpanda.com/ * No changes since. --- fs/btrfs/ioctl.c | 4 ++++ fs/btrfs/super.c | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index a8adf99ad0a8..1bade8fea16e 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -5198,6 +5198,10 @@ long btrfs_ioctl(struct file *file, unsigned int return -EOPNOTSUPP; if (sb_rdonly(fs_info->sb)) return -EROFS; + if (btrfs_fs_incompat(fs_info, RAID56)) { + btrfs_warn(fs_info, "can't enable encryption with RAID5/6"); + return -EINVAL; + } /* * If we crash before we commit, nothing encrypted could have * been written so it doesn't matter whether the encrypted diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 4a2887147ead..aefcbe56e85a 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -734,6 +734,12 @@ bool btrfs_check_options(const struct btrfs_fs_info *i= nfo, if (btrfs_check_mountopts_zoned(info, mount_opt)) ret =3D false; =20 + if (btrfs_fs_incompat(info, RAID56) && + btrfs_raw_test_opt(*mount_opt, TEST_DUMMY_ENCRYPTION)) { + btrfs_err(info, "cannot use test_dummy_encryption with RAID5/6"); + ret =3D false; + } + if (!test_bit(BTRFS_FS_STATE_REMOUNTING, &info->fs_state)) { if (btrfs_raw_test_opt(*mount_opt, SPACE_CACHE)) { btrfs_warn(info, --=20 2.51.0 From nobody Sun Feb 8 07:21:41 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AC22A44BC82 for ; Fri, 6 Feb 2026 18:25:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402324; cv=none; b=ozd/6oU/KLiHEMI8jyH222kPmrQo9QZUZa8IceuoQnyFLwPsukuQJXImku6QImLxjFSvslDSivGqPT3cJwRWpbl/BhAOvWLkteK4wHVoA2Y72wfDVv5livgO5W3vkx97ra/fhjkGAwnVIhzHg0c3n5vJY3YpZ8ajFhgaOjJIDr8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770402324; c=relaxed/simple; bh=GfnO68KXoBeEnqOHN5arOyt9p0B9ZFFw2XWnNOvoB4o=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eYzSTrzIxU+OiNVEu6RcCSpvoYaaP+O6Uc/MLZ7EkPxKGOtNoxVyE+NK3jcGQx5vK407AHPy5FkAocQvNBF8LBl7OGobpQMCFdv0rXjQ/nBpKN6ff+U2t1NUeELJif3+JiUi2ZVEo00dfInwNNvrsYb2koTvfz9brhUZLAhxHtA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=ZKtaFJlK; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b=ZKtaFJlK; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="ZKtaFJlK"; dkim=pass (1024-bit key) header.d=suse.com header.i=@suse.com header.b="ZKtaFJlK" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id BCF995BD35; Fri, 6 Feb 2026 18:24:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402260; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=sbKbFDBvAoHVV+zcvNVZ9N7jQeEHQ8nUuWj4G99Gpu0=; b=ZKtaFJlKLO4EkBCD9rOxrt3wRA/eB6Va5Y04z9GKP2c1HIs0s7R2RltIyO5sKyCH2X+rcq i+OmSfDOUess3SWpjYnq2Gs4zxWhqjAwgTz3NWfX6qx6VoCVpYTL6/9CtVdoqZCM4nHim/ sDauKpUpaqCiUu0grk0La6pHWEyEhWw= Authentication-Results: smtp-out2.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1770402260; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=sbKbFDBvAoHVV+zcvNVZ9N7jQeEHQ8nUuWj4G99Gpu0=; b=ZKtaFJlKLO4EkBCD9rOxrt3wRA/eB6Va5Y04z9GKP2c1HIs0s7R2RltIyO5sKyCH2X+rcq i+OmSfDOUess3SWpjYnq2Gs4zxWhqjAwgTz3NWfX6qx6VoCVpYTL6/9CtVdoqZCM4nHim/ sDauKpUpaqCiUu0grk0La6pHWEyEhWw= Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 8FD033EA63; Fri, 6 Feb 2026 18:24:20 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id eMOcItQxhmkTCQAAD6G6ig (envelope-from ); Fri, 06 Feb 2026 18:24:20 +0000 From: Daniel Vacek To: Chris Mason , Josef Bacik , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Jens Axboe , David Sterba Cc: linux-block@vger.kernel.org, Daniel Vacek , linux-fscrypt@vger.kernel.org, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org, Boris Burkov Subject: [PATCH v6 43/43] btrfs: disable send if we have encryption enabled Date: Fri, 6 Feb 2026 19:23:15 +0100 Message-ID: <20260206182336.1397715-44-neelx@suse.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260206182336.1397715-1-neelx@suse.com> References: <20260206182336.1397715-1-neelx@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spam-Score: -6.80 X-Spamd-Result: default: False [-6.80 / 50.00]; REPLY(-4.00)[]; BAYES_HAM(-3.00)[100.00%]; NEURAL_HAM_LONG(-1.00)[-1.000]; MID_CONTAINS_FROM(1.00)[]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; RCVD_COUNT_TWO(0.00)[2]; FROM_HAS_DN(0.00)[]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; TO_DN_SOME(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; RCVD_VIA_SMTP_AUTH(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; DKIM_SIGNED(0.00)[suse.com:s=susede1]; FUZZY_RATELIMITED(0.00)[rspamd.com]; RCPT_COUNT_TWELVE(0.00)[13]; DBL_BLOCKED_OPENRESOLVER(0.00)[toxicpanda.com:email,suse.com:mid,suse.com:email]; RCVD_TLS_ALL(0.00)[] X-Spam-Level: X-Spam-Flag: NO Content-Type: text/plain; charset="utf-8" From: Josef Bacik send needs to track the dir item values to see if files were renamed when doing an incremental send. There is code to decrypt the names, but this breaks the code that checks to see if something was overwritten. Until this gap is closed we need to disable send on encrypted file systems. Fixing this is straightforward, but a medium sized project. Signed-off-by: Josef Bacik Reviewed-by: Boris Burkov Signed-off-by: Daniel Vacek --- v5: https://lore.kernel.org/linux-btrfs/62ce86b38e2575c542eed7fbe8d986e6849= 6b1d7.1706116485.git.josef@toxicpanda.com/ * No changes since. --- fs/btrfs/send.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index bf9a99a9d24d..a3aa95ce4f87 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -8095,6 +8095,12 @@ long btrfs_ioctl_send(struct btrfs_root *send_root, = const struct btrfs_ioctl_sen if (!capable(CAP_SYS_ADMIN)) return -EPERM; =20 + if (btrfs_fs_incompat(fs_info, ENCRYPT)) { + btrfs_err(fs_info, + "send with encryption enabled isn't currently suported"); + return -EINVAL; + } + /* * The subvolume must remain read-only during send, protect against * making it RW. This also protects against deletion. --=20 2.51.0