diff mbox

[v2,3/3] btrfs: Add unprivileged version of ino_lookup ioctl

Message ID 64abe7b1-002d-33fd-f842-30bf72373327@jp.fujitsu.com (mailing list archive)
State New, archived
Headers show

Commit Message

Misono Tomohiro March 15, 2018, 8:11 a.m. UTC
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(+)

Comments

kernel test robot March 18, 2018, 5:11 a.m. UTC | #1
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
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

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 */