All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/3] btrfs: Add three new unprivileged ioctls to allow normal users to call "sub list/show" etc.
@ 2018-03-19  7:27 Misono, Tomohiro
  2018-03-19  7:27 ` [PATCH v3 1/3] btrfs: Add unprivileged ioctl which returns subvolume information Misono, Tomohiro
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Misono, Tomohiro @ 2018-03-19  7:27 UTC (permalink / raw)
  To: linux-btrfs

changelog:

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 peforms 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 rfc implementation of btrfs-progs can be found in the ML titled as follows: 
  [RFC PATCH v3 0/7] btrfs-progs: Allow normal user to call "subvolume list/show"


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           | 413 +++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/btrfs.h |  84 ++++++++++
 2 files changed, 497 insertions(+)

-- 
2.14.3


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v3 1/3] btrfs: Add unprivileged ioctl which returns subvolume information
  2018-03-19  7:27 [PATCH v3 0/3] btrfs: Add three new unprivileged ioctls to allow normal users to call "sub list/show" etc Misono, Tomohiro
@ 2018-03-19  7:27 ` Misono, Tomohiro
  2018-03-19  7:27 ` [PATCH v3 2/3] btrfs: Add unprivileged ioctl which returns subvolume's ROOT_REF Misono, Tomohiro
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Misono, Tomohiro @ 2018-03-19  7:27 UTC (permalink / raw)
  To: linux-btrfs

 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.)

Signed-off-by: Tomohiro Misono <misono.tomohiro@jp.fujitsu.com>
---
 fs/btrfs/ioctl.c           | 118 +++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/btrfs.h |  51 ++++++++++++++++++++
 2 files changed, 169 insertions(+)

diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 111ee282b777..be2abe7a5bba 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -2309,6 +2309,122 @@ 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) {
+		/* If the subvolume is a snapshot, offset is not zero */
+		u64 objectid = key.objectid;
+
+		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);
+	key.type = BTRFS_ROOT_BACKREF_KEY;
+	key.offset = 0;
+	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+	if (ret < 0)
+		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);
+	}
+
+	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)
 {
@@ -5663,6 +5779,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;
diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
index c8d99b9ca550..ed053852c71f 100644
--- a/include/uapi/linux/btrfs.h
+++ b/include/uapi/linux/btrfs.h
@@ -725,6 +725,55 @@ struct btrfs_ioctl_send_args {
 	__u64 reserved[4];		/* in */
 };
 
+struct btrfs_ioctl_get_subvol_info_args {
+	/* All filed 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 */
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v3 2/3] btrfs: Add unprivileged ioctl which returns subvolume's ROOT_REF
  2018-03-19  7:27 [PATCH v3 0/3] btrfs: Add three new unprivileged ioctls to allow normal users to call "sub list/show" etc Misono, Tomohiro
  2018-03-19  7:27 ` [PATCH v3 1/3] btrfs: Add unprivileged ioctl which returns subvolume information Misono, Tomohiro
@ 2018-03-19  7:27 ` Misono, Tomohiro
  2018-03-19  7:28 ` [PATCH v3 3/3] btrfs: Add unprivileged version of ino_lookup ioctl Misono, Tomohiro
  2018-04-10 18:23 ` [PATCH v3 0/3] btrfs: Add three new unprivileged ioctls to allow normal users to call "sub list/show" etc David Sterba
  3 siblings, 0 replies; 6+ messages in thread
From: Misono, Tomohiro @ 2018-03-19  7:27 UTC (permalink / raw)
  To: linux-btrfs

Add unprivileged ioctl BTRFS_IOC_GET_SUBVOL_ROOTREF which
returns ROOT_REF information of the subvolume containing this inode.
The min id of root ref's subvolume to be searched is specified by
min_id in struct btrfs_ioctl_get_subvol_rootref_args.

If there are more root refs than BTRFS_MAX_ROOTREF_BUFFER_NUM,
this ioctl sets min_id to the last searched root ref's subvolid + 1
and return -EOVERFLOW. Therefore the caller can just call this ioctl
again without changing the argument to continue search.

Signed-off-by: Tomohiro Misono <misono.tomohiro@jp.fujitsu.com>
---
 fs/btrfs/ioctl.c           | 91 ++++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/btrfs.h | 16 ++++++++
 2 files changed, 107 insertions(+)

diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index be2abe7a5bba..5b97a735790e 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -2425,6 +2425,95 @@ static noinline int btrfs_ioctl_get_subvol_info(struct file *file,
 	return ret;
 }
 
+/* Returns ROOT_REF information of the subvolume contining this inode. */
+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 i, nritems;
+	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;
+	while (1) {
+		ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+		if (ret < 0)
+			goto out;
+
+		l = path->nodes[0];
+		slot = path->slots[0];
+		nritems = btrfs_header_nritems(l);
+		if (nritems - slot == 0) {
+			ret = 0;
+			goto out;
+		}
+
+		for (i = slot; i < nritems; i++) {
+			btrfs_item_key_to_cpu(l, &key, i);
+			if (key.objectid != objectid ||
+					key.type != BTRFS_ROOT_REF_KEY) {
+				ret = 0;
+				goto out;
+			}
+
+			if (found == BTRFS_MAX_ROOTREF_BUFFER_NUM) {
+				/* update min_id for next search */
+				rootrefs->min_id = key.offset;
+				ret = -EOVERFLOW;
+				goto out;
+			}
+
+			rref = btrfs_item_ptr(l, i, struct btrfs_root_ref);
+			rootrefs->rootref[found].subvolid = key.offset;
+			rootrefs->rootref[found].dirid =
+					  btrfs_root_ref_dirid(l, rref);
+			found++;
+		}
+
+		btrfs_release_path(path);
+		key.offset++;
+	}
+
+out:
+	if (!ret || ret == -EOVERFLOW) {
+		rootrefs->num_items = found;
+		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)
 {
@@ -5781,6 +5870,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;
diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
index ed053852c71f..82c88d52d6e6 100644
--- a/include/uapi/linux/btrfs.h
+++ b/include/uapi/linux/btrfs.h
@@ -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 */
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v3 3/3] btrfs: Add unprivileged version of ino_lookup ioctl
  2018-03-19  7:27 [PATCH v3 0/3] btrfs: Add three new unprivileged ioctls to allow normal users to call "sub list/show" etc Misono, Tomohiro
  2018-03-19  7:27 ` [PATCH v3 1/3] btrfs: Add unprivileged ioctl which returns subvolume information Misono, Tomohiro
  2018-03-19  7:27 ` [PATCH v3 2/3] btrfs: Add unprivileged ioctl which returns subvolume's ROOT_REF Misono, Tomohiro
@ 2018-03-19  7:28 ` Misono, Tomohiro
  2018-04-10 18:23 ` [PATCH v3 0/3] btrfs: Add three new unprivileged ioctls to allow normal users to call "sub list/show" etc David Sterba
  3 siblings, 0 replies; 6+ messages in thread
From: Misono, Tomohiro @ 2018-03-19  7:28 UTC (permalink / raw)
  To: linux-btrfs

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.

Signed-off-by: Tomohiro Misono <misono.tomohiro@jp.fujitsu.com>
---
 fs/btrfs/ioctl.c           | 204 +++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/btrfs.h |  17 ++++
 2 files changed, 221 insertions(+)

diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 5b97a735790e..5ea1e7bd03ce 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -2267,6 +2267,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 = -1;
+
+	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 subolume'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)
 {
@@ -2309,6 +2469,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 = 0;
+
+	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)
@@ -5872,6 +6074,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;
diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
index 82c88d52d6e6..5f91de93dd5a 100644
--- a/include/uapi/linux/btrfs.h
+++ b/include/uapi/linux/btrfs.h
@@ -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 */
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v3 0/3] btrfs: Add three new unprivileged ioctls to allow normal users to call "sub list/show" etc.
  2018-03-19  7:27 [PATCH v3 0/3] btrfs: Add three new unprivileged ioctls to allow normal users to call "sub list/show" etc Misono, Tomohiro
                   ` (2 preceding siblings ...)
  2018-03-19  7:28 ` [PATCH v3 3/3] btrfs: Add unprivileged version of ino_lookup ioctl Misono, Tomohiro
@ 2018-04-10 18:23 ` David Sterba
  2018-04-13  4:30   ` Misono Tomohiro
  3 siblings, 1 reply; 6+ messages in thread
From: David Sterba @ 2018-04-10 18:23 UTC (permalink / raw)
  To: Misono, Tomohiro; +Cc: linux-btrfs

On Mon, Mar 19, 2018 at 04:27:09PM +0900, Misono, Tomohiro wrote:
> changelog:
> 
> 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 peforms permission check.

The overall approach to listing subvolumes looks good. We can enumerate
them, get the relations and the details. Making some sense of that in
the userspace is a whole different and maybe more difficult topic.

I hope we could use the opportunity to clean up the listing commandline
interface and output at the same time, as there's going to be possibly
some incompatibility introduced.

We need to start with current usecases and how they're implemented or
mis-implemented (ie. leading to confusion). The discussions I've read so
far cover a good part of that.

I'll review and add the kernel patches to misc-next to get some testing
coverage. As this is a non-restricted ioctl full of pointers and bytes
shuffled around, we will have to do an extra security-oriented review.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v3 0/3] btrfs: Add three new unprivileged ioctls to allow normal users to call "sub list/show" etc.
  2018-04-10 18:23 ` [PATCH v3 0/3] btrfs: Add three new unprivileged ioctls to allow normal users to call "sub list/show" etc David Sterba
@ 2018-04-13  4:30   ` Misono Tomohiro
  0 siblings, 0 replies; 6+ messages in thread
From: Misono Tomohiro @ 2018-04-13  4:30 UTC (permalink / raw)
  To: dsterba, linux-btrfs

On 2018/04/11 3:23, David Sterba wrote:
> On Mon, Mar 19, 2018 at 04:27:09PM +0900, Misono, Tomohiro wrote:
>> changelog:
>>
>> 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 peforms permission check.
> 
> The overall approach to listing subvolumes looks good. We can enumerate
> them, get the relations and the details. Making some sense of that in
> the userspace is a whole different and maybe more difficult topic.
> 
> I hope we could use the opportunity to clean up the listing commandline
> interface and output at the same time, as there's going to be possibly
> some incompatibility introduced.
> 
> We need to start with current usecases and how they're implemented or
> mis-implemented (ie. leading to confusion). The discussions I've read so
> far cover a good part of that.

So, I'd like to continue working on progs part based on these ioctls
but there are some things I want to confirm.

I think current problems of "sub list" are:
  - printed path is ambiguous\v (the output may differ when specified different path in the fs)
  - -a or -o options do not work well
  - top level filed is meaningless

My idea of new list is (as in [1]):
  - (default, use new ioctls for normal user)\v
     list the subvolumes under the specified path, including subvolumes mounted
\v     below the specified path. Any user can do this (with appropriate permission checks).
     The path to be printed is the relative from the specified path.
  - (-a option, use TREE_SEARCH ioctl)
\v     list all the subvolmumes in the filesystem. Only root can do this.
     The path to be printed is the absolute path from the toplevel subvolume.
  - (-o option)
     deprecated

and also cleanups of some fields.
For root, TREE_SEARCH ioctl still can be used so that new progs works with old kernels.

Do you have any comments about this?

Also, are you going to merge the omitted libbtrfsuitl patch which refactor "sub list" [2]?
This patch actually does two things:
 1. remove subvol print function from libbtrfs (abi break) and 
 2. refactor "sub list" by using libbtrfsutil.

Is the first part the reason this patch was omitted?

However, by this change subvolume information will be stored in an array instead of rb tree.
In order to implement proposed default behavior, this is a preferred way;
we can't use rb tree because there may exist the subvolume with the same id in different path.

In any case, I'd like to know which version of btrfs-progs I should use to implement the features.

Regards,
Tomohiro Misono

[1] https://www.mail-archive.com/linux-btrfs@vger.kernel.org/msg75119.html
[2] https://www.mail-archive.com/linux-btrfs@vger.kernel.org/msg73601.html

> 
> I'll review and add the kernel patches to misc-next to get some testing
> coverage. As this is a non-restricted ioctl full of pointers and bytes
> shuffled around, we will have to do an extra security-oriented review.
> 
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2018-04-13  4:31 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-03-19  7:27 [PATCH v3 0/3] btrfs: Add three new unprivileged ioctls to allow normal users to call "sub list/show" etc Misono, Tomohiro
2018-03-19  7:27 ` [PATCH v3 1/3] btrfs: Add unprivileged ioctl which returns subvolume information Misono, Tomohiro
2018-03-19  7:27 ` [PATCH v3 2/3] btrfs: Add unprivileged ioctl which returns subvolume's ROOT_REF Misono, Tomohiro
2018-03-19  7:28 ` [PATCH v3 3/3] btrfs: Add unprivileged version of ino_lookup ioctl Misono, Tomohiro
2018-04-10 18:23 ` [PATCH v3 0/3] btrfs: Add three new unprivileged ioctls to allow normal users to call "sub list/show" etc David Sterba
2018-04-13  4:30   ` Misono Tomohiro

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.