2006-10-11 12:20:50 +04:00
/*
2006-10-11 12:20:53 +04:00
* linux / fs / ext4 / ioctl . c
2006-10-11 12:20:50 +04:00
*
* Copyright ( C ) 1993 , 1994 , 1995
* Remy Card ( card @ masi . ibp . fr )
* Laboratoire MASI - Institut Blaise Pascal
* Universite Pierre et Marie Curie ( Paris VI )
*/
# include <linux/fs.h>
2006-10-11 12:21:01 +04:00
# include <linux/jbd2.h>
2006-10-11 12:20:50 +04:00
# include <linux/capability.h>
# include <linux/time.h>
# include <linux/compat.h>
2008-02-16 01:37:46 +03:00
# include <linux/mount.h>
2009-06-18 03:24:03 +04:00
# include <linux/file.h>
2006-10-11 12:20:50 +04:00
# include <asm/uaccess.h>
2008-04-30 02:13:32 +04:00
# include "ext4_jbd2.h"
# include "ext4.h"
2006-10-11 12:20:50 +04:00
2008-04-30 06:03:54 +04:00
long ext4_ioctl ( struct file * filp , unsigned int cmd , unsigned long arg )
2006-10-11 12:20:50 +04:00
{
2008-04-30 06:03:54 +04:00
struct inode * inode = filp - > f_dentry - > d_inode ;
2006-10-11 12:20:53 +04:00
struct ext4_inode_info * ei = EXT4_I ( inode ) ;
2006-10-11 12:20:50 +04:00
unsigned int flags ;
2008-09-09 06:25:24 +04:00
ext4_debug ( " cmd = %u, arg = %lu \n " , cmd , arg ) ;
2006-10-11 12:20:50 +04:00
switch ( cmd ) {
2006-10-11 12:20:53 +04:00
case EXT4_IOC_GETFLAGS :
2007-07-18 17:24:20 +04:00
ext4_get_inode_flags ( ei ) ;
2006-10-11 12:20:53 +04:00
flags = ei - > i_flags & EXT4_FL_USER_VISIBLE ;
2006-10-11 12:20:50 +04:00
return put_user ( flags , ( int __user * ) arg ) ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC_SETFLAGS : {
2006-10-11 12:20:50 +04:00
handle_t * handle = NULL ;
2008-10-09 07:34:06 +04:00
int err , migrate = 0 ;
2006-10-11 12:20:53 +04:00
struct ext4_iloc iloc ;
2006-10-11 12:20:50 +04:00
unsigned int oldflags ;
unsigned int jflag ;
2011-03-24 02:43:26 +03:00
if ( ! inode_owner_or_capable ( inode ) )
2006-10-11 12:20:50 +04:00
return - EACCES ;
if ( get_user ( flags , ( int __user * ) arg ) )
return - EFAULT ;
2008-02-16 01:37:46 +03:00
err = mnt_want_write ( filp - > f_path . mnt ) ;
if ( err )
return err ;
2009-02-16 02:09:20 +03:00
flags = ext4_mask_flags ( inode - > i_mode , flags ) ;
2006-10-11 12:20:50 +04:00
2008-02-16 01:37:46 +03:00
err = - EPERM ;
2006-10-11 12:20:50 +04:00
mutex_lock ( & inode - > i_mutex ) ;
2007-11-15 03:58:56 +03:00
/* Is it quota file? Do not allow user to mess with it */
2008-02-16 01:37:46 +03:00
if ( IS_NOQUOTA ( inode ) )
goto flags_out ;
2006-10-11 12:20:50 +04:00
oldflags = ei - > i_flags ;
/* The JOURNAL_DATA flag is modifiable only by root */
2006-10-11 12:20:53 +04:00
jflag = flags & EXT4_JOURNAL_DATA_FL ;
2006-10-11 12:20:50 +04:00
/*
* The IMMUTABLE and APPEND_ONLY flags can only be changed by
* the relevant capability .
*
* This test looks nicer . Thanks to Pauline Middelink
*/
2006-10-11 12:20:53 +04:00
if ( ( flags ^ oldflags ) & ( EXT4_APPEND_FL | EXT4_IMMUTABLE_FL ) ) {
2008-02-16 01:37:46 +03:00
if ( ! capable ( CAP_LINUX_IMMUTABLE ) )
goto flags_out ;
2006-10-11 12:20:50 +04:00
}
/*
* The JOURNAL_DATA flag can only be changed by
* the relevant capability .
*/
2006-10-11 12:20:53 +04:00
if ( ( jflag ^ oldflags ) & ( EXT4_JOURNAL_DATA_FL ) ) {
2008-02-16 01:37:46 +03:00
if ( ! capable ( CAP_SYS_RESOURCE ) )
goto flags_out ;
2006-10-11 12:20:50 +04:00
}
2008-10-09 07:34:06 +04:00
if ( oldflags & EXT4_EXTENTS_FL ) {
/* We don't support clearning extent flags */
if ( ! ( flags & EXT4_EXTENTS_FL ) ) {
err = - EOPNOTSUPP ;
goto flags_out ;
}
} else if ( flags & EXT4_EXTENTS_FL ) {
/* migrate the file */
migrate = 1 ;
flags & = ~ EXT4_EXTENTS_FL ;
}
2006-10-11 12:20:50 +04:00
2010-02-24 17:52:53 +03:00
if ( flags & EXT4_EOFBLOCKS_FL ) {
/* we don't support adding EOFBLOCKS flag */
if ( ! ( oldflags & EXT4_EOFBLOCKS_FL ) ) {
err = - EOPNOTSUPP ;
goto flags_out ;
}
} else if ( oldflags & EXT4_EOFBLOCKS_FL )
ext4_truncate ( inode ) ;
2006-10-11 12:20:53 +04:00
handle = ext4_journal_start ( inode , 1 ) ;
2006-10-11 12:20:50 +04:00
if ( IS_ERR ( handle ) ) {
2008-02-16 01:37:46 +03:00
err = PTR_ERR ( handle ) ;
goto flags_out ;
2006-10-11 12:20:50 +04:00
}
if ( IS_SYNC ( inode ) )
2009-01-07 08:06:22 +03:00
ext4_handle_sync ( handle ) ;
2006-10-11 12:20:53 +04:00
err = ext4_reserve_inode_write ( handle , inode , & iloc ) ;
2006-10-11 12:20:50 +04:00
if ( err )
goto flags_err ;
2006-10-11 12:20:53 +04:00
flags = flags & EXT4_FL_USER_MODIFIABLE ;
flags | = oldflags & ~ EXT4_FL_USER_MODIFIABLE ;
2006-10-11 12:20:50 +04:00
ei - > i_flags = flags ;
2006-10-11 12:20:53 +04:00
ext4_set_inode_flags ( inode ) ;
2007-07-18 17:15:20 +04:00
inode - > i_ctime = ext4_current_time ( inode ) ;
2006-10-11 12:20:50 +04:00
2006-10-11 12:20:53 +04:00
err = ext4_mark_iloc_dirty ( handle , inode , & iloc ) ;
2006-10-11 12:20:50 +04:00
flags_err :
2006-10-11 12:20:53 +04:00
ext4_journal_stop ( handle ) ;
2008-02-16 01:37:46 +03:00
if ( err )
goto flags_out ;
2006-10-11 12:20:50 +04:00
2006-10-11 12:20:53 +04:00
if ( ( jflag ^ oldflags ) & ( EXT4_JOURNAL_DATA_FL ) )
err = ext4_change_inode_journal_flag ( inode , jflag ) ;
2008-10-09 07:34:06 +04:00
if ( err )
goto flags_out ;
if ( migrate )
err = ext4_ext_migrate ( inode ) ;
2008-02-16 01:37:46 +03:00
flags_out :
2006-10-11 12:20:50 +04:00
mutex_unlock ( & inode - > i_mutex ) ;
2008-02-16 01:37:46 +03:00
mnt_drop_write ( filp - > f_path . mnt ) ;
2006-10-11 12:20:50 +04:00
return err ;
}
2006-10-11 12:20:53 +04:00
case EXT4_IOC_GETVERSION :
case EXT4_IOC_GETVERSION_OLD :
2006-10-11 12:20:50 +04:00
return put_user ( inode - > i_generation , ( int __user * ) arg ) ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC_SETVERSION :
case EXT4_IOC_SETVERSION_OLD : {
2006-10-11 12:20:50 +04:00
handle_t * handle ;
2006-10-11 12:20:53 +04:00
struct ext4_iloc iloc ;
2006-10-11 12:20:50 +04:00
__u32 generation ;
int err ;
2011-03-24 02:43:26 +03:00
if ( ! inode_owner_or_capable ( inode ) )
2006-10-11 12:20:50 +04:00
return - EPERM ;
2008-02-16 01:37:46 +03:00
err = mnt_want_write ( filp - > f_path . mnt ) ;
if ( err )
return err ;
if ( get_user ( generation , ( int __user * ) arg ) ) {
err = - EFAULT ;
goto setversion_out ;
}
2006-10-11 12:20:50 +04:00
2006-10-11 12:20:53 +04:00
handle = ext4_journal_start ( inode , 1 ) ;
2008-02-16 01:37:46 +03:00
if ( IS_ERR ( handle ) ) {
err = PTR_ERR ( handle ) ;
goto setversion_out ;
}
2006-10-11 12:20:53 +04:00
err = ext4_reserve_inode_write ( handle , inode , & iloc ) ;
2006-10-11 12:20:50 +04:00
if ( err = = 0 ) {
2007-07-18 17:15:20 +04:00
inode - > i_ctime = ext4_current_time ( inode ) ;
2006-10-11 12:20:50 +04:00
inode - > i_generation = generation ;
2006-10-11 12:20:53 +04:00
err = ext4_mark_iloc_dirty ( handle , inode , & iloc ) ;
2006-10-11 12:20:50 +04:00
}
2006-10-11 12:20:53 +04:00
ext4_journal_stop ( handle ) ;
2008-02-16 01:37:46 +03:00
setversion_out :
mnt_drop_write ( filp - > f_path . mnt ) ;
2006-10-11 12:20:50 +04:00
return err ;
}
2007-07-18 16:57:06 +04:00
# ifdef CONFIG_JBD2_DEBUG
2006-10-11 12:20:53 +04:00
case EXT4_IOC_WAIT_FOR_READONLY :
2006-10-11 12:20:50 +04:00
/*
* This is racy - by the time we ' re woken up and running ,
* the superblock could be released . And the module could
* have been unloaded . So sue me .
*
* Returns 1 if it slept , else zero .
*/
{
struct super_block * sb = inode - > i_sb ;
DECLARE_WAITQUEUE ( wait , current ) ;
int ret = 0 ;
set_current_state ( TASK_INTERRUPTIBLE ) ;
2006-10-11 12:20:53 +04:00
add_wait_queue ( & EXT4_SB ( sb ) - > ro_wait_queue , & wait ) ;
if ( timer_pending ( & EXT4_SB ( sb ) - > turn_ro_timer ) ) {
2006-10-11 12:20:50 +04:00
schedule ( ) ;
ret = 1 ;
}
2006-10-11 12:20:53 +04:00
remove_wait_queue ( & EXT4_SB ( sb ) - > ro_wait_queue , & wait ) ;
2006-10-11 12:20:50 +04:00
return ret ;
}
# endif
2006-10-11 12:20:53 +04:00
case EXT4_IOC_GROUP_EXTEND : {
ext4_fsblk_t n_blocks_count ;
2006-10-11 12:20:50 +04:00
struct super_block * sb = inode - > i_sb ;
2009-07-13 17:30:17 +04:00
int err , err2 = 0 ;
2006-10-11 12:20:50 +04:00
2011-07-27 05:35:44 +04:00
err = ext4_resize_begin ( sb ) ;
if ( err )
return err ;
2006-10-11 12:20:50 +04:00
if ( get_user ( n_blocks_count , ( __u32 __user * ) arg ) )
return - EFAULT ;
2008-02-16 01:37:46 +03:00
err = mnt_want_write ( filp - > f_path . mnt ) ;
if ( err )
return err ;
2006-10-11 12:20:53 +04:00
err = ext4_group_extend ( sb , EXT4_SB ( sb ) - > s_es , n_blocks_count ) ;
2009-07-13 17:30:17 +04:00
if ( EXT4_SB ( sb ) - > s_journal ) {
jbd2_journal_lock_updates ( EXT4_SB ( sb ) - > s_journal ) ;
err2 = jbd2_journal_flush ( EXT4_SB ( sb ) - > s_journal ) ;
jbd2_journal_unlock_updates ( EXT4_SB ( sb ) - > s_journal ) ;
}
2008-10-11 04:29:21 +04:00
if ( err = = 0 )
err = err2 ;
2008-02-16 01:37:46 +03:00
mnt_drop_write ( filp - > f_path . mnt ) ;
2011-07-27 05:35:44 +04:00
ext4_resize_end ( sb ) ;
2006-10-11 12:20:50 +04:00
return err ;
}
2009-06-18 03:24:03 +04:00
case EXT4_IOC_MOVE_EXT : {
struct move_extent me ;
struct file * donor_filp ;
int err ;
2009-12-07 07:38:31 +03:00
if ( ! ( filp - > f_mode & FMODE_READ ) | |
! ( filp - > f_mode & FMODE_WRITE ) )
return - EBADF ;
2009-06-18 03:24:03 +04:00
if ( copy_from_user ( & me ,
( struct move_extent __user * ) arg , sizeof ( me ) ) )
return - EFAULT ;
2009-12-07 07:38:31 +03:00
me . moved_len = 0 ;
2009-06-18 03:24:03 +04:00
donor_filp = fget ( me . donor_fd ) ;
if ( ! donor_filp )
return - EBADF ;
2009-12-07 07:38:31 +03:00
if ( ! ( donor_filp - > f_mode & FMODE_WRITE ) ) {
err = - EBADF ;
goto mext_out ;
2009-06-18 03:24:03 +04:00
}
2009-12-07 07:38:31 +03:00
err = mnt_want_write ( filp - > f_path . mnt ) ;
if ( err )
goto mext_out ;
2009-06-18 03:24:03 +04:00
err = ext4_move_extents ( filp , donor_filp , me . orig_start ,
me . donor_start , me . len , & me . moved_len ) ;
2009-12-07 07:38:31 +03:00
mnt_drop_write ( filp - > f_path . mnt ) ;
if ( me . moved_len > 0 )
file_remove_suid ( donor_filp ) ;
2009-06-18 03:24:03 +04:00
2010-05-17 15:00:00 +04:00
if ( copy_to_user ( ( struct move_extent __user * ) arg ,
2010-03-04 08:39:24 +03:00
& me , sizeof ( me ) ) )
2009-12-07 07:38:31 +03:00
err = - EFAULT ;
mext_out :
fput ( donor_filp ) ;
2009-06-18 03:24:03 +04:00
return err ;
}
2006-10-11 12:20:53 +04:00
case EXT4_IOC_GROUP_ADD : {
struct ext4_new_group_data input ;
2006-10-11 12:20:50 +04:00
struct super_block * sb = inode - > i_sb ;
2009-07-13 17:30:17 +04:00
int err , err2 = 0 ;
2006-10-11 12:20:50 +04:00
2011-07-27 05:35:44 +04:00
err = ext4_resize_begin ( sb ) ;
if ( err )
return err ;
2006-10-11 12:20:50 +04:00
2006-10-11 12:20:53 +04:00
if ( copy_from_user ( & input , ( struct ext4_new_group_input __user * ) arg ,
2006-10-11 12:20:50 +04:00
sizeof ( input ) ) )
return - EFAULT ;
2008-02-16 01:37:46 +03:00
err = mnt_want_write ( filp - > f_path . mnt ) ;
if ( err )
return err ;
2006-10-11 12:20:53 +04:00
err = ext4_group_add ( sb , & input ) ;
2009-07-13 17:30:17 +04:00
if ( EXT4_SB ( sb ) - > s_journal ) {
jbd2_journal_lock_updates ( EXT4_SB ( sb ) - > s_journal ) ;
err2 = jbd2_journal_flush ( EXT4_SB ( sb ) - > s_journal ) ;
jbd2_journal_unlock_updates ( EXT4_SB ( sb ) - > s_journal ) ;
}
2008-10-11 04:29:21 +04:00
if ( err = = 0 )
err = err2 ;
2008-02-16 01:37:46 +03:00
mnt_drop_write ( filp - > f_path . mnt ) ;
2011-07-27 05:35:44 +04:00
ext4_resize_end ( sb ) ;
2006-10-11 12:20:50 +04:00
return err ;
}
2008-01-29 07:58:26 +03:00
case EXT4_IOC_MIGRATE :
2008-09-13 20:52:26 +04:00
{
int err ;
2011-03-24 02:43:26 +03:00
if ( ! inode_owner_or_capable ( inode ) )
2008-09-13 20:52:26 +04:00
return - EACCES ;
err = mnt_want_write ( filp - > f_path . mnt ) ;
if ( err )
return err ;
/*
* inode_mutex prevent write and truncate on the file .
* Read still goes through . We take i_data_sem in
* ext4_ext_swap_inode_data before we switch the
* inode format to prevent read .
*/
mutex_lock ( & ( inode - > i_mutex ) ) ;
err = ext4_ext_migrate ( inode ) ;
mutex_unlock ( & ( inode - > i_mutex ) ) ;
mnt_drop_write ( filp - > f_path . mnt ) ;
return err ;
}
2008-01-29 07:58:26 +03:00
2009-02-26 09:04:07 +03:00
case EXT4_IOC_ALLOC_DA_BLKS :
{
int err ;
2011-03-24 02:43:26 +03:00
if ( ! inode_owner_or_capable ( inode ) )
2009-02-26 09:04:07 +03:00
return - EACCES ;
err = mnt_want_write ( filp - > f_path . mnt ) ;
if ( err )
return err ;
err = ext4_alloc_da_blocks ( inode ) ;
mnt_drop_write ( filp - > f_path . mnt ) ;
return err ;
}
2010-11-20 05:47:07 +03:00
case FITRIM :
{
struct super_block * sb = inode - > i_sb ;
2011-02-23 20:42:32 +03:00
struct request_queue * q = bdev_get_queue ( sb - > s_bdev ) ;
2010-11-20 05:47:07 +03:00
struct fstrim_range range ;
int ret = 0 ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
2011-02-23 20:42:32 +03:00
if ( ! blk_queue_discard ( q ) )
return - EOPNOTSUPP ;
2010-11-20 05:47:07 +03:00
if ( copy_from_user ( & range , ( struct fstrim_range * ) arg ,
sizeof ( range ) ) )
return - EFAULT ;
2011-02-24 01:49:51 +03:00
range . minlen = max ( ( unsigned int ) range . minlen ,
q - > limits . discard_granularity ) ;
2010-11-20 05:47:07 +03:00
ret = ext4_trim_fs ( sb , & range ) ;
if ( ret < 0 )
return ret ;
if ( copy_to_user ( ( struct fstrim_range * ) arg , & range ,
sizeof ( range ) ) )
return - EFAULT ;
return 0 ;
}
2006-10-11 12:20:50 +04:00
default :
return - ENOTTY ;
}
}
# ifdef CONFIG_COMPAT
2006-10-11 12:20:53 +04:00
long ext4_compat_ioctl ( struct file * file , unsigned int cmd , unsigned long arg )
2006-10-11 12:20:50 +04:00
{
/* These are just misnamed, they actually get/put from/to user an int */
switch ( cmd ) {
2006-10-11 12:20:53 +04:00
case EXT4_IOC32_GETFLAGS :
cmd = EXT4_IOC_GETFLAGS ;
2006-10-11 12:20:50 +04:00
break ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC32_SETFLAGS :
cmd = EXT4_IOC_SETFLAGS ;
2006-10-11 12:20:50 +04:00
break ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC32_GETVERSION :
cmd = EXT4_IOC_GETVERSION ;
2006-10-11 12:20:50 +04:00
break ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC32_SETVERSION :
cmd = EXT4_IOC_SETVERSION ;
2006-10-11 12:20:50 +04:00
break ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC32_GROUP_EXTEND :
cmd = EXT4_IOC_GROUP_EXTEND ;
2006-10-11 12:20:50 +04:00
break ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC32_GETVERSION_OLD :
cmd = EXT4_IOC_GETVERSION_OLD ;
2006-10-11 12:20:50 +04:00
break ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC32_SETVERSION_OLD :
cmd = EXT4_IOC_SETVERSION_OLD ;
2006-10-11 12:20:50 +04:00
break ;
2007-07-18 16:57:06 +04:00
# ifdef CONFIG_JBD2_DEBUG
2006-10-11 12:20:53 +04:00
case EXT4_IOC32_WAIT_FOR_READONLY :
cmd = EXT4_IOC_WAIT_FOR_READONLY ;
2006-10-11 12:20:50 +04:00
break ;
# endif
2006-10-11 12:20:53 +04:00
case EXT4_IOC32_GETRSVSZ :
cmd = EXT4_IOC_GETRSVSZ ;
2006-10-11 12:20:50 +04:00
break ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC32_SETRSVSZ :
cmd = EXT4_IOC_SETRSVSZ ;
2006-10-11 12:20:50 +04:00
break ;
2010-05-17 14:00:00 +04:00
case EXT4_IOC32_GROUP_ADD : {
struct compat_ext4_new_group_input __user * uinput ;
struct ext4_new_group_input input ;
mm_segment_t old_fs ;
int err ;
uinput = compat_ptr ( arg ) ;
err = get_user ( input . group , & uinput - > group ) ;
err | = get_user ( input . block_bitmap , & uinput - > block_bitmap ) ;
err | = get_user ( input . inode_bitmap , & uinput - > inode_bitmap ) ;
err | = get_user ( input . inode_table , & uinput - > inode_table ) ;
err | = get_user ( input . blocks_count , & uinput - > blocks_count ) ;
err | = get_user ( input . reserved_blocks ,
& uinput - > reserved_blocks ) ;
if ( err )
return - EFAULT ;
old_fs = get_fs ( ) ;
set_fs ( KERNEL_DS ) ;
err = ext4_ioctl ( file , EXT4_IOC_GROUP_ADD ,
( unsigned long ) & input ) ;
set_fs ( old_fs ) ;
return err ;
}
2010-05-15 08:00:00 +04:00
case EXT4_IOC_MOVE_EXT :
2011-03-21 06:16:58 +03:00
case FITRIM :
2010-05-15 08:00:00 +04:00
break ;
2006-10-11 12:20:50 +04:00
default :
return - ENOIOCTLCMD ;
}
2008-04-30 06:03:54 +04:00
return ext4_ioctl ( file , cmd , ( unsigned long ) compat_ptr ( arg ) ) ;
2006-10-11 12:20:50 +04:00
}
# endif