Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

btrfs: Add unprivileged ioctl which returns subvolume's ROOT_REF

Add unprivileged ioctl BTRFS_IOC_GET_SUBVOL_ROOTREF which returns
ROOT_REF information of the subvolume containing this inode except the
subvolume name (this is because to prevent potential name leak). The
subvolume name will be gained by user version of ino_lookup ioctl
(BTRFS_IOC_INO_LOOKUP_USER) which also performs permission check.

The min id of root ref's subvolume to be searched is specified by
@min_id in struct btrfs_ioctl_get_subvol_rootref_args. After the search
ends, @min_id is set to the last searched root ref's subvolid + 1. Also,
if there are more root refs than BTRFS_MAX_ROOTREF_BUFFER_NUM,
-EOVERFLOW is returned. Therefore the caller can just call this ioctl
again without changing the argument to continue search.

Reviewed-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: Gu Jinxiang <gujx@cn.fujitsu.com>
Tested-by: Gu Jinxiang <gujx@cn.fujitsu.com>
Signed-off-by: Tomohiro Misono <misono.tomohiro@jp.fujitsu.com>
[ style fixes and struct item renames ]
Signed-off-by: David Sterba <dsterba@suse.com>

authored by

Tomohiro Misono and committed by
David Sterba
42e4b520 b64ec075

+117
+99
fs/btrfs/ioctl.c
··· 2502 2502 return ret; 2503 2503 } 2504 2504 2505 + /* 2506 + * Return ROOT_REF information of the subvolume containing this inode 2507 + * except the subvolume name. 2508 + */ 2509 + static int btrfs_ioctl_get_subvol_rootref(struct file *file, void __user *argp) 2510 + { 2511 + struct btrfs_ioctl_get_subvol_rootref_args *rootrefs; 2512 + struct btrfs_root_ref *rref; 2513 + struct btrfs_root *root; 2514 + struct btrfs_path *path; 2515 + struct btrfs_key key; 2516 + struct extent_buffer *leaf; 2517 + struct inode *inode; 2518 + u64 objectid; 2519 + int slot; 2520 + int ret; 2521 + u8 found; 2522 + 2523 + path = btrfs_alloc_path(); 2524 + if (!path) 2525 + return -ENOMEM; 2526 + 2527 + rootrefs = memdup_user(argp, sizeof(*rootrefs)); 2528 + if (IS_ERR(rootrefs)) { 2529 + btrfs_free_path(path); 2530 + return PTR_ERR(rootrefs); 2531 + } 2532 + 2533 + inode = file_inode(file); 2534 + root = BTRFS_I(inode)->root->fs_info->tree_root; 2535 + objectid = BTRFS_I(inode)->root->root_key.objectid; 2536 + 2537 + key.objectid = objectid; 2538 + key.type = BTRFS_ROOT_REF_KEY; 2539 + key.offset = rootrefs->min_treeid; 2540 + found = 0; 2541 + 2542 + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); 2543 + if (ret < 0) { 2544 + goto out; 2545 + } else if (path->slots[0] >= 2546 + btrfs_header_nritems(path->nodes[0])) { 2547 + ret = btrfs_next_leaf(root, path); 2548 + if (ret < 0) { 2549 + goto out; 2550 + } else if (ret > 0) { 2551 + ret = -EUCLEAN; 2552 + goto out; 2553 + } 2554 + } 2555 + while (1) { 2556 + leaf = path->nodes[0]; 2557 + slot = path->slots[0]; 2558 + 2559 + btrfs_item_key_to_cpu(leaf, &key, slot); 2560 + if (key.objectid != objectid || key.type != BTRFS_ROOT_REF_KEY) { 2561 + ret = 0; 2562 + goto out; 2563 + } 2564 + 2565 + if (found == BTRFS_MAX_ROOTREF_BUFFER_NUM) { 2566 + ret = -EOVERFLOW; 2567 + goto out; 2568 + } 2569 + 2570 + rref = btrfs_item_ptr(leaf, slot, struct btrfs_root_ref); 2571 + rootrefs->rootref[found].treeid = key.offset; 2572 + rootrefs->rootref[found].dirid = 2573 + btrfs_root_ref_dirid(leaf, rref); 2574 + found++; 2575 + 2576 + ret = btrfs_next_item(root, path); 2577 + if (ret < 0) { 2578 + goto out; 2579 + } else if (ret > 0) { 2580 + ret = -EUCLEAN; 2581 + goto out; 2582 + } 2583 + } 2584 + 2585 + out: 2586 + if (!ret || ret == -EOVERFLOW) { 2587 + rootrefs->num_items = found; 2588 + /* update min_treeid for next search */ 2589 + if (found) 2590 + rootrefs->min_treeid = 2591 + rootrefs->rootref[found - 1].treeid + 1; 2592 + if (copy_to_user(argp, rootrefs, sizeof(*rootrefs))) 2593 + ret = -EFAULT; 2594 + } 2595 + 2596 + kfree(rootrefs); 2597 + btrfs_free_path(path); 2598 + 2599 + return ret; 2600 + } 2601 + 2505 2602 static noinline int btrfs_ioctl_snap_destroy(struct file *file, 2506 2603 void __user *arg) 2507 2604 { ··· 5763 5666 return btrfs_ioctl_fssetxattr(file, argp); 5764 5667 case BTRFS_IOC_GET_SUBVOL_INFO: 5765 5668 return btrfs_ioctl_get_subvol_info(file, argp); 5669 + case BTRFS_IOC_GET_SUBVOL_ROOTREF: 5670 + return btrfs_ioctl_get_subvol_rootref(file, argp); 5766 5671 } 5767 5672 5768 5673 return -ENOTTY;
+18
include/uapi/linux/btrfs.h
··· 785 785 __u64 reserved[8]; 786 786 }; 787 787 788 + #define BTRFS_MAX_ROOTREF_BUFFER_NUM 255 789 + struct btrfs_ioctl_get_subvol_rootref_args { 790 + /* in/out, minimum id of rootref's treeid to be searched */ 791 + __u64 min_treeid; 792 + 793 + /* out */ 794 + struct { 795 + __u64 treeid; 796 + __u64 dirid; 797 + } rootref[BTRFS_MAX_ROOTREF_BUFFER_NUM]; 798 + 799 + /* out, number of found items */ 800 + __u8 num_items; 801 + __u8 align[7]; 802 + }; 803 + 788 804 /* Error codes as returned by the kernel */ 789 805 enum btrfs_err_code { 790 806 BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET = 1, ··· 921 905 struct btrfs_ioctl_logical_ino_args) 922 906 #define BTRFS_IOC_GET_SUBVOL_INFO _IOR(BTRFS_IOCTL_MAGIC, 60, \ 923 907 struct btrfs_ioctl_get_subvol_info_args) 908 + #define BTRFS_IOC_GET_SUBVOL_ROOTREF _IOWR(BTRFS_IOCTL_MAGIC, 61, \ 909 + struct btrfs_ioctl_get_subvol_rootref_args) 924 910 925 911 #endif /* _UAPI_LINUX_BTRFS_H */