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

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 <gujx@cn.fujitsu.com>
Reviewed-by: Qu Wenruo <wqu@suse.com>
Tested-by: Gu Jinxiang <gujx@cn.fujitsu.com>
Signed-off-by: Tomohiro Misono <misono.tomohiro@jp.fujitsu.com>
[ style fixes ]
Signed-off-by: David Sterba <dsterba@suse.com>

authored by

Tomohiro Misono and committed by
David Sterba
23d0b79d 42e4b520

+221
+204
fs/btrfs/ioctl.c
··· 2341 2341 return ret; 2342 2342 } 2343 2343 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 + 2344 2503 static noinline int btrfs_ioctl_ino_lookup(struct file *file, 2345 2504 void __user *argp) 2346 2505 { ··· 2535 2376 args->name); 2536 2377 2537 2378 out: 2379 + if (ret == 0 && copy_to_user(argp, args, sizeof(*args))) 2380 + ret = -EFAULT; 2381 + 2382 + kfree(args); 2383 + return ret; 2384 + } 2385 + 2386 + /* 2387 + * Version of ino_lookup ioctl (unprivileged) 2388 + * 2389 + * The main differences from ino_lookup ioctl are: 2390 + * 2391 + * 1. Read + Exec permission will be checked using inode_permission() during 2392 + * path construction. -EACCES will be returned in case of failure. 2393 + * 2. Path construction will be stopped at the inode number which corresponds 2394 + * to the fd with which this ioctl is called. If constructed path does not 2395 + * exist under fd's inode, -EACCES will be returned. 2396 + * 3. The name of bottom subvolume is also searched and filled. 2397 + */ 2398 + static int btrfs_ioctl_ino_lookup_user(struct file *file, void __user *argp) 2399 + { 2400 + struct btrfs_ioctl_ino_lookup_user_args *args; 2401 + struct inode *inode; 2402 + int ret; 2403 + 2404 + args = memdup_user(argp, sizeof(*args)); 2405 + if (IS_ERR(args)) 2406 + return PTR_ERR(args); 2407 + 2408 + inode = file_inode(file); 2409 + 2410 + if (args->dirid == BTRFS_FIRST_FREE_OBJECTID && 2411 + BTRFS_I(inode)->location.objectid != BTRFS_FIRST_FREE_OBJECTID) { 2412 + /* 2413 + * The subvolume does not exist under fd with which this is 2414 + * called 2415 + */ 2416 + kfree(args); 2417 + return -EACCES; 2418 + } 2419 + 2420 + ret = btrfs_search_path_in_tree_user(inode, args); 2421 + 2538 2422 if (ret == 0 && copy_to_user(argp, args, sizeof(*args))) 2539 2423 ret = -EFAULT; 2540 2424 ··· 5967 5765 return btrfs_ioctl_get_subvol_info(file, argp); 5968 5766 case BTRFS_IOC_GET_SUBVOL_ROOTREF: 5969 5767 return btrfs_ioctl_get_subvol_rootref(file, argp); 5768 + case BTRFS_IOC_INO_LOOKUP_USER: 5769 + return btrfs_ioctl_ino_lookup_user(file, argp); 5970 5770 } 5971 5771 5972 5772 return -ENOTTY;
+17
include/uapi/linux/btrfs.h
··· 422 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) 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 + 425 440 /* Search criteria for the btrfs SEARCH ioctl family. */ 426 441 struct btrfs_ioctl_search_key { 427 442 /* ··· 938 923 struct btrfs_ioctl_get_subvol_info_args) 939 924 #define BTRFS_IOC_GET_SUBVOL_ROOTREF _IOWR(BTRFS_IOCTL_MAGIC, 61, \ 940 925 struct btrfs_ioctl_get_subvol_rootref_args) 926 + #define BTRFS_IOC_INO_LOOKUP_USER _IOWR(BTRFS_IOCTL_MAGIC, 62, \ 927 + struct btrfs_ioctl_ino_lookup_user_args) 941 928 942 929 #endif /* _UAPI_LINUX_BTRFS_H */