aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/btrfs/ioctl.c204
-rw-r--r--include/uapi/linux/btrfs.h17
2 files changed, 221 insertions, 0 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index be9b3f39183c..d29992f7dc63 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -2341,6 +2341,165 @@ out:
2341 return ret; 2341 return ret;
2342} 2342}
2343 2343
2344static 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
2498out:
2499 btrfs_free_path(path);
2500 return ret;
2501}
2502
2344static noinline int btrfs_ioctl_ino_lookup(struct file *file, 2503static noinline int btrfs_ioctl_ino_lookup(struct file *file,
2345 void __user *argp) 2504 void __user *argp)
2346{ 2505{
@@ -2383,6 +2542,49 @@ out:
2383 return ret; 2542 return ret;
2384} 2543}
2385 2544
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 */
2557static 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
2386/* Get the subvolume information in BTRFS_ROOT_ITEM and BTRFS_ROOT_BACKREF */ 2588/* Get the subvolume information in BTRFS_ROOT_ITEM and BTRFS_ROOT_BACKREF */
2387static int btrfs_ioctl_get_subvol_info(struct file *file, void __user *argp) 2589static int btrfs_ioctl_get_subvol_info(struct file *file, void __user *argp)
2388{ 2590{
@@ -5765,6 +5967,8 @@ long btrfs_ioctl(struct file *file, unsigned int
5765 return btrfs_ioctl_get_subvol_info(file, argp); 5967 return btrfs_ioctl_get_subvol_info(file, argp);
5766 case BTRFS_IOC_GET_SUBVOL_ROOTREF: 5968 case BTRFS_IOC_GET_SUBVOL_ROOTREF:
5767 return btrfs_ioctl_get_subvol_rootref(file, argp); 5969 return btrfs_ioctl_get_subvol_rootref(file, argp);
5970 case BTRFS_IOC_INO_LOOKUP_USER:
5971 return btrfs_ioctl_ino_lookup_user(file, argp);
5768 } 5972 }
5769 5973
5770 return -ENOTTY; 5974 return -ENOTTY;
diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
index f90d10478235..5ca1d21fc4a7 100644
--- a/include/uapi/linux/btrfs.h
+++ b/include/uapi/linux/btrfs.h
@@ -422,6 +422,21 @@ struct btrfs_ioctl_ino_lookup_args {
422 char name[BTRFS_INO_LOOKUP_PATH_MAX]; 422 char name[BTRFS_INO_LOOKUP_PATH_MAX];
423}; 423};
424 424
425#define BTRFS_INO_LOOKUP_USER_PATH_MAX (4080 - BTRFS_VOL_NAME_MAX - 1)
426struct 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
425/* Search criteria for the btrfs SEARCH ioctl family. */ 440/* Search criteria for the btrfs SEARCH ioctl family. */
426struct btrfs_ioctl_search_key { 441struct btrfs_ioctl_search_key {
427 /* 442 /*
@@ -923,5 +938,7 @@ enum btrfs_err_code {
923 struct btrfs_ioctl_get_subvol_info_args) 938 struct btrfs_ioctl_get_subvol_info_args)
924#define BTRFS_IOC_GET_SUBVOL_ROOTREF _IOWR(BTRFS_IOCTL_MAGIC, 61, \ 939#define BTRFS_IOC_GET_SUBVOL_ROOTREF _IOWR(BTRFS_IOCTL_MAGIC, 61, \
925 struct btrfs_ioctl_get_subvol_rootref_args) 940 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)
926 943
927#endif /* _UAPI_LINUX_BTRFS_H */ 944#endif /* _UAPI_LINUX_BTRFS_H */