2005-04-17 02:20:36 +04:00
/*
* linux / fs / ioctl . c
*
* Copyright ( C ) 1991 , 1992 Linus Torvalds
*/
# include <linux/config.h>
# include <linux/syscalls.h>
# include <linux/mm.h>
# include <linux/smp_lock.h>
2006-01-11 23:17:46 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/file.h>
# include <linux/fs.h>
# include <linux/security.h>
# include <linux/module.h>
# include <asm/uaccess.h>
# include <asm/ioctls.h>
static long do_ioctl ( struct file * filp , unsigned int cmd ,
unsigned long arg )
{
int error = - ENOTTY ;
if ( ! filp - > f_op )
goto out ;
if ( filp - > f_op - > unlocked_ioctl ) {
error = filp - > f_op - > unlocked_ioctl ( filp , cmd , arg ) ;
if ( error = = - ENOIOCTLCMD )
error = - EINVAL ;
goto out ;
} else if ( filp - > f_op - > ioctl ) {
lock_kernel ( ) ;
error = filp - > f_op - > ioctl ( filp - > f_dentry - > d_inode ,
filp , cmd , arg ) ;
unlock_kernel ( ) ;
}
out :
return error ;
}
static int file_ioctl ( struct file * filp , unsigned int cmd ,
unsigned long arg )
{
int error ;
int block ;
struct inode * inode = filp - > f_dentry - > d_inode ;
int __user * p = ( int __user * ) arg ;
switch ( cmd ) {
case FIBMAP :
{
struct address_space * mapping = filp - > f_mapping ;
int res ;
/* do we support this mess? */
if ( ! mapping - > a_ops - > bmap )
return - EINVAL ;
if ( ! capable ( CAP_SYS_RAWIO ) )
return - EPERM ;
if ( ( error = get_user ( block , p ) ) ! = 0 )
return error ;
lock_kernel ( ) ;
res = mapping - > a_ops - > bmap ( mapping , block ) ;
unlock_kernel ( ) ;
return put_user ( res , p ) ;
}
case FIGETBSZ :
if ( inode - > i_sb = = NULL )
return - EBADF ;
return put_user ( inode - > i_sb - > s_blocksize , p ) ;
case FIONREAD :
return put_user ( i_size_read ( inode ) - filp - > f_pos , p ) ;
}
return do_ioctl ( filp , cmd , arg ) ;
}
/*
* When you add any new common ioctls to the switches above and below
* please update compat_sys_ioctl ( ) too .
*
* vfs_ioctl ( ) is not for drivers and not intended to be EXPORT_SYMBOL ( ) ' d .
* It ' s just a simple helper for sys_ioctl and compat_sys_ioctl .
*/
int vfs_ioctl ( struct file * filp , unsigned int fd , unsigned int cmd , unsigned long arg )
{
unsigned int flag ;
int on , error = 0 ;
switch ( cmd ) {
case FIOCLEX :
set_close_on_exec ( fd , 1 ) ;
break ;
case FIONCLEX :
set_close_on_exec ( fd , 0 ) ;
break ;
case FIONBIO :
if ( ( error = get_user ( on , ( int __user * ) arg ) ) ! = 0 )
break ;
flag = O_NONBLOCK ;
# ifdef __sparc__
/* SunOS compatibility item. */
if ( O_NONBLOCK ! = O_NDELAY )
flag | = O_NDELAY ;
# endif
if ( on )
filp - > f_flags | = flag ;
else
filp - > f_flags & = ~ flag ;
break ;
case FIOASYNC :
if ( ( error = get_user ( on , ( int __user * ) arg ) ) ! = 0 )
break ;
flag = on ? FASYNC : 0 ;
/* Did FASYNC state change ? */
if ( ( flag ^ filp - > f_flags ) & FASYNC ) {
if ( filp - > f_op & & filp - > f_op - > fasync ) {
lock_kernel ( ) ;
error = filp - > f_op - > fasync ( fd , filp , on ) ;
unlock_kernel ( ) ;
}
else error = - ENOTTY ;
}
if ( error ! = 0 )
break ;
if ( on )
filp - > f_flags | = FASYNC ;
else
filp - > f_flags & = ~ FASYNC ;
break ;
case FIOQSIZE :
if ( S_ISDIR ( filp - > f_dentry - > d_inode - > i_mode ) | |
S_ISREG ( filp - > f_dentry - > d_inode - > i_mode ) | |
S_ISLNK ( filp - > f_dentry - > d_inode - > i_mode ) ) {
loff_t res = inode_get_bytes ( filp - > f_dentry - > d_inode ) ;
error = copy_to_user ( ( loff_t __user * ) arg , & res , sizeof ( res ) ) ? - EFAULT : 0 ;
}
else
error = - ENOTTY ;
break ;
default :
if ( S_ISREG ( filp - > f_dentry - > d_inode - > i_mode ) )
error = file_ioctl ( filp , cmd , arg ) ;
else
error = do_ioctl ( filp , cmd , arg ) ;
break ;
}
return error ;
}
asmlinkage long sys_ioctl ( unsigned int fd , unsigned int cmd , unsigned long arg )
{
struct file * filp ;
int error = - EBADF ;
int fput_needed ;
filp = fget_light ( fd , & fput_needed ) ;
if ( ! filp )
goto out ;
error = security_file_ioctl ( filp , cmd , arg ) ;
if ( error )
goto out_fput ;
error = vfs_ioctl ( filp , fd , cmd , arg ) ;
out_fput :
fput_light ( filp , fput_needed ) ;
out :
return error ;
}
/*
* Platforms implementing 32 bit compatibility ioctl handlers in
* modules need this exported
*/
# ifdef CONFIG_COMPAT
EXPORT_SYMBOL ( sys_ioctl ) ;
# endif