On 2018/03/08 18:47, Nikolay Borisov wrote: > > > On 6.03.2018 10:31, Misono, Tomohiro wrote: >> 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. >> >> This can be used like BTRFS_IOC_INO_LOOKUP but the argument is >> different because it also returns the name of bottom subvolume, >> which is ommited by BTRFS_IOC_GET_SUBVOL_INFO ioctl. >> Also, this must be called with fd which exists under the tree of >> args->treeid to prevent for user to search any tree. >> >> The main differences from original ino_lookup ioctl are: >> 1. Read 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 272 bytes than > > Did you mean 255? Since BTRFS_VOL_NAME_MAX is defined as 255?
My mistake, actually 255 + 1 (for null) + 8 (for subvolume id) = 264 bytes in this version. > >> ino_lookup ioctl because of space of subvolume's id and name. >> >> Signed-off-by: Tomohiro Misono <misono.tomoh...@jp.fujitsu.com> >> --- >> fs/btrfs/ioctl.c | 218 >> +++++++++++++++++++++++++++++++++++++++++++++ >> include/uapi/linux/btrfs.h | 16 ++++ >> 2 files changed, 234 insertions(+) >> >> diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c >> index 1dba309dce31..ac23da98b7e7 100644 >> --- a/fs/btrfs/ioctl.c >> +++ b/fs/btrfs/ioctl.c >> @@ -2425,6 +2425,179 @@ 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 btrfs_fs_info >> *info, >> + struct super_block *sb, >> + struct btrfs_key upper_limit, >> + struct btrfs_ioctl_ino_lookup_user_args *args) >> +{ >> + struct btrfs_root *root; >> + struct btrfs_key key, key2; >> + char *ptr; >> + int ret = -1; >> + int slot; >> + int len; >> + int total_len = 0; >> + u64 dirid = args->dirid; >> + struct btrfs_inode_ref *iref; >> + struct btrfs_root_ref rref; >> + >> + unsigned long item_off; >> + unsigned long item_len; >> + >> + struct extent_buffer *l; >> + struct btrfs_path *path = NULL; >> + >> + struct inode *inode; >> + int *new = 0; >> + >> + path = btrfs_alloc_path(); >> + if (!path) >> + return -ENOMEM; >> + >> + if (dirid == upper_limit.objectid) >> + /* >> + * If the bottom subvolume exists directly under upper limit, >> + * there is no need to construct the path and just get the >> + * subvolume's name >> + */ >> + goto get_subvol_name; > > Eliminate the label and factor out the code in a new function or just > put in the body of the if. ok. > >> + if (dirid == BTRFS_FIRST_FREE_OBJECTID) >> + /* The subvolume does not exist under upper_limit */ >> + goto access_err; > > just set ret to -EACCESS and goto out. And eliminate access_err label > altogether. Furthermore, shouldn't this check really ether: > > a) Be the first one in this function so that we return ASAP (i.e. even > before calling btrfs_alloc_path) > > or > > b) Put in the ioctl function itself. Currently for the I'd like to adopt b) approach. > btrfs_ioctl_ino_lookup case it's duplicated in both that function and > btrfs_search_path_in_tree > >> + >> + ptr = &args->path[BTRFS_INO_LOOKUP_PATH_MAX - 1]; >> + >> + key.objectid = args->treeid; >> + key.type = BTRFS_ROOT_ITEM_KEY; >> + key.offset = (u64)-1; >> + root = btrfs_read_fs_root_no_name(info, &key); >> + if (IS_ERR(root)) { >> + btrfs_err(info, "could not find root %llu", args->treeid); >> + 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 permission */ >> + 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); >> + inode = btrfs_iget(sb, &key2, root, new); >> + ret = inode_permission(inode, MAY_READ); >> + iput(inode); >> + if (ret) >> + goto access_err; >> + >> + if (key.offset == BTRFS_FIRST_FREE_OBJECTID || >> + key.offset == upper_limit.objectid) >> + break; >> + >> + btrfs_release_path(path); >> + key.objectid = key.offset; >> + key.offset = (u64)-1; >> + dirid = key.objectid; >> + } >> + /* Check if the searched path exists under the upper_limit */ >> + if (key.offset == BTRFS_FIRST_FREE_OBJECTID && >> + upper_limit.objectid != BTRFS_FIRST_FREE_OBJECTID) >> + goto access_err; >> + >> + memmove(args->path, ptr, total_len); >> + args->path[total_len] = '\0'; >> + btrfs_release_path(path); >> + ret = 0; >> + >> +get_subvol_name: >> + /* Get subolume's name from ROOT_REF */ >> + key.objectid = BTRFS_ROOT_TREE_OBJECTID; >> + key.type = BTRFS_ROOT_ITEM_KEY; >> + key.offset = (u64)-1; >> + root = btrfs_read_fs_root_no_name(info, &key); >> + if (IS_ERR(root)) { >> + ret = -ENOENT; >> + goto out; >> + } >> + >> + key.objectid = args->treeid; >> + key.type = BTRFS_ROOT_REF_KEY; >> + key.offset = args->subid; >> + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); >> + 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 arg */ >> + 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; >> + >> +access_err: >> + btrfs_free_path(path); >> + ret = -EACCES; >> + return ret; >> +} >> + >> static noinline int btrfs_ioctl_ino_lookup(struct file *file, >> void __user *argp) >> { >> @@ -2467,6 +2640,49 @@ 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 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); >> + /* >> + * args->treeid must be matched with this inode's objectid >> + * to previent for user to search any fs/file tree. >> + */ >> + if (args->treeid != BTRFS_I(inode)->root->root_key.objectid) >> + return -EINVAL; > > Do we want something like : > if (args->treeid == 0) > args->treeid = BTRFS_I(inode)->root->root_key.objectid; > > or does the ioctl impose a hard requirement for treeid to be set? My first plan is yes, but this seems unreasonable. so I will remove the variable 'treeid' from struct ino_lookup_args_user. >> + >> + ret = btrfs_search_path_in_tree_user(BTRFS_I(inode)->root->fs_info, >> + inode->i_sb, BTRFS_I(inode)->location, >> + args); > > Just pass in the the inode and reference all the parameters inside the > function. btrfs_search_path_in_tree_user really requires only 2 args: > inode and 'args' > ok. >> + >> + if (ret == 0 && copy_to_user(argp, args, sizeof(*args))) >> + ret = -EFAULT; >> + >> + kfree(args); >> + return ret; >> +} >> + >> static noinline int search_subvol(struct inode *inode, >> struct btrfs_ioctl_search_key *sk, >> size_t *buf_size, >> @@ -5917,6 +6133,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_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 1e9361cf66d5..22326d631417 100644 >> --- a/include/uapi/linux/btrfs.h >> +++ b/include/uapi/linux/btrfs.h >> @@ -422,6 +422,20 @@ struct btrfs_ioctl_ino_lookup_args { >> char name[BTRFS_INO_LOOKUP_PATH_MAX]; >> }; >> >> +#define BTRFS_INO_LOOKUP_USER_PATH_MAX (4072-BTRFS_VOL_NAME_MAX) > Where does 4072 come from? Is it related to > #define BTRFS_INO_LOOKUP_PATH_MAX 4080 > > in any way ? >> +struct btrfs_ioctl_ino_lookup_user_args { >> + /* in */ >> + __u64 treeid; >> + /* in, 'dirid' is the same as objectid in btrfs_ioctl_ino_lookup_args */ > > Given there is no description of the objectid parameter of > ino_lookup_args. Your reference to it doesn't really make its purpose > any more clear. The variable names in struct ino_lookup_args are rather confusing and I will add a comment too. Thanks, Tomohiro Misono > >> + __u64 dirid; >> + /* in, subvolume id which exists under the directory of 'dirid' */ >> + __u64 subid; >> + /* out, name of the subvolume of 'subid' */ >> + char name[BTRFS_VOL_NAME_MAX]; >> + /* out, 'path' is the same as 'name' in btrfs_ioctl_ino_lookup_args */ >> + char path[BTRFS_INO_LOOKUP_USER_PATH_MAX]; >> +}; >> + >> /* Search criteria for the btrfs SEARCH ioctl family. */ >> struct btrfs_ioctl_search_key { >> /* >> @@ -845,5 +859,7 @@ enum btrfs_err_code { >> struct btrfs_ioctl_logical_ino_args) >> #define BTRFS_IOC_GET_SUBVOL_INFO _IOWR(BTRFS_IOCTL_MAGIC, 60, \ >> struct btrfs_ioctl_search_args) >> +#define BTRFS_IOC_INO_LOOKUP_USER _IOWR(BTRFS_IOCTL_MAGIC, 61, \ >> + struct btrfs_ioctl_ino_lookup_args) >> >> #endif /* _UAPI_LINUX_BTRFS_H */ >> > -- > To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in > the body of a message to majord...@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html