Re: [PATCH v6 35/43] btrfs: make btrfs_ref_to_path handle encrypted filenames
Daniel Vacek <[email protected]> Wed, 25 Mar 2026 16:27:09 +0100
| Newsgroups | org.kernel.vger.linux-fscrypt,org.kernel.vger.linux-block,org.kernel.vger.linux-btrfs,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <CAPjX3Ffn_Q1mPs3-6ZpdOTrp6i=2c8ZiJSyR9Mu_MTe_=OQjnA@mail.gmail.com> |
On Sun, 8 Feb 2026 at 16:04, Chris Mason <[email protected]> wrote: > Daniel Vacek <[email protected]> wrote: > > From: Josef Bacik <[email protected]> > > > > We use this helper for inode-resolve and path resolution in send, so > > update this helper to properly decrypt any encrypted names it finds. > > > > Hi everyone, > > I'm running the btrfs for-next branch, along with the encryption patch set > through my AI review prompts: > > https://github.com/masoncl/review-prompts > > I'm working on some btrfs specific instructions, but hopefully these reviews > are already useful. I've kept the headers to make it easier to double check > that I'm actually replying to the right email, but please let me know if any > parts of this are incorrect: > > > commit 473bbb12052f731a4e2ce3c48e7d7e9f2510528b > Author: Josef Bacik <[email protected]> > > btrfs: make btrfs_ref_to_path handle encrypted filenames > > This commit updates btrfs_ref_to_path() to handle encrypted filenames by > introducing a new copy_resolved_iref_to_buf() helper that can decrypt > names when the filesystem has encryption enabled. > > > diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c > > index 9bb406f7dd302..577c3ef87791a 100644 > > --- a/fs/btrfs/backref.c > > +++ b/fs/btrfs/backref.c > > [ ... ] > > > @@ -2107,6 +2108,39 @@ int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid, > > return ret; > > } > > > > +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 = fs_root->fs_info; > > + struct fscrypt_str fname = FSTR_INIT(NULL, 0); > > + int ret; > > + > > + /* No encryption, just copy the name in. */ > > + if (!btrfs_fs_incompat(fs_info, ENCRYPT)) { > > + *bytes_left -= name_len; > > + if (*bytes_left >= 0) > > + read_extent_buffer(eb, dest + *bytes_left, name_off, name_len); > > + return 0; > > + } > > + > > + ret = fscrypt_fname_alloc_buffer(BTRFS_NAME_LEN, &fname); > > + if (ret) > > + return ret; > > + > > + ret = btrfs_decrypt_name(fs_root, eb, name_off, name_len, parent, &fname); > > + if (ret) > > + goto out; > > + > > + *bytes_left -= fname.len; > > + if (*bytes_left >= 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] = '\0'; > > > > while (1) { > > - bytes_left -= name_len; > > - if (bytes_left >= 0) > > - read_extent_buffer(eb, dest + bytes_left, > > - name_off, name_len); > > + ret = copy_resolved_iref_to_buf(fs_root, eb, dest, parent, > > + name_off, name_len, &bytes_left); > > + if (ret) > > + break; > > if (eb != eb_in) { > > if (!path->skip_locking) > > btrfs_tree_read_unlock(eb); > > Does this code leak the extent buffer when copy_resolved_iref_to_buf() > fails? Looking at the loop structure in btrfs_ref_to_path(), when > copy_resolved_iref_to_buf() returns an error and we break at line 2178, > the cleanup code that follows never executes: > > if (eb != eb_in) { > if (!path->skip_locking) > btrfs_tree_read_unlock(eb); > free_extent_buffer(eb); > } > > This can happen in subsequent iterations where eb != eb_in (the extent > buffer was detached from path->nodes[0] and set to NULL in the previous > iteration). The cleanup at the end of the function (btrfs_release_path() > at line 2216) cannot free eb since it's no longer in the path. Right, the break should be placed only after the cleanup. --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -2174,13 +2174,14 @@ char *btrfs_ref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path, while (1) { ret = copy_resolved_iref_to_buf(fs_root, eb, dest, parent, name_off, name_len, &bytes_left); - if (ret) - break; if (eb != eb_in) { if (!path->skip_locking) btrfs_tree_read_unlock(eb); free_extent_buffer(eb); } + if (ret) + break; + ret = btrfs_find_item(fs_root, path, parent, 0, BTRFS_INODE_REF_KEY, &found_key); if (ret > 0) Thanks. --nX > This could be triggered when decryption fails in copy_resolved_iref_to_buf() > due to btrfs_iget() error, fscrypt_fname_alloc_buffer() ENOMEM, or > fscrypt_prepare_readdir() failure on an encrypted filesystem. > > Before this patch, the name copy operation (read_extent_buffer) could not > fail, so the cleanup always happened before any error check. > On Sun, 8 Feb 2026 at 16:04, Chris Mason <[email protected]> wrote: > > Daniel Vacek <[email protected]> wrote: > > From: Josef Bacik <[email protected]> > > > > We use this helper for inode-resolve and path resolution in send, so > > update this helper to properly decrypt any encrypted names it finds. > > > > Hi everyone, > > I'm running the btrfs for-next branch, along with the encryption patch set > through my AI review prompts: > > https://github.com/masoncl/review-prompts > > I'm working on some btrfs specific instructions, but hopefully these reviews > are already useful. I've kept the headers to make it easier to double check > that I'm actually replying to the right email, but please let me know if any > parts of this are incorrect: > > > commit 473bbb12052f731a4e2ce3c48e7d7e9f2510528b > Author: Josef Bacik <[email protected]> > > btrfs: make btrfs_ref_to_path handle encrypted filenames > > This commit updates btrfs_ref_to_path() to handle encrypted filenames by > introducing a new copy_resolved_iref_to_buf() helper that can decrypt > names when the filesystem has encryption enabled. > > > diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c > > index 9bb406f7dd302..577c3ef87791a 100644 > > --- a/fs/btrfs/backref.c > > +++ b/fs/btrfs/backref.c > > [ ... ] > > > @@ -2107,6 +2108,39 @@ int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid, > > return ret; > > } > > > > +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 = fs_root->fs_info; > > + struct fscrypt_str fname = FSTR_INIT(NULL, 0); > > + int ret; > > + > > + /* No encryption, just copy the name in. */ > > + if (!btrfs_fs_incompat(fs_info, ENCRYPT)) { > > + *bytes_left -= name_len; > > + if (*bytes_left >= 0) > > + read_extent_buffer(eb, dest + *bytes_left, name_off, name_len); > > + return 0; > > + } > > + > > + ret = fscrypt_fname_alloc_buffer(BTRFS_NAME_LEN, &fname); > > + if (ret) > > + return ret; > > + > > + ret = btrfs_decrypt_name(fs_root, eb, name_off, name_len, parent, &fname); > > + if (ret) > > + goto out; > > + > > + *bytes_left -= fname.len; > > + if (*bytes_left >= 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] = '\0'; > > > > while (1) { > > - bytes_left -= name_len; > > - if (bytes_left >= 0) > > - read_extent_buffer(eb, dest + bytes_left, > > - name_off, name_len); > > + ret = copy_resolved_iref_to_buf(fs_root, eb, dest, parent, > > + name_off, name_len, &bytes_left); > > + if (ret) > > + break; > > if (eb != eb_in) { > > if (!path->skip_locking) > > btrfs_tree_read_unlock(eb); > > Does this code leak the extent buffer when copy_resolved_iref_to_buf() > fails? Looking at the loop structure in btrfs_ref_to_path(), when > copy_resolved_iref_to_buf() returns an error and we break at line 2178, > the cleanup code that follows never executes: > > if (eb != eb_in) { > if (!path->skip_locking) > btrfs_tree_read_unlock(eb); > free_extent_buffer(eb); > } > > This can happen in subsequent iterations where eb != eb_in (the extent > buffer was detached from path->nodes[0] and set to NULL in the previous > iteration). The cleanup at the end of the function (btrfs_release_path() > at line 2216) cannot free eb since it's no longer in the path. > > This could be triggered when decryption fails in copy_resolved_iref_to_buf() > due to btrfs_iget() error, fscrypt_fname_alloc_buffer() ENOMEM, or > fscrypt_prepare_readdir() failure on an encrypted filesystem. > > Before this patch, the name copy operation (read_extent_buffer) could not > fail, so the cleanup always happened before any error check. >