From nobody Mon Feb 9 13:57:12 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