Merge patch series "fs: btrfs: report file sizes from readdir"

Cole Munz <Munzzyy1@proton.me> says:

As agreed on the v1 thread, the fix and the cleanups are now separate
patches:

- patch 1 is the fix in the v1 shape
- patch 2 makes btrfs_search_slot() release the path on error like
  the kernel version does. That is where the btrfs_size() leak came
  from. Suggested by Qu.
- patch 3 is the dedup Alexey asked about. One helper shared by
  btrfs_readdir() and btrfs_size().

The pending btrfs test suite still passes on top of the readdir
series: 5 passed.

Link: https://lore.kernel.org/r/cover.1785660029.git.Munzzyy1@proton.me
This commit is contained in:
Tom Rini
2026-08-10 12:39:24 -06:00
4 changed files with 74 additions and 29 deletions
+54 -23
View File
@@ -88,12 +88,43 @@ static unsigned int btrfs_dirent_type_to_fs_type(u8 dirent_type)
}
}
/*
* Read the size stored in an inode item. A missing item is -ENOENT and
* leaves *size untouched.
*/
static int btrfs_get_inode_size(struct btrfs_root *root, u64 ino, u64 *size)
{
struct btrfs_inode_item *ii;
struct btrfs_path path;
struct btrfs_key key;
int ret;
key.objectid = ino;
key.type = BTRFS_INODE_ITEM_KEY;
key.offset = 0;
btrfs_init_path(&path);
ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
if (ret < 0)
return ret;
if (ret > 0)
ret = -ENOENT;
if (!ret) {
ii = btrfs_item_ptr(path.nodes[0], path.slots[0],
struct btrfs_inode_item);
*size = btrfs_inode_size(path.nodes[0], ii);
}
btrfs_release_path(&path);
return ret;
}
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_root *root;
struct btrfs_key location;
struct btrfs_key key;
u8 type;
int ret;
@@ -110,13 +141,29 @@ 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) {
u64 size;
ret = btrfs_get_inode_size(root, location.objectid, &size);
if (ret < 0 && ret != -ENOENT)
return ret;
if (!ret)
dent->size = size;
}
*dentp = dent;
return 0;
}
@@ -151,10 +198,8 @@ int btrfs_exists(const char *file)
int btrfs_size(const char *file, loff_t *size)
{
struct btrfs_fs_info *fs_info = current_fs_info;
struct btrfs_inode_item *ii;
struct btrfs_root *root;
struct btrfs_path path;
struct btrfs_key key;
u64 isize;
u64 ino;
u8 type;
int ret;
@@ -169,27 +214,13 @@ int btrfs_size(const char *file, loff_t *size)
printf("Not a regular file: %s\n", file);
return -ENOENT;
}
btrfs_init_path(&path);
key.objectid = ino;
key.type = BTRFS_INODE_ITEM_KEY;
key.offset = 0;
ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
if (ret < 0) {
printf("Cannot lookup ino %llu\n", ino);
ret = btrfs_get_inode_size(root, ino, &isize);
if (ret) {
printf("Cannot read size of ino %llu\n", ino);
return ret;
}
if (ret > 0) {
printf("Ino %llu does not exist\n", ino);
ret = -ENOENT;
goto out;
}
ii = btrfs_item_ptr(path.nodes[0], path.slots[0],
struct btrfs_inode_item);
*size = btrfs_inode_size(path.nodes[0], ii);
out:
btrfs_release_path(&path);
return ret;
*size = isize;
return 0;
}
int btrfs_read(const char *file, void *buf, loff_t offset, loff_t len,
+12 -4
View File
@@ -425,8 +425,10 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans,
level = btrfs_header_level(b);
p->nodes[level] = b;
ret = check_block(fs_info, p, level);
if (ret)
return -1;
if (ret) {
ret = -1;
goto err;
}
ret = btrfs_bin_search(b, key, &slot);
if (level != 0) {
if (ret && slot > 0)
@@ -461,8 +463,10 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans,
break;
b = read_node_slot(fs_info, b, slot);
if (!extent_buffer_uptodate(b))
return -EIO;
if (!extent_buffer_uptodate(b)) {
ret = -EIO;
goto err;
}
} else {
p->slots[level] = slot;
/*
@@ -479,6 +483,10 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans,
}
}
return 1;
err:
btrfs_release_path(p);
return ret;
}
/*
+2 -1
View File
@@ -1221,7 +1221,8 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
const char *name, int name_len,
int mod);
int btrfs_next_dir_entry(struct btrfs_root *root, u64 ino, u64 *offset,
char *namebuf, int namebuf_len, u8 *ftype);
char *namebuf, int namebuf_len, u8 *ftype,
struct btrfs_key *location);
/* inode.c */
int btrfs_lookup_path(struct btrfs_root *root, u64 ino, const char *filename,
struct btrfs_root **root_ret, u64 *ino_ret,
+6 -1
View File
@@ -126,12 +126,16 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
* @namebuf: caller buffer that receives the NUL-terminated name
* @namebuf_len: size of @namebuf in bytes
* @ftype: receives the BTRFS_FT_* type of the entry
* @location: receives the key the entry points at, so the caller can
* reach the inode item without searching for the name
* again
*
* Return: 0 if an entry was returned, 1 when the directory is exhausted,
* -ve on error.
*/
int btrfs_next_dir_entry(struct btrfs_root *root, u64 ino, u64 *offset,
char *namebuf, int namebuf_len, u8 *ftype)
char *namebuf, int namebuf_len, u8 *ftype,
struct btrfs_key *location)
{
struct btrfs_path path;
struct btrfs_key key;
@@ -180,6 +184,7 @@ int btrfs_next_dir_entry(struct btrfs_root *root, u64 ino, u64 *offset,
(unsigned long)(di + 1), name_len);
namebuf[name_len] = '\0';
*ftype = btrfs_dir_type(path.nodes[0], di);
btrfs_dir_item_key_to_cpu(path.nodes[0], di, location);
ret = 0;
out: