From: Goffredo Baroncelli <kreijack@xxxxxxxxx>
Add a new ioctl to get info about chunk without requiring the root
privileges. This allow to a non root user to know how the space of the
filesystem is allocated.
Signed-off-by: Goffredo Baroncelli <kreijack@xxxxxxxxx>
---
fs/btrfs/ioctl.c | 211 +++++++++++++++++++++++++++++++++++++
include/uapi/linux/btrfs.h | 38 +++++++
2 files changed, 249 insertions(+)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 40b729dce91c..b3296a479bf6 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -2234,6 +2234,215 @@ static noinline int btrfs_ioctl_tree_search_v2(struct file *file,
return ret;
}
+/*
+ * Return:
+ * 1 -> copied all data, possible further data
+ * 0 -> copied all data, no further data
+ * -EAGAIN -> not enough space, restart it
+ * -EFAULT -> the user passed an invalid address/size pair
+ */
+static noinline int copy_chunk_info(struct btrfs_path *path,
+ char __user *ubuf,
+ size_t buf_size,
+ u64 *used_buf,
+ int *num_found,
+ u64 *offset)
+{
+ struct extent_buffer *leaf;
+ unsigned long item_off;
+ unsigned long item_len;
+ int nritems;
+ int i;
+ int slot;
+ struct btrfs_key key;
+
+ leaf = path->nodes[0];
+ slot = path->slots[0];
+ nritems = btrfs_header_nritems(leaf);
+
+ for (i = slot; i < nritems; i++) {
+ u64 destsize;
+ struct btrfs_chunk_info ci;
+ struct btrfs_chunk chunk;
+ int j, chunk_size;
+
+ item_off = btrfs_item_ptr_offset(leaf, i);
+ item_len = btrfs_item_size_nr(leaf, i);
+
+ btrfs_item_key_to_cpu(leaf, &key, i);
+ /*
+ * we are not interested in other items type
+ */
+ if (key.type != BTRFS_CHUNK_ITEM_KEY)
+ return 0;
+
+ /*
+ * In any case, the next search must start from here
+ */
+ *offset = key.offset;
+ read_extent_buffer(leaf, &chunk, item_off, sizeof(chunk));
+
+ /*
+ * chunk.num_stripes-1 is correct, because btrfs_chunk includes
+ * already a stripe
+ */
+ destsize = sizeof(struct btrfs_chunk_info) +
+ (chunk.num_stripes - 1) * sizeof(struct btrfs_stripe);
+
+ if (destsize > item_len) {
+ ASSERT(0);
+ return -EINVAL;
+ }
+
+ if (buf_size < destsize + *used_buf) {
+ if (*num_found)
+ /* try onother time */
+ return -EAGAIN;
+ else
+ /* in any case the buffer is too small */
+ return -EOVERFLOW;
+ }
+
+ /* copy chunk */
+ chunk_size = offsetof(struct btrfs_chunk_info, stripes);
+ memset(&ci, 0, chunk_size);
+ ci.length = btrfs_stack_chunk_length(&chunk);
+ ci.stripe_len = btrfs_stack_chunk_stripe_len(&chunk);
+ ci.type = btrfs_stack_chunk_type(&chunk);
+ ci.num_stripes = btrfs_stack_chunk_num_stripes(&chunk);
+ ci.sub_stripes = btrfs_stack_chunk_sub_stripes(&chunk);
+ ci.offset = key.offset;
+
+ if (copy_to_user(ubuf + *used_buf, &ci, chunk_size))
+ return -EFAULT;
+
+ *used_buf += chunk_size;
+
+ /* copy stripes */
+ for (j = 0 ; j < chunk.num_stripes ; j++) {
+ struct btrfs_stripe chunk_stripe;
+ struct btrfs_chunk_info_stripe csi;
+
+ /*
+ * j-1 is correct, because btrfs_chunk includes already
+ * a stripe
+ */
+ read_extent_buffer(leaf, &chunk_stripe,
+ item_off + sizeof(struct btrfs_chunk) +
+ sizeof(struct btrfs_stripe) *
+ (j - 1), sizeof(chunk_stripe));
+
+ memset(&csi, 0, sizeof(csi));
+
+ csi.devid = btrfs_stack_stripe_devid(&chunk_stripe);
+ csi.offset = btrfs_stack_stripe_offset(&chunk_stripe);
+ memcpy(csi.dev_uuid, chunk_stripe.dev_uuid,
+ sizeof(chunk_stripe.dev_uuid));
+ if (copy_to_user(ubuf + *used_buf, &csi, sizeof(csi)))
+ return -EFAULT;
+
+ *used_buf += sizeof(csi);
+ }
+
+ ++(*num_found);
+ }
+
+ if (*offset < (u64)-1)
+ ++(*offset);
+
+ return 1;
+}
+
+static noinline int search_chunk_info(struct inode *inode, u64 *offset,
+ int *items_count,
+ char __user *ubuf, u64 buf_size)
+{
+ struct btrfs_fs_info *info = btrfs_sb(inode->i_sb);
+ struct btrfs_root *root;
+ struct btrfs_key key;
+ struct btrfs_path *path;
+ int ret = -EAGAIN;
+ u64 used_buf = 0;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ /* search for BTRFS_CHUNK_TREE_OBJECTID tree */
+ key.objectid = BTRFS_CHUNK_TREE_OBJECTID;
+ key.type = BTRFS_ROOT_ITEM_KEY;
+ key.offset = (u64)-1;
+ root = btrfs_get_fs_root(info, &key, true);
+ if (IS_ERR(root)) {
+ btrfs_err(info, "could not find root\n");
+ btrfs_free_path(path);
+ return -ENOENT;
+ }
+
+
+ while (used_buf < buf_size) {
+ key.objectid = 0x0100;
+ key.type = BTRFS_CHUNK_ITEM_KEY;
+ key.offset = *offset;
+
+ ret = btrfs_search_forward(root, &key, path, 0);
+ if (ret != 0) {
+ if (ret > 0)
+ ret = 0;
+ goto ret;
+ }
+
+ ret = copy_chunk_info(path, ubuf, buf_size,
+ &used_buf, items_count, offset);
+
+ btrfs_release_path(path);
+
+ if (ret < 1)
+ break;
+ }
+
+ret:
+ btrfs_free_path(path);
+ return ret;
+}
+
+static noinline int btrfs_ioctl_get_chunk_info(struct file *file,
+ void __user *argp)
+{
+ struct btrfs_ioctl_chunk_info arg;
+ struct inode *inode;
+ int ret;
+ size_t buf_size;
+ u64 data_offset;
+ const size_t buf_limit = SZ_16M;
+
+
+ data_offset = sizeof(struct btrfs_ioctl_chunk_info);