linux-btrfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/3] btrfs: Add three new unprivileged ioctls to allow normal users to call "sub list/show" etc.
@ 2018-03-15  8:09 Misono, Tomohiro
  2018-03-15  8:10 ` [PATCH v2 1/3] btrfs: Add unprivileged ioctl which returns subvolume information Misono, Tomohiro
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Misono, Tomohiro @ 2018-03-15  8:09 UTC (permalink / raw)
  To: linux-btrfs

changelog:
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 details.

The rfc implementation of btrfs-progs can be found in the ML titled as follows: 
  [RFC PATCH v2 0/8] 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] 7+ messages in thread

* [PATCH v2 1/3] btrfs: Add unprivileged ioctl which returns subvolume information
  2018-03-15  8:09 [PATCH v2 0/3] btrfs: Add three new unprivileged ioctls to allow normal users to call "sub list/show" etc Misono, Tomohiro
@ 2018-03-15  8:10 ` Misono, Tomohiro
  2018-03-18  4:16   ` kbuild test robot
  2018-03-15  8:11 ` [PATCH v2 2/3] btrfs: Add unprivileged ioctl which returns subvolume's ROOT_REF Misono, Tomohiro
  2018-03-15  8:11 ` [PATCH v2 3/3] btrfs: Add unprivileged version of ino_lookup ioctl Misono, Tomohiro
  2 siblings, 1 reply; 7+ messages in thread
From: Misono, Tomohiro @ 2018-03-15  8:10 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..852421ac81cd 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 __user *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] 7+ messages in thread

* [PATCH v2 2/3] btrfs: Add unprivileged ioctl which returns subvolume's ROOT_REF
  2018-03-15  8:09 [PATCH v2 0/3] btrfs: Add three new unprivileged ioctls to allow normal users to call "sub list/show" etc Misono, Tomohiro
  2018-03-15  8:10 ` [PATCH v2 1/3] btrfs: Add unprivileged ioctl which returns subvolume information Misono, Tomohiro
@ 2018-03-15  8:11 ` Misono, Tomohiro
  2018-03-18  4:46   ` kbuild test robot
  2018-03-15  8:11 ` [PATCH v2 3/3] btrfs: Add unprivileged version of ino_lookup ioctl Misono, Tomohiro
  2 siblings, 1 reply; 7+ messages in thread
From: Misono, Tomohiro @ 2018-03-15  8:11 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 852421ac81cd..816a3eb60020 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 __user *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] 7+ messages in thread

* [PATCH v2 3/3] btrfs: Add unprivileged version of ino_lookup ioctl
  2018-03-15  8:09 [PATCH v2 0/3] btrfs: Add three new unprivileged ioctls to allow normal users to call "sub list/show" etc Misono, Tomohiro
  2018-03-15  8:10 ` [PATCH v2 1/3] btrfs: Add unprivileged ioctl which returns subvolume information Misono, Tomohiro
  2018-03-15  8:11 ` [PATCH v2 2/3] btrfs: Add unprivileged ioctl which returns subvolume's ROOT_REF Misono, Tomohiro
@ 2018-03-15  8:11 ` Misono, Tomohiro
  2018-03-18  5:11   ` kbuild test robot
  2 siblings, 1 reply; 7+ messages in thread
From: Misono, Tomohiro @ 2018-03-15  8:11 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 816a3eb60020..87534e1855f2 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 */
+	read_extent_buffer(l, &rref, item_off, sizeof(struct btrfs_root_ref));
+	if (rref.dirid != args->dirid) {
+		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] 7+ messages in thread

* Re: [PATCH v2 1/3] btrfs: Add unprivileged ioctl which returns subvolume information
  2018-03-15  8:10 ` [PATCH v2 1/3] btrfs: Add unprivileged ioctl which returns subvolume information Misono, Tomohiro
@ 2018-03-18  4:16   ` kbuild test robot
  0 siblings, 0 replies; 7+ messages in thread
From: kbuild test robot @ 2018-03-18  4:16 UTC (permalink / raw)
  To: Misono, Tomohiro; +Cc: kbuild-all, linux-btrfs

Hi Tomohiro,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v4.16-rc5 next-20180316]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Misono-Tomohiro/btrfs-Add-three-new-unprivileged-ioctls-to-allow-normal-users-to-call-sub-list-show-etc/20180318-101013
reproduce:
        # apt-get install sparse
        make ARCH=x86_64 allmodconfig
        make C=1 CF=-D__CHECK_ENDIAN__


sparse warnings: (new ones prefixed by >>)

   fs/btrfs/ioctl.c:1561:9: sparse: incompatible types in comparison expression (different address spaces)
>> fs/btrfs/ioctl.c:2336:21: sparse: incorrect type in assignment (different address spaces) @@    expected struct btrfs_ioctl_get_subvol_info_args [noderef] <asn:1>*subvol_info @@    got sn:1>*subvol_info @@
   fs/btrfs/ioctl.c:2336:21:    expected struct btrfs_ioctl_get_subvol_info_args [noderef] <asn:1>*subvol_info
   fs/btrfs/ioctl.c:2336:21:    got void *
>> fs/btrfs/ioctl.c:2373:9: sparse: incorrect type in argument 1 (different address spaces) @@    expected void *to @@    got unsigned char [noderef] <avoid *to @@
   fs/btrfs/ioctl.c:2373:9:    expected void *to
   fs/btrfs/ioctl.c:2373:9:    got unsigned char [noderef] <asn:1>*<noident>
   fs/btrfs/ioctl.c:2374:9: sparse: incorrect type in argument 1 (different address spaces) @@    expected void *to @@    got unsigned char [noderef] <avoid *to @@
   fs/btrfs/ioctl.c:2374:9:    expected void *to
   fs/btrfs/ioctl.c:2374:9:    got unsigned char [noderef] <asn:1>*<noident>
   fs/btrfs/ioctl.c:2376:9: sparse: incorrect type in argument 1 (different address spaces) @@    expected void *to @@    got unsigned char [noderef] <avoid *to @@
   fs/btrfs/ioctl.c:2376:9:    expected void *to
   fs/btrfs/ioctl.c:2376:9:    got unsigned char [noderef] <asn:1>*<noident>
>> fs/btrfs/ioctl.c:2416:39: sparse: incorrect type in argument 2 (different address spaces) @@    expected void *dst @@    got char [noderef] <avoid *dst @@
   fs/btrfs/ioctl.c:2416:39:    expected void *dst
   fs/btrfs/ioctl.c:2416:39:    got char [noderef] <asn:1>*<noident>
>> fs/btrfs/ioctl.c:2419:32: sparse: incorrect type in argument 2 (different address spaces) @@    expected void const *from @@    got struct btrfs_ioctl_get_subvol_info_args [nodervoid const *from @@
   fs/btrfs/ioctl.c:2419:32:    expected void const *from
   fs/btrfs/ioctl.c:2419:32:    got struct btrfs_ioctl_get_subvol_info_args [noderef] <asn:1>*subvol_info
>> fs/btrfs/ioctl.c:2423:16: sparse: incorrect type in argument 1 (different address spaces) @@    expected void const *<noident> @@    got struct btrfs_ioctl_get_subvol_info_args [nodervoid const *<noident> @@
   fs/btrfs/ioctl.c:2423:16:    expected void const *<noident>
   fs/btrfs/ioctl.c:2423:16:    got struct btrfs_ioctl_get_subvol_info_args [noderef] <asn:1>*subvol_info
   fs/btrfs/ioctl.c:2917:24: sparse: incompatible types in comparison expression (different address spaces)
>> fs/btrfs/ioctl.c:2368:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2370:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2371:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2379:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2380:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2381:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2383:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2384:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2385:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2387:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2388:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2389:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2391:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2392:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2393:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2405:29: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2407:17: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2410:17: sparse: dereference of noderef expression

vim +2336 fs/btrfs/ioctl.c

  2311	
  2312	/* Get the subvolume information in BTRFS_ROOT_ITEM and BTRFS_ROOT_BACKREF */
  2313	static noinline int btrfs_ioctl_get_subvol_info(struct file *file,
  2314						   void __user *argp)
  2315	{
  2316		struct btrfs_ioctl_get_subvol_info_args __user *subvol_info;
  2317		struct btrfs_root *root;
  2318		struct btrfs_path *path;
  2319		struct btrfs_key key;
  2320	
  2321		struct btrfs_root_item root_item;
  2322		struct btrfs_root_ref *rref;
  2323		struct extent_buffer *l;
  2324		int slot;
  2325	
  2326		unsigned long item_off;
  2327		unsigned long item_len;
  2328	
  2329		struct inode *inode;
  2330		int ret;
  2331	
  2332		path = btrfs_alloc_path();
  2333		if (!path)
  2334			return -ENOMEM;
  2335	
> 2336		subvol_info = kzalloc(sizeof(*subvol_info), GFP_KERNEL);
  2337		if (!subvol_info) {
  2338			btrfs_free_path(path);
  2339			return -ENOMEM;
  2340		}
  2341		inode = file_inode(file);
  2342	
  2343		root = BTRFS_I(inode)->root->fs_info->tree_root;
  2344		key.objectid = BTRFS_I(inode)->root->root_key.objectid;
  2345		key.type = BTRFS_ROOT_ITEM_KEY;
  2346		key.offset = 0;
  2347		ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
  2348		if (ret < 0) {
  2349			goto out;
  2350		} else if (ret > 0) {
  2351			/* If the subvolume is a snapshot, offset is not zero */
  2352			u64 objectid = key.objectid;
  2353	
  2354			btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
  2355			if (key.objectid != objectid ||
  2356			    key.type != BTRFS_ROOT_ITEM_KEY) {
  2357				ret = -ENOENT;
  2358				goto out;
  2359			}
  2360		}
  2361	
  2362		l = path->nodes[0];
  2363		slot = path->slots[0];
  2364		item_off = btrfs_item_ptr_offset(l, slot);
  2365		item_len = btrfs_item_size_nr(l, slot);
  2366		read_extent_buffer(l, &root_item, item_off, item_len);
  2367	
> 2368		subvol_info->id = key.objectid;
  2369	
  2370		subvol_info->generation = btrfs_root_generation(&root_item);
  2371		subvol_info->flags = btrfs_root_flags(&root_item);
  2372	
> 2373		memcpy(subvol_info->uuid, root_item.uuid, BTRFS_UUID_SIZE);
> 2374		memcpy(subvol_info->parent_uuid, root_item.parent_uuid,
  2375							    BTRFS_UUID_SIZE);
> 2376		memcpy(subvol_info->received_uuid, root_item.received_uuid,
  2377							    BTRFS_UUID_SIZE);
  2378	
  2379		subvol_info->ctransid = btrfs_root_ctransid(&root_item);
  2380		subvol_info->ctime.sec = btrfs_stack_timespec_sec(&root_item.ctime);
  2381		subvol_info->ctime.nsec = btrfs_stack_timespec_nsec(&root_item.ctime);
  2382	
  2383		subvol_info->otransid = btrfs_root_otransid(&root_item);
  2384		subvol_info->otime.sec = btrfs_stack_timespec_sec(&root_item.otime);
  2385		subvol_info->otime.nsec = btrfs_stack_timespec_nsec(&root_item.otime);
  2386	
  2387		subvol_info->stransid = btrfs_root_stransid(&root_item);
  2388		subvol_info->stime.sec = btrfs_stack_timespec_sec(&root_item.stime);
  2389		subvol_info->stime.nsec = btrfs_stack_timespec_nsec(&root_item.stime);
  2390	
  2391		subvol_info->rtransid = btrfs_root_rtransid(&root_item);
  2392		subvol_info->rtime.sec = btrfs_stack_timespec_sec(&root_item.rtime);
  2393		subvol_info->rtime.nsec = btrfs_stack_timespec_nsec(&root_item.rtime);
  2394	
  2395		btrfs_release_path(path);
  2396		key.type = BTRFS_ROOT_BACKREF_KEY;
  2397		key.offset = 0;
  2398		ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
  2399		if (ret < 0)
  2400			goto out;
  2401	
  2402		l = path->nodes[0];
  2403		slot = path->slots[0];
  2404		btrfs_item_key_to_cpu(l, &key, slot);
  2405		if (key.objectid == subvol_info->id &&
  2406				key.type == BTRFS_ROOT_BACKREF_KEY){
  2407			subvol_info->parent_id = key.offset;
  2408	
  2409			rref = btrfs_item_ptr(l, slot, struct btrfs_root_ref);
  2410			subvol_info->dirid = btrfs_root_ref_dirid(l, rref);
  2411	
  2412			item_off = btrfs_item_ptr_offset(l, slot)
  2413					+ sizeof(struct btrfs_root_ref);
  2414			item_len = btrfs_item_size_nr(l, slot)
  2415					- sizeof(struct btrfs_root_ref);
> 2416			read_extent_buffer(l, subvol_info->name, item_off, item_len);
  2417		}
  2418	
> 2419		if (copy_to_user(argp, subvol_info, sizeof(*subvol_info)))
  2420			ret = -EFAULT;
  2421	
  2422	out:
> 2423		kzfree(subvol_info);
  2424		btrfs_free_path(path);
  2425		return ret;
  2426	}
  2427	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

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

* Re: [PATCH v2 2/3] btrfs: Add unprivileged ioctl which returns subvolume's ROOT_REF
  2018-03-15  8:11 ` [PATCH v2 2/3] btrfs: Add unprivileged ioctl which returns subvolume's ROOT_REF Misono, Tomohiro
@ 2018-03-18  4:46   ` kbuild test robot
  0 siblings, 0 replies; 7+ messages in thread
From: kbuild test robot @ 2018-03-18  4:46 UTC (permalink / raw)
  To: Misono, Tomohiro; +Cc: kbuild-all, linux-btrfs

Hi Tomohiro,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v4.16-rc5 next-20180316]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Misono-Tomohiro/btrfs-Add-three-new-unprivileged-ioctls-to-allow-normal-users-to-call-sub-list-show-etc/20180318-101013
reproduce:
        # apt-get install sparse
        make ARCH=x86_64 allmodconfig
        make C=1 CF=-D__CHECK_ENDIAN__


sparse warnings: (new ones prefixed by >>)

   fs/btrfs/ioctl.c:1561:9: sparse: incompatible types in comparison expression (different address spaces)
   fs/btrfs/ioctl.c:2336:21: sparse: incorrect type in assignment (different address spaces) @@    expected struct btrfs_ioctl_get_subvol_info_args [noderef] <asn:1>*subvol_info @@    got sn:1>*subvol_info @@
   fs/btrfs/ioctl.c:2336:21:    expected struct btrfs_ioctl_get_subvol_info_args [noderef] <asn:1>*subvol_info
   fs/btrfs/ioctl.c:2336:21:    got void *
   fs/btrfs/ioctl.c:2373:9: sparse: incorrect type in argument 1 (different address spaces) @@    expected void *to @@    got unsigned char [noderef] <avoid *to @@
   fs/btrfs/ioctl.c:2373:9:    expected void *to
   fs/btrfs/ioctl.c:2373:9:    got unsigned char [noderef] <asn:1>*<noident>
   fs/btrfs/ioctl.c:2374:9: sparse: incorrect type in argument 1 (different address spaces) @@    expected void *to @@    got unsigned char [noderef] <avoid *to @@
   fs/btrfs/ioctl.c:2374:9:    expected void *to
   fs/btrfs/ioctl.c:2374:9:    got unsigned char [noderef] <asn:1>*<noident>
   fs/btrfs/ioctl.c:2376:9: sparse: incorrect type in argument 1 (different address spaces) @@    expected void *to @@    got unsigned char [noderef] <avoid *to @@
   fs/btrfs/ioctl.c:2376:9:    expected void *to
   fs/btrfs/ioctl.c:2376:9:    got unsigned char [noderef] <asn:1>*<noident>
   fs/btrfs/ioctl.c:2416:39: sparse: incorrect type in argument 2 (different address spaces) @@    expected void *dst @@    got char [noderef] <avoid *dst @@
   fs/btrfs/ioctl.c:2416:39:    expected void *dst
   fs/btrfs/ioctl.c:2416:39:    got char [noderef] <asn:1>*<noident>
   fs/btrfs/ioctl.c:2419:32: sparse: incorrect type in argument 2 (different address spaces) @@    expected void const *from @@    got struct btrfs_ioctl_get_subvol_info_args [nodervoid const *from @@
   fs/btrfs/ioctl.c:2419:32:    expected void const *from
   fs/btrfs/ioctl.c:2419:32:    got struct btrfs_ioctl_get_subvol_info_args [noderef] <asn:1>*subvol_info
   fs/btrfs/ioctl.c:2423:16: sparse: incorrect type in argument 1 (different address spaces) @@    expected void const *<noident> @@    got struct btrfs_ioctl_get_subvol_info_args [nodervoid const *<noident> @@
   fs/btrfs/ioctl.c:2423:16:    expected void const *<noident>
   fs/btrfs/ioctl.c:2423:16:    got struct btrfs_ioctl_get_subvol_info_args [noderef] <asn:1>*subvol_info
>> fs/btrfs/ioctl.c:2451:18: sparse: incorrect type in assignment (different address spaces) @@    expected struct btrfs_ioctl_get_subvol_rootref_args [noderef] <asn:1>*rootrefs @@    got sn:1>*rootrefs @@
   fs/btrfs/ioctl.c:2451:18:    expected struct btrfs_ioctl_get_subvol_rootref_args [noderef] <asn:1>*rootrefs
   fs/btrfs/ioctl.c:2451:18:    got void *
>> fs/btrfs/ioctl.c:2507:40: sparse: incorrect type in argument 2 (different address spaces) @@    expected void const *from @@    got struct btrfs_ioctl_get_subvol_rootref_args [nodervoid const *from @@
   fs/btrfs/ioctl.c:2507:40:    expected void const *from
   fs/btrfs/ioctl.c:2507:40:    got struct btrfs_ioctl_get_subvol_rootref_args [noderef] <asn:1>*rootrefs
>> fs/btrfs/ioctl.c:2512:15: sparse: incorrect type in argument 1 (different address spaces) @@    expected void const *<noident> @@    got struct btrfs_ioctl_get_subvol_rootref_args [nodervoid const *<noident> @@
   fs/btrfs/ioctl.c:2512:15:    expected void const *<noident>
   fs/btrfs/ioctl.c:2512:15:    got struct btrfs_ioctl_get_subvol_rootref_args [noderef] <asn:1>*rootrefs
   fs/btrfs/ioctl.c:3006:24: sparse: incompatible types in comparison expression (different address spaces)
   fs/btrfs/ioctl.c:2368:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2370:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2371:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2379:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2380:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2381:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2383:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2384:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2385:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2387:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2388:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2389:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2391:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2392:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2393:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2405:29: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2407:17: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2410:17: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2463:22: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2488:33: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2494:42: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2495:42: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2506:17: sparse: dereference of noderef expression

vim +2451 fs/btrfs/ioctl.c

  2311	
  2312	/* Get the subvolume information in BTRFS_ROOT_ITEM and BTRFS_ROOT_BACKREF */
  2313	static noinline int btrfs_ioctl_get_subvol_info(struct file *file,
  2314						   void __user *argp)
  2315	{
  2316		struct btrfs_ioctl_get_subvol_info_args __user *subvol_info;
  2317		struct btrfs_root *root;
  2318		struct btrfs_path *path;
  2319		struct btrfs_key key;
  2320	
  2321		struct btrfs_root_item root_item;
  2322		struct btrfs_root_ref *rref;
  2323		struct extent_buffer *l;
  2324		int slot;
  2325	
  2326		unsigned long item_off;
  2327		unsigned long item_len;
  2328	
  2329		struct inode *inode;
  2330		int ret;
  2331	
  2332		path = btrfs_alloc_path();
  2333		if (!path)
  2334			return -ENOMEM;
  2335	
  2336		subvol_info = kzalloc(sizeof(*subvol_info), GFP_KERNEL);
  2337		if (!subvol_info) {
  2338			btrfs_free_path(path);
  2339			return -ENOMEM;
  2340		}
  2341		inode = file_inode(file);
  2342	
  2343		root = BTRFS_I(inode)->root->fs_info->tree_root;
  2344		key.objectid = BTRFS_I(inode)->root->root_key.objectid;
  2345		key.type = BTRFS_ROOT_ITEM_KEY;
  2346		key.offset = 0;
  2347		ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
  2348		if (ret < 0) {
  2349			goto out;
  2350		} else if (ret > 0) {
  2351			/* If the subvolume is a snapshot, offset is not zero */
  2352			u64 objectid = key.objectid;
  2353	
  2354			btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
  2355			if (key.objectid != objectid ||
  2356			    key.type != BTRFS_ROOT_ITEM_KEY) {
  2357				ret = -ENOENT;
  2358				goto out;
  2359			}
  2360		}
  2361	
  2362		l = path->nodes[0];
  2363		slot = path->slots[0];
  2364		item_off = btrfs_item_ptr_offset(l, slot);
  2365		item_len = btrfs_item_size_nr(l, slot);
  2366		read_extent_buffer(l, &root_item, item_off, item_len);
  2367	
  2368		subvol_info->id = key.objectid;
  2369	
  2370		subvol_info->generation = btrfs_root_generation(&root_item);
  2371		subvol_info->flags = btrfs_root_flags(&root_item);
  2372	
  2373		memcpy(subvol_info->uuid, root_item.uuid, BTRFS_UUID_SIZE);
  2374		memcpy(subvol_info->parent_uuid, root_item.parent_uuid,
  2375							    BTRFS_UUID_SIZE);
  2376		memcpy(subvol_info->received_uuid, root_item.received_uuid,
  2377							    BTRFS_UUID_SIZE);
  2378	
  2379		subvol_info->ctransid = btrfs_root_ctransid(&root_item);
  2380		subvol_info->ctime.sec = btrfs_stack_timespec_sec(&root_item.ctime);
  2381		subvol_info->ctime.nsec = btrfs_stack_timespec_nsec(&root_item.ctime);
  2382	
  2383		subvol_info->otransid = btrfs_root_otransid(&root_item);
  2384		subvol_info->otime.sec = btrfs_stack_timespec_sec(&root_item.otime);
  2385		subvol_info->otime.nsec = btrfs_stack_timespec_nsec(&root_item.otime);
  2386	
  2387		subvol_info->stransid = btrfs_root_stransid(&root_item);
  2388		subvol_info->stime.sec = btrfs_stack_timespec_sec(&root_item.stime);
  2389		subvol_info->stime.nsec = btrfs_stack_timespec_nsec(&root_item.stime);
  2390	
  2391		subvol_info->rtransid = btrfs_root_rtransid(&root_item);
  2392		subvol_info->rtime.sec = btrfs_stack_timespec_sec(&root_item.rtime);
  2393		subvol_info->rtime.nsec = btrfs_stack_timespec_nsec(&root_item.rtime);
  2394	
  2395		btrfs_release_path(path);
  2396		key.type = BTRFS_ROOT_BACKREF_KEY;
  2397		key.offset = 0;
  2398		ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
  2399		if (ret < 0)
  2400			goto out;
  2401	
  2402		l = path->nodes[0];
  2403		slot = path->slots[0];
  2404		btrfs_item_key_to_cpu(l, &key, slot);
  2405		if (key.objectid == subvol_info->id &&
  2406				key.type == BTRFS_ROOT_BACKREF_KEY){
  2407			subvol_info->parent_id = key.offset;
  2408	
  2409			rref = btrfs_item_ptr(l, slot, struct btrfs_root_ref);
  2410			subvol_info->dirid = btrfs_root_ref_dirid(l, rref);
  2411	
  2412			item_off = btrfs_item_ptr_offset(l, slot)
  2413					+ sizeof(struct btrfs_root_ref);
  2414			item_len = btrfs_item_size_nr(l, slot)
  2415					- sizeof(struct btrfs_root_ref);
> 2416			read_extent_buffer(l, subvol_info->name, item_off, item_len);
  2417		}
  2418	
> 2419		if (copy_to_user(argp, subvol_info, sizeof(*subvol_info)))
  2420			ret = -EFAULT;
  2421	
  2422	out:
> 2423		kzfree(subvol_info);
  2424		btrfs_free_path(path);
  2425		return ret;
  2426	}
  2427	
  2428	/* Returns ROOT_REF information of the subvolume contining this inode. */
  2429	static noinline int btrfs_ioctl_get_subvol_rootref(struct file *file,
  2430						   void __user *argp)
  2431	{
  2432		struct btrfs_ioctl_get_subvol_rootref_args __user *rootrefs;
  2433		struct btrfs_root_ref *rref;
  2434		struct btrfs_root *root;
  2435		struct btrfs_path *path;
  2436		struct btrfs_key key;
  2437	
  2438		struct extent_buffer *l;
  2439		int slot;
  2440	
  2441		struct inode *inode;
  2442		int i, nritems;
  2443		int ret;
  2444		u64 objectid;
  2445		u8 found;
  2446	
  2447		path = btrfs_alloc_path();
  2448		if (!path)
  2449			return -ENOMEM;
  2450	
> 2451		rootrefs = memdup_user(argp, sizeof(*rootrefs));
  2452		if (!rootrefs) {
  2453			btrfs_free_path(path);
  2454			return -ENOMEM;
  2455		}
  2456	
  2457		inode = file_inode(file);
  2458		root = BTRFS_I(inode)->root->fs_info->tree_root;
  2459		objectid = BTRFS_I(inode)->root->root_key.objectid;
  2460	
  2461		key.objectid = objectid;
  2462		key.type = BTRFS_ROOT_REF_KEY;
  2463		key.offset = rootrefs->min_id;
  2464		found = 0;
  2465		while (1) {
  2466			ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
  2467			if (ret < 0)
  2468				goto out;
  2469	
  2470			l = path->nodes[0];
  2471			slot = path->slots[0];
  2472			nritems = btrfs_header_nritems(l);
  2473			if (nritems - slot == 0) {
  2474				ret = 0;
  2475				goto out;
  2476			}
  2477	
  2478			for (i = slot; i < nritems; i++) {
  2479				btrfs_item_key_to_cpu(l, &key, i);
  2480				if (key.objectid != objectid ||
  2481						key.type != BTRFS_ROOT_REF_KEY) {
  2482					ret = 0;
  2483					goto out;
  2484				}
  2485	
  2486				if (found == BTRFS_MAX_ROOTREF_BUFFER_NUM) {
  2487					/* update min_id for next search */
  2488					rootrefs->min_id = key.offset;
  2489					ret = -EOVERFLOW;
  2490					goto out;
  2491				}
  2492	
  2493				rref = btrfs_item_ptr(l, i, struct btrfs_root_ref);
  2494				rootrefs->rootref[found].subvolid = key.offset;
  2495				rootrefs->rootref[found].dirid =
  2496						  btrfs_root_ref_dirid(l, rref);
  2497				found++;
  2498			}
  2499	
  2500			btrfs_release_path(path);
  2501			key.offset++;
  2502		}
  2503	
  2504	out:
  2505		if (!ret || ret == -EOVERFLOW) {
  2506			rootrefs->num_items = found;
> 2507			if (copy_to_user(argp, rootrefs, sizeof(*rootrefs)))
  2508				ret = -EFAULT;
  2509		}
  2510	
  2511		btrfs_free_path(path);
> 2512		kfree(rootrefs);
  2513	
  2514		return ret;
  2515	}
  2516	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

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

* Re: [PATCH v2 3/3] btrfs: Add unprivileged version of ino_lookup ioctl
  2018-03-15  8:11 ` [PATCH v2 3/3] btrfs: Add unprivileged version of ino_lookup ioctl Misono, Tomohiro
@ 2018-03-18  5:11   ` kbuild test robot
  0 siblings, 0 replies; 7+ messages in thread
From: kbuild test robot @ 2018-03-18  5:11 UTC (permalink / raw)
  To: Misono, Tomohiro; +Cc: kbuild-all, linux-btrfs

Hi Tomohiro,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v4.16-rc5 next-20180316]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Misono-Tomohiro/btrfs-Add-three-new-unprivileged-ioctls-to-allow-normal-users-to-call-sub-list-show-etc/20180318-101013
reproduce:
        # apt-get install sparse
        make ARCH=x86_64 allmodconfig
        make C=1 CF=-D__CHECK_ENDIAN__


sparse warnings: (new ones prefixed by >>)

   fs/btrfs/ioctl.c:1561:9: sparse: incompatible types in comparison expression (different address spaces)
>> fs/btrfs/ioctl.c:2414:17: sparse: restricted __le64 degrades to integer
   fs/btrfs/ioctl.c:2538:21: sparse: incorrect type in assignment (different address spaces) @@    expected struct btrfs_ioctl_get_subvol_info_args [noderef] <asn:1>*subvol_info @@    got sn:1>*subvol_info @@
   fs/btrfs/ioctl.c:2538:21:    expected struct btrfs_ioctl_get_subvol_info_args [noderef] <asn:1>*subvol_info
   fs/btrfs/ioctl.c:2538:21:    got void *
   fs/btrfs/ioctl.c:2575:9: sparse: incorrect type in argument 1 (different address spaces) @@    expected void *to @@    got unsigned char [noderef] <avoid *to @@
   fs/btrfs/ioctl.c:2575:9:    expected void *to
   fs/btrfs/ioctl.c:2575:9:    got unsigned char [noderef] <asn:1>*<noident>
   fs/btrfs/ioctl.c:2576:9: sparse: incorrect type in argument 1 (different address spaces) @@    expected void *to @@    got unsigned char [noderef] <avoid *to @@
   fs/btrfs/ioctl.c:2576:9:    expected void *to
   fs/btrfs/ioctl.c:2576:9:    got unsigned char [noderef] <asn:1>*<noident>
   fs/btrfs/ioctl.c:2578:9: sparse: incorrect type in argument 1 (different address spaces) @@    expected void *to @@    got unsigned char [noderef] <avoid *to @@
   fs/btrfs/ioctl.c:2578:9:    expected void *to
   fs/btrfs/ioctl.c:2578:9:    got unsigned char [noderef] <asn:1>*<noident>
   fs/btrfs/ioctl.c:2618:39: sparse: incorrect type in argument 2 (different address spaces) @@    expected void *dst @@    got char [noderef] <avoid *dst @@
   fs/btrfs/ioctl.c:2618:39:    expected void *dst
   fs/btrfs/ioctl.c:2618:39:    got char [noderef] <asn:1>*<noident>
   fs/btrfs/ioctl.c:2621:32: sparse: incorrect type in argument 2 (different address spaces) @@    expected void const *from @@    got struct btrfs_ioctl_get_subvol_info_args [nodervoid const *from @@
   fs/btrfs/ioctl.c:2621:32:    expected void const *from
   fs/btrfs/ioctl.c:2621:32:    got struct btrfs_ioctl_get_subvol_info_args [noderef] <asn:1>*subvol_info
   fs/btrfs/ioctl.c:2625:16: sparse: incorrect type in argument 1 (different address spaces) @@    expected void const *<noident> @@    got struct btrfs_ioctl_get_subvol_info_args [nodervoid const *<noident> @@
   fs/btrfs/ioctl.c:2625:16:    expected void const *<noident>
   fs/btrfs/ioctl.c:2625:16:    got struct btrfs_ioctl_get_subvol_info_args [noderef] <asn:1>*subvol_info
   fs/btrfs/ioctl.c:2653:18: sparse: incorrect type in assignment (different address spaces) @@    expected struct btrfs_ioctl_get_subvol_rootref_args [noderef] <asn:1>*rootrefs @@    got sn:1>*rootrefs @@
   fs/btrfs/ioctl.c:2653:18:    expected struct btrfs_ioctl_get_subvol_rootref_args [noderef] <asn:1>*rootrefs
   fs/btrfs/ioctl.c:2653:18:    got void *
   fs/btrfs/ioctl.c:2709:40: sparse: incorrect type in argument 2 (different address spaces) @@    expected void const *from @@    got struct btrfs_ioctl_get_subvol_rootref_args [nodervoid const *from @@
   fs/btrfs/ioctl.c:2709:40:    expected void const *from
   fs/btrfs/ioctl.c:2709:40:    got struct btrfs_ioctl_get_subvol_rootref_args [noderef] <asn:1>*rootrefs
   fs/btrfs/ioctl.c:2714:15: sparse: incorrect type in argument 1 (different address spaces) @@    expected void const *<noident> @@    got struct btrfs_ioctl_get_subvol_rootref_args [nodervoid const *<noident> @@
   fs/btrfs/ioctl.c:2714:15:    expected void const *<noident>
   fs/btrfs/ioctl.c:2714:15:    got struct btrfs_ioctl_get_subvol_rootref_args [noderef] <asn:1>*rootrefs
   fs/btrfs/ioctl.c:3208:24: sparse: incompatible types in comparison expression (different address spaces)
   fs/btrfs/ioctl.c:2570:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2572:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2573:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2581:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2582:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2583:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2585:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2586:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2587:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2589:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2590:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2591:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2593:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2594:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2595:9: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2607:29: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2609:17: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2612:17: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2665:22: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2690:33: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2696:42: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2697:42: sparse: dereference of noderef expression
   fs/btrfs/ioctl.c:2708:17: sparse: dereference of noderef expression

vim +2414 fs/btrfs/ioctl.c

  2269	
  2270	static noinline int btrfs_search_path_in_tree_user(struct inode *inode,
  2271					struct btrfs_ioctl_ino_lookup_user_args *args)
  2272	{
  2273		struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info;
  2274		struct super_block *sb = inode->i_sb;
  2275		struct btrfs_key upper_limit = BTRFS_I(inode)->location;
  2276		u64 treeid = BTRFS_I(inode)->root->root_key.objectid;
  2277		u64 dirid = args->dirid;
  2278	
  2279		unsigned long item_off;
  2280		unsigned long item_len;
  2281		struct btrfs_inode_ref *iref;
  2282		struct btrfs_root_ref rref;
  2283		struct btrfs_root *root;
  2284		struct btrfs_path *path;
  2285		struct btrfs_key key, key2;
  2286		struct extent_buffer *l;
  2287		struct inode *temp_inode;
  2288		char *ptr;
  2289		int slot;
  2290		int len;
  2291		int total_len = 0;
  2292		int ret = -1;
  2293	
  2294		path = btrfs_alloc_path();
  2295		if (!path)
  2296			return -ENOMEM;
  2297	
  2298		/*
  2299		 * If the bottom subvolume does not exist directly under upper_limit,
  2300		 * construct the path in bottomup way.
  2301		 */
  2302		if (dirid != upper_limit.objectid) {
  2303			ptr = &args->path[BTRFS_INO_LOOKUP_USER_PATH_MAX - 1];
  2304	
  2305			key.objectid = treeid;
  2306			key.type = BTRFS_ROOT_ITEM_KEY;
  2307			key.offset = (u64)-1;
  2308			root = btrfs_read_fs_root_no_name(fs_info, &key);
  2309			if (IS_ERR(root)) {
  2310				ret = -ENOENT;
  2311				goto out;
  2312			}
  2313	
  2314			key.objectid = dirid;
  2315			key.type = BTRFS_INODE_REF_KEY;
  2316			key.offset = (u64)-1;
  2317			while (1) {
  2318				ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
  2319				if (ret < 0) {
  2320					goto out;
  2321				} else if (ret > 0) {
  2322					ret = btrfs_previous_item(root, path, dirid,
  2323								  BTRFS_INODE_REF_KEY);
  2324					if (ret < 0) {
  2325						goto out;
  2326					} else if (ret > 0) {
  2327						ret = -ENOENT;
  2328						goto out;
  2329					}
  2330				}
  2331	
  2332				l = path->nodes[0];
  2333				slot = path->slots[0];
  2334				btrfs_item_key_to_cpu(l, &key, slot);
  2335	
  2336				iref = btrfs_item_ptr(l, slot, struct btrfs_inode_ref);
  2337				len = btrfs_inode_ref_name_len(l, iref);
  2338				ptr -= len + 1;
  2339				total_len += len + 1;
  2340				if (ptr < args->path) {
  2341					ret = -ENAMETOOLONG;
  2342					goto out;
  2343				}
  2344	
  2345				*(ptr + len) = '/';
  2346				read_extent_buffer(l, ptr,
  2347				    (unsigned long)(iref + 1), len);
  2348	
  2349				/* Check the read+exec permission of this directory */
  2350				ret = btrfs_previous_item(root, path, dirid,
  2351							  BTRFS_INODE_ITEM_KEY);
  2352				if (ret < 0) {
  2353					goto out;
  2354				} else if (ret > 0) {
  2355					ret = -ENOENT;
  2356					goto out;
  2357				}
  2358	
  2359				l = path->nodes[0];
  2360				slot = path->slots[0];
  2361				btrfs_item_key_to_cpu(l, &key2, slot);
  2362				if (key2.objectid != dirid) {
  2363					ret = -ENOENT;
  2364					goto out;
  2365				}
  2366	
  2367				temp_inode = btrfs_iget(sb, &key2, root, NULL);
  2368				ret = inode_permission(temp_inode, MAY_READ | MAY_EXEC);
  2369				iput(temp_inode);
  2370				if (ret) {
  2371					ret = -EACCES;
  2372					goto out;
  2373				}
  2374	
  2375				if (key.offset == upper_limit.objectid)
  2376					break;
  2377				if (key.objectid == BTRFS_FIRST_FREE_OBJECTID) {
  2378					ret = -EACCES;
  2379					goto out;
  2380				}
  2381	
  2382				btrfs_release_path(path);
  2383				key.objectid = key.offset;
  2384				key.offset = (u64)-1;
  2385				dirid = key.objectid;
  2386			}
  2387	
  2388			memmove(args->path, ptr, total_len);
  2389			args->path[total_len] = '\0';
  2390			btrfs_release_path(path);
  2391		}
  2392	
  2393		/* get the bottom subolume's name from ROOT_REF */
  2394		root = fs_info->tree_root;
  2395		key.objectid = treeid;
  2396		key.type = BTRFS_ROOT_REF_KEY;
  2397		key.offset = args->subvolid;
  2398		ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
  2399		if (ret < 0) {
  2400			goto out;
  2401		} else if (ret > 0) {
  2402			ret = -ENOENT;
  2403			goto out;
  2404		}
  2405	
  2406		l = path->nodes[0];
  2407		slot = path->slots[0];
  2408		btrfs_item_key_to_cpu(l, &key, slot);
  2409	
  2410		item_off = btrfs_item_ptr_offset(l, slot);
  2411		item_len = btrfs_item_size_nr(l, slot);
  2412		/* check if dirid in ROOT_REF corresponds to passed dirid */
  2413		read_extent_buffer(l, &rref, item_off, sizeof(struct btrfs_root_ref));
> 2414		if (rref.dirid != args->dirid) {
  2415			ret = -EINVAL;
  2416			goto out;
  2417		}
  2418	
  2419		/* copy subvolume's name */
  2420		item_off += sizeof(struct btrfs_root_ref);
  2421		item_len -= sizeof(struct btrfs_root_ref);
  2422		read_extent_buffer(l, args->name, item_off, item_len);
  2423		args->name[item_len] = '\0';
  2424	
  2425	out:
  2426		btrfs_free_path(path);
  2427		return ret;
  2428	}
  2429	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

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

end of thread, other threads:[~2018-03-18  5:11 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-03-15  8:09 [PATCH v2 0/3] btrfs: Add three new unprivileged ioctls to allow normal users to call "sub list/show" etc Misono, Tomohiro
2018-03-15  8:10 ` [PATCH v2 1/3] btrfs: Add unprivileged ioctl which returns subvolume information Misono, Tomohiro
2018-03-18  4:16   ` kbuild test robot
2018-03-15  8:11 ` [PATCH v2 2/3] btrfs: Add unprivileged ioctl which returns subvolume's ROOT_REF Misono, Tomohiro
2018-03-18  4:46   ` kbuild test robot
2018-03-15  8:11 ` [PATCH v2 3/3] btrfs: Add unprivileged version of ino_lookup ioctl Misono, Tomohiro
2018-03-18  5:11   ` kbuild test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).