summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <[email protected]>2026-08-10 12:39:04 -0600
committerTom Rini <[email protected]>2026-08-10 12:39:24 -0600
commit3cafdbb9cb6a3bdf250d17f5f875ba7712aa278f (patch)
tree3b2e480af4bed4ad37f000551ebda10574549e0a
parent6ea67890d034c8f285dcea438dcee2e06af3921c (diff)
parenta11f8659f451c5601bac6fdf5b48594d347db38d (diff)
Merge patch series "fs: btrfs: report file sizes from readdir"
Cole Munz <[email protected]> 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/[email protected]
-rw-r--r--fs/btrfs/btrfs.c77
-rw-r--r--fs/btrfs/ctree.c16
-rw-r--r--fs/btrfs/ctree.h3
-rw-r--r--fs/btrfs/dir-item.c7
4 files changed, 74 insertions, 29 deletions
diff --git a/fs/btrfs/btrfs.c b/fs/btrfs/btrfs.c
index e663dda12e8..f5f6d638ffd 100644
--- a/fs/btrfs/btrfs.c
+++ b/fs/btrfs/btrfs.c
@@ -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,
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 8e932adc425..48c50e556b1 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -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;
}
/*
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 3fa9a8c9c02..cd3fd669f9a 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -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,
diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
index c7b87d60d98..6edda34818b 100644
--- a/fs/btrfs/dir-item.c
+++ b/fs/btrfs/dir-item.c
@@ -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: