2017-03-17 09:18:50 +03:00
// SPDX-License-Identifier: GPL-2.0
# ifndef NO_BCACHEFS_FS
# include "bcachefs.h"
# include "chardev.h"
2018-12-17 14:11:14 +03:00
# include "dirent.h"
2017-03-17 09:18:50 +03:00
# include "fs.h"
2019-10-03 01:35:36 +03:00
# include "fs-common.h"
2017-03-17 09:18:50 +03:00
# include "fs-ioctl.h"
# include "quota.h"
# include <linux/compat.h>
2021-03-17 06:28:43 +03:00
# include <linux/fsnotify.h>
2017-03-17 09:18:50 +03:00
# include <linux/mount.h>
2021-03-17 06:28:43 +03:00
# include <linux/namei.h>
# include <linux/security.h>
# include <linux/writeback.h>
2017-03-17 09:18:50 +03:00
# define FS_IOC_GOINGDOWN _IOR('X', 125, __u32)
2021-05-23 04:13:17 +03:00
# define FSOP_GOING_FLAGS_DEFAULT 0x0 /* going down */
# define FSOP_GOING_FLAGS_LOGFLUSH 0x1 /* flush log but not data */
# define FSOP_GOING_FLAGS_NOLOGFLUSH 0x2 /* don't flush log nor data */
2017-03-17 09:18:50 +03:00
struct flags_set {
unsigned mask ;
unsigned flags ;
unsigned projid ;
2022-10-12 23:21:08 +03:00
bool set_projinherit ;
bool projinherit ;
2017-03-17 09:18:50 +03:00
} ;
2023-08-12 17:47:45 +03:00
static int bch2_inode_flags_set ( struct btree_trans * trans ,
struct bch_inode_info * inode ,
2017-03-17 09:18:50 +03:00
struct bch_inode_unpacked * bi ,
void * p )
{
2018-07-17 21:12:42 +03:00
struct bch_fs * c = inode - > v . i_sb - > s_fs_info ;
2017-03-17 09:18:50 +03:00
/*
* We ' re relying on btree locking here for exclusion with other ioctl
* calls - use the flags in the btree ( @ bi ) , not inode - > i_flags :
*/
struct flags_set * s = p ;
unsigned newflags = s - > flags ;
unsigned oldflags = bi - > bi_flags & s - > mask ;
2023-11-02 18:42:48 +03:00
if ( ( ( newflags ^ oldflags ) & ( BCH_INODE_append | BCH_INODE_immutable ) ) & &
2017-03-17 09:18:50 +03:00
! capable ( CAP_LINUX_IMMUTABLE ) )
return - EPERM ;
2018-07-17 21:12:42 +03:00
if ( ! S_ISREG ( bi - > bi_mode ) & &
! S_ISDIR ( bi - > bi_mode ) & &
2023-11-02 18:42:48 +03:00
( newflags & ( BCH_INODE_nodump | BCH_INODE_noatime ) ) ! = newflags )
2017-03-17 09:18:50 +03:00
return - EINVAL ;
2022-10-12 23:21:08 +03:00
if ( s - > set_projinherit ) {
bi - > bi_fields_set & = ~ ( 1 < < Inode_opt_project ) ;
bi - > bi_fields_set | = ( ( int ) s - > projinherit < < Inode_opt_project ) ;
}
2017-03-17 09:18:50 +03:00
bi - > bi_flags & = ~ s - > mask ;
bi - > bi_flags | = newflags ;
2018-07-17 21:12:42 +03:00
bi - > bi_ctime = timespec_to_bch2_time ( c , current_time ( & inode - > v ) ) ;
2017-03-17 09:18:50 +03:00
return 0 ;
}
static int bch2_ioc_getflags ( struct bch_inode_info * inode , int __user * arg )
{
unsigned flags = map_flags ( bch_flags_to_uflags , inode - > ei_inode . bi_flags ) ;
return put_user ( flags , arg ) ;
}
static int bch2_ioc_setflags ( struct bch_fs * c ,
struct file * file ,
struct bch_inode_info * inode ,
void __user * arg )
{
struct flags_set s = { . mask = map_defined ( bch_flags_to_uflags ) } ;
unsigned uflags ;
int ret ;
if ( get_user ( uflags , ( int __user * ) arg ) )
return - EFAULT ;
s . flags = map_flags_rev ( bch_flags_to_uflags , uflags ) ;
if ( uflags )
return - EOPNOTSUPP ;
ret = mnt_want_write_file ( file ) ;
if ( ret )
return ret ;
inode_lock ( & inode - > v ) ;
if ( ! inode_owner_or_capable ( file_mnt_idmap ( file ) , & inode - > v ) ) {
ret = - EACCES ;
goto setflags_out ;
}
mutex_lock ( & inode - > ei_update_lock ) ;
2023-12-30 05:16:32 +03:00
ret = bch2_subvol_is_ro ( c , inode - > ei_subvol ) ? :
bch2_write_inode ( c , inode , bch2_inode_flags_set , & s ,
2018-07-17 21:12:42 +03:00
ATTR_CTIME ) ;
2017-03-17 09:18:50 +03:00
mutex_unlock ( & inode - > ei_update_lock ) ;
setflags_out :
inode_unlock ( & inode - > v ) ;
mnt_drop_write_file ( file ) ;
return ret ;
}
static int bch2_ioc_fsgetxattr ( struct bch_inode_info * inode ,
struct fsxattr __user * arg )
{
struct fsxattr fa = { 0 } ;
fa . fsx_xflags = map_flags ( bch_flags_to_xflags , inode - > ei_inode . bi_flags ) ;
2022-10-12 23:21:08 +03:00
if ( inode - > ei_inode . bi_fields_set & ( 1 < < Inode_opt_project ) )
fa . fsx_xflags | = FS_XFLAG_PROJINHERIT ;
2017-03-17 09:18:50 +03:00
fa . fsx_projid = inode - > ei_qid . q [ QTYP_PRJ ] ;
2023-09-15 15:55:23 +03:00
if ( copy_to_user ( arg , & fa , sizeof ( fa ) ) )
return - EFAULT ;
return 0 ;
2017-03-17 09:18:50 +03:00
}
2023-08-12 17:47:45 +03:00
static int fssetxattr_inode_update_fn ( struct btree_trans * trans ,
struct bch_inode_info * inode ,
2017-03-17 09:18:50 +03:00
struct bch_inode_unpacked * bi ,
void * p )
{
struct flags_set * s = p ;
2018-12-13 16:32:11 +03:00
if ( s - > projid ! = bi - > bi_project ) {
2018-12-19 16:43:01 +03:00
bi - > bi_fields_set | = 1U < < Inode_opt_project ;
2018-12-13 16:32:11 +03:00
bi - > bi_project = s - > projid ;
}
2017-03-17 09:18:50 +03:00
2023-08-12 17:47:45 +03:00
return bch2_inode_flags_set ( trans , inode , bi , p ) ;
2017-03-17 09:18:50 +03:00
}
static int bch2_ioc_fssetxattr ( struct bch_fs * c ,
struct file * file ,
struct bch_inode_info * inode ,
struct fsxattr __user * arg )
{
struct flags_set s = { . mask = map_defined ( bch_flags_to_xflags ) } ;
struct fsxattr fa ;
int ret ;
if ( copy_from_user ( & fa , arg , sizeof ( fa ) ) )
return - EFAULT ;
2022-10-12 23:21:08 +03:00
s . set_projinherit = true ;
s . projinherit = ( fa . fsx_xflags & FS_XFLAG_PROJINHERIT ) ! = 0 ;
fa . fsx_xflags & = ~ FS_XFLAG_PROJINHERIT ;
2017-03-17 09:18:50 +03:00
s . flags = map_flags_rev ( bch_flags_to_xflags , fa . fsx_xflags ) ;
if ( fa . fsx_xflags )
return - EOPNOTSUPP ;
2018-12-19 16:43:01 +03:00
if ( fa . fsx_projid > = U32_MAX )
return - EINVAL ;
2020-08-24 21:57:48 +03:00
/*
* inode fields accessible via the xattr interface are stored with a + 1
* bias , so that 0 means unset :
*/
2018-12-19 16:43:01 +03:00
s . projid = fa . fsx_projid + 1 ;
2017-03-17 09:18:50 +03:00
ret = mnt_want_write_file ( file ) ;
if ( ret )
return ret ;
inode_lock ( & inode - > v ) ;
if ( ! inode_owner_or_capable ( file_mnt_idmap ( file ) , & inode - > v ) ) {
ret = - EACCES ;
goto err ;
}
mutex_lock ( & inode - > ei_update_lock ) ;
2023-12-30 05:16:32 +03:00
ret = bch2_subvol_is_ro ( c , inode - > ei_subvol ) ? :
bch2_set_projid ( c , inode , fa . fsx_projid ) ? :
bch2_write_inode ( c , inode , fssetxattr_inode_update_fn , & s ,
2018-07-17 21:12:42 +03:00
ATTR_CTIME ) ;
2017-03-17 09:18:50 +03:00
mutex_unlock ( & inode - > ei_update_lock ) ;
err :
inode_unlock ( & inode - > v ) ;
mnt_drop_write_file ( file ) ;
return ret ;
}
2023-08-12 17:47:45 +03:00
static int bch2_reinherit_attrs_fn ( struct btree_trans * trans ,
struct bch_inode_info * inode ,
2019-10-03 01:35:36 +03:00
struct bch_inode_unpacked * bi ,
void * p )
{
struct bch_inode_info * dir = p ;
return ! bch2_reinherit_attrs ( bi , & dir - > ei_inode ) ;
}
2018-12-17 14:11:14 +03:00
static int bch2_ioc_reinherit_attrs ( struct bch_fs * c ,
struct file * file ,
struct bch_inode_info * src ,
const char __user * name )
{
2021-03-03 02:35:30 +03:00
struct bch_hash_info hash = bch2_hash_info_init ( c , & src - > ei_inode ) ;
2018-12-17 14:11:14 +03:00
struct bch_inode_info * dst ;
struct inode * vinode = NULL ;
char * kname = NULL ;
struct qstr qstr ;
int ret = 0 ;
2021-03-16 07:28:17 +03:00
subvol_inum inum ;
2018-12-17 14:11:14 +03:00
kname = kmalloc ( BCH_NAME_MAX + 1 , GFP_KERNEL ) ;
if ( ! kname )
return - ENOMEM ;
ret = strncpy_from_user ( kname , name , BCH_NAME_MAX ) ;
if ( unlikely ( ret < 0 ) )
goto err1 ;
2018-12-20 00:01:38 +03:00
qstr . len = ret ;
2018-12-17 14:11:14 +03:00
qstr . name = kname ;
2021-03-16 07:28:17 +03:00
ret = bch2_dirent_lookup ( c , inode_inum ( src ) , & hash , & qstr , & inum ) ;
if ( ret )
2018-12-17 14:11:14 +03:00
goto err1 ;
vinode = bch2_vfs_inode_get ( c , inum ) ;
ret = PTR_ERR_OR_ZERO ( vinode ) ;
if ( ret )
goto err1 ;
dst = to_bch_ei ( vinode ) ;
ret = mnt_want_write_file ( file ) ;
if ( ret )
goto err2 ;
2019-06-25 01:24:38 +03:00
bch2_lock_inodes ( INODE_UPDATE_LOCK , src , dst ) ;
2018-12-17 14:11:14 +03:00
if ( inode_attr_changing ( src , dst , Inode_opt_project ) ) {
ret = bch2_fs_quota_transfer ( c , dst ,
src - > ei_qid ,
1 < < QTYP_PRJ ,
KEY_TYPE_QUOTA_PREALLOC ) ;
if ( ret )
goto err3 ;
}
ret = bch2_write_inode ( c , dst , bch2_reinherit_attrs_fn , src , 0 ) ;
err3 :
2019-06-25 01:24:38 +03:00
bch2_unlock_inodes ( INODE_UPDATE_LOCK , src , dst ) ;
2018-12-17 14:11:14 +03:00
/* return true if we did work */
if ( ret > = 0 )
ret = ! ret ;
mnt_drop_write_file ( file ) ;
err2 :
iput ( vinode ) ;
err1 :
kfree ( kname ) ;
return ret ;
}
2021-05-23 04:13:17 +03:00
static int bch2_ioc_goingdown ( struct bch_fs * c , u32 __user * arg )
{
u32 flags ;
int ret = 0 ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
if ( get_user ( flags , arg ) )
return - EFAULT ;
bch_notice ( c , " shutdown by ioctl type %u " , flags ) ;
switch ( flags ) {
case FSOP_GOING_FLAGS_DEFAULT :
2023-10-24 16:01:08 +03:00
ret = bdev_freeze ( c - > vfs_sb - > s_bdev ) ;
2021-05-23 04:13:17 +03:00
if ( ret )
2023-11-30 22:17:11 +03:00
break ;
2021-05-23 04:13:17 +03:00
bch2_journal_flush ( & c - > journal ) ;
bch2_fs_emergency_read_only ( c ) ;
2023-10-24 16:01:08 +03:00
bdev_thaw ( c - > vfs_sb - > s_bdev ) ;
2021-05-23 04:13:17 +03:00
break ;
case FSOP_GOING_FLAGS_LOGFLUSH :
bch2_journal_flush ( & c - > journal ) ;
fallthrough ;
case FSOP_GOING_FLAGS_NOLOGFLUSH :
bch2_fs_emergency_read_only ( c ) ;
break ;
default :
ret = - EINVAL ;
break ;
}
2023-11-30 22:17:11 +03:00
2021-05-23 04:13:17 +03:00
return ret ;
}
2023-09-29 08:15:33 +03:00
static long __bch2_ioctl_subvolume_create ( struct bch_fs * c , struct file * filp ,
struct bch_ioctl_subvolume arg )
2021-03-17 06:28:43 +03:00
{
struct inode * dir ;
struct bch_inode_info * inode ;
struct user_namespace * s_user_ns ;
struct dentry * dst_dentry ;
struct path src_path , dst_path ;
int how = LOOKUP_FOLLOW ;
int error ;
subvol_inum snapshot_src = { 0 } ;
unsigned lookup_flags = 0 ;
unsigned create_flags = BCH_CREATE_SUBVOL ;
if ( arg . flags & ~ ( BCH_SUBVOL_SNAPSHOT_CREATE |
BCH_SUBVOL_SNAPSHOT_RO ) )
return - EINVAL ;
if ( ! ( arg . flags & BCH_SUBVOL_SNAPSHOT_CREATE ) & &
( arg . src_ptr | |
( arg . flags & BCH_SUBVOL_SNAPSHOT_RO ) ) )
return - EINVAL ;
if ( arg . flags & BCH_SUBVOL_SNAPSHOT_CREATE )
create_flags | = BCH_CREATE_SNAPSHOT ;
if ( arg . flags & BCH_SUBVOL_SNAPSHOT_RO )
create_flags | = BCH_CREATE_SNAPSHOT_RO ;
2024-01-15 05:21:25 +03:00
if ( arg . flags & BCH_SUBVOL_SNAPSHOT_CREATE ) {
/* sync_inodes_sb enforce s_umount is locked */
down_read ( & c - > vfs_sb - > s_umount ) ;
2021-03-17 06:28:43 +03:00
sync_inodes_sb ( c - > vfs_sb ) ;
2024-01-15 05:21:25 +03:00
up_read ( & c - > vfs_sb - > s_umount ) ;
}
2021-03-17 06:28:43 +03:00
retry :
if ( arg . src_ptr ) {
error = user_path_at ( arg . dirfd ,
( const char __user * ) ( unsigned long ) arg . src_ptr ,
how , & src_path ) ;
if ( error )
goto err1 ;
if ( src_path . dentry - > d_sb - > s_fs_info ! = c ) {
path_put ( & src_path ) ;
error = - EXDEV ;
goto err1 ;
}
snapshot_src = inode_inum ( to_bch_ei ( src_path . dentry - > d_inode ) ) ;
}
dst_dentry = user_path_create ( arg . dirfd ,
( const char __user * ) ( unsigned long ) arg . dst_ptr ,
& dst_path , lookup_flags ) ;
error = PTR_ERR_OR_ZERO ( dst_dentry ) ;
if ( error )
goto err2 ;
if ( dst_dentry - > d_sb - > s_fs_info ! = c ) {
error = - EXDEV ;
goto err3 ;
}
if ( dst_dentry - > d_inode ) {
error = - EEXIST ;
goto err3 ;
}
dir = dst_path . dentry - > d_inode ;
if ( IS_DEADDIR ( dir ) ) {
2023-05-28 02:59:59 +03:00
error = - BCH_ERR_ENOENT_directory_dead ;
2021-03-17 06:28:43 +03:00
goto err3 ;
}
s_user_ns = dir - > i_sb - > s_user_ns ;
if ( ! kuid_has_mapping ( s_user_ns , current_fsuid ( ) ) | |
! kgid_has_mapping ( s_user_ns , current_fsgid ( ) ) ) {
error = - EOVERFLOW ;
goto err3 ;
}
error = inode_permission ( file_mnt_idmap ( filp ) ,
dir , MAY_WRITE | MAY_EXEC ) ;
if ( error )
goto err3 ;
if ( ! IS_POSIXACL ( dir ) )
arg . mode & = ~ current_umask ( ) ;
error = security_path_mkdir ( & dst_path , dst_dentry , arg . mode ) ;
if ( error )
goto err3 ;
if ( ( arg . flags & BCH_SUBVOL_SNAPSHOT_CREATE ) & &
! arg . src_ptr )
2023-12-04 23:44:15 +03:00
snapshot_src . subvol = inode_inum ( to_bch_ei ( dir ) ) . subvol ;
2021-03-17 06:28:43 +03:00
inode = __bch2_create ( file_mnt_idmap ( filp ) , to_bch_ei ( dir ) ,
dst_dentry , arg . mode | S_IFDIR ,
0 , snapshot_src , create_flags ) ;
error = PTR_ERR_OR_ZERO ( inode ) ;
if ( error )
goto err3 ;
d_instantiate ( dst_dentry , & inode - > v ) ;
fsnotify_mkdir ( dir , dst_dentry ) ;
err3 :
done_path_create ( & dst_path , dst_dentry ) ;
err2 :
if ( arg . src_ptr )
path_put ( & src_path ) ;
if ( retry_estale ( error , lookup_flags ) ) {
lookup_flags | = LOOKUP_REVAL ;
goto retry ;
}
err1 :
return error ;
}
2023-09-29 08:15:33 +03:00
static long bch2_ioctl_subvolume_create ( struct bch_fs * c , struct file * filp ,
struct bch_ioctl_subvolume arg )
{
down_write ( & c - > snapshot_create_lock ) ;
long ret = __bch2_ioctl_subvolume_create ( c , filp , arg ) ;
up_write ( & c - > snapshot_create_lock ) ;
return ret ;
}
2021-03-17 06:28:43 +03:00
static long bch2_ioctl_subvolume_destroy ( struct bch_fs * c , struct file * filp ,
struct bch_ioctl_subvolume arg )
{
2023-11-15 02:52:42 +03:00
const char __user * name = ( void __user * ) ( unsigned long ) arg . dst_ptr ;
2021-03-17 06:28:43 +03:00
struct path path ;
2021-10-21 03:50:07 +03:00
struct inode * dir ;
2023-11-15 02:52:42 +03:00
struct dentry * victim ;
2021-03-17 06:28:43 +03:00
int ret = 0 ;
if ( arg . flags )
return - EINVAL ;
2023-11-15 02:52:42 +03:00
victim = user_path_locked_at ( arg . dirfd , name , & path ) ;
if ( IS_ERR ( victim ) )
return PTR_ERR ( victim ) ;
2021-03-17 06:28:43 +03:00
2024-01-28 11:46:17 +03:00
dir = d_inode ( path . dentry ) ;
2023-11-15 02:52:42 +03:00
if ( victim - > d_sb - > s_fs_info ! = c ) {
2023-03-16 19:47:35 +03:00
ret = - EXDEV ;
goto err ;
2021-03-17 06:28:43 +03:00
}
2023-11-15 02:52:42 +03:00
if ( ! d_is_positive ( victim ) ) {
ret = - ENOENT ;
2023-03-16 19:47:35 +03:00
goto err ;
2023-11-15 02:52:42 +03:00
}
ret = __bch2_unlink ( dir , victim , true ) ;
if ( ! ret ) {
fsnotify_rmdir ( dir , victim ) ;
d_delete ( victim ) ;
}
2023-03-16 19:47:35 +03:00
err :
2024-01-28 11:46:17 +03:00
inode_unlock ( dir ) ;
2023-11-15 02:52:42 +03:00
dput ( victim ) ;
2023-03-16 19:47:35 +03:00
path_put ( & path ) ;
2021-03-17 06:28:43 +03:00
return ret ;
}
2017-03-17 09:18:50 +03:00
long bch2_fs_file_ioctl ( struct file * file , unsigned cmd , unsigned long arg )
{
struct bch_inode_info * inode = file_bch_inode ( file ) ;
2021-05-23 04:13:17 +03:00
struct bch_fs * c = inode - > v . i_sb - > s_fs_info ;
2022-09-18 22:43:50 +03:00
long ret ;
2017-03-17 09:18:50 +03:00
switch ( cmd ) {
case FS_IOC_GETFLAGS :
2022-09-18 22:43:50 +03:00
ret = bch2_ioc_getflags ( inode , ( int __user * ) arg ) ;
break ;
2017-03-17 09:18:50 +03:00
case FS_IOC_SETFLAGS :
2022-09-18 22:43:50 +03:00
ret = bch2_ioc_setflags ( c , file , inode , ( int __user * ) arg ) ;
break ;
2017-03-17 09:18:50 +03:00
case FS_IOC_FSGETXATTR :
2022-09-18 22:43:50 +03:00
ret = bch2_ioc_fsgetxattr ( inode , ( void __user * ) arg ) ;
break ;
2017-03-17 09:18:50 +03:00
case FS_IOC_FSSETXATTR :
2022-09-18 22:43:50 +03:00
ret = bch2_ioc_fssetxattr ( c , file , inode ,
( void __user * ) arg ) ;
break ;
2018-12-17 14:11:14 +03:00
case BCHFS_IOC_REINHERIT_ATTRS :
2022-09-18 22:43:50 +03:00
ret = bch2_ioc_reinherit_attrs ( c , file , inode ,
( void __user * ) arg ) ;
break ;
2017-03-17 09:18:50 +03:00
case FS_IOC_GETVERSION :
2022-09-18 22:43:50 +03:00
ret = - ENOTTY ;
break ;
2017-03-17 09:18:50 +03:00
case FS_IOC_SETVERSION :
2022-09-18 22:43:50 +03:00
ret = - ENOTTY ;
break ;
2017-03-17 09:18:50 +03:00
case FS_IOC_GOINGDOWN :
2022-09-18 22:43:50 +03:00
ret = bch2_ioc_goingdown ( c , ( u32 __user * ) arg ) ;
break ;
2017-03-17 09:18:50 +03:00
2021-03-17 06:28:43 +03:00
case BCH_IOCTL_SUBVOLUME_CREATE : {
struct bch_ioctl_subvolume i ;
2022-09-18 22:43:50 +03:00
ret = copy_from_user ( & i , ( void __user * ) arg , sizeof ( i ) )
? - EFAULT
: bch2_ioctl_subvolume_create ( c , file , i ) ;
break ;
2021-03-17 06:28:43 +03:00
}
case BCH_IOCTL_SUBVOLUME_DESTROY : {
struct bch_ioctl_subvolume i ;
2022-09-18 22:43:50 +03:00
ret = copy_from_user ( & i , ( void __user * ) arg , sizeof ( i ) )
? - EFAULT
: bch2_ioctl_subvolume_destroy ( c , file , i ) ;
break ;
2021-03-17 06:28:43 +03:00
}
2017-03-17 09:18:50 +03:00
default :
2022-09-18 22:43:50 +03:00
ret = bch2_fs_ioctl ( c , cmd , ( void __user * ) arg ) ;
break ;
2017-03-17 09:18:50 +03:00
}
2022-09-18 22:43:50 +03:00
return bch2_err_class ( ret ) ;
2017-03-17 09:18:50 +03:00
}
# ifdef CONFIG_COMPAT
long bch2_compat_fs_ioctl ( struct file * file , unsigned cmd , unsigned long arg )
{
/* These are just misnamed, they actually get/put from/to user an int */
switch ( cmd ) {
case FS_IOC_GETFLAGS :
cmd = FS_IOC_GETFLAGS ;
break ;
case FS_IOC32_SETFLAGS :
cmd = FS_IOC_SETFLAGS ;
break ;
default :
return - ENOIOCTLCMD ;
}
return bch2_fs_file_ioctl ( file , cmd , ( unsigned long ) compat_ptr ( arg ) ) ;
}
# endif
# endif /* NO_BCACHEFS_FS */