@@ -2341,6 +2341,165 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info,
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
{
@@ -2383,6 +2542,49 @@ static noinline int btrfs_ioctl_ino_lookup(struct file *file,
2383
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
+ */
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
+
2386
2588
/* Get the subvolume information in BTRFS_ROOT_ITEM and BTRFS_ROOT_BACKREF */
2387
2589
static 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
5967
return btrfs_ioctl_get_subvol_info (file , argp );
5766
5968
case BTRFS_IOC_GET_SUBVOL_ROOTREF :
5767
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
5974
return - ENOTTY ;
0 commit comments