2005-04-17 02:20:36 +04:00
/*
* linux / fs / ext3 / ioctl . c
*
* 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>
# include <linux/jbd.h>
2006-01-11 23:17:46 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/ext3_fs.h>
# include <linux/ext3_jbd.h>
2008-02-16 01:37:46 +03:00
# include <linux/mount.h>
2005-04-17 02:20:36 +04:00
# include <linux/time.h>
2006-08-29 22:06:23 +04:00
# include <linux/compat.h>
# include <linux/smp_lock.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
int ext3_ioctl ( struct inode * inode , struct file * filp , unsigned int cmd ,
unsigned long arg )
{
struct ext3_inode_info * ei = EXT3_I ( inode ) ;
unsigned int flags ;
unsigned short rsv_window_size ;
ext3_debug ( " cmd = %u, arg = %lu \n " , cmd , arg ) ;
switch ( cmd ) {
case EXT3_IOC_GETFLAGS :
2007-05-08 11:30:33 +04:00
ext3_get_inode_flags ( ei ) ;
2005-04-17 02:20:36 +04:00
flags = ei - > i_flags & EXT3_FL_USER_VISIBLE ;
return put_user ( flags , ( int __user * ) arg ) ;
case EXT3_IOC_SETFLAGS : {
handle_t * handle = NULL ;
int err ;
struct ext3_iloc iloc ;
unsigned int oldflags ;
unsigned int jflag ;
2008-02-16 01:37:46 +03:00
err = mnt_want_write ( filp - > f_path . mnt ) ;
if ( err )
return err ;
2005-04-17 02:20:36 +04:00
2008-02-16 01:37:46 +03:00
if ( ! is_owner_or_cap ( inode ) ) {
err = - EACCES ;
goto flags_out ;
}
2005-04-17 02:20:36 +04:00
2008-02-16 01:37:46 +03:00
if ( get_user ( flags , ( int __user * ) arg ) ) {
err = - EFAULT ;
goto flags_out ;
}
2005-04-17 02:20:36 +04:00
if ( ! S_ISDIR ( inode - > i_mode ) )
flags & = ~ EXT3_DIRSYNC_FL ;
2006-04-26 10:32:40 +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 */
if ( IS_NOQUOTA ( inode ) ) {
mutex_unlock ( & inode - > i_mutex ) ;
2008-02-16 01:37:46 +03:00
err = - EPERM ;
goto flags_out ;
2007-11-15 03:58:56 +03:00
}
2005-04-17 02:20:36 +04:00
oldflags = ei - > i_flags ;
/* The JOURNAL_DATA flag is modifiable only by root */
jflag = flags & EXT3_JOURNAL_DATA_FL ;
/*
* The IMMUTABLE and APPEND_ONLY flags can only be changed by
* the relevant capability .
*
* This test looks nicer . Thanks to Pauline Middelink
*/
if ( ( flags ^ oldflags ) & ( EXT3_APPEND_FL | EXT3_IMMUTABLE_FL ) ) {
2006-04-26 10:32:40 +04:00
if ( ! capable ( CAP_LINUX_IMMUTABLE ) ) {
mutex_unlock ( & inode - > i_mutex ) ;
2008-02-16 01:37:46 +03:00
err = - EPERM ;
goto flags_out ;
2006-04-26 10:32:40 +04:00
}
2005-04-17 02:20:36 +04:00
}
/*
* The JOURNAL_DATA flag can only be changed by
* the relevant capability .
*/
if ( ( jflag ^ oldflags ) & ( EXT3_JOURNAL_DATA_FL ) ) {
2006-04-26 10:32:40 +04:00
if ( ! capable ( CAP_SYS_RESOURCE ) ) {
mutex_unlock ( & inode - > i_mutex ) ;
2008-02-16 01:37:46 +03:00
err = - EPERM ;
goto flags_out ;
2006-04-26 10:32:40 +04:00
}
2005-04-17 02:20:36 +04:00
}
handle = ext3_journal_start ( inode , 1 ) ;
2006-04-26 10:32:40 +04:00
if ( IS_ERR ( handle ) ) {
mutex_unlock ( & inode - > i_mutex ) ;
2008-02-16 01:37:46 +03:00
err = PTR_ERR ( handle ) ;
goto flags_out ;
2006-04-26 10:32:40 +04:00
}
2005-04-17 02:20:36 +04:00
if ( IS_SYNC ( inode ) )
handle - > h_sync = 1 ;
err = ext3_reserve_inode_write ( handle , inode , & iloc ) ;
if ( err )
goto flags_err ;
flags = flags & EXT3_FL_USER_MODIFIABLE ;
flags | = oldflags & ~ EXT3_FL_USER_MODIFIABLE ;
ei - > i_flags = flags ;
ext3_set_inode_flags ( inode ) ;
inode - > i_ctime = CURRENT_TIME_SEC ;
err = ext3_mark_iloc_dirty ( handle , inode , & iloc ) ;
flags_err :
ext3_journal_stop ( handle ) ;
2006-04-26 10:32:40 +04:00
if ( err ) {
mutex_unlock ( & inode - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
return err ;
2006-04-26 10:32:40 +04:00
}
2005-04-17 02:20:36 +04:00
if ( ( jflag ^ oldflags ) & ( EXT3_JOURNAL_DATA_FL ) )
err = ext3_change_inode_journal_flag ( inode , jflag ) ;
2006-04-26 10:32:40 +04:00
mutex_unlock ( & inode - > i_mutex ) ;
2008-02-16 01:37:46 +03:00
flags_out :
mnt_drop_write ( filp - > f_path . mnt ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
case EXT3_IOC_GETVERSION :
case EXT3_IOC_GETVERSION_OLD :
return put_user ( inode - > i_generation , ( int __user * ) arg ) ;
case EXT3_IOC_SETVERSION :
case EXT3_IOC_SETVERSION_OLD : {
handle_t * handle ;
struct ext3_iloc iloc ;
__u32 generation ;
int err ;
2007-07-17 13:30:08 +04:00
if ( ! is_owner_or_cap ( inode ) )
2005-04-17 02:20:36 +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 ;
}
2005-04-17 02:20:36 +04:00
handle = ext3_journal_start ( inode , 1 ) ;
2008-02-16 01:37:46 +03:00
if ( IS_ERR ( handle ) ) {
err = PTR_ERR ( handle ) ;
goto setversion_out ;
}
2005-04-17 02:20:36 +04:00
err = ext3_reserve_inode_write ( handle , inode , & iloc ) ;
if ( err = = 0 ) {
inode - > i_ctime = CURRENT_TIME_SEC ;
inode - > i_generation = generation ;
err = ext3_mark_iloc_dirty ( handle , inode , & iloc ) ;
}
ext3_journal_stop ( handle ) ;
2008-02-16 01:37:46 +03:00
setversion_out :
mnt_drop_write ( filp - > f_path . mnt ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
# ifdef CONFIG_JBD_DEBUG
case EXT3_IOC_WAIT_FOR_READONLY :
/*
* 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 ) ;
add_wait_queue ( & EXT3_SB ( sb ) - > ro_wait_queue , & wait ) ;
if ( timer_pending ( & EXT3_SB ( sb ) - > turn_ro_timer ) ) {
schedule ( ) ;
ret = 1 ;
}
remove_wait_queue ( & EXT3_SB ( sb ) - > ro_wait_queue , & wait ) ;
return ret ;
}
# endif
case EXT3_IOC_GETRSVSZ :
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 ;
case EXT3_IOC_SETRSVSZ : {
2008-02-16 01:37:46 +03:00
int err ;
2005-04-17 02:20:36 +04:00
if ( ! test_opt ( inode - > i_sb , RESERVATION ) | | ! S_ISREG ( inode - > i_mode ) )
return - ENOTTY ;
2008-02-16 01:37:46 +03:00
err = mnt_want_write ( filp - > f_path . mnt ) ;
if ( err )
return err ;
2005-04-17 02:20:36 +04:00
2008-02-16 01:37:46 +03:00
if ( ! is_owner_or_cap ( inode ) ) {
err = - EACCES ;
goto setrsvsz_out ;
}
2005-04-17 02:20:36 +04:00
2008-02-16 01:37:46 +03:00
if ( get_user ( rsv_window_size , ( int __user * ) arg ) ) {
err = - EFAULT ;
goto setrsvsz_out ;
}
2005-04-17 02:20:36 +04:00
if ( rsv_window_size > EXT3_MAX_RESERVE_BLOCKS )
rsv_window_size = EXT3_MAX_RESERVE_BLOCKS ;
/*
* need to allocate reservation structure for this inode
* before set the window size
*/
2006-03-23 14:00:42 +03:00
mutex_lock ( & ei - > truncate_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( ! ei - > i_block_alloc_info )
ext3_init_block_alloc_info ( inode ) ;
if ( ei - > i_block_alloc_info ) {
struct ext3_reserve_window_node * rsv = & ei - > i_block_alloc_info - > rsv_window_node ;
rsv - > rsv_goal_size = rsv_window_size ;
}
2006-03-23 14:00:42 +03:00
mutex_unlock ( & ei - > truncate_mutex ) ;
2008-02-16 01:37:46 +03:00
setrsvsz_out :
mnt_drop_write ( filp - > f_path . mnt ) ;
return err ;
2005-04-17 02:20:36 +04:00
}
case EXT3_IOC_GROUP_EXTEND : {
2006-06-25 16:48:07 +04:00
ext3_fsblk_t n_blocks_count ;
2005-04-17 02:20:36 +04:00
struct super_block * sb = inode - > i_sb ;
2008-10-23 01:15:01 +04:00
int err , err2 ;
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_SYS_RESOURCE ) )
return - EPERM ;
2008-02-16 01:37:46 +03:00
err = mnt_want_write ( filp - > f_path . mnt ) ;
if ( err )
return err ;
2005-04-17 02:20:36 +04:00
2008-02-16 01:37:46 +03:00
if ( get_user ( n_blocks_count , ( __u32 __user * ) arg ) ) {
err = - EFAULT ;
goto group_extend_out ;
}
2005-04-17 02:20:36 +04:00
err = ext3_group_extend ( sb , EXT3_SB ( sb ) - > s_es , n_blocks_count ) ;
journal_lock_updates ( EXT3_SB ( sb ) - > s_journal ) ;
2008-10-23 01:15:01 +04:00
err2 = journal_flush ( EXT3_SB ( sb ) - > s_journal ) ;
2005-04-17 02:20:36 +04:00
journal_unlock_updates ( EXT3_SB ( sb ) - > s_journal ) ;
2008-10-23 01:15:01 +04:00
if ( err = = 0 )
err = err2 ;
2008-02-16 01:37:46 +03:00
group_extend_out :
mnt_drop_write ( filp - > f_path . mnt ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
case EXT3_IOC_GROUP_ADD : {
struct ext3_new_group_data input ;
struct super_block * sb = inode - > i_sb ;
2008-10-23 01:15:01 +04:00
int err , err2 ;
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_SYS_RESOURCE ) )
return - EPERM ;
2008-02-16 01:37:46 +03:00
err = mnt_want_write ( filp - > f_path . mnt ) ;
if ( err )
return err ;
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( & input , ( struct ext3_new_group_input __user * ) arg ,
2008-02-16 01:37:46 +03:00
sizeof ( input ) ) ) {
err = - EFAULT ;
goto group_add_out ;
}
2005-04-17 02:20:36 +04:00
err = ext3_group_add ( sb , & input ) ;
journal_lock_updates ( EXT3_SB ( sb ) - > s_journal ) ;
2008-10-23 01:15:01 +04:00
err2 = journal_flush ( EXT3_SB ( sb ) - > s_journal ) ;
2005-04-17 02:20:36 +04:00
journal_unlock_updates ( EXT3_SB ( sb ) - > s_journal ) ;
2008-10-23 01:15:01 +04:00
if ( err = = 0 )
err = err2 ;
2008-02-16 01:37:46 +03:00
group_add_out :
mnt_drop_write ( filp - > f_path . mnt ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
default :
return - ENOTTY ;
}
}
2006-08-29 22:06:23 +04:00
# ifdef CONFIG_COMPAT
long ext3_compat_ioctl ( struct file * file , unsigned int cmd , unsigned long arg )
{
2006-12-08 13:36:38 +03:00
struct inode * inode = file - > f_path . dentry - > d_inode ;
2006-08-29 22:06:23 +04:00
int ret ;
/* These are just misnamed, they actually get/put from/to user an int */
switch ( cmd ) {
case EXT3_IOC32_GETFLAGS :
cmd = EXT3_IOC_GETFLAGS ;
break ;
case EXT3_IOC32_SETFLAGS :
cmd = EXT3_IOC_SETFLAGS ;
break ;
case EXT3_IOC32_GETVERSION :
cmd = EXT3_IOC_GETVERSION ;
break ;
case EXT3_IOC32_SETVERSION :
cmd = EXT3_IOC_SETVERSION ;
break ;
case EXT3_IOC32_GROUP_EXTEND :
cmd = EXT3_IOC_GROUP_EXTEND ;
break ;
case EXT3_IOC32_GETVERSION_OLD :
cmd = EXT3_IOC_GETVERSION_OLD ;
break ;
case EXT3_IOC32_SETVERSION_OLD :
cmd = EXT3_IOC_SETVERSION_OLD ;
break ;
# ifdef CONFIG_JBD_DEBUG
case EXT3_IOC32_WAIT_FOR_READONLY :
cmd = EXT3_IOC_WAIT_FOR_READONLY ;
break ;
# endif
case EXT3_IOC32_GETRSVSZ :
cmd = EXT3_IOC_GETRSVSZ ;
break ;
case EXT3_IOC32_SETRSVSZ :
cmd = EXT3_IOC_SETRSVSZ ;
break ;
case EXT3_IOC_GROUP_ADD :
break ;
default :
return - ENOIOCTLCMD ;
}
lock_kernel ( ) ;
ret = ext3_ioctl ( inode , file , cmd , ( unsigned long ) compat_ptr ( arg ) ) ;
unlock_kernel ( ) ;
return ret ;
}
# endif