Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Ben Hutchings <ben.hutchings@codethink.co.uk>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>

+64 -105
-6
fs/internal.h
··· 180 180 */ 181 181 extern const struct dentry_operations ns_dentry_operations; 182 182 183 - /* 184 - * fs/ioctl.c 185 - */ 186 - extern int do_vfs_ioctl(struct file *file, unsigned int fd, unsigned int cmd, 187 - unsigned long arg); 188 - 189 183 /* direct-io.c: */ 190 184 int sb_init_dio_done_wq(struct super_block *sb);
+64 -93
fs/ioctl.c
··· 467 467 * Only the l_start, l_len and l_whence fields of the 'struct space_resv' 468 468 * are used here, rest are ignored. 469 469 */ 470 - int ioctl_preallocate(struct file *filp, int mode, void __user *argp) 470 + static int ioctl_preallocate(struct file *filp, int mode, void __user *argp) 471 471 { 472 472 struct inode *inode = file_inode(filp); 473 473 struct space_resv sr; ··· 495 495 /* on ia32 l_start is on a 32-bit boundary */ 496 496 #if defined CONFIG_COMPAT && defined(CONFIG_X86_64) 497 497 /* just account for different alignment */ 498 - int compat_ioctl_preallocate(struct file *file, int mode, 499 - struct space_resv_32 __user *argp) 498 + static int compat_ioctl_preallocate(struct file *file, int mode, 499 + struct space_resv_32 __user *argp) 500 500 { 501 501 struct inode *inode = file_inode(file); 502 502 struct space_resv_32 sr; ··· 521 521 } 522 522 #endif 523 523 524 - static int file_ioctl(struct file *filp, unsigned int cmd, 525 - unsigned long arg) 524 + static int file_ioctl(struct file *filp, unsigned int cmd, int __user *p) 526 525 { 527 526 struct inode *inode = file_inode(filp); 528 - int __user *p = (int __user *)arg; 529 527 530 528 switch (cmd) { 531 529 case FIBMAP: ··· 540 542 return ioctl_preallocate(filp, FALLOC_FL_ZERO_RANGE, p); 541 543 } 542 544 543 - return vfs_ioctl(filp, cmd, arg); 545 + return -ENOIOCTLCMD; 544 546 } 545 547 546 548 static int ioctl_fionbio(struct file *filp, int __user *argp) ··· 659 661 } 660 662 661 663 /* 662 - * When you add any new common ioctls to the switches above and below 663 - * please update compat_sys_ioctl() too. 664 - * 665 664 * do_vfs_ioctl() is not for drivers and not intended to be EXPORT_SYMBOL()'d. 666 665 * It's just a simple helper for sys_ioctl and compat_sys_ioctl. 666 + * 667 + * When you add any new common ioctls to the switches above and below, 668 + * please ensure they have compatible arguments in compat mode. 667 669 */ 668 - int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, 669 - unsigned long arg) 670 + static int do_vfs_ioctl(struct file *filp, unsigned int fd, 671 + unsigned int cmd, unsigned long arg) 670 672 { 671 - int error = 0; 672 673 void __user *argp = (void __user *)arg; 673 674 struct inode *inode = file_inode(filp); 674 675 675 676 switch (cmd) { 676 677 case FIOCLEX: 677 678 set_close_on_exec(fd, 1); 678 - break; 679 + return 0; 679 680 680 681 case FIONCLEX: 681 682 set_close_on_exec(fd, 0); 682 - break; 683 + return 0; 683 684 684 685 case FIONBIO: 685 - error = ioctl_fionbio(filp, argp); 686 - break; 686 + return ioctl_fionbio(filp, argp); 687 687 688 688 case FIOASYNC: 689 - error = ioctl_fioasync(fd, filp, argp); 690 - break; 689 + return ioctl_fioasync(fd, filp, argp); 691 690 692 691 case FIOQSIZE: 693 692 if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode) || 694 693 S_ISLNK(inode->i_mode)) { 695 694 loff_t res = inode_get_bytes(inode); 696 - error = copy_to_user(argp, &res, sizeof(res)) ? 697 - -EFAULT : 0; 698 - } else 699 - error = -ENOTTY; 700 - break; 695 + return copy_to_user(argp, &res, sizeof(res)) ? 696 + -EFAULT : 0; 697 + } 698 + 699 + return -ENOTTY; 701 700 702 701 case FIFREEZE: 703 - error = ioctl_fsfreeze(filp); 704 - break; 702 + return ioctl_fsfreeze(filp); 705 703 706 704 case FITHAW: 707 - error = ioctl_fsthaw(filp); 708 - break; 705 + return ioctl_fsthaw(filp); 709 706 710 707 case FS_IOC_FIEMAP: 711 708 return ioctl_fiemap(filp, argp); ··· 709 716 /* anon_bdev filesystems may not have a block size */ 710 717 if (!inode->i_sb->s_blocksize) 711 718 return -EINVAL; 719 + 712 720 return put_user(inode->i_sb->s_blocksize, (int __user *)argp); 713 721 714 722 case FICLONE: ··· 723 729 724 730 default: 725 731 if (S_ISREG(inode->i_mode)) 726 - error = file_ioctl(filp, cmd, arg); 727 - else 728 - error = vfs_ioctl(filp, cmd, arg); 732 + return file_ioctl(filp, cmd, argp); 729 733 break; 730 734 } 731 - return error; 735 + 736 + return -ENOIOCTLCMD; 732 737 } 733 738 734 739 int ksys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) 735 740 { 736 - int error; 737 741 struct fd f = fdget(fd); 742 + int error; 738 743 739 744 if (!f.file) 740 745 return -EBADF; 746 + 741 747 error = security_file_ioctl(f.file, cmd, arg); 742 - if (!error) 743 - error = do_vfs_ioctl(f.file, fd, cmd, arg); 748 + if (error) 749 + goto out; 750 + 751 + error = do_vfs_ioctl(f.file, fd, cmd, arg); 752 + if (error == -ENOIOCTLCMD) 753 + error = vfs_ioctl(f.file, cmd, arg); 754 + 755 + out: 744 756 fdput(f); 745 757 return error; 746 758 } ··· 790 790 EXPORT_SYMBOL(compat_ptr_ioctl); 791 791 792 792 COMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, 793 - compat_ulong_t, arg32) 793 + compat_ulong_t, arg) 794 794 { 795 - unsigned long arg = arg32; 796 795 struct fd f = fdget(fd); 797 - int error = -EBADF; 796 + int error; 797 + 798 798 if (!f.file) 799 - goto out; 799 + return -EBADF; 800 800 801 801 /* RED-PEN how should LSM module know it's handling 32bit? */ 802 802 error = security_file_ioctl(f.file, cmd, arg); 803 803 if (error) 804 - goto out_fput; 804 + goto out; 805 805 806 806 switch (cmd) { 807 - /* these are never seen by ->ioctl(), no argument or int argument */ 808 - case FIOCLEX: 809 - case FIONCLEX: 810 - case FIFREEZE: 811 - case FITHAW: 807 + /* FICLONE takes an int argument, so don't use compat_ptr() */ 812 808 case FICLONE: 813 - goto do_ioctl; 814 - /* these are never seen by ->ioctl(), pointer argument */ 815 - case FIONBIO: 816 - case FIOASYNC: 817 - case FIOQSIZE: 818 - case FS_IOC_FIEMAP: 819 - case FIGETBSZ: 820 - case FICLONERANGE: 821 - case FIDEDUPERANGE: 822 - goto found_handler; 823 - /* 824 - * The next group is the stuff handled inside file_ioctl(). 825 - * For regular files these never reach ->ioctl(); for 826 - * devices, sockets, etc. they do and one (FIONREAD) is 827 - * even accepted in some cases. In all those cases 828 - * argument has the same type, so we can handle these 829 - * here, shunting them towards do_vfs_ioctl(). 830 - * ->compat_ioctl() will never see any of those. 831 - */ 832 - /* pointer argument, never actually handled by ->ioctl() */ 833 - case FIBMAP: 834 - goto found_handler; 835 - /* handled by some ->ioctl(); always a pointer to int */ 836 - case FIONREAD: 837 - goto found_handler; 838 - /* these get messy on amd64 due to alignment differences */ 809 + error = ioctl_file_clone(f.file, arg, 0, 0, 0); 810 + break; 811 + 839 812 #if defined(CONFIG_X86_64) 813 + /* these get messy on amd64 due to alignment differences */ 840 814 case FS_IOC_RESVSP_32: 841 815 case FS_IOC_RESVSP64_32: 842 816 error = compat_ioctl_preallocate(f.file, 0, compat_ptr(arg)); 843 - goto out_fput; 817 + break; 844 818 case FS_IOC_UNRESVSP_32: 845 819 case FS_IOC_UNRESVSP64_32: 846 820 error = compat_ioctl_preallocate(f.file, FALLOC_FL_PUNCH_HOLE, 847 821 compat_ptr(arg)); 848 - goto out_fput; 822 + break; 849 823 case FS_IOC_ZERO_RANGE_32: 850 824 error = compat_ioctl_preallocate(f.file, FALLOC_FL_ZERO_RANGE, 851 825 compat_ptr(arg)); 852 - goto out_fput; 853 - #else 854 - case FS_IOC_RESVSP: 855 - case FS_IOC_RESVSP64: 856 - case FS_IOC_UNRESVSP: 857 - case FS_IOC_UNRESVSP64: 858 - case FS_IOC_ZERO_RANGE: 859 - goto found_handler; 826 + break; 860 827 #endif 861 828 829 + /* 830 + * everything else in do_vfs_ioctl() takes either a compatible 831 + * pointer argument or no argument -- call it with a modified 832 + * argument. 833 + */ 862 834 default: 863 - if (f.file->f_op->compat_ioctl) { 835 + error = do_vfs_ioctl(f.file, fd, cmd, 836 + (unsigned long)compat_ptr(arg)); 837 + if (error != -ENOIOCTLCMD) 838 + break; 839 + 840 + if (f.file->f_op->compat_ioctl) 864 841 error = f.file->f_op->compat_ioctl(f.file, cmd, arg); 865 - if (error != -ENOIOCTLCMD) 866 - goto out_fput; 867 - } 868 - error = -ENOTTY; 869 - goto out_fput; 842 + if (error == -ENOIOCTLCMD) 843 + error = -ENOTTY; 844 + break; 870 845 } 871 846 872 - found_handler: 873 - arg = (unsigned long)compat_ptr(arg); 874 - do_ioctl: 875 - error = do_vfs_ioctl(f.file, fd, cmd, arg); 876 - out_fput: 877 - fdput(f); 878 847 out: 848 + fdput(f); 849 + 879 850 return error; 880 851 } 881 852 #endif
-2
include/linux/falloc.h
··· 51 51 #define FS_IOC_UNRESVSP64_32 _IOW ('X', 43, struct space_resv_32) 52 52 #define FS_IOC_ZERO_RANGE_32 _IOW ('X', 57, struct space_resv_32) 53 53 54 - int compat_ioctl_preallocate(struct file *, int, struct space_resv_32 __user *); 55 - 56 54 #endif 57 55 58 56 #endif /* _FALLOC_H_ */
-4
include/linux/fs.h
··· 2552 2552 int (*open)(struct inode *, struct file *)); 2553 2553 extern int finish_no_open(struct file *file, struct dentry *dentry); 2554 2554 2555 - /* fs/ioctl.c */ 2556 - 2557 - extern int ioctl_preallocate(struct file *filp, int mode, void __user *argp); 2558 - 2559 2555 /* fs/dcache.c */ 2560 2556 extern void __init vfs_caches_init_early(void); 2561 2557 extern void __init vfs_caches_init(void);