summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/btrfs/btrfs.c24
-rw-r--r--fs/btrfs/ctree.h3
-rw-r--r--fs/btrfs/dir-item.c7
3 files changed, 31 insertions, 3 deletions
diff --git a/fs/btrfs/btrfs.c b/fs/btrfs/btrfs.c
index e663dda12e8..b2856be0662 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);
+ if (ret < 0)
+ return ret;
+ }
+
*dentp = dent;
return 0;
}
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: