2005-04-17 02:20:36 +04:00
/*
* linux / fs / ext2 / 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 "ext2.h"
2006-01-11 23:17:46 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/time.h>
# include <linux/sched.h>
2006-08-29 22:06:20 +04:00
# include <linux/compat.h>
2008-02-16 01:37:46 +03:00
# include <linux/mount.h>
2006-08-29 22:06:20 +04:00
# include <linux/smp_lock.h>
2005-04-17 02:20:36 +04:00
# include <asm/current.h>
# include <asm/uaccess.h>
2008-02-06 12:40:10 +03:00
long ext2_ioctl ( struct file * filp , unsigned int cmd , unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2008-02-06 12:40:10 +03:00
struct inode * inode = filp - > f_dentry - > d_inode ;
2005-04-17 02:20:36 +04:00
struct ext2_inode_info * ei = EXT2_I ( inode ) ;
unsigned int flags ;
2007-10-17 10:30:46 +04:00
unsigned short rsv_window_size ;
2008-02-16 01:37:46 +03:00
int ret ;
2005-04-17 02:20:36 +04:00
ext2_debug ( " cmd = %u, arg = %lu \n " , cmd , arg ) ;
switch ( cmd ) {
case EXT2_IOC_GETFLAGS :
2007-05-08 11:31:04 +04:00
ext2_get_inode_flags ( ei ) ;
2005-04-17 02:20:36 +04:00
flags = ei - > i_flags & EXT2_FL_USER_VISIBLE ;
return put_user ( flags , ( int __user * ) arg ) ;
case EXT2_IOC_SETFLAGS : {
unsigned int oldflags ;
2008-02-16 01:37:46 +03:00
ret = mnt_want_write ( filp - > f_path . mnt ) ;
if ( ret )
return ret ;
2005-04-17 02:20:36 +04:00
2008-02-16 01:37:46 +03:00
if ( ! is_owner_or_cap ( inode ) ) {
ret = - EACCES ;
goto setflags_out ;
}
2005-04-17 02:20:36 +04:00
2008-02-16 01:37:46 +03:00
if ( get_user ( flags , ( int __user * ) arg ) ) {
ret = - EFAULT ;
goto setflags_out ;
}
2005-04-17 02:20:36 +04:00
if ( ! S_ISDIR ( inode - > i_mode ) )
flags & = ~ EXT2_DIRSYNC_FL ;
2006-12-07 07:37:33 +03: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
ret = - EPERM ;
goto setflags_out ;
2007-11-15 03:58:56 +03:00
}
2005-04-17 02:20:36 +04:00
oldflags = ei - > i_flags ;
/*
* 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 ) & ( EXT2_APPEND_FL | EXT2_IMMUTABLE_FL ) ) {
2006-12-07 07:37:33 +03:00
if ( ! capable ( CAP_LINUX_IMMUTABLE ) ) {
mutex_unlock ( & inode - > i_mutex ) ;
2008-02-16 01:37:46 +03:00
ret = - EPERM ;
goto setflags_out ;
2006-12-07 07:37:33 +03:00
}
2005-04-17 02:20:36 +04:00
}
flags = flags & EXT2_FL_USER_MODIFIABLE ;
flags | = oldflags & ~ EXT2_FL_USER_MODIFIABLE ;
ei - > i_flags = flags ;
2006-12-07 07:37:33 +03:00
mutex_unlock ( & inode - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
ext2_set_inode_flags ( inode ) ;
inode - > i_ctime = CURRENT_TIME_SEC ;
mark_inode_dirty ( inode ) ;
2008-02-16 01:37:46 +03:00
setflags_out :
mnt_drop_write ( filp - > f_path . mnt ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
case EXT2_IOC_GETVERSION :
return put_user ( inode - > i_generation , ( int __user * ) arg ) ;
case EXT2_IOC_SETVERSION :
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
ret = mnt_want_write ( filp - > f_path . mnt ) ;
if ( ret )
return ret ;
if ( get_user ( inode - > i_generation , ( int __user * ) arg ) ) {
ret = - EFAULT ;
} else {
inode - > i_ctime = CURRENT_TIME_SEC ;
mark_inode_dirty ( inode ) ;
}
mnt_drop_write ( filp - > f_path . mnt ) ;
return ret ;
2007-10-17 10:30:46 +04:00
case EXT2_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 EXT2_IOC_SETRSVSZ : {
if ( ! test_opt ( inode - > i_sb , RESERVATION ) | | ! S_ISREG ( inode - > i_mode ) )
return - ENOTTY ;
2008-02-16 01:37:46 +03:00
if ( ! is_owner_or_cap ( inode ) )
2007-10-17 10:30:46 +04:00
return - EACCES ;
if ( get_user ( rsv_window_size , ( int __user * ) arg ) )
return - EFAULT ;
2008-02-16 01:37:46 +03:00
ret = mnt_want_write ( filp - > f_path . mnt ) ;
if ( ret )
return ret ;
2007-10-17 10:30:46 +04:00
if ( rsv_window_size > EXT2_MAX_RESERVE_BLOCKS )
rsv_window_size = EXT2_MAX_RESERVE_BLOCKS ;
/*
* need to allocate reservation structure for this inode
* before set the window size
*/
/*
* XXX What lock should protect the rsv_goal_size ?
* Accessed in ext2_get_block only . ext3 uses i_truncate .
*/
mutex_lock ( & ei - > truncate_mutex ) ;
if ( ! ei - > i_block_alloc_info )
ext2_init_block_alloc_info ( inode ) ;
if ( ei - > i_block_alloc_info ) {
struct ext2_reserve_window_node * rsv = & ei - > i_block_alloc_info - > rsv_window_node ;
rsv - > rsv_goal_size = rsv_window_size ;
}
mutex_unlock ( & ei - > truncate_mutex ) ;
2008-02-16 01:37:46 +03:00
mnt_drop_write ( filp - > f_path . mnt ) ;
2007-10-17 10:30:46 +04:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
default :
return - ENOTTY ;
}
}
2006-08-29 22:06:20 +04:00
# ifdef CONFIG_COMPAT
long ext2_compat_ioctl ( struct file * file , unsigned int cmd , unsigned long arg )
{
/* These are just misnamed, they actually get/put from/to user an int */
switch ( cmd ) {
case EXT2_IOC32_GETFLAGS :
cmd = EXT2_IOC_GETFLAGS ;
break ;
case EXT2_IOC32_SETFLAGS :
cmd = EXT2_IOC_SETFLAGS ;
break ;
case EXT2_IOC32_GETVERSION :
cmd = EXT2_IOC_GETVERSION ;
break ;
case EXT2_IOC32_SETVERSION :
cmd = EXT2_IOC_SETVERSION ;
break ;
default :
return - ENOIOCTLCMD ;
}
2008-02-06 12:40:10 +03:00
return ext2_ioctl ( file , cmd , ( unsigned long ) compat_ptr ( arg ) ) ;
2006-08-29 22:06:20 +04:00
}
# endif