Skip to content

Commit 23d0b79

Browse files
t-msnkdave
authored andcommitted
btrfs: Add unprivileged version of ino_lookup ioctl
Add unprivileged version of ino_lookup ioctl BTRFS_IOC_INO_LOOKUP_USER to allow normal users to call "btrfs subvolume 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. Reviewed-by: Gu Jinxiang <[email protected]> Reviewed-by: Qu Wenruo <[email protected]> Tested-by: Gu Jinxiang <[email protected]> Signed-off-by: Tomohiro Misono <[email protected]> [ style fixes ] Signed-off-by: David Sterba <[email protected]>
1 parent 42e4b52 commit 23d0b79

File tree

2 files changed

+221
-0
lines changed

2 files changed

+221
-0
lines changed

fs/btrfs/ioctl.c

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2341,6 +2341,165 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info,
23412341
return ret;
23422342
}
23432343

2344+
static int btrfs_search_path_in_tree_user(struct inode *inode,
2345+
struct btrfs_ioctl_ino_lookup_user_args *args)
2346+
{
2347+
struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info;
2348+
struct super_block *sb = inode->i_sb;
2349+
struct btrfs_key upper_limit = BTRFS_I(inode)->location;
2350+
u64 treeid = BTRFS_I(inode)->root->root_key.objectid;
2351+
u64 dirid = args->dirid;
2352+
unsigned long item_off;
2353+
unsigned long item_len;
2354+
struct btrfs_inode_ref *iref;
2355+
struct btrfs_root_ref *rref;
2356+
struct btrfs_root *root;
2357+
struct btrfs_path *path;
2358+
struct btrfs_key key, key2;
2359+
struct extent_buffer *leaf;
2360+
struct inode *temp_inode;
2361+
char *ptr;
2362+
int slot;
2363+
int len;
2364+
int total_len = 0;
2365+
int ret;
2366+
2367+
path = btrfs_alloc_path();
2368+
if (!path)
2369+
return -ENOMEM;
2370+
2371+
/*
2372+
* If the bottom subvolume does not exist directly under upper_limit,
2373+
* construct the path in from the bottom up.
2374+
*/
2375+
if (dirid != upper_limit.objectid) {
2376+
ptr = &args->path[BTRFS_INO_LOOKUP_USER_PATH_MAX - 1];
2377+
2378+
key.objectid = treeid;
2379+
key.type = BTRFS_ROOT_ITEM_KEY;
2380+
key.offset = (u64)-1;
2381+
root = btrfs_read_fs_root_no_name(fs_info, &key);
2382+
if (IS_ERR(root)) {
2383+
ret = PTR_ERR(root);
2384+
goto out;
2385+
}
2386+
2387+
key.objectid = dirid;
2388+
key.type = BTRFS_INODE_REF_KEY;
2389+
key.offset = (u64)-1;
2390+
while (1) {
2391+
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
2392+
if (ret < 0) {
2393+
goto out;
2394+
} else if (ret > 0) {
2395+
ret = btrfs_previous_item(root, path, dirid,
2396+
BTRFS_INODE_REF_KEY);
2397+
if (ret < 0) {
2398+
goto out;
2399+
} else if (ret > 0) {
2400+
ret = -ENOENT;
2401+
goto out;
2402+
}
2403+
}
2404+
2405+
leaf = path->nodes[0];
2406+
slot = path->slots[0];
2407+
btrfs_item_key_to_cpu(leaf, &key, slot);
2408+
2409+
iref = btrfs_item_ptr(leaf, slot, struct btrfs_inode_ref);
2410+
len = btrfs_inode_ref_name_len(leaf, iref);
2411+
ptr -= len + 1;
2412+
total_len += len + 1;
2413+
if (ptr < args->path) {
2414+
ret = -ENAMETOOLONG;
2415+
goto out;
2416+
}
2417+
2418+
*(ptr + len) = '/';
2419+
read_extent_buffer(leaf, ptr,
2420+
(unsigned long)(iref + 1), len);
2421+
2422+
/* Check the read+exec permission of this directory */
2423+
ret = btrfs_previous_item(root, path, dirid,
2424+
BTRFS_INODE_ITEM_KEY);
2425+
if (ret < 0) {
2426+
goto out;
2427+
} else if (ret > 0) {
2428+
ret = -ENOENT;
2429+
goto out;
2430+
}
2431+
2432+
leaf = path->nodes[0];
2433+
slot = path->slots[0];
2434+
btrfs_item_key_to_cpu(leaf, &key2, slot);
2435+
if (key2.objectid != dirid) {
2436+
ret = -ENOENT;
2437+
goto out;
2438+
}
2439+
2440+
temp_inode = btrfs_iget(sb, &key2, root, NULL);
2441+
ret = inode_permission(temp_inode, MAY_READ | MAY_EXEC);
2442+
iput(temp_inode);
2443+
if (ret) {
2444+
ret = -EACCES;
2445+
goto out;
2446+
}
2447+
2448+
if (key.offset == upper_limit.objectid)
2449+
break;
2450+
if (key.objectid == BTRFS_FIRST_FREE_OBJECTID) {
2451+
ret = -EACCES;
2452+
goto out;
2453+
}
2454+
2455+
btrfs_release_path(path);
2456+
key.objectid = key.offset;
2457+
key.offset = (u64)-1;
2458+
dirid = key.objectid;
2459+
}
2460+
2461+
memmove(args->path, ptr, total_len);
2462+
args->path[total_len] = '\0';
2463+
btrfs_release_path(path);
2464+
}
2465+
2466+
/* Get the bottom subvolume's name from ROOT_REF */
2467+
root = fs_info->tree_root;
2468+
key.objectid = treeid;
2469+
key.type = BTRFS_ROOT_REF_KEY;
2470+
key.offset = args->treeid;
2471+
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
2472+
if (ret < 0) {
2473+
goto out;
2474+
} else if (ret > 0) {
2475+
ret = -ENOENT;
2476+
goto out;
2477+
}
2478+
2479+
leaf = path->nodes[0];
2480+
slot = path->slots[0];
2481+
btrfs_item_key_to_cpu(leaf, &key, slot);
2482+
2483+
item_off = btrfs_item_ptr_offset(leaf, slot);
2484+
item_len = btrfs_item_size_nr(leaf, slot);
2485+
/* Check if dirid in ROOT_REF corresponds to passed dirid */
2486+
rref = btrfs_item_ptr(leaf, slot, struct btrfs_root_ref);
2487+
if (args->dirid != btrfs_root_ref_dirid(leaf, rref)) {
2488+
ret = -EINVAL;
2489+
goto out;
2490+
}
2491+
2492+
/* Copy subvolume's name */
2493+
item_off += sizeof(struct btrfs_root_ref);
2494+
item_len -= sizeof(struct btrfs_root_ref);
2495+
read_extent_buffer(leaf, args->name, item_off, item_len);
2496+
args->name[item_len] = 0;
2497+
2498+
out:
2499+
btrfs_free_path(path);
2500+
return ret;
2501+
}
2502+
23442503
static noinline int btrfs_ioctl_ino_lookup(struct file *file,
23452504
void __user *argp)
23462505
{
@@ -2383,6 +2542,49 @@ static noinline int btrfs_ioctl_ino_lookup(struct file *file,
23832542
return ret;
23842543
}
23852544

2545+
/*
2546+
* Version of ino_lookup ioctl (unprivileged)
2547+
*
2548+
* The main differences from ino_lookup ioctl are:
2549+
*
2550+
* 1. Read + Exec permission will be checked using inode_permission() during
2551+
* path construction. -EACCES will be returned in case of failure.
2552+
* 2. Path construction will be stopped at the inode number which corresponds
2553+
* to the fd with which this ioctl is called. If constructed path does not
2554+
* exist under fd's inode, -EACCES will be returned.
2555+
* 3. The name of bottom subvolume is also searched and filled.
2556+
*/
2557+
static int btrfs_ioctl_ino_lookup_user(struct file *file, void __user *argp)
2558+
{
2559+
struct btrfs_ioctl_ino_lookup_user_args *args;
2560+
struct inode *inode;
2561+
int ret;
2562+
2563+
args = memdup_user(argp, sizeof(*args));
2564+
if (IS_ERR(args))
2565+
return PTR_ERR(args);
2566+
2567+
inode = file_inode(file);
2568+
2569+
if (args->dirid == BTRFS_FIRST_FREE_OBJECTID &&
2570+
BTRFS_I(inode)->location.objectid != BTRFS_FIRST_FREE_OBJECTID) {
2571+
/*
2572+
* The subvolume does not exist under fd with which this is
2573+
* called
2574+
*/
2575+
kfree(args);
2576+
return -EACCES;
2577+
}
2578+
2579+
ret = btrfs_search_path_in_tree_user(inode, args);
2580+
2581+
if (ret == 0 && copy_to_user(argp, args, sizeof(*args)))
2582+
ret = -EFAULT;
2583+
2584+
kfree(args);
2585+
return ret;
2586+
}
2587+
23862588
/* Get the subvolume information in BTRFS_ROOT_ITEM and BTRFS_ROOT_BACKREF */
23872589
static int btrfs_ioctl_get_subvol_info(struct file *file, void __user *argp)
23882590
{
@@ -5765,6 +5967,8 @@ long btrfs_ioctl(struct file *file, unsigned int
57655967
return btrfs_ioctl_get_subvol_info(file, argp);
57665968
case BTRFS_IOC_GET_SUBVOL_ROOTREF:
57675969
return btrfs_ioctl_get_subvol_rootref(file, argp);
5970+
case BTRFS_IOC_INO_LOOKUP_USER:
5971+
return btrfs_ioctl_ino_lookup_user(file, argp);
57685972
}
57695973

57705974
return -ENOTTY;

include/uapi/linux/btrfs.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,21 @@ struct btrfs_ioctl_ino_lookup_args {
422422
char name[BTRFS_INO_LOOKUP_PATH_MAX];
423423
};
424424

425+
#define BTRFS_INO_LOOKUP_USER_PATH_MAX (4080 - BTRFS_VOL_NAME_MAX - 1)
426+
struct btrfs_ioctl_ino_lookup_user_args {
427+
/* in, inode number containing the subvolume of 'subvolid' */
428+
__u64 dirid;
429+
/* in */
430+
__u64 treeid;
431+
/* out, name of the subvolume of 'treeid' */
432+
char name[BTRFS_VOL_NAME_MAX + 1];
433+
/*
434+
* out, constructed path from the directory with which the ioctl is
435+
* called to dirid
436+
*/
437+
char path[BTRFS_INO_LOOKUP_USER_PATH_MAX];
438+
};
439+
425440
/* Search criteria for the btrfs SEARCH ioctl family. */
426441
struct btrfs_ioctl_search_key {
427442
/*
@@ -923,5 +938,7 @@ enum btrfs_err_code {
923938
struct btrfs_ioctl_get_subvol_info_args)
924939
#define BTRFS_IOC_GET_SUBVOL_ROOTREF _IOWR(BTRFS_IOCTL_MAGIC, 61, \
925940
struct btrfs_ioctl_get_subvol_rootref_args)
941+
#define BTRFS_IOC_INO_LOOKUP_USER _IOWR(BTRFS_IOCTL_MAGIC, 62, \
942+
struct btrfs_ioctl_ino_lookup_user_args)
926943

927944
#endif /* _UAPI_LINUX_BTRFS_H */

0 commit comments

Comments
 (0)