Re: [PATCH] fs: btrfs: report file sizes from readdir

Qu Wenruo <[email protected]> Sun, 2 Aug 2026 16:17:59 +0930
Newsgroups gmane.comp.boot-loaders.u-boot
Message-ID <[email protected]>

在 2026/8/2 15:32, Alexey Charkov 写道:
> Hi Cole,
> 
> On Sun, Aug 2, 2026 at 9:42 AM Cole Munz <[email protected]> wrote:
>>
>> btrfs_readdir() zeroes the dirent and fills in only the name and the
>> type, so dent->size stays 0 and every file is listed as zero bytes:
>>
>>    => ls host 0 /
>>            0   f_192k.bin
>>            0   small_3k.bin
>>
>> Reads themselves are fine, since btrfs_read() takes the size from
>> btrfs_size(), which does its own inode item lookup. It affects EFI
>> too: dir_read() in lib/efi_loader/efi_file.c copies dent->size into
>> both file_size and physical_size, so an EFI application enumerating a
>> directory on btrfs sees every file as empty, which is the generic-code
>> path Alexey's readdir series moves btrfs onto.
>>
>> The custom listing that fs_ls_generic() replaced looked the inode item
>> up and printed the real size, and every other filesystem in the tree
>> fills dent->size in its own readdir: ext4fs.c:327, exfat io.c:805,
>> erofs fs.c:186, squashfs sqfs.c:1095 and fat.c:1555.
>>
>> btrfs_next_dir_entry() already has the dir item mapped, so read the
>> key it points at while we are there and hand it back to the caller,
>> and use that to reach the inode item. A subvolume entry points at a
>> root item instead and has no size of its own, so leave that one at 0.
>>
>>    => ls host 0 /
>>       196608   f_192k.bin
>>         3000   small_3k.bin
>>
>> Fixes: 31cf3f177823 ("fs: btrfs: use fs_ls_generic() and drop custom implementation")
>> Signed-off-by: Cole Munz <[email protected]>
>> ---
>> The Fixes: commit is in Tom's tree, applied 2026-07-10 (b4-ty
>> [email protected]); it has not
>> reached master yet, so that SHA will not resolve on a public clone today.
>>
>> Found by a btrfs test suite I am preparing for test/py; I will send that
>> separately once the pending zstd inline extent fix is applied, since it
>> depends on it.
>>   fs/btrfs/btrfs.c    | 24 +++++++++++++++++++++++-
>>   fs/btrfs/ctree.h    |  3 ++-
>>   fs/btrfs/dir-item.c |  7 ++++++-
>>   3 files changed, 31 insertions(+), 3 deletions(-)
>>
>> diff --git a/fs/btrfs/btrfs.c b/fs/btrfs/btrfs.c
>> index e663dda12e80..b2856be0662f 100644
>> --- a/fs/btrfs/btrfs.c
>> +++ b/fs/btrfs/btrfs.c
>> @@ -93,7 +93,10 @@ int btrfs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp)
>>          struct btrfs_dir_stream *dirs = container_of(fs_dirs, struct btrfs_dir_stream, parent);
>>          struct btrfs_fs_info *fs_info = current_fs_info;
>>          struct fs_dirent *dent = &dirs->dirent;
>> +       struct btrfs_inode_item *ii;
>>          struct btrfs_root *root;
>> +       struct btrfs_path path;
>> +       struct btrfs_key location;
>>          struct btrfs_key key;
>>          u8 type;
>>          int ret;
>> @@ -110,13 +113,32 @@ int btrfs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp)
>>
>>          memset(dent, 0, sizeof(*dent));
>>          ret = btrfs_next_dir_entry(root, dirs->ino, &dirs->offset, dent->name,
>> -                                  sizeof(dent->name), &type);
>> +                                  sizeof(dent->name), &type, &location);
>>          if (ret < 0)
>>                  return ret;
>>          if (ret > 0)
>>                  return -ENOENT;
>>
>>          dent->type = btrfs_dirent_type_to_fs_type(type);
>> +
>> +       /*
>> +        * A subvolume entry points at a root item rather than an inode, and
>> +        * has no size of its own.  Everything else carries one, and the fs
>> +        * layer prints it, so look it up.
>> +        */
>> +       if (location.type == BTRFS_INODE_ITEM_KEY) {
>> +               btrfs_init_path(&path);
>> +               ret = btrfs_search_slot(NULL, root, &location, &path, 0, 0);
>> +               if (ret == 0) {
>> +                       ii = btrfs_item_ptr(path.nodes[0], path.slots[0],
>> +                                           struct btrfs_inode_item);
>> +                       dent->size = btrfs_inode_size(path.nodes[0], ii);
>> +               }
>> +               btrfs_release_path(&path);
> 
> The above repeats much of the calls that have just been done for this
> exact entry several lines above inside btrfs_read_next_dir_enrty()
> 
> Can you perhaps refactor to avoid this repetition?

Sorry I didn't see the point.

The function btrfs_read_next_dirty_entry() is search the key inside the 
parent directory.

Meanwhile this one is search for the inode item of the child entry.

They are completely different and I didn't see why "refactor" can improve.

In fact, this version is much simpler:

  fs/btrfs/btrfs.c    | 24 +++++++++++++++++++++++-
  fs/btrfs/ctree.h    |  3 ++-
  fs/btrfs/dir-item.c |  7 ++++++-
  3 files changed, 31 insertions(+), 3 deletions(-)

Compared to the v2:

  fs/btrfs/btrfs.c    | 75 +++++++++++++++++++++++++++++++--------------
  fs/btrfs/ctree.h    |  3 +-
  fs/btrfs/dir-item.c |  7 ++++-
  3 files changed, 60 insertions(+), 25 deletions(-)

Thanks,
Qu
> 
> Best regards,
> Alexey