Commit 77b90401 authored by Arnd Bergmann's avatar Arnd Bergmann

compat_ioctl: simplify the implementation

Now that both native and compat ioctl syscalls are
in the same file, a couple of simplifications can
be made, bringing the implementation closer together:

- do_vfs_ioctl(), ioctl_preallocate(), and compat_ioctl_preallocate()
  can become static, allowing the compiler to optimize better

- slightly update the coding style for consistency between
  the functions.

- rather than listing each command in two switch statements
  for the compat case, just call a single function that has
  all the common commands.

As a side-effect, FS_IOC_RESVSP/FS_IOC_RESVSP64 are now available
to x86 compat tasks, along with FS_IOC_RESVSP_32/FS_IOC_RESVSP64_32.
This is harmless for i386 emulation, and can be considered a bugfix
for x32 emulation, which never supported these in the past.
Reviewed-by: default avatarBen Hutchings <ben.hutchings@codethink.co.uk>
Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parent 2af563d0
...@@ -180,11 +180,5 @@ extern void mnt_pin_kill(struct mount *m); ...@@ -180,11 +180,5 @@ extern void mnt_pin_kill(struct mount *m);
*/ */
extern const struct dentry_operations ns_dentry_operations; extern const struct dentry_operations ns_dentry_operations;
/*
* fs/ioctl.c
*/
extern int do_vfs_ioctl(struct file *file, unsigned int fd, unsigned int cmd,
unsigned long arg);
/* direct-io.c: */ /* direct-io.c: */
int sb_init_dio_done_wq(struct super_block *sb); int sb_init_dio_done_wq(struct super_block *sb);
...@@ -467,7 +467,7 @@ EXPORT_SYMBOL(generic_block_fiemap); ...@@ -467,7 +467,7 @@ EXPORT_SYMBOL(generic_block_fiemap);
* Only the l_start, l_len and l_whence fields of the 'struct space_resv' * Only the l_start, l_len and l_whence fields of the 'struct space_resv'
* are used here, rest are ignored. * are used here, rest are ignored.
*/ */
int ioctl_preallocate(struct file *filp, int mode, void __user *argp) static int ioctl_preallocate(struct file *filp, int mode, void __user *argp)
{ {
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
struct space_resv sr; struct space_resv sr;
...@@ -495,8 +495,8 @@ int ioctl_preallocate(struct file *filp, int mode, void __user *argp) ...@@ -495,8 +495,8 @@ int ioctl_preallocate(struct file *filp, int mode, void __user *argp)
/* on ia32 l_start is on a 32-bit boundary */ /* on ia32 l_start is on a 32-bit boundary */
#if defined CONFIG_COMPAT && defined(CONFIG_X86_64) #if defined CONFIG_COMPAT && defined(CONFIG_X86_64)
/* just account for different alignment */ /* just account for different alignment */
int compat_ioctl_preallocate(struct file *file, int mode, static int compat_ioctl_preallocate(struct file *file, int mode,
struct space_resv_32 __user *argp) struct space_resv_32 __user *argp)
{ {
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
struct space_resv_32 sr; struct space_resv_32 sr;
...@@ -521,11 +521,9 @@ int compat_ioctl_preallocate(struct file *file, int mode, ...@@ -521,11 +521,9 @@ int compat_ioctl_preallocate(struct file *file, int mode,
} }
#endif #endif
static int file_ioctl(struct file *filp, unsigned int cmd, static int file_ioctl(struct file *filp, unsigned int cmd, int __user *p)
unsigned long arg)
{ {
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
int __user *p = (int __user *)arg;
switch (cmd) { switch (cmd) {
case FIBMAP: case FIBMAP:
...@@ -542,7 +540,7 @@ static int file_ioctl(struct file *filp, unsigned int cmd, ...@@ -542,7 +540,7 @@ static int file_ioctl(struct file *filp, unsigned int cmd,
return ioctl_preallocate(filp, FALLOC_FL_ZERO_RANGE, p); return ioctl_preallocate(filp, FALLOC_FL_ZERO_RANGE, p);
} }
return vfs_ioctl(filp, cmd, arg); return -ENOIOCTLCMD;
} }
static int ioctl_fionbio(struct file *filp, int __user *argp) static int ioctl_fionbio(struct file *filp, int __user *argp)
...@@ -661,53 +659,48 @@ static int ioctl_file_dedupe_range(struct file *file, ...@@ -661,53 +659,48 @@ static int ioctl_file_dedupe_range(struct file *file,
} }
/* /*
* When you add any new common ioctls to the switches above and below
* please update compat_sys_ioctl() too.
*
* do_vfs_ioctl() is not for drivers and not intended to be EXPORT_SYMBOL()'d. * do_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. * It's just a simple helper for sys_ioctl and compat_sys_ioctl.
*
* When you add any new common ioctls to the switches above and below,
* please ensure they have compatible arguments in compat mode.
*/ */
int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, static int do_vfs_ioctl(struct file *filp, unsigned int fd,
unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
int error = 0;
void __user *argp = (void __user *)arg; void __user *argp = (void __user *)arg;
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
switch (cmd) { switch (cmd) {
case FIOCLEX: case FIOCLEX:
set_close_on_exec(fd, 1); set_close_on_exec(fd, 1);
break; return 0;
case FIONCLEX: case FIONCLEX:
set_close_on_exec(fd, 0); set_close_on_exec(fd, 0);
break; return 0;
case FIONBIO: case FIONBIO:
error = ioctl_fionbio(filp, argp); return ioctl_fionbio(filp, argp);
break;
case FIOASYNC: case FIOASYNC:
error = ioctl_fioasync(fd, filp, argp); return ioctl_fioasync(fd, filp, argp);
break;
case FIOQSIZE: case FIOQSIZE:
if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode) || if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode) ||
S_ISLNK(inode->i_mode)) { S_ISLNK(inode->i_mode)) {
loff_t res = inode_get_bytes(inode); loff_t res = inode_get_bytes(inode);
error = copy_to_user(argp, &res, sizeof(res)) ? return copy_to_user(argp, &res, sizeof(res)) ?
-EFAULT : 0; -EFAULT : 0;
} else }
error = -ENOTTY;
break; return -ENOTTY;
case FIFREEZE: case FIFREEZE:
error = ioctl_fsfreeze(filp); return ioctl_fsfreeze(filp);
break;
case FITHAW: case FITHAW:
error = ioctl_fsthaw(filp); return ioctl_fsthaw(filp);
break;
case FS_IOC_FIEMAP: case FS_IOC_FIEMAP:
return ioctl_fiemap(filp, argp); return ioctl_fiemap(filp, argp);
...@@ -716,6 +709,7 @@ int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, ...@@ -716,6 +709,7 @@ int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
/* anon_bdev filesystems may not have a block size */ /* anon_bdev filesystems may not have a block size */
if (!inode->i_sb->s_blocksize) if (!inode->i_sb->s_blocksize)
return -EINVAL; return -EINVAL;
return put_user(inode->i_sb->s_blocksize, (int __user *)argp); return put_user(inode->i_sb->s_blocksize, (int __user *)argp);
case FICLONE: case FICLONE:
...@@ -729,24 +723,30 @@ int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, ...@@ -729,24 +723,30 @@ int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
default: default:
if (S_ISREG(inode->i_mode)) if (S_ISREG(inode->i_mode))
error = file_ioctl(filp, cmd, arg); return file_ioctl(filp, cmd, argp);
else
error = vfs_ioctl(filp, cmd, arg);
break; break;
} }
return error;
return -ENOIOCTLCMD;
} }
int ksys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) int ksys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
{ {
int error;
struct fd f = fdget(fd); struct fd f = fdget(fd);
int error;
if (!f.file) if (!f.file)
return -EBADF; return -EBADF;
error = security_file_ioctl(f.file, cmd, arg); error = security_file_ioctl(f.file, cmd, arg);
if (!error) if (error)
error = do_vfs_ioctl(f.file, fd, cmd, arg); goto out;
error = do_vfs_ioctl(f.file, fd, cmd, arg);
if (error == -ENOIOCTLCMD)
error = vfs_ioctl(f.file, cmd, arg);
out:
fdput(f); fdput(f);
return error; return error;
} }
...@@ -790,92 +790,63 @@ long compat_ptr_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -790,92 +790,63 @@ long compat_ptr_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
EXPORT_SYMBOL(compat_ptr_ioctl); EXPORT_SYMBOL(compat_ptr_ioctl);
COMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, COMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd,
compat_ulong_t, arg32) compat_ulong_t, arg)
{ {
unsigned long arg = arg32;
struct fd f = fdget(fd); struct fd f = fdget(fd);
int error = -EBADF; int error;
if (!f.file) if (!f.file)
goto out; return -EBADF;
/* RED-PEN how should LSM module know it's handling 32bit? */ /* RED-PEN how should LSM module know it's handling 32bit? */
error = security_file_ioctl(f.file, cmd, arg); error = security_file_ioctl(f.file, cmd, arg);
if (error) if (error)
goto out_fput; goto out;
switch (cmd) { switch (cmd) {
/* these are never seen by ->ioctl(), no argument or int argument */ /* FICLONE takes an int argument, so don't use compat_ptr() */
case FIOCLEX:
case FIONCLEX:
case FIFREEZE:
case FITHAW:
case FICLONE: case FICLONE:
goto do_ioctl; error = ioctl_file_clone(f.file, arg, 0, 0, 0);
/* these are never seen by ->ioctl(), pointer argument */ break;
case FIONBIO:
case FIOASYNC:
case FIOQSIZE:
case FS_IOC_FIEMAP:
case FIGETBSZ:
case FICLONERANGE:
case FIDEDUPERANGE:
goto found_handler;
/*
* The next group is the stuff handled inside file_ioctl().
* For regular files these never reach ->ioctl(); for
* devices, sockets, etc. they do and one (FIONREAD) is
* even accepted in some cases. In all those cases
* argument has the same type, so we can handle these
* here, shunting them towards do_vfs_ioctl().
* ->compat_ioctl() will never see any of those.
*/
/* pointer argument, never actually handled by ->ioctl() */
case FIBMAP:
goto found_handler;
/* handled by some ->ioctl(); always a pointer to int */
case FIONREAD:
goto found_handler;
/* these get messy on amd64 due to alignment differences */
#if defined(CONFIG_X86_64) #if defined(CONFIG_X86_64)
/* these get messy on amd64 due to alignment differences */
case FS_IOC_RESVSP_32: case FS_IOC_RESVSP_32:
case FS_IOC_RESVSP64_32: case FS_IOC_RESVSP64_32:
error = compat_ioctl_preallocate(f.file, 0, compat_ptr(arg)); error = compat_ioctl_preallocate(f.file, 0, compat_ptr(arg));
goto out_fput; break;
case FS_IOC_UNRESVSP_32: case FS_IOC_UNRESVSP_32:
case FS_IOC_UNRESVSP64_32: case FS_IOC_UNRESVSP64_32:
error = compat_ioctl_preallocate(f.file, FALLOC_FL_PUNCH_HOLE, error = compat_ioctl_preallocate(f.file, FALLOC_FL_PUNCH_HOLE,
compat_ptr(arg)); compat_ptr(arg));
goto out_fput; break;
case FS_IOC_ZERO_RANGE_32: case FS_IOC_ZERO_RANGE_32:
error = compat_ioctl_preallocate(f.file, FALLOC_FL_ZERO_RANGE, error = compat_ioctl_preallocate(f.file, FALLOC_FL_ZERO_RANGE,
compat_ptr(arg)); compat_ptr(arg));
goto out_fput; break;
#else
case FS_IOC_RESVSP:
case FS_IOC_RESVSP64:
case FS_IOC_UNRESVSP:
case FS_IOC_UNRESVSP64:
case FS_IOC_ZERO_RANGE:
goto found_handler;
#endif #endif
/*
* everything else in do_vfs_ioctl() takes either a compatible
* pointer argument or no argument -- call it with a modified
* argument.
*/
default: default:
if (f.file->f_op->compat_ioctl) { error = do_vfs_ioctl(f.file, fd, cmd,
(unsigned long)compat_ptr(arg));
if (error != -ENOIOCTLCMD)
break;
if (f.file->f_op->compat_ioctl)
error = f.file->f_op->compat_ioctl(f.file, cmd, arg); error = f.file->f_op->compat_ioctl(f.file, cmd, arg);
if (error != -ENOIOCTLCMD) if (error == -ENOIOCTLCMD)
goto out_fput; error = -ENOTTY;
} break;
error = -ENOTTY;
goto out_fput;
} }
found_handler:
arg = (unsigned long)compat_ptr(arg);
do_ioctl:
error = do_vfs_ioctl(f.file, fd, cmd, arg);
out_fput:
fdput(f);
out: out:
fdput(f);
return error; return error;
} }
#endif #endif
...@@ -51,8 +51,6 @@ struct space_resv_32 { ...@@ -51,8 +51,6 @@ struct space_resv_32 {
#define FS_IOC_UNRESVSP64_32 _IOW ('X', 43, struct space_resv_32) #define FS_IOC_UNRESVSP64_32 _IOW ('X', 43, struct space_resv_32)
#define FS_IOC_ZERO_RANGE_32 _IOW ('X', 57, struct space_resv_32) #define FS_IOC_ZERO_RANGE_32 _IOW ('X', 57, struct space_resv_32)
int compat_ioctl_preallocate(struct file *, int, struct space_resv_32 __user *);
#endif #endif
#endif /* _FALLOC_H_ */ #endif /* _FALLOC_H_ */
...@@ -2552,10 +2552,6 @@ extern int finish_open(struct file *file, struct dentry *dentry, ...@@ -2552,10 +2552,6 @@ extern int finish_open(struct file *file, struct dentry *dentry,
int (*open)(struct inode *, struct file *)); int (*open)(struct inode *, struct file *));
extern int finish_no_open(struct file *file, struct dentry *dentry); extern int finish_no_open(struct file *file, struct dentry *dentry);
/* fs/ioctl.c */
extern int ioctl_preallocate(struct file *filp, int mode, void __user *argp);
/* fs/dcache.c */ /* fs/dcache.c */
extern void __init vfs_caches_init_early(void); extern void __init vfs_caches_init_early(void);
extern void __init vfs_caches_init(void); extern void __init vfs_caches_init(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment