2005-04-17 02:20:36 +04:00
/*
* linux / fs / ioctl . c
*
* Copyright ( C ) 1991 , 1992 Linus Torvalds
*/
# 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>
2008-02-07 11:13:23 +03:00
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
# include <asm/ioctls.h>
2008-02-07 11:13:25 +03:00
/**
* vfs_ioctl - call filesystem specific ioctl methods
2008-02-09 11:10:16 +03:00
* @ filp : open file to invoke ioctl method on
* @ cmd : ioctl command to execute
* @ arg : command - specific argument for ioctl
2008-02-07 11:13:25 +03:00
*
* Invokes filesystem specific - > unlocked_ioctl , if one exists ; otherwise
2008-02-09 11:10:16 +03:00
* invokes filesystem specific - > ioctl method . If neither method exists ,
2008-02-07 11:13:25 +03:00
* returns - ENOTTY .
*
* Returns 0 on success , - errno on error .
*/
2008-04-29 11:58:55 +04:00
static long vfs_ioctl ( struct file * filp , unsigned int cmd ,
unsigned long arg )
2005-04-17 02:20:36 +04:00
{
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 ;
2007-07-16 10:41:02 +04:00
} else if ( filp - > f_op - > ioctl ) {
2005-04-17 02:20:36 +04:00
lock_kernel ( ) ;
2007-07-16 10:41:02 +04:00
error = filp - > f_op - > ioctl ( filp - > f_path . dentry - > d_inode ,
filp , cmd , arg ) ;
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
}
out :
return error ;
}
2008-02-07 11:13:25 +03:00
static int ioctl_fibmap ( struct file * filp , int __user * p )
{
struct address_space * mapping = filp - > f_mapping ;
int res , block ;
/* do we support this mess? */
if ( ! mapping - > a_ops - > bmap )
return - EINVAL ;
if ( ! capable ( CAP_SYS_RAWIO ) )
return - EPERM ;
res = get_user ( block , p ) ;
if ( res )
return res ;
lock_kernel ( ) ;
res = mapping - > a_ops - > bmap ( mapping , block ) ;
unlock_kernel ( ) ;
return put_user ( res , p ) ;
}
2005-04-17 02:20:36 +04:00
static int file_ioctl ( struct file * filp , unsigned int cmd ,
unsigned long arg )
{
2008-02-07 11:13:23 +03:00
struct inode * inode = filp - > f_path . dentry - > d_inode ;
2005-04-17 02:20:36 +04:00
int __user * p = ( int __user * ) arg ;
switch ( cmd ) {
2008-02-07 11:13:23 +03:00
case FIBMAP :
2008-02-07 11:13:25 +03:00
return ioctl_fibmap ( filp , p ) ;
2008-02-07 11:13:23 +03:00
case FIGETBSZ :
return put_user ( inode - > i_sb - > s_blocksize , p ) ;
case FIONREAD :
return put_user ( i_size_read ( inode ) - filp - > f_pos , p ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:13:25 +03:00
return vfs_ioctl ( filp , cmd , arg ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:13:25 +03:00
static int ioctl_fionbio ( struct file * filp , int __user * argp )
{
unsigned int flag ;
int on , error ;
error = get_user ( on , argp ) ;
if ( error )
return error ;
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 ;
return error ;
}
static int ioctl_fioasync ( unsigned int fd , struct file * filp ,
int __user * argp )
{
unsigned int flag ;
int on , error ;
error = get_user ( on , argp ) ;
if ( error )
return error ;
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 )
return error ;
if ( on )
filp - > f_flags | = FASYNC ;
else
filp - > f_flags & = ~ FASYNC ;
return error ;
}
2005-04-17 02:20:36 +04:00
/*
* When you add any new common ioctls to the switches above and below
* please update compat_sys_ioctl ( ) too .
*
2008-02-07 11:13:25 +03:00
* do_vfs_ioctl ( ) is not for drivers and not intended to be EXPORT_SYMBOL ( ) ' d .
2005-04-17 02:20:36 +04:00
* It ' s just a simple helper for sys_ioctl and compat_sys_ioctl .
*/
2008-02-07 11:13:25 +03:00
int do_vfs_ioctl ( struct file * filp , unsigned int fd , unsigned int cmd ,
unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:13:25 +03:00
int error = 0 ;
int __user * argp = ( int __user * ) arg ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
2008-02-07 11:13:23 +03:00
case FIOCLEX :
set_close_on_exec ( fd , 1 ) ;
break ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:13:23 +03:00
case FIONCLEX :
set_close_on_exec ( fd , 0 ) ;
break ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:13:23 +03:00
case FIONBIO :
2008-02-07 11:13:25 +03:00
error = ioctl_fionbio ( filp , argp ) ;
2008-02-07 11:13:23 +03:00
break ;
case FIOASYNC :
2008-02-07 11:13:25 +03:00
error = ioctl_fioasync ( fd , filp , argp ) ;
2008-02-07 11:13:23 +03:00
break ;
case FIOQSIZE :
if ( S_ISDIR ( filp - > f_path . dentry - > d_inode - > i_mode ) | |
S_ISREG ( filp - > f_path . dentry - > d_inode - > i_mode ) | |
S_ISLNK ( filp - > f_path . dentry - > d_inode - > i_mode ) ) {
loff_t res =
inode_get_bytes ( filp - > f_path . 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_path . dentry - > d_inode - > i_mode ) )
error = file_ioctl ( filp , cmd , arg ) ;
else
2008-02-07 11:13:25 +03:00
error = vfs_ioctl ( filp , cmd , arg ) ;
2008-02-07 11:13:23 +03:00
break ;
2005-04-17 02:20:36 +04:00
}
return error ;
}
asmlinkage long sys_ioctl ( unsigned int fd , unsigned int cmd , unsigned long arg )
{
2008-02-07 11:13:23 +03:00
struct file * filp ;
2005-04-17 02:20:36 +04:00
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 ;
2008-02-07 11:13:25 +03:00
error = do_vfs_ioctl ( filp , fd , cmd , arg ) ;
2005-04-17 02:20:36 +04:00
out_fput :
fput_light ( filp , fput_needed ) ;
out :
return error ;
}