Skip to contentSkip to conversation

btrfs: Add three new unprivileged ioctls to allow normal users to call "sub list/show" etc.

PATCH Superseded Tomohiro Misono 3 participants 14 messages May 16, 2018 – May 18, 2018 Btrfs file system

v5 of 7 Superseded Latest is v7 →

lore
Share & tools
lore
#0 lore raw

v5 cover btrfs: Add three new unprivileged ioctls to allow normal users to call "sub list/show" etc.

[based on current misc-next]

changelog:
v4 -> v5
- Update error handling of 1st/2nd patch. See each log for details
- Fix misspelling
v3 -> v4
- call btrfs_next_leaf() after btrfs_search_slot() when the slot position exceeds the number of items
- rebased to current misc-next
v2 -> v3
- fix kbuild test bot warning
v1 -> v2
- completely reimplement 1st/2nd ioctl to have user friendly api
- various cleanup, remove unnecessary goto
===

This adds three new unprivileged ioctls:

1st patch:
ioctl which returns subvolume information of ROOT_ITEM and ROOT_BACKREF
2nd patch:
ioctl which returns subvolume information of ROOT_REF (without subvolume name)
3rd patch:
user version of ino_lookup ioctl which also performs permission check.

They will be used to implement user version of "subvolume list/show" etc. in user tools.
See each commit log for more detals.

The implementation of btrfs-progs can be found in the ML titled as follows:
[PATCH 0/11] btrfs-progs: Rework of "subvolume list/show" and relax the root privileges of them

Tomohiro Misono (3):
btrfs: Add unprivileged ioctl which returns subvolume information
btrfs: Add unprivileged ioctl which returns subvolume's ROOT_REF
btrfs: Add unprivileged version of ino_lookup ioctl

 fs/btrfs/ioctl.c           | 452 +++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/btrfs.h |  84 +++++++++
 2 files changed, 536 insertions(+)
Signature · 1 line
2.14.3
#1 lore raw

PATCH v5 1/3 btrfs: Add unprivileged ioctl which returns subvolume information

Add new unprivileged ioctl BTRFS_IOC_GET_SUBVOL_INFO which returns the information of subvolume containing this inode. (i.e. returns the information in ROOT_ITEM and ROOT_BACKREF.)

---
v4 -> v5
- Update error handling of btrfs_next_leaf() to cover all cases
- Return error if ROOT_BACKREF is not found (except top-level)

 fs/btrfs/ioctl.c           | 146 +++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/btrfs.h |  51 ++++++++++++++++
 2 files changed, 197 insertions(+)
fs/btrfs/ioctl.c +146 -0
index 48e2ddff32bd..c1c9ae9a937d 100644
@@ -2242,6 +2242,150 @@ static noinline int btrfs_ioctl_ino_lookup(struct file *file,
 	return ret;
 }
 
+/* Get the subvolume information in BTRFS_ROOT_ITEM and BTRFS_ROOT_BACKREF */
+static noinline int btrfs_ioctl_get_subvol_info(struct file *file,
+					   void __user *argp)
+{
+	struct btrfs_ioctl_get_subvol_info_args *subvol_info;
+	struct btrfs_root *root;
+	struct btrfs_path *path;
+	struct btrfs_key key;
+
+	struct btrfs_root_item root_item;
+	struct btrfs_root_ref *rref;
+	struct extent_buffer *l;
+	int slot;
+
+	unsigned long item_off;
+	unsigned long item_len;
+
+	struct inode *inode;
+	int ret;
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+
+	subvol_info = kzalloc(sizeof(*subvol_info), GFP_KERNEL);
+	if (!subvol_info) {
+		btrfs_free_path(path);
+		return -ENOMEM;
+	}
+
+	inode = file_inode(file);
+	root = BTRFS_I(inode)->root->fs_info->tree_root;
+
+	key.objectid = BTRFS_I(inode)->root->root_key.objectid;
+	key.type = BTRFS_ROOT_ITEM_KEY;
+	key.offset = 0;
+
+	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+	if (ret < 0) {
+		goto out;
+	} else if (ret > 0) {
+		u64 objectid = key.objectid;
+
+		if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
+			ret = btrfs_next_leaf(root, path);
+			if (ret < 0) {
+				goto out;
+			} else if (ret > 0) {
+				ret = -ENOENT;
+				goto out;
+			}
+		}
+
+		/* If the subvolume is a snapshot, offset is not zero */
+		btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+		if (key.objectid != objectid ||
+		    key.type != BTRFS_ROOT_ITEM_KEY) {
+			ret = -ENOENT;
+			goto out;
+		}
+	}
+
+	l = path->nodes[0];
+	slot = path->slots[0];
+	item_off = btrfs_item_ptr_offset(l, slot);
+	item_len = btrfs_item_size_nr(l, slot);
+	read_extent_buffer(l, &root_item, item_off, item_len);
+
+	subvol_info->id = key.objectid;
+
+	subvol_info->generation = btrfs_root_generation(&root_item);
+	subvol_info->flags = btrfs_root_flags(&root_item);
+
+	memcpy(subvol_info->uuid, root_item.uuid, BTRFS_UUID_SIZE);
+	memcpy(subvol_info->parent_uuid, root_item.parent_uuid,
+						    BTRFS_UUID_SIZE);
+	memcpy(subvol_info->received_uuid, root_item.received_uuid,
+						    BTRFS_UUID_SIZE);
+
+	subvol_info->ctransid = btrfs_root_ctransid(&root_item);
+	subvol_info->ctime.sec = btrfs_stack_timespec_sec(&root_item.ctime);
+	subvol_info->ctime.nsec = btrfs_stack_timespec_nsec(&root_item.ctime);
+
+	subvol_info->otransid = btrfs_root_otransid(&root_item);
+	subvol_info->otime.sec = btrfs_stack_timespec_sec(&root_item.otime);
+	subvol_info->otime.nsec = btrfs_stack_timespec_nsec(&root_item.otime);
+
+	subvol_info->stransid = btrfs_root_stransid(&root_item);
+	subvol_info->stime.sec = btrfs_stack_timespec_sec(&root_item.stime);
+	subvol_info->stime.nsec = btrfs_stack_timespec_nsec(&root_item.stime);
+
+	subvol_info->rtransid = btrfs_root_rtransid(&root_item);
+	subvol_info->rtime.sec = btrfs_stack_timespec_sec(&root_item.rtime);
+	subvol_info->rtime.nsec = btrfs_stack_timespec_nsec(&root_item.rtime);
+
+	btrfs_release_path(path);
+	if (key.objectid != BTRFS_FS_TREE_OBJECTID) {
+		key.type = BTRFS_ROOT_BACKREF_KEY;
+		key.offset = 0;
+		ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+		if (ret < 0) {
+			goto out;
+		} else if (path->slots[0] >=
+				btrfs_header_nritems(path->nodes[0])) {
+			ret = btrfs_next_leaf(root, path);
+			if (ret < 0) {
+				goto out;
+			} else if (ret > 0) {
+				ret = -ENOENT;
+				goto out;
+			}
+		}
+
+		l = path->nodes[0];
+		slot = path->slots[0];
+		btrfs_item_key_to_cpu(l, &key, slot);
+		if (key.objectid == subvol_info->id &&
+		    key.type == BTRFS_ROOT_BACKREF_KEY) {
+			subvol_info->parent_id = key.offset;
+
+			rref = btrfs_item_ptr(l, slot, struct btrfs_root_ref);
+			subvol_info->dirid = btrfs_root_ref_dirid(l, rref);
+
+			item_off = btrfs_item_ptr_offset(l, slot)
+					+ sizeof(struct btrfs_root_ref);
+			item_len = btrfs_item_size_nr(l, slot)
+					- sizeof(struct btrfs_root_ref);
+			read_extent_buffer(l, subvol_info->name,
+					   item_off, item_len);
+		} else {
+			ret = -ENOENT;
+			goto out;
+		}
+	}
+
+	if (copy_to_user(argp, subvol_info, sizeof(*subvol_info)))
+		ret = -EFAULT;
+
+out:
+	kzfree(subvol_info);
+	btrfs_free_path(path);
+	return ret;
+}
+
 static noinline int btrfs_ioctl_snap_destroy(struct file *file,
 					     void __user *arg)
 {
@@ -5374,6 +5518,8 @@ long btrfs_ioctl(struct file *file, unsigned int
 		return btrfs_ioctl_get_features(file, argp);
 	case BTRFS_IOC_SET_FEATURES:
 		return btrfs_ioctl_set_features(file, argp);
+	case BTRFS_IOC_GET_SUBVOL_INFO:
+		return btrfs_ioctl_get_subvol_info(file, argp);
 	}
 
 	return -ENOTTY;
include/uapi/linux/btrfs.h +51 -0

62 lines not on this page · raw message

Signature · 1 line
2.14.3
Reviewed-by on v5 1/3
#2 lore raw

RE: [PATCH v5 1/3] btrfs: Add unprivileged ioctl which returns subvolume information

Quoted 244 lines

-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of Tomohiro Misono
Sent: Wednesday, May 16, 2018 1:50 PM
To: [email protected]
Subject: [PATCH v5 1/3] btrfs: Add unprivileged ioctl which returns subvolume information

Add new unprivileged ioctl BTRFS_IOC_GET_SUBVOL_INFO which returns the information of subvolume containing this inode. (i.e. returns the information in ROOT_ITEM and ROOT_BACKREF.)

---
v4 -> v5
- Update error handling of btrfs_next_leaf() to cover all cases
- Return error if ROOT_BACKREF is not found (except top-level)

 fs/btrfs/ioctl.c           | 146 +++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/btrfs.h |  51 ++++++++++++++++
 2 files changed, 197 insertions(+)
fs/btrfs/ioctl.c +146 -0
@@ -2242,6 +2242,150 @@ static noinline int btrfs_ioctl_ino_lookup(struct file *file,
 	return ret;
 }
 
+/* Get the subvolume information in BTRFS_ROOT_ITEM and
+BTRFS_ROOT_BACKREF */ static noinline int btrfs_ioctl_get_subvol_info(struct file *file,
+					   void __user *argp)
+{
+	struct btrfs_ioctl_get_subvol_info_args *subvol_info;
+	struct btrfs_root *root;
+	struct btrfs_path *path;
+	struct btrfs_key key;
+
+	struct btrfs_root_item root_item;
+	struct btrfs_root_ref *rref;
+	struct extent_buffer *l;
+	int slot;
+
+	unsigned long item_off;
+	unsigned long item_len;
+
+	struct inode *inode;
+	int ret;
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+
+	subvol_info = kzalloc(sizeof(*subvol_info), GFP_KERNEL);
+	if (!subvol_info) {
+		btrfs_free_path(path);
+		return -ENOMEM;
+	}
+
+	inode = file_inode(file);
+	root = BTRFS_I(inode)->root->fs_info->tree_root;
+
+	key.objectid = BTRFS_I(inode)->root->root_key.objectid;
+	key.type = BTRFS_ROOT_ITEM_KEY;
+	key.offset = 0;
+
+	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+	if (ret < 0) {
+		goto out;
+	} else if (ret > 0) {
+		u64 objectid = key.objectid;
+
+		if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
+			ret = btrfs_next_leaf(root, path);
+			if (ret < 0) {
+				goto out;
+			} else if (ret > 0) {
+				ret = -ENOENT;
+				goto out;
+			}
+		}
+
+		/* If the subvolume is a snapshot, offset is not zero */
+		btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+		if (key.objectid != objectid ||
+		    key.type != BTRFS_ROOT_ITEM_KEY) {
+			ret = -ENOENT;
+			goto out;
+		}
+	}
+
+	l = path->nodes[0];
+	slot = path->slots[0];
+	item_off = btrfs_item_ptr_offset(l, slot);
+	item_len = btrfs_item_size_nr(l, slot);
+	read_extent_buffer(l, &root_item, item_off, item_len);
+
+	subvol_info->id = key.objectid;
+
+	subvol_info->generation = btrfs_root_generation(&root_item);
+	subvol_info->flags = btrfs_root_flags(&root_item);
+
+	memcpy(subvol_info->uuid, root_item.uuid, BTRFS_UUID_SIZE);
+	memcpy(subvol_info->parent_uuid, root_item.parent_uuid,
+						    BTRFS_UUID_SIZE);
+	memcpy(subvol_info->received_uuid, root_item.received_uuid,
+						    BTRFS_UUID_SIZE);
+
+	subvol_info->ctransid = btrfs_root_ctransid(&root_item);
+	subvol_info->ctime.sec = btrfs_stack_timespec_sec(&root_item.ctime);
+	subvol_info->ctime.nsec = btrfs_stack_timespec_nsec(&root_item.ctime);
+
+	subvol_info->otransid = btrfs_root_otransid(&root_item);
+	subvol_info->otime.sec = btrfs_stack_timespec_sec(&root_item.otime);
+	subvol_info->otime.nsec = btrfs_stack_timespec_nsec(&root_item.otime);
+
+	subvol_info->stransid = btrfs_root_stransid(&root_item);
+	subvol_info->stime.sec = btrfs_stack_timespec_sec(&root_item.stime);
+	subvol_info->stime.nsec = btrfs_stack_timespec_nsec(&root_item.stime);
+
+	subvol_info->rtransid = btrfs_root_rtransid(&root_item);
+	subvol_info->rtime.sec = btrfs_stack_timespec_sec(&root_item.rtime);
+	subvol_info->rtime.nsec = btrfs_stack_timespec_nsec(&root_item.rtime);
+
+	btrfs_release_path(path);
+	if (key.objectid != BTRFS_FS_TREE_OBJECTID) {
+		key.type = BTRFS_ROOT_BACKREF_KEY;
+		key.offset = 0;
+		ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+		if (ret < 0) {
+			goto out;
+		} else if (path->slots[0] >=
+				btrfs_header_nritems(path->nodes[0])) {
+			ret = btrfs_next_leaf(root, path);
+			if (ret < 0) {
+				goto out;
+			} else if (ret > 0) {
+				ret = -ENOENT;
+				goto out;
+			}
+		}
+
+		l = path->nodes[0];
+		slot = path->slots[0];
+		btrfs_item_key_to_cpu(l, &key, slot);
+		if (key.objectid == subvol_info->id &&
+		    key.type == BTRFS_ROOT_BACKREF_KEY) {
+			subvol_info->parent_id = key.offset;
+
+			rref = btrfs_item_ptr(l, slot, struct btrfs_root_ref);
+			subvol_info->dirid = btrfs_root_ref_dirid(l, rref);
+
+			item_off = btrfs_item_ptr_offset(l, slot)
+					+ sizeof(struct btrfs_root_ref);
+			item_len = btrfs_item_size_nr(l, slot)
+					- sizeof(struct btrfs_root_ref);
+			read_extent_buffer(l, subvol_info->name,
+					   item_off, item_len);
+		} else {
+			ret = -ENOENT;
+			goto out;
+		}
+	}
+
+	if (copy_to_user(argp, subvol_info, sizeof(*subvol_info)))
+		ret = -EFAULT;
+
+out:
+	kzfree(subvol_info);
+	btrfs_free_path(path);
+	return ret;
+}
+
 static noinline int btrfs_ioctl_snap_destroy(struct file *file,
 					     void __user *arg)
 {
@@ -5374,6 +5518,8 @@ long btrfs_ioctl(struct file *file, unsigned int
 		return btrfs_ioctl_get_features(file, argp);
 	case BTRFS_IOC_SET_FEATURES:
 		return btrfs_ioctl_set_features(file, argp);
+	case BTRFS_IOC_GET_SUBVOL_INFO:
+		return btrfs_ioctl_get_subvol_info(file, argp);
 	}
 
 	return -ENOTTY;
include/uapi/linux/btrfs.h +49 -0
@@ -725,6 +725,55 @@ struct btrfs_ioctl_send_args {
 	__u64 reserved[4];		/* in */
 };
 
+struct btrfs_ioctl_get_subvol_info_args {
+	/* All field is out */
+	/* Id of this subvolume */
+	__u64 id;
+	/* Name of this subvolume, used to get the real name at mount point */
+	char name[BTRFS_VOL_NAME_MAX + 1];
+	/*
+	 * Id of the subvolume which contains this subvolume.
+	 * Zero for top-level subvolume or deleted subvolume
+	 */
+	__u64 parent_id;
+	/*
+	 * Inode number of the directory which contains this subvolume.
+	 * Zero for top-level subvolume or deleted subvolume
+	 */
+	__u64 dirid;
+
+	/* Latest transaction id of this subvolume */
+	__u64 generation;
+	/* Flags of this subvolume */
+	__u64 flags;
+
+	/* uuid of this subvolume */
+	__u8 uuid[BTRFS_UUID_SIZE];
+	/*
+	 * uuid of the subvolume of which this subvolume is a snapshot.
+	 * All zero for non-snapshot subvolume
+	 */
+	__u8 parent_uuid[BTRFS_UUID_SIZE];
+	/*
+	 * uuid of the subvolume from which this subvolume is received.
+	 * All zero for non-received subvolume
+	 */
+	__u8 received_uuid[BTRFS_UUID_SIZE];
+
+	/* Transaction id indicates when change/create/send/receive happens */
+	__u64 ctransid;
+	__u64 otransid;
+	__u64 stransid;
+	__u64 rtransid;
+	/* Time corresponds to c/o/s/rtransid */
+	struct btrfs_ioctl_timespec ctime;
+	struct btrfs_ioctl_timespec otime;
+	struct btrfs_ioctl_timespec stime;
+	struct btrfs_ioctl_timespec rtime;
+
+	__u64 reserved[8];
+};
+
 /* Error codes as returned by the kernel */  enum btrfs_err_code {
 	BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET = 1, @@ -843,5 +892,7 @@ enum btrfs_err_code {
 				   struct btrfs_ioctl_vol_args_v2)

#define BTRFS_IOC_LOGICAL_INO_V2 _IOWR(BTRFS_IOCTL_MAGIC, 59, \

include/uapi/linux/btrfs.h enum btrfs_err_code {

Quoted 6 lines
include/uapi/linux/btrfs.h +2 -1
 					struct btrfs_ioctl_logical_ino_args)
+#define BTRFS_IOC_GET_SUBVOL_INFO _IOR(BTRFS_IOCTL_MAGIC, 60, \
+				struct btrfs_ioctl_get_subvol_info_args)
 
 #endif /* _UAPI_LINUX_BTRFS_H */
--

Looks good to me.

Thks.

Quoted 8 lines

2.14.3

--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

�翳簕.n�+壏煯壄�+%娝�遍荻w�簕.n�+壏{饼籯~镶炟^n噐■�侂h櫒璀�&{鄗夸z罐楘+€�+zf"穐殘啳嗃i��飦�戧鐉_璁鎗:+v墾�)撸鴐

#3 lore raw

Re: [PATCH v5 1/3] btrfs: Add unprivileged ioctl which returns subvolume information

In reply to Tomohiro Misono
Quoted 50 lines · Tomohiro Misono

Add new unprivileged ioctl BTRFS_IOC_GET_SUBVOL_INFO which returns the information of subvolume containing this inode. (i.e. returns the information in ROOT_ITEM and ROOT_BACKREF.)

---
v4 -> v5
- Update error handling of btrfs_next_leaf() to cover all cases
- Return error if ROOT_BACKREF is not found (except top-level)

 fs/btrfs/ioctl.c           | 146 +++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/btrfs.h |  51 ++++++++++++++++
 2 files changed, 197 insertions(+)
fs/btrfs/ioctl.c +28 -0
index 48e2ddff32bd..c1c9ae9a937d 100644
@@ -2242,6 +2242,150 @@ static noinline int btrfs_ioctl_ino_lookup(struct file *file,
 	return ret;
 }
 
+/* Get the subvolume information in BTRFS_ROOT_ITEM and BTRFS_ROOT_BACKREF */
+static noinline int btrfs_ioctl_get_subvol_info(struct file *file,
+					   void __user *argp)
+{
+	struct btrfs_ioctl_get_subvol_info_args *subvol_info;
+	struct btrfs_root *root;
+	struct btrfs_path *path;
+	struct btrfs_key key;
+
+	struct btrfs_root_item root_item;
+	struct btrfs_root_ref *rref;
+	struct extent_buffer *l;
+	int slot;
+
+	unsigned long item_off;
+	unsigned long item_len;
+
+	struct inode *inode;
+	int ret;
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+
+	subvol_info = kzalloc(sizeof(*subvol_info), GFP_KERNEL);
+	if (!subvol_info) {
+		btrfs_free_path(path);
+		return -ENOMEM;

fs/btrfs/ioctl.c static noinline int btrfs_ioctl_ino_lookup(struct file *file, · line 2282

Quoted 10 lines
fs/btrfs/ioctl.c +10 -0
+	}
+
+	inode = file_inode(file);
+	root = BTRFS_I(inode)->root->fs_info->tree_root;
+
+	key.objectid = BTRFS_I(inode)->root->root_key.objectid;
+	key.type = BTRFS_ROOT_ITEM_KEY;
+	key.offset = 0;
+
+	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);

What about just using btrfs_get_fs_root()?

It would save several lines, and have better error handling along with better tree cache.

Thanks,
Qu

Quoted 187 lines
fs/btrfs/ioctl.c +108 -0
+	if (ret < 0) {
+		goto out;
+	} else if (ret > 0) {
+		u64 objectid = key.objectid;
+
+		if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
+			ret = btrfs_next_leaf(root, path);
+			if (ret < 0) {
+				goto out;
+			} else if (ret > 0) {
+				ret = -ENOENT;
+				goto out;
+			}
+		}
+
+		/* If the subvolume is a snapshot, offset is not zero */
+		btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+		if (key.objectid != objectid ||
+		    key.type != BTRFS_ROOT_ITEM_KEY) {
+			ret = -ENOENT;
+			goto out;
+		}
+	}
+
+	l = path->nodes[0];
+	slot = path->slots[0];
+	item_off = btrfs_item_ptr_offset(l, slot);
+	item_len = btrfs_item_size_nr(l, slot);
+	read_extent_buffer(l, &root_item, item_off, item_len);
+
+	subvol_info->id = key.objectid;
+
+	subvol_info->generation = btrfs_root_generation(&root_item);
+	subvol_info->flags = btrfs_root_flags(&root_item);
+
+	memcpy(subvol_info->uuid, root_item.uuid, BTRFS_UUID_SIZE);
+	memcpy(subvol_info->parent_uuid, root_item.parent_uuid,
+						    BTRFS_UUID_SIZE);
+	memcpy(subvol_info->received_uuid, root_item.received_uuid,
+						    BTRFS_UUID_SIZE);
+
+	subvol_info->ctransid = btrfs_root_ctransid(&root_item);
+	subvol_info->ctime.sec = btrfs_stack_timespec_sec(&root_item.ctime);
+	subvol_info->ctime.nsec = btrfs_stack_timespec_nsec(&root_item.ctime);
+
+	subvol_info->otransid = btrfs_root_otransid(&root_item);
+	subvol_info->otime.sec = btrfs_stack_timespec_sec(&root_item.otime);
+	subvol_info->otime.nsec = btrfs_stack_timespec_nsec(&root_item.otime);
+
+	subvol_info->stransid = btrfs_root_stransid(&root_item);
+	subvol_info->stime.sec = btrfs_stack_timespec_sec(&root_item.stime);
+	subvol_info->stime.nsec = btrfs_stack_timespec_nsec(&root_item.stime);
+
+	subvol_info->rtransid = btrfs_root_rtransid(&root_item);
+	subvol_info->rtime.sec = btrfs_stack_timespec_sec(&root_item.rtime);
+	subvol_info->rtime.nsec = btrfs_stack_timespec_nsec(&root_item.rtime);
+
+	btrfs_release_path(path);
+	if (key.objectid != BTRFS_FS_TREE_OBJECTID) {
+		key.type = BTRFS_ROOT_BACKREF_KEY;
+		key.offset = 0;
+		ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+		if (ret < 0) {
+			goto out;
+		} else if (path->slots[0] >=
+				btrfs_header_nritems(path->nodes[0])) {
+			ret = btrfs_next_leaf(root, path);
+			if (ret < 0) {
+				goto out;
+			} else if (ret > 0) {
+				ret = -ENOENT;
+				goto out;
+			}
+		}
+
+		l = path->nodes[0];
+		slot = path->slots[0];
+		btrfs_item_key_to_cpu(l, &key, slot);
+		if (key.objectid == subvol_info->id &&
+		    key.type == BTRFS_ROOT_BACKREF_KEY) {
+			subvol_info->parent_id = key.offset;
+
+			rref = btrfs_item_ptr(l, slot, struct btrfs_root_ref);
+			subvol_info->dirid = btrfs_root_ref_dirid(l, rref);
+
+			item_off = btrfs_item_ptr_offset(l, slot)
+					+ sizeof(struct btrfs_root_ref);
+			item_len = btrfs_item_size_nr(l, slot)
+					- sizeof(struct btrfs_root_ref);
+			read_extent_buffer(l, subvol_info->name,
+					   item_off, item_len);
+		} else {
+			ret = -ENOENT;
+			goto out;
+		}
+	}
+
+	if (copy_to_user(argp, subvol_info, sizeof(*subvol_info)))
+		ret = -EFAULT;
+
+out:
+	kzfree(subvol_info);
+	btrfs_free_path(path);
+	return ret;
+}
+
 static noinline int btrfs_ioctl_snap_destroy(struct file *file,
 					     void __user *arg)
 {
@@ -5374,6 +5518,8 @@ long btrfs_ioctl(struct file *file, unsigned int
 		return btrfs_ioctl_get_features(file, argp);
 	case BTRFS_IOC_SET_FEATURES:
 		return btrfs_ioctl_set_features(file, argp);
+	case BTRFS_IOC_GET_SUBVOL_INFO:
+		return btrfs_ioctl_get_subvol_info(file, argp);
 	}
 
 	return -ENOTTY;
include/uapi/linux/btrfs.h +51 -0
index c8d99b9ca550..02cd1f1994e8 100644
@@ -725,6 +725,55 @@ struct btrfs_ioctl_send_args {
 	__u64 reserved[4];		/* in */
 };
 
+struct btrfs_ioctl_get_subvol_info_args {
+	/* All field is out */
+	/* Id of this subvolume */
+	__u64 id;
+	/* Name of this subvolume, used to get the real name at mount point */
+	char name[BTRFS_VOL_NAME_MAX + 1];
+	/*
+	 * Id of the subvolume which contains this subvolume.
+	 * Zero for top-level subvolume or deleted subvolume
+	 */
+	__u64 parent_id;
+	/*
+	 * Inode number of the directory which contains this subvolume.
+	 * Zero for top-level subvolume or deleted subvolume
+	 */
+	__u64 dirid;
+
+	/* Latest transaction id of this subvolume */
+	__u64 generation;
+	/* Flags of this subvolume */
+	__u64 flags;
+
+	/* uuid of this subvolume */
+	__u8 uuid[BTRFS_UUID_SIZE];
+	/*
+	 * uuid of the subvolume of which this subvolume is a snapshot.
+	 * All zero for non-snapshot subvolume
+	 */
+	__u8 parent_uuid[BTRFS_UUID_SIZE];
+	/*
+	 * uuid of the subvolume from which this subvolume is received.
+	 * All zero for non-received subvolume
+	 */
+	__u8 received_uuid[BTRFS_UUID_SIZE];
+
+	/* Transaction id indicates when change/create/send/receive happens */
+	__u64 ctransid;
+	__u64 otransid;
+	__u64 stransid;
+	__u64 rtransid;
+	/* Time corresponds to c/o/s/rtransid */
+	struct btrfs_ioctl_timespec ctime;
+	struct btrfs_ioctl_timespec otime;
+	struct btrfs_ioctl_timespec stime;
+	struct btrfs_ioctl_timespec rtime;
+
+	__u64 reserved[8];
+};
+
 /* Error codes as returned by the kernel */
 enum btrfs_err_code {
 	BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET = 1,
@@ -843,5 +892,7 @@ enum btrfs_err_code {
 				   struct btrfs_ioctl_vol_args_v2)
 #define BTRFS_IOC_LOGICAL_INO_V2 _IOWR(BTRFS_IOCTL_MAGIC, 59, \
 					struct btrfs_ioctl_logical_ino_args)
+#define BTRFS_IOC_GET_SUBVOL_INFO _IOR(BTRFS_IOCTL_MAGIC, 60, \
+				struct btrfs_ioctl_get_subvol_info_args)
 
 #endif /* _UAPI_LINUX_BTRFS_H */
#4 lore raw

Re: [PATCH v5 1/3] btrfs: Add unprivileged ioctl which returns subvolume information

Quoted 66 lines · Qu Wenruo
Quoted 50 lines · Tomohiro Misono

Add new unprivileged ioctl BTRFS_IOC_GET_SUBVOL_INFO which returns the information of subvolume containing this inode. (i.e. returns the information in ROOT_ITEM and ROOT_BACKREF.)

---
v4 -> v5
- Update error handling of btrfs_next_leaf() to cover all cases
- Return error if ROOT_BACKREF is not found (except top-level)

 fs/btrfs/ioctl.c           | 146 +++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/btrfs.h |  51 ++++++++++++++++
 2 files changed, 197 insertions(+)
fs/btrfs/ioctl.c +28 -0
index 48e2ddff32bd..c1c9ae9a937d 100644
@@ -2242,6 +2242,150 @@ static noinline int btrfs_ioctl_ino_lookup(struct file *file,
 	return ret;
 }
 
+/* Get the subvolume information in BTRFS_ROOT_ITEM and BTRFS_ROOT_BACKREF */
+static noinline int btrfs_ioctl_get_subvol_info(struct file *file,
+					   void __user *argp)
+{
+	struct btrfs_ioctl_get_subvol_info_args *subvol_info;
+	struct btrfs_root *root;
+	struct btrfs_path *path;
+	struct btrfs_key key;
+
+	struct btrfs_root_item root_item;
+	struct btrfs_root_ref *rref;
+	struct extent_buffer *l;
+	int slot;
+
+	unsigned long item_off;
+	unsigned long item_len;
+
+	struct inode *inode;
+	int ret;
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+
+	subvol_info = kzalloc(sizeof(*subvol_info), GFP_KERNEL);
+	if (!subvol_info) {
+		btrfs_free_path(path);
+		return -ENOMEM;

fs/btrfs/ioctl.c static noinline int btrfs_ioctl_ino_lookup(struct file *file, · line 2282

Quoted 10 lines
fs/btrfs/ioctl.c +10 -0
+	}
+
+	inode = file_inode(file);
+	root = BTRFS_I(inode)->root->fs_info->tree_root;
+
+	key.objectid = BTRFS_I(inode)->root->root_key.objectid;
+	key.type = BTRFS_ROOT_ITEM_KEY;
+	key.offset = 0;
+
+	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);

What about just using btrfs_get_fs_root()?

Quoted 2 lines

It would save several lines, and have better error handling along with better tree cache.

Here we search ROOT_ITEM of subvolume in root_tree and I don't think btrfs_get_fs_root() would save lines, right?

Thanks,

Quoted 192 lines

Thanks,
Qu

Quoted 187 lines
fs/btrfs/ioctl.c +108 -0
+	if (ret < 0) {
+		goto out;
+	} else if (ret > 0) {
+		u64 objectid = key.objectid;
+
+		if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
+			ret = btrfs_next_leaf(root, path);
+			if (ret < 0) {
+				goto out;
+			} else if (ret > 0) {
+				ret = -ENOENT;
+				goto out;
+			}
+		}
+
+		/* If the subvolume is a snapshot, offset is not zero */
+		btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+		if (key.objectid != objectid ||
+		    key.type != BTRFS_ROOT_ITEM_KEY) {
+			ret = -ENOENT;
+			goto out;
+		}
+	}
+
+	l = path->nodes[0];
+	slot = path->slots[0];
+	item_off = btrfs_item_ptr_offset(l, slot);
+	item_len = btrfs_item_size_nr(l, slot);
+	read_extent_buffer(l, &root_item, item_off, item_len);
+
+	subvol_info->id = key.objectid;
+
+	subvol_info->generation = btrfs_root_generation(&root_item);
+	subvol_info->flags = btrfs_root_flags(&root_item);
+
+	memcpy(subvol_info->uuid, root_item.uuid, BTRFS_UUID_SIZE);
+	memcpy(subvol_info->parent_uuid, root_item.parent_uuid,
+						    BTRFS_UUID_SIZE);
+	memcpy(subvol_info->received_uuid, root_item.received_uuid,
+						    BTRFS_UUID_SIZE);
+
+	subvol_info->ctransid = btrfs_root_ctransid(&root_item);
+	subvol_info->ctime.sec = btrfs_stack_timespec_sec(&root_item.ctime);
+	subvol_info->ctime.nsec = btrfs_stack_timespec_nsec(&root_item.ctime);
+
+	subvol_info->otransid = btrfs_root_otransid(&root_item);
+	subvol_info->otime.sec = btrfs_stack_timespec_sec(&root_item.otime);
+	subvol_info->otime.nsec = btrfs_stack_timespec_nsec(&root_item.otime);
+
+	subvol_info->stransid = btrfs_root_stransid(&root_item);
+	subvol_info->stime.sec = btrfs_stack_timespec_sec(&root_item.stime);
+	subvol_info->stime.nsec = btrfs_stack_timespec_nsec(&root_item.stime);
+
+	subvol_info->rtransid = btrfs_root_rtransid(&root_item);
+	subvol_info->rtime.sec = btrfs_stack_timespec_sec(&root_item.rtime);
+	subvol_info->rtime.nsec = btrfs_stack_timespec_nsec(&root_item.rtime);
+
+	btrfs_release_path(path);
+	if (key.objectid != BTRFS_FS_TREE_OBJECTID) {
+		key.type = BTRFS_ROOT_BACKREF_KEY;
+		key.offset = 0;
+		ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+		if (ret < 0) {
+			goto out;
+		} else if (path->slots[0] >=
+				btrfs_header_nritems(path->nodes[0])) {
+			ret = btrfs_next_leaf(root, path);
+			if (ret < 0) {
+				goto out;
+			} else if (ret > 0) {
+				ret = -ENOENT;
+				goto out;
+			}
+		}
+
+		l = path->nodes[0];
+		slot = path->slots[0];
+		btrfs_item_key_to_cpu(l, &key, slot);
+		if (key.objectid == subvol_info->id &&
+		    key.type == BTRFS_ROOT_BACKREF_KEY) {
+			subvol_info->parent_id = key.offset;
+
+			rref = btrfs_item_ptr(l, slot, struct btrfs_root_ref);
+			subvol_info->dirid = btrfs_root_ref_dirid(l, rref);
+
+			item_off = btrfs_item_ptr_offset(l, slot)
+					+ sizeof(struct btrfs_root_ref);
+			item_len = btrfs_item_size_nr(l, slot)
+					- sizeof(struct btrfs_root_ref);
+			read_extent_buffer(l, subvol_info->name,
+					   item_off, item_len);
+		} else {
+			ret = -ENOENT;
+			goto out;
+		}
+	}
+
+	if (copy_to_user(argp, subvol_info, sizeof(*subvol_info)))
+		ret = -EFAULT;
+
+out:
+	kzfree(subvol_info);
+	btrfs_free_path(path);
+	return ret;
+}
+
 static noinline int btrfs_ioctl_snap_destroy(struct file *file,
 					     void __user *arg)
 {
@@ -5374,6 +5518,8 @@ long btrfs_ioctl(struct file *file, unsigned int
 		return btrfs_ioctl_get_features(file, argp);
 	case BTRFS_IOC_SET_FEATURES:
 		return btrfs_ioctl_set_features(file, argp);
+	case BTRFS_IOC_GET_SUBVOL_INFO:
+		return btrfs_ioctl_get_subvol_info(file, argp);
 	}
 
 	return -ENOTTY;
include/uapi/linux/btrfs.h +51 -0
index c8d99b9ca550..02cd1f1994e8 100644
@@ -725,6 +725,55 @@ struct btrfs_ioctl_send_args {
 	__u64 reserved[4];		/* in */
 };
 
+struct btrfs_ioctl_get_subvol_info_args {
+	/* All field is out */
+	/* Id of this subvolume */
+	__u64 id;
+	/* Name of this subvolume, used to get the real name at mount point */
+	char name[BTRFS_VOL_NAME_MAX + 1];
+	/*
+	 * Id of the subvolume which contains this subvolume.
+	 * Zero for top-level subvolume or deleted subvolume
+	 */
+	__u64 parent_id;
+	/*
+	 * Inode number of the directory which contains this subvolume.
+	 * Zero for top-level subvolume or deleted subvolume
+	 */
+	__u64 dirid;
+
+	/* Latest transaction id of this subvolume */
+	__u64 generation;
+	/* Flags of this subvolume */
+	__u64 flags;
+
+	/* uuid of this subvolume */
+	__u8 uuid[BTRFS_UUID_SIZE];
+	/*
+	 * uuid of the subvolume of which this subvolume is a snapshot.
+	 * All zero for non-snapshot subvolume
+	 */
+	__u8 parent_uuid[BTRFS_UUID_SIZE];
+	/*
+	 * uuid of the subvolume from which this subvolume is received.
+	 * All zero for non-received subvolume
+	 */
+	__u8 received_uuid[BTRFS_UUID_SIZE];
+
+	/* Transaction id indicates when change/create/send/receive happens */
+	__u64 ctransid;
+	__u64 otransid;
+	__u64 stransid;
+	__u64 rtransid;
+	/* Time corresponds to c/o/s/rtransid */
+	struct btrfs_ioctl_timespec ctime;
+	struct btrfs_ioctl_timespec otime;
+	struct btrfs_ioctl_timespec stime;
+	struct btrfs_ioctl_timespec rtime;
+
+	__u64 reserved[8];
+};
+
 /* Error codes as returned by the kernel */
 enum btrfs_err_code {
 	BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET = 1,
@@ -843,5 +892,7 @@ enum btrfs_err_code {
 				   struct btrfs_ioctl_vol_args_v2)
 #define BTRFS_IOC_LOGICAL_INO_V2 _IOWR(BTRFS_IOCTL_MAGIC, 59, \
 					struct btrfs_ioctl_logical_ino_args)
+#define BTRFS_IOC_GET_SUBVOL_INFO _IOR(BTRFS_IOCTL_MAGIC, 60, \
+				struct btrfs_ioctl_get_subvol_info_args)
 
 #endif /* _UAPI_LINUX_BTRFS_H */
#5 lore raw

Re: [PATCH v5 1/3] btrfs: Add unprivileged ioctl which returns subvolume information

Quoted 67 lines · Misono Tomohiro
Quoted 66 lines · Qu Wenruo
Quoted 50 lines · Tomohiro Misono

Add new unprivileged ioctl BTRFS_IOC_GET_SUBVOL_INFO which returns the information of subvolume containing this inode. (i.e. returns the information in ROOT_ITEM and ROOT_BACKREF.)

---
v4 -> v5
- Update error handling of btrfs_next_leaf() to cover all cases
- Return error if ROOT_BACKREF is not found (except top-level)

 fs/btrfs/ioctl.c           | 146 +++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/btrfs.h |  51 ++++++++++++++++
 2 files changed, 197 insertions(+)
fs/btrfs/ioctl.c +28 -0
index 48e2ddff32bd..c1c9ae9a937d 100644
@@ -2242,6 +2242,150 @@ static noinline int btrfs_ioctl_ino_lookup(struct file *file,
 	return ret;
 }
 
+/* Get the subvolume information in BTRFS_ROOT_ITEM and BTRFS_ROOT_BACKREF */
+static noinline int btrfs_ioctl_get_subvol_info(struct file *file,
+					   void __user *argp)
+{
+	struct btrfs_ioctl_get_subvol_info_args *subvol_info;
+	struct btrfs_root *root;
+	struct btrfs_path *path;
+	struct btrfs_key key;
+
+	struct btrfs_root_item root_item;
+	struct btrfs_root_ref *rref;
+	struct extent_buffer *l;
+	int slot;
+
+	unsigned long item_off;
+	unsigned long item_len;
+
+	struct inode *inode;
+	int ret;
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+
+	subvol_info = kzalloc(sizeof(*subvol_info), GFP_KERNEL);
+	if (!subvol_info) {
+		btrfs_free_path(path);
+		return -ENOMEM;

fs/btrfs/ioctl.c static noinline int btrfs_ioctl_ino_lookup(struct file *file, · line 2282

Quoted 10 lines
fs/btrfs/ioctl.c +10 -0
+	}
+
+	inode = file_inode(file);
+	root = BTRFS_I(inode)->root->fs_info->tree_root;
+
+	key.objectid = BTRFS_I(inode)->root->root_key.objectid;
+	key.type = BTRFS_ROOT_ITEM_KEY;
+	key.offset = 0;
+
+	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);

What about just using btrfs_get_fs_root()?

Quoted 5 lines
Quoted 2 lines

It would save several lines, and have better error handling along with better tree cache.

Here we search ROOT_ITEM of subvolume in root_tree and I don't think btrfs_get_fs_root() would save lines, right?

btrfs_root has root_item member.

So just btrfs_get_fs_root(), then use root->root_item.

Quoted 15 lines
Quoted 15 lines
Quoted 15 lines
fs/btrfs/ioctl.c +15 -0
+	if (ret < 0) {
+		goto out;
+	} else if (ret > 0) {
+		u64 objectid = key.objectid;
+
+		if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
+			ret = btrfs_next_leaf(root, path);
+			if (ret < 0) {
+				goto out;
+			} else if (ret > 0) {
+				ret = -ENOENT;
+				goto out;
+			}
+		}
+
Quoted 8 lines
Quoted 8 lines

fs/btrfs/ioctl.c static noinline int btrfs_ioctl_ino_lookup(struct file *file, · line 2305

Quoted 8 lines
fs/btrfs/ioctl.c +8 -0
+		/* If the subvolume is a snapshot, offset is not zero */
+		btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+		if (key.objectid != objectid ||
+		    key.type != BTRFS_ROOT_ITEM_KEY) {
+			ret = -ENOENT;
+			goto out;
+		}
+	}

These lines can be saved.

Thanks,
Qu

Quoted 165 lines
Quoted 164 lines
Quoted 163 lines
fs/btrfs/ioctl.c +84 -0
+	l = path->nodes[0];
+	slot = path->slots[0];
+	item_off = btrfs_item_ptr_offset(l, slot);
+	item_len = btrfs_item_size_nr(l, slot);
+	read_extent_buffer(l, &root_item, item_off, item_len);
+
+	subvol_info->id = key.objectid;
+
+	subvol_info->generation = btrfs_root_generation(&root_item);
+	subvol_info->flags = btrfs_root_flags(&root_item);
+
+	memcpy(subvol_info->uuid, root_item.uuid, BTRFS_UUID_SIZE);
+	memcpy(subvol_info->parent_uuid, root_item.parent_uuid,
+						    BTRFS_UUID_SIZE);
+	memcpy(subvol_info->received_uuid, root_item.received_uuid,
+						    BTRFS_UUID_SIZE);
+
+	subvol_info->ctransid = btrfs_root_ctransid(&root_item);
+	subvol_info->ctime.sec = btrfs_stack_timespec_sec(&root_item.ctime);
+	subvol_info->ctime.nsec = btrfs_stack_timespec_nsec(&root_item.ctime);
+
+	subvol_info->otransid = btrfs_root_otransid(&root_item);
+	subvol_info->otime.sec = btrfs_stack_timespec_sec(&root_item.otime);
+	subvol_info->otime.nsec = btrfs_stack_timespec_nsec(&root_item.otime);
+
+	subvol_info->stransid = btrfs_root_stransid(&root_item);
+	subvol_info->stime.sec = btrfs_stack_timespec_sec(&root_item.stime);
+	subvol_info->stime.nsec = btrfs_stack_timespec_nsec(&root_item.stime);
+
+	subvol_info->rtransid = btrfs_root_rtransid(&root_item);
+	subvol_info->rtime.sec = btrfs_stack_timespec_sec(&root_item.rtime);
+	subvol_info->rtime.nsec = btrfs_stack_timespec_nsec(&root_item.rtime);
+
+	btrfs_release_path(path);
+	if (key.objectid != BTRFS_FS_TREE_OBJECTID) {
+		key.type = BTRFS_ROOT_BACKREF_KEY;
+		key.offset = 0;
+		ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+		if (ret < 0) {
+			goto out;
+		} else if (path->slots[0] >=
+				btrfs_header_nritems(path->nodes[0])) {
+			ret = btrfs_next_leaf(root, path);
+			if (ret < 0) {
+				goto out;
+			} else if (ret > 0) {
+				ret = -ENOENT;
+				goto out;
+			}
+		}
+
+		l = path->nodes[0];
+		slot = path->slots[0];
+		btrfs_item_key_to_cpu(l, &key, slot);
+		if (key.objectid == subvol_info->id &&
+		    key.type == BTRFS_ROOT_BACKREF_KEY) {
+			subvol_info->parent_id = key.offset;
+
+			rref = btrfs_item_ptr(l, slot, struct btrfs_root_ref);
+			subvol_info->dirid = btrfs_root_ref_dirid(l, rref);
+
+			item_off = btrfs_item_ptr_offset(l, slot)
+					+ sizeof(struct btrfs_root_ref);
+			item_len = btrfs_item_size_nr(l, slot)
+					- sizeof(struct btrfs_root_ref);
+			read_extent_buffer(l, subvol_info->name,
+					   item_off, item_len);
+		} else {
+			ret = -ENOENT;
+			goto out;
+		}
+	}
+
+	if (copy_to_user(argp, subvol_info, sizeof(*subvol_info)))
+		ret = -EFAULT;
+
+out:
+	kzfree(subvol_info);
+	btrfs_free_path(path);
+	return ret;
+}
+
 static noinline int btrfs_ioctl_snap_destroy(struct file *file,
 					     void __user *arg)
 {
@@ -5374,6 +5518,8 @@ long btrfs_ioctl(struct file *file, unsigned int
 		return btrfs_ioctl_get_features(file, argp);
 	case BTRFS_IOC_SET_FEATURES:
 		return btrfs_ioctl_set_features(file, argp);
+	case BTRFS_IOC_GET_SUBVOL_INFO:
+		return btrfs_ioctl_get_subvol_info(file, argp);
 	}
 
 	return -ENOTTY;
include/uapi/linux/btrfs.h +51 -0
index c8d99b9ca550..02cd1f1994e8 100644
@@ -725,6 +725,55 @@ struct btrfs_ioctl_send_args {
 	__u64 reserved[4];		/* in */
 };
 
+struct btrfs_ioctl_get_subvol_info_args {
+	/* All field is out */
+	/* Id of this subvolume */
+	__u64 id;
+	/* Name of this subvolume, used to get the real name at mount point */
+	char name[BTRFS_VOL_NAME_MAX + 1];
+	/*
+	 * Id of the subvolume which contains this subvolume.
+	 * Zero for top-level subvolume or deleted subvolume
+	 */
+	__u64 parent_id;
+	/*
+	 * Inode number of the directory which contains this subvolume.
+	 * Zero for top-level subvolume or deleted subvolume
+	 */
+	__u64 dirid;
+
+	/* Latest transaction id of this subvolume */
+	__u64 generation;
+	/* Flags of this subvolume */
+	__u64 flags;
+
+	/* uuid of this subvolume */
+	__u8 uuid[BTRFS_UUID_SIZE];
+	/*
+	 * uuid of the subvolume of which this subvolume is a snapshot.
+	 * All zero for non-snapshot subvolume
+	 */
+	__u8 parent_uuid[BTRFS_UUID_SIZE];
+	/*
+	 * uuid of the subvolume from which this subvolume is received.
+	 * All zero for non-received subvolume
+	 */
+	__u8 received_uuid[BTRFS_UUID_SIZE];
+
+	/* Transaction id indicates when change/create/send/receive happens */
+	__u64 ctransid;
+	__u64 otransid;
+	__u64 stransid;
+	__u64 rtransid;
+	/* Time corresponds to c/o/s/rtransid */
+	struct btrfs_ioctl_timespec ctime;
+	struct btrfs_ioctl_timespec otime;
+	struct btrfs_ioctl_timespec stime;
+	struct btrfs_ioctl_timespec rtime;
+
+	__u64 reserved[8];
+};
+
 /* Error codes as returned by the kernel */
 enum btrfs_err_code {
 	BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET = 1,
@@ -843,5 +892,7 @@ enum btrfs_err_code {
 				   struct btrfs_ioctl_vol_args_v2)
 #define BTRFS_IOC_LOGICAL_INO_V2 _IOWR(BTRFS_IOCTL_MAGIC, 59, \
 					struct btrfs_ioctl_logical_ino_args)
+#define BTRFS_IOC_GET_SUBVOL_INFO _IOR(BTRFS_IOCTL_MAGIC, 60, \
+				struct btrfs_ioctl_get_subvol_info_args)
 
 #endif /* _UAPI_LINUX_BTRFS_H */
#6 lore raw

Re: [PATCH v5 1/3] btrfs: Add unprivileged ioctl which returns subvolume information

Quoted 73 lines · Qu Wenruo
Quoted 70 lines · Misono Tomohiro
Quoted 68 lines · Qu Wenruo
Quoted 50 lines · Tomohiro Misono

Add new unprivileged ioctl BTRFS_IOC_GET_SUBVOL_INFO which returns the information of subvolume containing this inode. (i.e. returns the information in ROOT_ITEM and ROOT_BACKREF.)

---
v4 -> v5
- Update error handling of btrfs_next_leaf() to cover all cases
- Return error if ROOT_BACKREF is not found (except top-level)

 fs/btrfs/ioctl.c           | 146 +++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/btrfs.h |  51 ++++++++++++++++
 2 files changed, 197 insertions(+)
fs/btrfs/ioctl.c +28 -0
index 48e2ddff32bd..c1c9ae9a937d 100644
@@ -2242,6 +2242,150 @@ static noinline int btrfs_ioctl_ino_lookup(struct file *file,
 	return ret;
 }
 
+/* Get the subvolume information in BTRFS_ROOT_ITEM and BTRFS_ROOT_BACKREF */
+static noinline int btrfs_ioctl_get_subvol_info(struct file *file,
+					   void __user *argp)
+{
+	struct btrfs_ioctl_get_subvol_info_args *subvol_info;
+	struct btrfs_root *root;
+	struct btrfs_path *path;
+	struct btrfs_key key;
+
+	struct btrfs_root_item root_item;
+	struct btrfs_root_ref *rref;
+	struct extent_buffer *l;
+	int slot;
+
+	unsigned long item_off;
+	unsigned long item_len;
+
+	struct inode *inode;
+	int ret;
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+
+	subvol_info = kzalloc(sizeof(*subvol_info), GFP_KERNEL);
+	if (!subvol_info) {
+		btrfs_free_path(path);
+		return -ENOMEM;

fs/btrfs/ioctl.c static noinline int btrfs_ioctl_ino_lookup(struct file *file, · line 2282

Quoted 10 lines
fs/btrfs/ioctl.c +10 -0
+	}
+
+	inode = file_inode(file);
+	root = BTRFS_I(inode)->root->fs_info->tree_root;
+
+	key.objectid = BTRFS_I(inode)->root->root_key.objectid;
+	key.type = BTRFS_ROOT_ITEM_KEY;
+	key.offset = 0;
+
+	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);

What about just using btrfs_get_fs_root()?

It would save several lines, and have better error handling along with better tree cache.

Quoted 6 lines
Quoted 2 lines

Here we search ROOT_ITEM of subvolume in root_tree and I don't think btrfs_get_fs_root() would save lines, right?

btrfs_root has root_item member.

So just btrfs_get_fs_root(), then use root->root_item.

Ok, I see. Thanks for you kind help.

Quoted 197 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
fs/btrfs/ioctl.c +15 -0
+	if (ret < 0) {
+		goto out;
+	} else if (ret > 0) {
+		u64 objectid = key.objectid;
+
+		if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
+			ret = btrfs_next_leaf(root, path);
+			if (ret < 0) {
+				goto out;
+			} else if (ret > 0) {
+				ret = -ENOENT;
+				goto out;
+			}
+		}
+
Quoted 8 lines
Quoted 8 lines

fs/btrfs/ioctl.c static noinline int btrfs_ioctl_ino_lookup(struct file *file, · line 2305

Quoted 8 lines
fs/btrfs/ioctl.c +8 -0
+		/* If the subvolume is a snapshot, offset is not zero */
+		btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+		if (key.objectid != objectid ||
+		    key.type != BTRFS_ROOT_ITEM_KEY) {
+			ret = -ENOENT;
+			goto out;
+		}
+	}

These lines can be saved.

Thanks,
Qu

Quoted 165 lines
Quoted 164 lines
Quoted 163 lines
fs/btrfs/ioctl.c +84 -0
+	l = path->nodes[0];
+	slot = path->slots[0];
+	item_off = btrfs_item_ptr_offset(l, slot);
+	item_len = btrfs_item_size_nr(l, slot);
+	read_extent_buffer(l, &root_item, item_off, item_len);
+
+	subvol_info->id = key.objectid;
+
+	subvol_info->generation = btrfs_root_generation(&root_item);
+	subvol_info->flags = btrfs_root_flags(&root_item);
+
+	memcpy(subvol_info->uuid, root_item.uuid, BTRFS_UUID_SIZE);
+	memcpy(subvol_info->parent_uuid, root_item.parent_uuid,
+						    BTRFS_UUID_SIZE);
+	memcpy(subvol_info->received_uuid, root_item.received_uuid,
+						    BTRFS_UUID_SIZE);
+
+	subvol_info->ctransid = btrfs_root_ctransid(&root_item);
+	subvol_info->ctime.sec = btrfs_stack_timespec_sec(&root_item.ctime);
+	subvol_info->ctime.nsec = btrfs_stack_timespec_nsec(&root_item.ctime);
+
+	subvol_info->otransid = btrfs_root_otransid(&root_item);
+	subvol_info->otime.sec = btrfs_stack_timespec_sec(&root_item.otime);
+	subvol_info->otime.nsec = btrfs_stack_timespec_nsec(&root_item.otime);
+
+	subvol_info->stransid = btrfs_root_stransid(&root_item);
+	subvol_info->stime.sec = btrfs_stack_timespec_sec(&root_item.stime);
+	subvol_info->stime.nsec = btrfs_stack_timespec_nsec(&root_item.stime);
+
+	subvol_info->rtransid = btrfs_root_rtransid(&root_item);
+	subvol_info->rtime.sec = btrfs_stack_timespec_sec(&root_item.rtime);
+	subvol_info->rtime.nsec = btrfs_stack_timespec_nsec(&root_item.rtime);
+
+	btrfs_release_path(path);
+	if (key.objectid != BTRFS_FS_TREE_OBJECTID) {
+		key.type = BTRFS_ROOT_BACKREF_KEY;
+		key.offset = 0;
+		ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+		if (ret < 0) {
+			goto out;
+		} else if (path->slots[0] >=
+				btrfs_header_nritems(path->nodes[0])) {
+			ret = btrfs_next_leaf(root, path);
+			if (ret < 0) {
+				goto out;
+			} else if (ret > 0) {
+				ret = -ENOENT;
+				goto out;
+			}
+		}
+
+		l = path->nodes[0];
+		slot = path->slots[0];
+		btrfs_item_key_to_cpu(l, &key, slot);
+		if (key.objectid == subvol_info->id &&
+		    key.type == BTRFS_ROOT_BACKREF_KEY) {
+			subvol_info->parent_id = key.offset;
+
+			rref = btrfs_item_ptr(l, slot, struct btrfs_root_ref);
+			subvol_info->dirid = btrfs_root_ref_dirid(l, rref);
+
+			item_off = btrfs_item_ptr_offset(l, slot)
+					+ sizeof(struct btrfs_root_ref);
+			item_len = btrfs_item_size_nr(l, slot)
+					- sizeof(struct btrfs_root_ref);
+			read_extent_buffer(l, subvol_info->name,
+					   item_off, item_len);
+		} else {
+			ret = -ENOENT;
+			goto out;
+		}
+	}
+
+	if (copy_to_user(argp, subvol_info, sizeof(*subvol_info)))
+		ret = -EFAULT;
+
+out:
+	kzfree(subvol_info);
+	btrfs_free_path(path);
+	return ret;
+}
+
 static noinline int btrfs_ioctl_snap_destroy(struct file *file,
 					     void __user *arg)
 {
@@ -5374,6 +5518,8 @@ long btrfs_ioctl(struct file *file, unsigned int
 		return btrfs_ioctl_get_features(file, argp);
 	case BTRFS_IOC_SET_FEATURES:
 		return btrfs_ioctl_set_features(file, argp);
+	case BTRFS_IOC_GET_SUBVOL_INFO:
+		return btrfs_ioctl_get_subvol_info(file, argp);
 	}
 
 	return -ENOTTY;
include/uapi/linux/btrfs.h +51 -0
index c8d99b9ca550..02cd1f1994e8 100644
@@ -725,6 +725,55 @@ struct btrfs_ioctl_send_args {
 	__u64 reserved[4];		/* in */
 };
 
+struct btrfs_ioctl_get_subvol_info_args {
+	/* All field is out */
+	/* Id of this subvolume */
+	__u64 id;
+	/* Name of this subvolume, used to get the real name at mount point */
+	char name[BTRFS_VOL_NAME_MAX + 1];
+	/*
+	 * Id of the subvolume which contains this subvolume.
+	 * Zero for top-level subvolume or deleted subvolume
+	 */
+	__u64 parent_id;
+	/*
+	 * Inode number of the directory which contains this subvolume.
+	 * Zero for top-level subvolume or deleted subvolume
+	 */
+	__u64 dirid;
+
+	/* Latest transaction id of this subvolume */
+	__u64 generation;
+	/* Flags of this subvolume */
+	__u64 flags;
+
+	/* uuid of this subvolume */
+	__u8 uuid[BTRFS_UUID_SIZE];
+	/*
+	 * uuid of the subvolume of which this subvolume is a snapshot.
+	 * All zero for non-snapshot subvolume
+	 */
+	__u8 parent_uuid[BTRFS_UUID_SIZE];
+	/*
+	 * uuid of the subvolume from which this subvolume is received.
+	 * All zero for non-received subvolume
+	 */
+	__u8 received_uuid[BTRFS_UUID_SIZE];
+
+	/* Transaction id indicates when change/create/send/receive happens */
+	__u64 ctransid;
+	__u64 otransid;
+	__u64 stransid;
+	__u64 rtransid;
+	/* Time corresponds to c/o/s/rtransid */
+	struct btrfs_ioctl_timespec ctime;
+	struct btrfs_ioctl_timespec otime;
+	struct btrfs_ioctl_timespec stime;
+	struct btrfs_ioctl_timespec rtime;
+
+	__u64 reserved[8];
+};
+
 /* Error codes as returned by the kernel */
 enum btrfs_err_code {
 	BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET = 1,
@@ -843,5 +892,7 @@ enum btrfs_err_code {
 				   struct btrfs_ioctl_vol_args_v2)
 #define BTRFS_IOC_LOGICAL_INO_V2 _IOWR(BTRFS_IOCTL_MAGIC, 59, \
 					struct btrfs_ioctl_logical_ino_args)
+#define BTRFS_IOC_GET_SUBVOL_INFO _IOR(BTRFS_IOCTL_MAGIC, 60, \
+				struct btrfs_ioctl_get_subvol_info_args)
 
 #endif /* _UAPI_LINUX_BTRFS_H */
#7 lore raw

PATCH v5 2/3 btrfs: Add unprivileged ioctl which returns subvolume's ROOT_REF

In reply to Tomohiro Misono

Add unprivileged ioctl BTRFS_IOC_GET_SUBVOL_ROOTREF which returns ROOT_REF information of the subvolume containing this inode except the subvolume name (this is because to prevent potential name leak). The subvolume name will be gained by user version of ino_lookup ioctl (BTRFS_IOC_INO_LOOKUP_USER) which also performs permission check.

The min id of root ref's subvolume to be searched is specified by @min_id in struct btrfs_ioctl_get_subvol_rootref_args. After the search ends, @min_id is set to the last searched root ref's subvolid + 1. Also, if there are more root refs than BTRFS_MAX_ROOTREF_BUFFER_NUM, -EOVERFLOW is returned. Therefore the caller can just call this ioctl again without changing the argument to continue search.

---
v4 -> v5
- Update error handling of btrfs_next_leaf() to cover all cases
- Use btrfs_next_item() to reduce the call of btrfs_search_slot()

 fs/btrfs/ioctl.c           | 102 +++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/btrfs.h |  16 +++++++
 2 files changed, 118 insertions(+)
fs/btrfs/ioctl.c +102 -0
index c1c9ae9a937d..db5de77540e1 100644
@@ -2386,6 +2386,106 @@ static noinline int btrfs_ioctl_get_subvol_info(struct file *file,
 	return ret;
 }
 
+/*
+ * Return ROOT_REF information of the subvolume containing this inode
+ * except the subvolume name.
+ */
+static noinline int btrfs_ioctl_get_subvol_rootref(struct file *file,
+					   void __user *argp)
+{
+	struct btrfs_ioctl_get_subvol_rootref_args *rootrefs;
+	struct btrfs_root_ref *rref;
+	struct btrfs_root *root;
+	struct btrfs_path *path;
+	struct btrfs_key key;
+
+	struct extent_buffer *l;
+	int slot;
+
+	struct inode *inode;
+	int ret;
+	u64 objectid;
+	u8 found;
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+
+	rootrefs = memdup_user(argp, sizeof(*rootrefs));
+	if (!rootrefs) {
+		btrfs_free_path(path);
+		return -ENOMEM;
+	}
+
+	inode = file_inode(file);
+	root = BTRFS_I(inode)->root->fs_info->tree_root;
+	objectid = BTRFS_I(inode)->root->root_key.objectid;
+
+	key.objectid = objectid;
+	key.type = BTRFS_ROOT_REF_KEY;
+	key.offset = rootrefs->min_id;
+	found = 0;
+
+	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+	if (ret < 0) {
+		goto out;
+	} else if (path->slots[0] >=
+			btrfs_header_nritems(path->nodes[0])) {
+		ret = btrfs_next_leaf(root, path);
+		if (ret < 0) {
+			goto out;
+		} else if (ret > 0) {
+			ret = -ENOENT;
+			goto out;
+		}
+	}
+	while (1) {
+		l = path->nodes[0];
+		slot = path->slots[0];
+
+		btrfs_item_key_to_cpu(l, &key, slot);
+		if (key.objectid != objectid ||
+		    key.type != BTRFS_ROOT_REF_KEY) {
+			ret = 0;
+			goto out;
+		}
+
+		if (found == BTRFS_MAX_ROOTREF_BUFFER_NUM) {
+			ret = -EOVERFLOW;
+			goto out;
+		}
+
+		rref = btrfs_item_ptr(l, slot, struct btrfs_root_ref);
+		rootrefs->rootref[found].subvolid = key.offset;
+		rootrefs->rootref[found].dirid =
+				  btrfs_root_ref_dirid(l, rref);
+		found++;
+
+		ret = btrfs_next_item(root, path);
+		if (ret < 0) {
+			goto out;
+		} else if (ret > 0) {
+			ret = -ENOENT;
+			goto out;
+		}
+	}
+
+out:
+	if (!ret || ret == -EOVERFLOW) {
+		rootrefs->num_items = found;
+		/* update min_id for next search */
+		if (found)
+			rootrefs->min_id =
+				rootrefs->rootref[found - 1].subvolid + 1;
+		if (copy_to_user(argp, rootrefs, sizeof(*rootrefs)))
+			ret = -EFAULT;
+	}
+
+	btrfs_free_path(path);
+	kfree(rootrefs);
+	return ret;
+}
+
 static noinline int btrfs_ioctl_snap_destroy(struct file *file,
 					     void __user *arg)
 {
@@ -5520,6 +5620,8 @@ long btrfs_ioctl(struct file *file, unsigned int
 		return btrfs_ioctl_set_features(file, argp);
 	case BTRFS_IOC_GET_SUBVOL_INFO:
 		return btrfs_ioctl_get_subvol_info(file, argp);
+	case BTRFS_IOC_GET_SUBVOL_ROOTREF:
+		return btrfs_ioctl_get_subvol_rootref(file, argp);
 	}
 
 	return -ENOTTY;
include/uapi/linux/btrfs.h +16 -0
index 02cd1f1994e8..b6633f5d4847 100644
@@ -774,6 +774,20 @@ struct btrfs_ioctl_get_subvol_info_args {
 	__u64 reserved[8];
 };
 
+#define BTRFS_MAX_ROOTREF_BUFFER_NUM 255
+struct btrfs_ioctl_get_subvol_rootref_args {
+		/* in/out, min id of rootref's subvolid to be searched */
+		__u64 min_id;
+		/* out */
+		struct {
+			__u64 subvolid;
+			__u64 dirid;
+		} rootref[BTRFS_MAX_ROOTREF_BUFFER_NUM];
+		/* out, number of found items */
+		__u8 num_items;
+		__u8 align[7];
+};
+
 /* Error codes as returned by the kernel */
 enum btrfs_err_code {
 	BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET = 1,
@@ -894,5 +908,7 @@ enum btrfs_err_code {
 					struct btrfs_ioctl_logical_ino_args)
 #define BTRFS_IOC_GET_SUBVOL_INFO _IOR(BTRFS_IOCTL_MAGIC, 60, \
 				struct btrfs_ioctl_get_subvol_info_args)
+#define BTRFS_IOC_GET_SUBVOL_ROOTREF _IOWR(BTRFS_IOCTL_MAGIC, 61, \
+				struct btrfs_ioctl_get_subvol_rootref_args)
 
 #endif /* _UAPI_LINUX_BTRFS_H */
Signature · 1 line
2.14.3
#8 lore raw

RE: [PATCH v5 2/3] btrfs: Add unprivileged ioctl which returns subvolume's ROOT_REF

Hi,

Quoted 102 lines

-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of Tomohiro Misono
Sent: Wednesday, May 16, 2018 1:50 PM
To: [email protected]
Subject: [PATCH v5 2/3] btrfs: Add unprivileged ioctl which returns subvolume's ROOT_REF

Add unprivileged ioctl BTRFS_IOC_GET_SUBVOL_ROOTREF which returns ROOT_REF information of the subvolume containing this inode except the subvolume name (this is because to prevent potential name leak). The subvolume name will be gained by user version of ino_lookup ioctl (BTRFS_IOC_INO_LOOKUP_USER) which also performs permission check.

The min id of root ref's subvolume to be searched is specified by @min_id in struct btrfs_ioctl_get_subvol_rootref_args. After the search ends, @min_id is set to the last searched root ref's subvolid + 1. Also, if there are more root refs than BTRFS_MAX_ROOTREF_BUFFER_NUM, -EOVERFLOW is returned. Therefore the caller can just call this ioctl again without changing the argument to continue search.

---
v4 -> v5
- Update error handling of btrfs_next_leaf() to cover all cases
- Use btrfs_next_item() to reduce the call of btrfs_search_slot()

 fs/btrfs/ioctl.c           | 102 +++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/btrfs.h |  16 +++++++
 2 files changed, 118 insertions(+)
fs/btrfs/ioctl.c +70 -0
@@ -2386,6 +2386,106 @@ static noinline int btrfs_ioctl_get_subvol_info(struct file *file,
 	return ret;
 }
 
+/*
+ * Return ROOT_REF information of the subvolume containing this inode
+ * except the subvolume name.
+ */
+static noinline int btrfs_ioctl_get_subvol_rootref(struct file *file,
+					   void __user *argp)
+{
+	struct btrfs_ioctl_get_subvol_rootref_args *rootrefs;
+	struct btrfs_root_ref *rref;
+	struct btrfs_root *root;
+	struct btrfs_path *path;
+	struct btrfs_key key;
+
+	struct extent_buffer *l;
+	int slot;
+
+	struct inode *inode;
+	int ret;
+	u64 objectid;
+	u8 found;
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+
+	rootrefs = memdup_user(argp, sizeof(*rootrefs));
+	if (!rootrefs) {
+		btrfs_free_path(path);
+		return -ENOMEM;
+	}
+
+	inode = file_inode(file);
+	root = BTRFS_I(inode)->root->fs_info->tree_root;
+	objectid = BTRFS_I(inode)->root->root_key.objectid;
+
+	key.objectid = objectid;
+	key.type = BTRFS_ROOT_REF_KEY;
+	key.offset = rootrefs->min_id;
+	found = 0;
+
+	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+	if (ret < 0) {
+		goto out;
+	} else if (path->slots[0] >=
+			btrfs_header_nritems(path->nodes[0])) {
+		ret = btrfs_next_leaf(root, path);
+		if (ret < 0) {
+			goto out;
+		} else if (ret > 0) {
+			ret = -ENOENT;
+			goto out;
+		}
+	}
+	while (1) {
+		l = path->nodes[0];
+		slot = path->slots[0];
+
+		btrfs_item_key_to_cpu(l, &key, slot);
+		if (key.objectid != objectid ||
+		    key.type != BTRFS_ROOT_REF_KEY) {
+			ret = 0;
+			goto out;
+		}
+
+		if (found == BTRFS_MAX_ROOTREF_BUFFER_NUM) {
+			ret = -EOVERFLOW;
+			goto out;
+		}
+
+		rref = btrfs_item_ptr(l, slot, struct btrfs_root_ref);

fs/btrfs/ioctl.c static noinline int btrfs_ioctl_get_subvol_info(struct file *file, · line 2468

Quoted 10 lines
fs/btrfs/ioctl.c +10 -0
+		rootrefs->rootref[found].subvolid = key.offset;
+		rootrefs->rootref[found].dirid =
+				  btrfs_root_ref_dirid(l, rref);
+		found++;
+
+		ret = btrfs_next_item(root, path);
+		if (ret < 0) {
+			goto out;
+		} else if (ret > 0) {
+			ret = -ENOENT;

Should return -EUCLEAN in this scenario.

Quoted 66 lines
fs/btrfs/ioctl.c +22 -0
+			goto out;
+		}
+	}
+
+out:
+	if (!ret || ret == -EOVERFLOW) {
+		rootrefs->num_items = found;
+		/* update min_id for next search */
+		if (found)
+			rootrefs->min_id =
+				rootrefs->rootref[found - 1].subvolid + 1;
+		if (copy_to_user(argp, rootrefs, sizeof(*rootrefs)))
+			ret = -EFAULT;
+	}
+
+	btrfs_free_path(path);
+	kfree(rootrefs);
+	return ret;
+}
+
 static noinline int btrfs_ioctl_snap_destroy(struct file *file,
 					     void __user *arg)
 {
@@ -5520,6 +5620,8 @@ long btrfs_ioctl(struct file *file, unsigned int
 		return btrfs_ioctl_set_features(file, argp);
 	case BTRFS_IOC_GET_SUBVOL_INFO:
 		return btrfs_ioctl_get_subvol_info(file, argp);
+	case BTRFS_IOC_GET_SUBVOL_ROOTREF:
+		return btrfs_ioctl_get_subvol_rootref(file, argp);
 	}
 
 	return -ENOTTY;
include/uapi/linux/btrfs.h +14 -0
@@ -774,6 +774,20 @@ struct btrfs_ioctl_get_subvol_info_args {
 	__u64 reserved[8];
 };
 
+#define BTRFS_MAX_ROOTREF_BUFFER_NUM 255 struct
+btrfs_ioctl_get_subvol_rootref_args {
+		/* in/out, min id of rootref's subvolid to be searched */
+		__u64 min_id;
+		/* out */
+		struct {
+			__u64 subvolid;
+			__u64 dirid;
+		} rootref[BTRFS_MAX_ROOTREF_BUFFER_NUM];
+		/* out, number of found items */
+		__u8 num_items;
+		__u8 align[7];
+};
+
 /* Error codes as returned by the kernel */  enum btrfs_err_code {
 	BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET = 1, @@ -894,5 +908,7 @@ enum btrfs_err_code {
 					struct btrfs_ioctl_logical_ino_args)  #define BTRFS_IOC_GET_SUBVOL_INFO

_IOR(BTRFS_IOCTL_MAGIC, 60, \

include/uapi/linux/btrfs.h +2 -1
 				struct btrfs_ioctl_get_subvol_info_args)
+#define BTRFS_IOC_GET_SUBVOL_ROOTREF _IOWR(BTRFS_IOCTL_MAGIC, 61, \
+				struct btrfs_ioctl_get_subvol_rootref_args)
 
 #endif /* _UAPI_LINUX_BTRFS_H */
--

2.14.3

Quoted 5 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

�翳簕.n�+壏煯壄�+%娝�遍荻w�簕.n�+壏{饼籯~镶炟^n噐■�侂h櫒璀�&{鄗夸z罐楘+€�+zf"穐殘啳嗃i��飦�戧鐉_璁鎗:+v墾�)撸鴐

Reviewed-by on v5 2/3
#9 lore raw

Re: [PATCH v5 2/3] btrfs: Add unprivileged ioctl which returns subvolume's ROOT_REF

In reply to Tomohiro Misono
Quoted 6 lines · Tomohiro Misono

Add unprivileged ioctl BTRFS_IOC_GET_SUBVOL_ROOTREF which returns ROOT_REF information of the subvolume containing this inode except the subvolume name (this is because to prevent potential name leak). The subvolume name will be gained by user version of ino_lookup ioctl (BTRFS_IOC_INO_LOOKUP_USER) which also performs permission check.

Quoted 8 lines

The min id of root ref's subvolume to be searched is specified by @min_id in struct btrfs_ioctl_get_subvol_rootref_args. After the search ends, @min_id is set to the last searched root ref's subvolid + 1. Also, if there are more root refs than BTRFS_MAX_ROOTREF_BUFFER_NUM, -EOVERFLOW is returned. Therefore the caller can just call this ioctl again without changing the argument to continue search.

Looks good.

Just a little off-topic nitpick below, no need to address in this patch.

Quoted 141 lines

---
v4 -> v5
- Update error handling of btrfs_next_leaf() to cover all cases
- Use btrfs_next_item() to reduce the call of btrfs_search_slot()

 fs/btrfs/ioctl.c           | 102 +++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/btrfs.h |  16 +++++++
 2 files changed, 118 insertions(+)
fs/btrfs/ioctl.c +102 -0
index c1c9ae9a937d..db5de77540e1 100644
@@ -2386,6 +2386,106 @@ static noinline int btrfs_ioctl_get_subvol_info(struct file *file,
 	return ret;
 }
 
+/*
+ * Return ROOT_REF information of the subvolume containing this inode
+ * except the subvolume name.
+ */
+static noinline int btrfs_ioctl_get_subvol_rootref(struct file *file,
+					   void __user *argp)
+{
+	struct btrfs_ioctl_get_subvol_rootref_args *rootrefs;
+	struct btrfs_root_ref *rref;
+	struct btrfs_root *root;
+	struct btrfs_path *path;
+	struct btrfs_key key;
+
+	struct extent_buffer *l;
+	int slot;
+
+	struct inode *inode;
+	int ret;
+	u64 objectid;
+	u8 found;
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+
+	rootrefs = memdup_user(argp, sizeof(*rootrefs));
+	if (!rootrefs) {
+		btrfs_free_path(path);
+		return -ENOMEM;
+	}
+
+	inode = file_inode(file);
+	root = BTRFS_I(inode)->root->fs_info->tree_root;
+	objectid = BTRFS_I(inode)->root->root_key.objectid;
+
+	key.objectid = objectid;
+	key.type = BTRFS_ROOT_REF_KEY;
+	key.offset = rootrefs->min_id;
+	found = 0;
+
+	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+	if (ret < 0) {
+		goto out;
+	} else if (path->slots[0] >=
+			btrfs_header_nritems(path->nodes[0])) {
+		ret = btrfs_next_leaf(root, path);
+		if (ret < 0) {
+			goto out;
+		} else if (ret > 0) {
+			ret = -ENOENT;
+			goto out;
+		}
+	}
+	while (1) {
+		l = path->nodes[0];
+		slot = path->slots[0];
+
+		btrfs_item_key_to_cpu(l, &key, slot);
+		if (key.objectid != objectid ||
+		    key.type != BTRFS_ROOT_REF_KEY) {
+			ret = 0;
+			goto out;
+		}
+
+		if (found == BTRFS_MAX_ROOTREF_BUFFER_NUM) {
+			ret = -EOVERFLOW;
+			goto out;
+		}
+
+		rref = btrfs_item_ptr(l, slot, struct btrfs_root_ref);
+		rootrefs->rootref[found].subvolid = key.offset;
+		rootrefs->rootref[found].dirid =
+				  btrfs_root_ref_dirid(l, rref);
+		found++;
+
+		ret = btrfs_next_item(root, path);
+		if (ret < 0) {
+			goto out;
+		} else if (ret > 0) {
+			ret = -ENOENT;
+			goto out;
+		}
+	}
+
+out:
+	if (!ret || ret == -EOVERFLOW) {
+		rootrefs->num_items = found;
+		/* update min_id for next search */
+		if (found)
+			rootrefs->min_id =
+				rootrefs->rootref[found - 1].subvolid + 1;
+		if (copy_to_user(argp, rootrefs, sizeof(*rootrefs)))
+			ret = -EFAULT;
+	}
+
+	btrfs_free_path(path);
+	kfree(rootrefs);
+	return ret;
+}
+
 static noinline int btrfs_ioctl_snap_destroy(struct file *file,
 					     void __user *arg)
 {
@@ -5520,6 +5620,8 @@ long btrfs_ioctl(struct file *file, unsigned int
 		return btrfs_ioctl_set_features(file, argp);
 	case BTRFS_IOC_GET_SUBVOL_INFO:
 		return btrfs_ioctl_get_subvol_info(file, argp);
+	case BTRFS_IOC_GET_SUBVOL_ROOTREF:
+		return btrfs_ioctl_get_subvol_rootref(file, argp);
 	}
 
 	return -ENOTTY;
include/uapi/linux/btrfs.h +4 -0
index 02cd1f1994e8..b6633f5d4847 100644
@@ -774,6 +774,20 @@ struct btrfs_ioctl_get_subvol_info_args {
 	__u64 reserved[8];
 };
 
+#define BTRFS_MAX_ROOTREF_BUFFER_NUM 255
+struct btrfs_ioctl_get_subvol_rootref_args {
+		/* in/out, min id of rootref's subvolid to be searched */
+		__u64 min_id;

include/uapi/linux/btrfs.h struct btrfs_ioctl_get_subvol_info_args { · line 790

Quoted 10 lines
include/uapi/linux/btrfs.h +10 -0
+		/* out */
+		struct {
+			__u64 subvolid;
+			__u64 dirid;
+		} rootref[BTRFS_MAX_ROOTREF_BUFFER_NUM];
+		/* out, number of found items */
+		__u8 num_items;
+		__u8 align[7];
+};
+

What about some BUILD_BUG_ON() to verify the structure size?

Thanks,
Qu

Quoted 12 lines
include/uapi/linux/btrfs.h +2 -0
 /* Error codes as returned by the kernel */
 enum btrfs_err_code {
 	BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET = 1,
@@ -894,5 +908,7 @@ enum btrfs_err_code {
 					struct btrfs_ioctl_logical_ino_args)
 #define BTRFS_IOC_GET_SUBVOL_INFO _IOR(BTRFS_IOCTL_MAGIC, 60, \
 				struct btrfs_ioctl_get_subvol_info_args)
+#define BTRFS_IOC_GET_SUBVOL_ROOTREF _IOWR(BTRFS_IOCTL_MAGIC, 61, \
+				struct btrfs_ioctl_get_subvol_rootref_args)
 
 #endif /* _UAPI_LINUX_BTRFS_H */
#10 lore raw

PATCH v5 3/3 btrfs: Add unprivileged version of ino_lookup ioctl

In reply to Tomohiro Misono

Add unprivileged version of ino_lookup ioctl BTRFS_IOC_INO_LOOKUP_USER to allow normal users to call "btrfs subvololume list/show" etc. in combination with BTRFS_IOC_GET_SUBVOL_INFO/BTRFS_IOC_GET_SUBVOL_ROOTREF.

This can be used like BTRFS_IOC_INO_LOOKUP but the argument is different. This is because it always searches the fs/file tree correspoinding to the fd with which this ioctl is called and also returns the name of bottom subvolume.

The main differences from original ino_lookup ioctl are:
1. Read + Exec permission will be checked using inode_permission() during path construction. -EACCES will be returned in case of failure.
2. Path construction will be stopped at the inode number which corresponds to the fd with which this ioctl is called. If constructed path does not exist under fd's inode, -EACCES will be returned.
3. The name of bottom subvolume is also searched and filled.

Note that the maximum length of path is shorter 256 (BTRFS_VOL_NAME_MAX+1) bytes than ino_lookup ioctl because of space of subvolume's name.

---
 fs/btrfs/ioctl.c           | 204 +++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/btrfs.h |  17 ++++
 2 files changed, 221 insertions(+)
fs/btrfs/ioctl.c +204 -0
index db5de77540e1..5120e934f602 100644
@@ -2200,6 +2200,166 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info,
 	return ret;
 }
 
+static noinline int btrfs_search_path_in_tree_user(struct inode *inode,
+				struct btrfs_ioctl_ino_lookup_user_args *args)
+{
+	struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info;
+	struct super_block *sb = inode->i_sb;
+	struct btrfs_key upper_limit = BTRFS_I(inode)->location;
+	u64 treeid = BTRFS_I(inode)->root->root_key.objectid;
+	u64 dirid = args->dirid;
+
+	unsigned long item_off;
+	unsigned long item_len;
+	struct btrfs_inode_ref *iref;
+	struct btrfs_root_ref *rref;
+	struct btrfs_root *root;
+	struct btrfs_path *path;
+	struct btrfs_key key, key2;
+	struct extent_buffer *l;
+	struct inode *temp_inode;
+	char *ptr;
+	int slot;
+	int len;
+	int total_len = 0;
+	int ret;
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+
+	/*
+	 * If the bottom subvolume does not exist directly under upper_limit,
+	 * construct the path in bottomup way.
+	 */
+	if (dirid != upper_limit.objectid) {
+		ptr = &args->path[BTRFS_INO_LOOKUP_USER_PATH_MAX - 1];
+
+		key.objectid = treeid;
+		key.type = BTRFS_ROOT_ITEM_KEY;
+		key.offset = (u64)-1;
+		root = btrfs_read_fs_root_no_name(fs_info, &key);
+		if (IS_ERR(root)) {
+			ret = -ENOENT;
+			goto out;
+		}
+
+		key.objectid = dirid;
+		key.type = BTRFS_INODE_REF_KEY;
+		key.offset = (u64)-1;
+		while (1) {
+			ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+			if (ret < 0) {
+				goto out;
+			} else if (ret > 0) {
+				ret = btrfs_previous_item(root, path, dirid,
+							  BTRFS_INODE_REF_KEY);
+				if (ret < 0) {
+					goto out;
+				} else if (ret > 0) {
+					ret = -ENOENT;
+					goto out;
+				}
+			}
+
+			l = path->nodes[0];
+			slot = path->slots[0];
+			btrfs_item_key_to_cpu(l, &key, slot);
+
+			iref = btrfs_item_ptr(l, slot, struct btrfs_inode_ref);
+			len = btrfs_inode_ref_name_len(l, iref);
+			ptr -= len + 1;
+			total_len += len + 1;
+			if (ptr < args->path) {
+				ret = -ENAMETOOLONG;
+				goto out;
+			}
+
+			*(ptr + len) = '/';
+			read_extent_buffer(l, ptr,
+			    (unsigned long)(iref + 1), len);
+
+			/* Check the read+exec permission of this directory */
+			ret = btrfs_previous_item(root, path, dirid,
+						  BTRFS_INODE_ITEM_KEY);
+			if (ret < 0) {
+				goto out;
+			} else if (ret > 0) {
+				ret = -ENOENT;
+				goto out;
+			}
+
+			l = path->nodes[0];
+			slot = path->slots[0];
+			btrfs_item_key_to_cpu(l, &key2, slot);
+			if (key2.objectid != dirid) {
+				ret = -ENOENT;
+				goto out;
+			}
+
+			temp_inode = btrfs_iget(sb, &key2, root, NULL);
+			ret = inode_permission(temp_inode, MAY_READ | MAY_EXEC);
+			iput(temp_inode);
+			if (ret) {
+				ret = -EACCES;
+				goto out;
+			}
+
+			if (key.offset == upper_limit.objectid)
+				break;
+			if (key.objectid == BTRFS_FIRST_FREE_OBJECTID) {
+				ret = -EACCES;
+				goto out;
+			}
+
+			btrfs_release_path(path);
+			key.objectid = key.offset;
+			key.offset = (u64)-1;
+			dirid = key.objectid;
+		}
+
+		memmove(args->path, ptr, total_len);
+		args->path[total_len] = '\0';
+		btrfs_release_path(path);
+	}
+
+	/* get the bottom subvolume's name from ROOT_REF */
+	root = fs_info->tree_root;
+	key.objectid = treeid;
+	key.type = BTRFS_ROOT_REF_KEY;
+	key.offset = args->subvolid;
+	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+	if (ret < 0) {
+		goto out;
+	} else if (ret > 0) {
+		ret = -ENOENT;
+		goto out;
+	}
+
+	l = path->nodes[0];
+	slot = path->slots[0];
+	btrfs_item_key_to_cpu(l, &key, slot);
+
+	item_off = btrfs_item_ptr_offset(l, slot);
+	item_len = btrfs_item_size_nr(l, slot);
+	/* check if dirid in ROOT_REF corresponds to passed dirid */
+	rref = btrfs_item_ptr(l, slot, struct btrfs_root_ref);
+	if (args->dirid != btrfs_root_ref_dirid(l, rref)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* copy subvolume's name */
+	item_off += sizeof(struct btrfs_root_ref);
+	item_len -= sizeof(struct btrfs_root_ref);
+	read_extent_buffer(l, args->name, item_off, item_len);
+	args->name[item_len] = '\0';
+
+out:
+	btrfs_free_path(path);
+	return ret;
+}
+
 static noinline int btrfs_ioctl_ino_lookup(struct file *file,
 					   void __user *argp)
 {
@@ -2242,6 +2402,48 @@ static noinline int btrfs_ioctl_ino_lookup(struct file *file,
 	return ret;
 }
 
+/*
+ * User version of ino_lookup ioctl (unprivileged)
+ *
+ * The main differences from original ino_lookup ioctl are:
+ *   1. Read + Exec permission will be checked using inode_permission()
+ *      during path construction. -EACCES will be returned in case
+ *      of failure.
+ *   2. Path construction will be stopped at the inode number which
+ *      corresponds to the fd with which this ioctl is called. If
+ *      constructed path does not exist under fd's inode, -EACCES
+ *      will be returned.
+ *   3. The name of bottom subvolume is also searched and filled.
+ */
+static noinline int btrfs_ioctl_ino_lookup_user(struct file *file,
+					   void __user *argp)
+{
+	struct btrfs_ioctl_ino_lookup_user_args *args;
+	struct inode *inode;
+	int ret;
+
+	args = memdup_user(argp, sizeof(*args));
+	if (IS_ERR(args))
+		return PTR_ERR(args);
+
+	inode = file_inode(file);
+
+	if (args->dirid == BTRFS_FIRST_FREE_OBJECTID &&
+	    BTRFS_I(inode)->location.objectid != BTRFS_FIRST_FREE_OBJECTID) {
+	/* The subvolume does not exist under fd with which this is called */
+		kfree(args);
+		return -EACCES;
+	}
+
+	ret = btrfs_search_path_in_tree_user(inode, args);
+
+	if (ret == 0 && copy_to_user(argp, args, sizeof(*args)))
+		ret = -EFAULT;
+
+	kfree(args);
+	return ret;
+}
+
 /* Get the subvolume information in BTRFS_ROOT_ITEM and BTRFS_ROOT_BACKREF */
 static noinline int btrfs_ioctl_get_subvol_info(struct file *file,
 					   void __user *argp)
@@ -5622,6 +5824,8 @@ long btrfs_ioctl(struct file *file, unsigned int
 		return btrfs_ioctl_get_subvol_info(file, argp);
 	case BTRFS_IOC_GET_SUBVOL_ROOTREF:
 		return btrfs_ioctl_get_subvol_rootref(file, argp);
+	case BTRFS_IOC_INO_LOOKUP_USER:
+		return btrfs_ioctl_ino_lookup_user(file, argp);
 	}
 
 	return -ENOTTY;
include/uapi/linux/btrfs.h +17 -0
index b6633f5d4847..177447c4d4c6 100644
@@ -422,6 +422,21 @@ struct btrfs_ioctl_ino_lookup_args {
 	char name[BTRFS_INO_LOOKUP_PATH_MAX];
 };
 
+#define BTRFS_INO_LOOKUP_USER_PATH_MAX (4080-BTRFS_VOL_NAME_MAX-1)
+struct btrfs_ioctl_ino_lookup_user_args {
+	/* in, inode number containing the subvolume of 'subvolid' */
+	__u64 dirid;
+	/* in */
+	__u64 subvolid;
+	/* out, name of the subvolume of 'subvolid' */
+	char name[BTRFS_VOL_NAME_MAX + 1];
+	/*
+	 * out, constructed path from the directory with which
+	 * the ioctl is called to dirid
+	 */
+	char path[BTRFS_INO_LOOKUP_USER_PATH_MAX];
+};
+
 /* Search criteria for the btrfs SEARCH ioctl family. */
 struct btrfs_ioctl_search_key {
 	/*
@@ -910,5 +925,7 @@ enum btrfs_err_code {
 				struct btrfs_ioctl_get_subvol_info_args)
 #define BTRFS_IOC_GET_SUBVOL_ROOTREF _IOWR(BTRFS_IOCTL_MAGIC, 61, \
 				struct btrfs_ioctl_get_subvol_rootref_args)
+#define BTRFS_IOC_INO_LOOKUP_USER _IOWR(BTRFS_IOCTL_MAGIC, 62, \
+				struct btrfs_ioctl_ino_lookup_user_args)
 
 #endif /* _UAPI_LINUX_BTRFS_H */
Signature · 1 line
2.14.3
#11 lore raw
In reply to Tomohiro Misono
Quoted 17 lines · Tomohiro Misono

[based on current misc-next]

changelog:
v4 -> v5
- Update error handling of 1st/2nd patch. See each log for details
- Fix misspelling
v3 -> v4
- call btrfs_next_leaf() after btrfs_search_slot() when the slot position exceeds the number of items
- rebased to current misc-next
v2 -> v3
- fix kbuild test bot warning
v1 -> v2
- completely reimplement 1st/2nd ioctl to have user friendly api
- various cleanup, remove unnecessary goto
===

Quoted 6 lines

This adds three new unprivileged ioctls:

1st patch:
ioctl which returns subvolume information of ROOT_ITEM and ROOT_BACKREF
2nd patch:
ioctl which returns subvolume information of ROOT_REF (without subvolume name)

First 2 patches looks mostly fine.

Quoted 2 lines

3rd patch:
user version of ino_lookup ioctl which also performs permission check.

I'm a little concerned about this.

What will happen in the following scenario?
- Environment is container whose rootfs is a subvolume of btrfs
- The root and normal use try to call subvolume list on their rootfs

Will it leak the real subvolume layout to the container root/normal user?

Or it will leak anyway even without the unprivileged ioctl?

Thanks,
Qu

Quoted 17 lines

They will be used to implement user version of "subvolume list/show" etc. in user tools.
See each commit log for more detals.

The implementation of btrfs-progs can be found in the ML titled as follows:
[PATCH 0/11] btrfs-progs: Rework of "subvolume list/show" and relax the root privileges of them

Tomohiro Misono (3):
btrfs: Add unprivileged ioctl which returns subvolume information
btrfs: Add unprivileged ioctl which returns subvolume's ROOT_REF
btrfs: Add unprivileged version of ino_lookup ioctl

 fs/btrfs/ioctl.c           | 452 +++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/btrfs.h |  84 +++++++++
 2 files changed, 536 insertions(+)
#12 lore raw
Quoted 34 lines · Qu Wenruo
Quoted 17 lines · Tomohiro Misono

[based on current misc-next]

changelog:
v4 -> v5
- Update error handling of 1st/2nd patch. See each log for details
- Fix misspelling
v3 -> v4
- call btrfs_next_leaf() after btrfs_search_slot() when the slot position exceeds the number of items
- rebased to current misc-next
v2 -> v3
- fix kbuild test bot warning
v1 -> v2
- completely reimplement 1st/2nd ioctl to have user friendly api
- various cleanup, remove unnecessary goto
===

Quoted 6 lines

This adds three new unprivileged ioctls:

1st patch:
ioctl which returns subvolume information of ROOT_ITEM and ROOT_BACKREF
2nd patch:
ioctl which returns subvolume information of ROOT_REF (without subvolume name)

First 2 patches looks mostly fine.

Quoted 2 lines

3rd patch:
user version of ino_lookup ioctl which also performs permission check.

I'm a little concerned about this.

Quoted 7 lines

What will happen in the following scenario?
- Environment is container whose rootfs is a subvolume of btrfs
- The root and normal use try to call subvolume list on their rootfs

Will it leak the real subvolume layout to the container root/normal user?

Or it will leak anyway even without the unprivileged ioctl?

Hi,

I'm not sure about container, but these ioctls searches subvolume (fs tree) of fd with witch ioctl is called (i.e. the caller needs to open the subvolume first) and cannot search arbitrary tree. So, normal user can only get the information under the rootfs's subvolume.

On the other and, root can use TREE_SEARCH/INO_LOOKUP ioctl too which can search arbitrary tree and get all info. So, I think root can get real layout.

Does this answer make sense?

Quoted 22 lines

Thanks,
Qu

Quoted 17 lines

They will be used to implement user version of "subvolume list/show" etc. in user tools.
See each commit log for more detals.

The implementation of btrfs-progs can be found in the ML titled as follows:
[PATCH 0/11] btrfs-progs: Rework of "subvolume list/show" and relax the root privileges of them

Tomohiro Misono (3):
btrfs: Add unprivileged ioctl which returns subvolume information
btrfs: Add unprivileged ioctl which returns subvolume's ROOT_REF
btrfs: Add unprivileged version of ino_lookup ioctl

 fs/btrfs/ioctl.c           | 452 +++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/btrfs.h |  84 +++++++++
 2 files changed, 536 insertions(+)
Reviewed-by
#13 lore raw
Quoted 50 lines · Misono Tomohiro
Quoted 34 lines · Qu Wenruo
Quoted 17 lines · Tomohiro Misono

[based on current misc-next]

changelog:
v4 -> v5
- Update error handling of 1st/2nd patch. See each log for details
- Fix misspelling
v3 -> v4
- call btrfs_next_leaf() after btrfs_search_slot() when the slot position exceeds the number of items
- rebased to current misc-next
v2 -> v3
- fix kbuild test bot warning
v1 -> v2
- completely reimplement 1st/2nd ioctl to have user friendly api
- various cleanup, remove unnecessary goto
===

Quoted 6 lines

This adds three new unprivileged ioctls:

1st patch:
ioctl which returns subvolume information of ROOT_ITEM and ROOT_BACKREF
2nd patch:
ioctl which returns subvolume information of ROOT_REF (without subvolume name)

First 2 patches looks mostly fine.

Quoted 2 lines

3rd patch:
user version of ino_lookup ioctl which also performs permission check.

I'm a little concerned about this.

Quoted 7 lines

What will happen in the following scenario?
- Environment is container whose rootfs is a subvolume of btrfs
- The root and normal use try to call subvolume list on their rootfs

Will it leak the real subvolume layout to the container root/normal user?

Or it will leak anyway even without the unprivileged ioctl?

Hi,

I'm not sure about container, but these ioctls searches subvolume (fs tree) of fd with witch ioctl is called (i.e. the caller needs to open the subvolume first) and cannot search arbitrary tree. So, normal user can only get the information under the rootfs's subvolume.

Quoted 4 lines

On the other and, root can use TREE_SEARCH/INO_LOOKUP ioctl too which can search arbitrary tree and get all info. So, I think root can get real layout.

Does this answer make sense?

Makes sense now.

Thanks,
Qu

Quoted 29 lines
Quoted 23 lines

Thanks,
Qu

Quoted 17 lines

They will be used to implement user version of "subvolume list/show" etc. in user tools.
See each commit log for more detals.

The implementation of btrfs-progs can be found in the ML titled as follows:
[PATCH 0/11] btrfs-progs: Rework of "subvolume list/show" and relax the root privileges of them

Tomohiro Misono (3):
btrfs: Add unprivileged ioctl which returns subvolume information
btrfs: Add unprivileged ioctl which returns subvolume's ROOT_REF
btrfs: Add unprivileged version of ino_lookup ioctl

 fs/btrfs/ioctl.c           | 452 +++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/btrfs.h |  84 +++++++++
 2 files changed, 536 insertions(+)

--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html