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>
2006-10-11 12:20:53 +04:00
# include <linux/ext4_fs.h>
2006-10-11 12:21:01 +04:00
# include <linux/ext4_jbd2.h>
2006-10-11 12:20:50 +04:00
# include <linux/time.h>
# include <linux/compat.h>
# include <linux/smp_lock.h>
2008-02-16 01:37:46 +03:00
# include <linux/mount.h>
2006-10-11 12:20:50 +04:00
# include <asm/uaccess.h>
2006-10-11 12:20:53 +04:00
int ext4_ioctl ( struct inode * inode , struct file * filp , unsigned int cmd ,
2006-10-11 12:20:50 +04:00
unsigned long arg )
{
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 ;
unsigned short rsv_window_size ;
2006-10-11 12:20:53 +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 ;
int err ;
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 ;
2007-07-17 13:30:08 +04:00
if ( ! is_owner_or_cap ( 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 ;
2006-10-11 12:20:50 +04:00
if ( ! S_ISDIR ( inode - > i_mode ) )
2006-10-11 12:20:53 +04:00
flags & = ~ EXT4_DIRSYNC_FL ;
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
}
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 ) )
handle - > h_sync = 1 ;
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-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 ;
2007-07-17 13:30:08 +04:00
if ( ! is_owner_or_cap ( 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_GETRSVSZ :
2006-10-11 12:20:50 +04:00
if ( test_opt ( inode - > i_sb , RESERVATION )
& & S_ISREG ( inode - > i_mode )
& & ei - > i_block_alloc_info ) {
rsv_window_size = ei - > i_block_alloc_info - > rsv_window_node . rsv_goal_size ;
return put_user ( rsv_window_size , ( int __user * ) arg ) ;
}
return - ENOTTY ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC_SETRSVSZ : {
2008-02-16 01:37:46 +03:00
int err ;
2006-10-11 12:20:50 +04:00
if ( ! test_opt ( inode - > i_sb , RESERVATION ) | | ! S_ISREG ( inode - > i_mode ) )
return - ENOTTY ;
2007-07-17 13:30:08 +04:00
if ( ! is_owner_or_cap ( inode ) )
2006-10-11 12:20:50 +04:00
return - EACCES ;
if ( get_user ( rsv_window_size , ( 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 ;
2006-10-11 12:20:53 +04:00
if ( rsv_window_size > EXT4_MAX_RESERVE_BLOCKS )
rsv_window_size = EXT4_MAX_RESERVE_BLOCKS ;
2006-10-11 12:20:50 +04:00
/*
* need to allocate reservation structure for this inode
* before set the window size
*/
2008-01-29 07:58:26 +03:00
down_write ( & ei - > i_data_sem ) ;
2006-10-11 12:20:50 +04:00
if ( ! ei - > i_block_alloc_info )
2006-10-11 12:20:53 +04:00
ext4_init_block_alloc_info ( inode ) ;
2006-10-11 12:20:50 +04:00
if ( ei - > i_block_alloc_info ) {
2006-10-11 12:20:53 +04:00
struct ext4_reserve_window_node * rsv = & ei - > i_block_alloc_info - > rsv_window_node ;
2006-10-11 12:20:50 +04:00
rsv - > rsv_goal_size = rsv_window_size ;
}
2008-01-29 07:58:26 +03:00
up_write ( & ei - > i_data_sem ) ;
2008-02-16 01:37:46 +03:00
mnt_drop_write ( filp - > f_path . mnt ) ;
2006-10-11 12:20:50 +04:00
return 0 ;
}
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 ;
int err ;
if ( ! capable ( CAP_SYS_RESOURCE ) )
return - EPERM ;
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 ) ;
2006-10-11 12:21:01 +04:00
jbd2_journal_lock_updates ( EXT4_SB ( sb ) - > s_journal ) ;
jbd2_journal_flush ( EXT4_SB ( sb ) - > s_journal ) ;
jbd2_journal_unlock_updates ( EXT4_SB ( sb ) - > s_journal ) ;
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_GROUP_ADD : {
struct ext4_new_group_data input ;
2006-10-11 12:20:50 +04:00
struct super_block * sb = inode - > i_sb ;
int err ;
if ( ! capable ( CAP_SYS_RESOURCE ) )
return - EPERM ;
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 ) ;
2006-10-11 12:21:01 +04:00
jbd2_journal_lock_updates ( EXT4_SB ( sb ) - > s_journal ) ;
jbd2_journal_flush ( EXT4_SB ( sb ) - > s_journal ) ;
jbd2_journal_unlock_updates ( EXT4_SB ( sb ) - > s_journal ) ;
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 ;
}
2008-01-29 07:58:26 +03:00
case EXT4_IOC_MIGRATE :
return ext4_ext_migrate ( inode , filp , cmd , arg ) ;
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
{
2006-12-08 13:36:38 +03:00
struct inode * inode = file - > f_path . dentry - > d_inode ;
2006-10-11 12:20:50 +04:00
int ret ;
/* 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 ;
2006-10-11 12:20:53 +04:00
case EXT4_IOC_GROUP_ADD :
2006-10-11 12:20:50 +04:00
break ;
default :
return - ENOIOCTLCMD ;
}
lock_kernel ( ) ;
2006-10-11 12:20:53 +04:00
ret = ext4_ioctl ( inode , file , cmd , ( unsigned long ) compat_ptr ( arg ) ) ;
2006-10-11 12:20:50 +04:00
unlock_kernel ( ) ;
return ret ;
}
# endif