diff options
| author | Tom Rini <[email protected]> | 2026-08-10 12:39:04 -0600 |
|---|---|---|
| committer | Tom Rini <[email protected]> | 2026-08-10 12:39:24 -0600 |
| commit | 3cafdbb9cb6a3bdf250d17f5f875ba7712aa278f (patch) | |
| tree | 3b2e480af4bed4ad37f000551ebda10574549e0a | |
| parent | 6ea67890d034c8f285dcea438dcee2e06af3921c (diff) | |
| parent | a11f8659f451c5601bac6fdf5b48594d347db38d (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.c | 77 | ||||
| -rw-r--r-- | fs/btrfs/ctree.c | 16 | ||||
| -rw-r--r-- | fs/btrfs/ctree.h | 3 | ||||
| -rw-r--r-- | fs/btrfs/dir-item.c | 7 |
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: |
