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>
# 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 :
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 ;
if ( IS_RDONLY ( inode ) )
return - EROFS ;
if ( ( current - > fsuid ! = inode - > i_uid ) & & ! capable ( CAP_FOWNER ) )
return - EACCES ;
if ( get_user ( flags , ( int __user * ) arg ) )
return - EFAULT ;
if ( ! S_ISDIR ( inode - > i_mode ) )
flags & = ~ EXT3_DIRSYNC_FL ;
2006-04-26 10:32:40 +04:00
mutex_lock ( & inode - > i_mutex ) ;
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 ) ;
2005-04-17 02:20:36 +04:00
return - EPERM ;
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 ) ;
2005-04-17 02:20:36 +04:00
return - EPERM ;
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 ) ;
2005-04-17 02:20:36 +04:00
return PTR_ERR ( handle ) ;
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 ) ;
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 ;
if ( ( current - > fsuid ! = inode - > i_uid ) & & ! capable ( CAP_FOWNER ) )
return - EPERM ;
if ( IS_RDONLY ( inode ) )
return - EROFS ;
if ( get_user ( generation , ( int __user * ) arg ) )
return - EFAULT ;
handle = ext3_journal_start ( inode , 1 ) ;
if ( IS_ERR ( handle ) )
return PTR_ERR ( handle ) ;
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 ) ;
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 : {
if ( ! test_opt ( inode - > i_sb , RESERVATION ) | | ! S_ISREG ( inode - > i_mode ) )
return - ENOTTY ;
if ( IS_RDONLY ( inode ) )
return - EROFS ;
if ( ( current - > fsuid ! = inode - > i_uid ) & & ! capable ( CAP_FOWNER ) )
return - EACCES ;
if ( get_user ( rsv_window_size , ( int __user * ) arg ) )
return - EFAULT ;
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 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
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 ;
int err ;
if ( ! capable ( CAP_SYS_RESOURCE ) )
return - EPERM ;
if ( IS_RDONLY ( inode ) )
return - EROFS ;
if ( get_user ( n_blocks_count , ( __u32 __user * ) arg ) )
return - EFAULT ;
err = ext3_group_extend ( sb , EXT3_SB ( sb ) - > s_es , n_blocks_count ) ;
journal_lock_updates ( EXT3_SB ( sb ) - > s_journal ) ;
journal_flush ( EXT3_SB ( sb ) - > s_journal ) ;
journal_unlock_updates ( EXT3_SB ( sb ) - > s_journal ) ;
return err ;
}
case EXT3_IOC_GROUP_ADD : {
struct ext3_new_group_data input ;
struct super_block * sb = inode - > i_sb ;
int err ;
if ( ! capable ( CAP_SYS_RESOURCE ) )
return - EPERM ;
if ( IS_RDONLY ( inode ) )
return - EROFS ;
if ( copy_from_user ( & input , ( struct ext3_new_group_input __user * ) arg ,
sizeof ( input ) ) )
return - EFAULT ;
err = ext3_group_add ( sb , & input ) ;
journal_lock_updates ( EXT3_SB ( sb ) - > s_journal ) ;
journal_flush ( EXT3_SB ( sb ) - > s_journal ) ;
journal_unlock_updates ( EXT3_SB ( sb ) - > s_journal ) ;
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 )
{
struct inode * inode = file - > f_dentry - > d_inode ;
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