/* * linux/fs/compat.c * * Kernel compatibililty routines for e.g. 32 bit syscall support * on 64 bit kernels. * * Copyright (C) 2002 Stephen Rothwell, IBM Corporation * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs * Copyright (C) 2003 Pavel Machek (pavel@suse.cz) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/linkage.h>#include <linux/compat.h>#include <linux/errno.h>#include <linux/time.h>#include <linux/fs.h>#include <linux/fcntl.h>#include <linux/namei.h>#include <linux/file.h>#include <linux/vfs.h>#include <linux/ioctl32.h>#include <linux/ioctl.h>#include <linux/init.h>#include <linux/sockios.h> /* for SIOCDEVPRIVATE */#include <linux/smb.h>#include <linux/smb_mount.h>#include <linux/ncp_mount.h>#include <linux/nfs4_mount.h>#include <linux/smp_lock.h>#include <linux/syscalls.h>#include <linux/ctype.h>#include <linux/module.h>#include <linux/dirent.h>#include <linux/fsnotify.h>#include <linux/highuid.h>#include <linux/sunrpc/svc.h>#include <linux/nfsd/nfsd.h>#include <linux/nfsd/syscall.h>#include <linux/personality.h>#include <linux/rwsem.h>#include <linux/acct.h>#include <linux/mm.h> #include <net/sock.h> /* siocdevprivate_ioctl */ #include <asm/uaccess.h>#include <asm/mmu_context.h>#include <asm/ioctls.h> extern void sigset_from_compat(sigset_t *set, compat_sigset_t *compat); /* * Not all architectures have sys_utime, so implement this in terms * of sys_utimes. */asmlinkage long compat_sys_utime(char __user *filename, struct compat_utimbuf __user *t){ struct timeval tv[2]; if (t) { if (get_user(tv[0].tv_sec, &t->actime) || get_user(tv[1].tv_sec, &t->modtime)) return -EFAULT; tv[0].tv_usec = 0; tv[1].tv_usec = 0; } return do_utimes(AT_FDCWD, filename, t ? tv : NULL);} asmlinkage long compat_sys_futimesat(unsigned int dfd, char __user *filename, struct compat_timeval __user *t){ struct timeval tv[2]; if (t) { if (get_user(tv[0].tv_sec, &t[0].tv_sec) || get_user(tv[0].tv_usec, &t[0].tv_usec) || get_user(tv[1].tv_sec, &t[1].tv_sec) || get_user(tv[1].tv_usec, &t[1].tv_usec)) return -EFAULT; } return do_utimes(dfd, filename, t ? tv : NULL);} asmlinkage long compat_sys_utimes(char __user *filename, struct compat_timeval __user *t){ return compat_sys_futimesat(AT_FDCWD, filename, t);} asmlinkage long compat_sys_newstat(char __user * filename, struct compat_stat __user *statbuf){ struct kstat stat; int error = vfs_stat_fd(AT_FDCWD, filename, &stat); if (!error) error = cp_compat_stat(&stat, statbuf); return error;} asmlinkage long compat_sys_newlstat(char __user * filename, struct compat_stat __user *statbuf){ struct kstat stat; int error = vfs_lstat_fd(AT_FDCWD, filename, &stat); if (!error) error = cp_compat_stat(&stat, statbuf); return error;} #ifndef __ARCH_WANT_STAT64asmlinkage long compat_sys_newfstatat(unsigned int dfd, char __user *filename, struct compat_stat __user *statbuf, int flag){ struct kstat stat; int error = -EINVAL; if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0) goto out; if (flag & AT_SYMLINK_NOFOLLOW) error = vfs_lstat_fd(dfd, filename, &stat); else error = vfs_stat_fd(dfd, filename, &stat); if (!error) error = cp_compat_stat(&stat, statbuf); out: return error;}#endif asmlinkage long compat_sys_newfstat(unsigned int fd, struct compat_stat __user * statbuf){ struct kstat stat; int error = vfs_fstat(fd, &stat); if (!error) error = cp_compat_stat(&stat, statbuf); return error;} static int put_compat_statfs(struct compat_statfs __user *ubuf, struct kstatfs *kbuf){ if (sizeof ubuf->f_blocks == 4) { if ((kbuf->f_blocks | kbuf->f_bfree | kbuf->f_bavail) & 0xffffffff00000000ULL) return -EOVERFLOW; /* f_files and f_ffree may be -1; it's okay * to stuff that into 32 bits */ if (kbuf->f_files != 0xffffffffffffffffULL && (kbuf->f_files & 0xffffffff00000000ULL)) return -EOVERFLOW; if (kbuf->f_ffree != 0xffffffffffffffffULL && (kbuf->f_ffree & 0xffffffff00000000ULL)) return -EOVERFLOW; } if (!access_ok(VERIFY_WRITE, ubuf, sizeof(*ubuf)) || __put_user(kbuf->f_type, &ubuf->f_type) || __put_user(kbuf->f_bsize, &ubuf->f_bsize) || __put_user(kbuf->f_blocks, &ubuf->f_blocks) || __put_user(kbuf->f_bfree, &ubuf->f_bfree) || __put_user(kbuf->f_bavail, &ubuf->f_bavail) || __put_user(kbuf->f_files, &ubuf->f_files) || __put_user(kbuf->f_ffree, &ubuf->f_ffree) || __put_user(kbuf->f_namelen, &ubuf->f_namelen) || __put_user(kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) || __put_user(kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]) || __put_user(kbuf->f_frsize, &ubuf->f_frsize) || __put_user(0, &ubuf->f_spare[0]) || __put_user(0, &ubuf->f_spare[1]) || __put_user(0, &ubuf->f_spare[2]) || __put_user(0, &ubuf->f_spare[3]) || __put_user(0, &ubuf->f_spare[4])) return -EFAULT; return 0;} /* * The following statfs calls are copies of code from fs/open.c and * should be checked against those from time to time */asmlinkage long compat_sys_statfs(const char __user *path, struct compat_statfs __user *buf){ struct nameidata nd; int error; error = user_path_walk(path, &nd); if (!error) { struct kstatfs tmp; error = vfs_statfs(nd.dentry->d_inode->i_sb, &tmp); if (!error) error = put_compat_statfs(buf, &tmp); path_release(&nd); } return error;} asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs __user *buf){ struct file * file; struct kstatfs tmp; int error; error = -EBADF; file = fget(fd); if (!file) goto out; error = vfs_statfs(file->f_dentry->d_inode->i_sb, &tmp); if (!error) error = put_compat_statfs(buf, &tmp); fput(file);out: return error;} static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstatfs *kbuf){ if (sizeof ubuf->f_blocks == 4) { if ((kbuf->f_blocks | kbuf->f_bfree | kbuf->f_bavail) & 0xffffffff00000000ULL) return -EOVERFLOW; /* f_files and f_ffree may be -1; it's okay * to stuff that into 32 bits */ if (kbuf->f_files != 0xffffffffffffffffULL && (kbuf->f_files & 0xffffffff00000000ULL)) return -EOVERFLOW; if (kbuf->f_ffree != 0xffffffffffffffffULL && (kbuf->f_ffree & 0xffffffff00000000ULL)) return -EOVERFLOW; } if (!access_ok(VERIFY_WRITE, ubuf, sizeof(*ubuf)) || __put_user(kbuf->f_type, &ubuf->f_type) || __put_user(kbuf->f_bsize, &ubuf->f_bsize) || __put_user(kbuf->f_blocks, &ubuf->f_blocks) || __put_user(kbuf->f_bfree, &ubuf->f_bfree) || __put_user(kbuf->f_bavail, &ubuf->f_bavail) || __put_user(kbuf->f_files, &ubuf->f_files) || __put_user(kbuf->f_ffree, &ubuf->f_ffree) || __put_user(kbuf->f_namelen, &ubuf->f_namelen) || __put_user(kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) || __put_user(kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]) || __put_user(kbuf->f_frsize, &ubuf->f_frsize)) return -EFAULT; return 0;} asmlinkage long compat_sys_statfs64(const char __user *path, compat_size_t sz, struct compat_statfs64 __user *buf){ struct nameidata nd; int error; if (sz != sizeof(*buf)) return -EINVAL; error = user_path_walk(path, &nd); if (!error) { struct kstatfs tmp; error = vfs_statfs(nd.dentry->d_inode->i_sb, &tmp); if (!error) error = put_compat_statfs64(buf, &tmp); path_release(&nd); } return error;} asmlinkage long compat_sys_fstatfs64(unsigned int fd, compat_size_t sz, struct compat_statfs64 __user *buf){ struct file * file; struct kstatfs tmp; int error; if (sz != sizeof(*buf)) return -EINVAL; error = -EBADF; file = fget(fd); if (!file) goto out; error = vfs_statfs(file->f_dentry->d_inode->i_sb, &tmp); if (!error) error = put_compat_statfs64(buf, &tmp); fput(file);out: return error;} /* ioctl32 stuff, used by sparc64, parisc, s390x, ppc64, x86_64, MIPS */ #define IOCTL_HASHSIZE 256static struct ioctl_trans *ioctl32_hash_table[IOCTL_HASHSIZE]; extern struct ioctl_trans ioctl_start[];extern int ioctl_table_size; static inline unsigned long ioctl32_hash(unsigned long cmd){ return (((cmd >> 6) ^ (cmd >> 4) ^ cmd)) % IOCTL_HASHSIZE;} static void ioctl32_insert_translation(struct ioctl_trans *trans){ unsigned long hash; struct ioctl_trans *t; hash = ioctl32_hash (trans->cmd); if (!ioctl32_hash_table[hash]) ioctl32_hash_table[hash] = trans; else { t = ioctl32_hash_table[hash]; while (t->next) t = t->next; trans->next = NULL; t->next = trans; }} static int __init init_sys32_ioctl(void){ int i; for (i = 0; i < ioctl_table_size; i++) { if (ioctl_start[i].next != 0) { printk("ioctl translation %d bad\n",i); return -1; } ioctl32_insert_translation(&ioctl_start[i]); } return 0;} __initcall(init_sys32_ioctl); static void compat_ioctl_error(struct file *filp, unsigned int fd, unsigned int cmd, unsigned long arg){ char buf[10]; char *fn = "?"; char *path; /* find the name of the device. */ path = (char *)__get_free_page(GFP_KERNEL); if (path) { fn = d_path(filp->f_dentry, filp->f_vfsmnt, path, PAGE_SIZE); if (IS_ERR(fn)) fn = "?"; } sprintf(buf,"'%c'", (cmd>>24) & 0x3f); if (!isprint(buf[1])) sprintf(buf, "%02x", buf[1]); printk("ioctl32(%s:%d): Unknown cmd fd(%d) " "cmd(%08x){%s} arg(%08x) on %s\n", current->comm, current->pid, (int)fd, (unsigned int)cmd, buf, (unsigned int)arg, fn); if (path) free_page((unsigned long)path);} asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg){ struct file *filp; int error = -EBADF; struct ioctl_trans *t; int fput_needed; filp = fget_light(fd, &fput_needed); if (!filp) goto out; /* RED-PEN how should LSM module know it's handling 32bit? */ error = security_file_ioctl(filp, cmd, arg); if (error) goto out_fput; /* * To allow the compat_ioctl handlers to be self contained * we need to check the common ioctls here first. * Just handle them with the standard handlers below. */ switch (cmd) { case FIOCLEX: case FIONCLEX: case FIONBIO: case FIOASYNC: case FIOQSIZE: break; case FIBMAP: case FIGETBSZ: case FIONREAD: if (S_ISREG(filp->f_dentry->d_inode->i_mode)) break; /*FALL THROUGH*/ default: if (filp->f_op && filp->f_op->compat_ioctl) { error = filp->f_op->compat_ioctl(filp, cmd, arg); if (error != -ENOIOCTLCMD) goto out_fput; } if (!filp->f_op || (!filp->f_op->ioctl && !filp->f_op->unlocked_ioctl)) goto do_ioctl; break; } for (t = ioctl32_hash_table[ioctl32_hash(cmd)]; t; t = t->next) { if (t->cmd == cmd) goto found_handler; } if (S_ISSOCK(filp->f_dentry->d_inode->i_mode) && cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) { error = siocdevprivate_ioctl(fd, cmd, arg); } else { static int count; if (++count <= 50) compat_ioctl_error(filp, fd, cmd, arg); error = -EINVAL; } goto out_fput; found_handler: if (t->handler) { lock_kernel(); error = t->handler(fd, cmd, arg, filp); unlock_kernel(); goto out_fput; } do_ioctl: error = vfs_ioctl(filp, fd, cmd, arg); out_fput: fput_light(filp, fput_needed); out: return error;} static int get_compat_flock(struct flock *kfl, struct compat_flock __user *ufl){ if (!access_ok(VERIFY_READ, ufl, sizeof(*ufl)) || __get_user(kfl->l_type, &ufl->l_type) || __get_user(kfl->l_whence, &ufl->l_whence) || __get_user(kfl->l_start, &ufl->l_start) || __get_user(kfl->l_len, &ufl->l_len) || __get_user(kfl->l_pid, &ufl->l_pid)) return -EFAULT; return 0;} static int put_compat_flock(struct flock *kfl, struct compat_flock __user *ufl){ if (!access_ok(VERIFY_WRITE, ufl, sizeof(*ufl)) || __put_user(kfl->l_type, &ufl->l_type) || __put_user(kfl->l_whence, &ufl->l_whence) || __put_user(kfl->l_start, &ufl->l_start) || __put_user(kfl->l_len, &ufl->l_len) || __put_user(kfl->l_pid, &ufl->l_pid)) return -EFAULT; return 0;} #ifndef HAVE_ARCH_GET_COMPAT_FLOCK64static int get_compat_flock64(struct flock *kfl, struct compat_flock64 __user *ufl){ if (!access_ok(VERIFY_READ, ufl, sizeof(*ufl)) || __get_user(kfl->l_type, &ufl->l_type) || __get_user(kfl->l_whence, &ufl->l_whence) || __get_user(kfl->l_start, &ufl->l_start) || __get_user(kfl->l_len, &ufl->l_len) || __get_user(kfl->l_pid, &ufl->l_pid)) return -EFAULT; return 0;}#endif #ifndef HAVE_ARCH_PUT_COMPAT_FLOCK64static int put_compat_flock64(struct flock *kfl, struct compat_flock64 __user *ufl){ if (!access_ok(VERIFY_WRITE, ufl, sizeof(*ufl)) || __put_user(kfl->l_type, &ufl->l_type) || __put_user(kfl->l_whence, &ufl->l_whence) || __put_user(kfl->l_start, &ufl->l_start) ||Lines 1–500 of 2223. Load more
/* * linux/fs/compat.c * * Kernel compatibililty routines for e.g. 32 bit syscall support * on 64 bit kernels. * * Copyright (C) 2002 Stephen Rothwell, IBM Corporation * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs * Copyright (C) 2003 Pavel Machek (pavel@suse.cz) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/linkage.h> #include <linux/compat.h> #include <linux/errno.h> #include <linux/time.h> #include <linux/fs.h> #include <linux/fcntl.h> #include <linux/namei.h> #include <linux/file.h> #include <linux/vfs.h> #include <linux/ioctl32.h> #include <linux/ioctl.h> #include <linux/init.h> #include <linux/sockios.h> /* for SIOCDEVPRIVATE */ #include <linux/smb.h> #include <linux/smb_mount.h> #include <linux/ncp_mount.h> #include <linux/nfs4_mount.h> #include <linux/smp_lock.h> #include <linux/syscalls.h> #include <linux/ctype.h> #include <linux/module.h> #include <linux/dirent.h> #include <linux/fsnotify.h> #include <linux/highuid.h> #include <linux/sunrpc/svc.h> #include <linux/nfsd/nfsd.h> #include <linux/nfsd/syscall.h> #include <linux/personality.h> #include <linux/rwsem.h> #include <linux/acct.h> #include <linux/mm.h> #include <net/sock.h> /* siocdevprivate_ioctl */ #include <asm/uaccess.h> #include <asm/mmu_context.h> #include <asm/ioctls.h> extern void sigset_from_compat(sigset_t *set, compat_sigset_t *compat); /* * Not all architectures have sys_utime, so implement this in terms * of sys_utimes. */ asmlinkage long compat_sys_utime(char __user *filename, struct compat_utimbuf __user *t) { struct timeval tv[2]; if (t) { if (get_user(tv[0].tv_sec, &t->actime) || get_user(tv[1].tv_sec, &t->modtime)) return -EFAULT; tv[0].tv_usec = 0; tv[1].tv_usec = 0; } return do_utimes(AT_FDCWD, filename, t ? tv : NULL); } asmlinkage long compat_sys_futimesat(unsigned int dfd, char __user *filename, struct compat_timeval __user *t) { struct timeval tv[2]; if (t) { if (get_user(tv[0].tv_sec, &t[0].tv_sec) || get_user(tv[0].tv_usec, &t[0].tv_usec) || get_user(tv[1].tv_sec, &t[1].tv_sec) || get_user(tv[1].tv_usec, &t[1].tv_usec)) return -EFAULT; } return do_utimes(dfd, filename, t ? tv : NULL); } asmlinkage long compat_sys_utimes(char __user *filename, struct compat_timeval __user *t) { return compat_sys_futimesat(AT_FDCWD, filename, t); } asmlinkage long compat_sys_newstat(char __user * filename, struct compat_stat __user *statbuf) { struct kstat stat; int error = vfs_stat_fd(AT_FDCWD, filename, &stat); if (!error) error = cp_compat_stat(&stat, statbuf); return error; } asmlinkage long compat_sys_newlstat(char __user * filename, struct compat_stat __user *statbuf) { struct kstat stat; int error = vfs_lstat_fd(AT_FDCWD, filename, &stat); if (!error) error = cp_compat_stat(&stat, statbuf); return error; } #ifndef __ARCH_WANT_STAT64 asmlinkage long compat_sys_newfstatat(unsigned int dfd, char __user *filename, struct compat_stat __user *statbuf, int flag) { struct kstat stat; int error = -EINVAL; if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0) goto out; if (flag & AT_SYMLINK_NOFOLLOW) error = vfs_lstat_fd(dfd, filename, &stat); else error = vfs_stat_fd(dfd, filename, &stat); if (!error) error = cp_compat_stat(&stat, statbuf); out: return error; } #endif asmlinkage long compat_sys_newfstat(unsigned int fd, struct compat_stat __user * statbuf) { struct kstat stat; int error = vfs_fstat(fd, &stat); if (!error) error = cp_compat_stat(&stat, statbuf); return error; } static int put_compat_statfs(struct compat_statfs __user *ubuf, struct kstatfs *kbuf) { if (sizeof ubuf->f_blocks == 4) { if ((kbuf->f_blocks | kbuf->f_bfree | kbuf->f_bavail) & 0xffffffff00000000ULL) return -EOVERFLOW; /* f_files and f_ffree may be -1; it's okay * to stuff that into 32 bits */ if (kbuf->f_files != 0xffffffffffffffffULL && (kbuf->f_files & 0xffffffff00000000ULL)) return -EOVERFLOW; if (kbuf->f_ffree != 0xffffffffffffffffULL && (kbuf->f_ffree & 0xffffffff00000000ULL)) return -EOVERFLOW; } if (!access_ok(VERIFY_WRITE, ubuf, sizeof(*ubuf)) || __put_user(kbuf->f_type, &ubuf->f_type) || __put_user(kbuf->f_bsize, &ubuf->f_bsize) || __put_user(kbuf->f_blocks, &ubuf->f_blocks) || __put_user(kbuf->f_bfree, &ubuf->f_bfree) || __put_user(kbuf->f_bavail, &ubuf->f_bavail) || __put_user(kbuf->f_files, &ubuf->f_files) || __put_user(kbuf->f_ffree, &ubuf->f_ffree) || __put_user(kbuf->f_namelen, &ubuf->f_namelen) || __put_user(kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) || __put_user(kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]) || __put_user(kbuf->f_frsize, &ubuf->f_frsize) || __put_user(0, &ubuf->f_spare[0]) || __put_user(0, &ubuf->f_spare[1]) || __put_user(0, &ubuf->f_spare[2]) || __put_user(0, &ubuf->f_spare[3]) || __put_user(0, &ubuf->f_spare[4])) return -EFAULT; return 0; } /* * The following statfs calls are copies of code from fs/open.c and * should be checked against those from time to time */ asmlinkage long compat_sys_statfs(const char __user *path, struct compat_statfs __user *buf) { struct nameidata nd; int error; error = user_path_walk(path, &nd); if (!error) { struct kstatfs tmp; error = vfs_statfs(nd.dentry->d_inode->i_sb, &tmp); if (!error) error = put_compat_statfs(buf, &tmp); path_release(&nd); } return error; } asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs __user *buf) { struct file * file; struct kstatfs tmp; int error; error = -EBADF; file = fget(fd); if (!file) goto out; error = vfs_statfs(file->f_dentry->d_inode->i_sb, &tmp); if (!error) error = put_compat_statfs(buf, &tmp); fput(file); out: return error; } static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstatfs *kbuf) { if (sizeof ubuf->f_blocks == 4) { if ((kbuf->f_blocks | kbuf->f_bfree | kbuf->f_bavail) & 0xffffffff00000000ULL) return -EOVERFLOW; /* f_files and f_ffree may be -1; it's okay * to stuff that into 32 bits */ if (kbuf->f_files != 0xffffffffffffffffULL && (kbuf->f_files & 0xffffffff00000000ULL)) return -EOVERFLOW; if (kbuf->f_ffree != 0xffffffffffffffffULL && (kbuf->f_ffree & 0xffffffff00000000ULL)) return -EOVERFLOW; } if (!access_ok(VERIFY_WRITE, ubuf, sizeof(*ubuf)) || __put_user(kbuf->f_type, &ubuf->f_type) || __put_user(kbuf->f_bsize, &ubuf->f_bsize) || __put_user(kbuf->f_blocks, &ubuf->f_blocks) || __put_user(kbuf->f_bfree, &ubuf->f_bfree) || __put_user(kbuf->f_bavail, &ubuf->f_bavail) || __put_user(kbuf->f_files, &ubuf->f_files) || __put_user(kbuf->f_ffree, &ubuf->f_ffree) || __put_user(kbuf->f_namelen, &ubuf->f_namelen) || __put_user(kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) || __put_user(kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]) || __put_user(kbuf->f_frsize, &ubuf->f_frsize)) return -EFAULT; return 0; } asmlinkage long compat_sys_statfs64(const char __user *path, compat_size_t sz, struct compat_statfs64 __user *buf) { struct nameidata nd; int error; if (sz != sizeof(*buf)) return -EINVAL; error = user_path_walk(path, &nd); if (!error) { struct kstatfs tmp; error = vfs_statfs(nd.dentry->d_inode->i_sb, &tmp); if (!error) error = put_compat_statfs64(buf, &tmp); path_release(&nd); } return error; } asmlinkage long compat_sys_fstatfs64(unsigned int fd, compat_size_t sz, struct compat_statfs64 __user *buf) { struct file * file; struct kstatfs tmp; int error; if (sz != sizeof(*buf)) return -EINVAL; error = -EBADF; file = fget(fd); if (!file) goto out; error = vfs_statfs(file->f_dentry->d_inode->i_sb, &tmp); if (!error) error = put_compat_statfs64(buf, &tmp); fput(file); out: return error; } /* ioctl32 stuff, used by sparc64, parisc, s390x, ppc64, x86_64, MIPS */ #define IOCTL_HASHSIZE 256 static struct ioctl_trans *ioctl32_hash_table[IOCTL_HASHSIZE]; extern struct ioctl_trans ioctl_start[]; extern int ioctl_table_size; static inline unsigned long ioctl32_hash(unsigned long cmd) { return (((cmd >> 6) ^ (cmd >> 4) ^ cmd)) % IOCTL_HASHSIZE; } static void ioctl32_insert_translation(struct ioctl_trans *trans) { unsigned long hash; struct ioctl_trans *t; hash = ioctl32_hash (trans->cmd); if (!ioctl32_hash_table[hash]) ioctl32_hash_table[hash] = trans; else { t = ioctl32_hash_table[hash]; while (t->next) t = t->next; trans->next = NULL; t->next = trans; } } static int __init init_sys32_ioctl(void) { int i; for (i = 0; i < ioctl_table_size; i++) { if (ioctl_start[i].next != 0) { printk("ioctl translation %d bad\n",i); return -1; } ioctl32_insert_translation(&ioctl_start[i]); } return 0; } __initcall(init_sys32_ioctl); static void compat_ioctl_error(struct file *filp, unsigned int fd, unsigned int cmd, unsigned long arg) { char buf[10]; char *fn = "?"; char *path; /* find the name of the device. */ path = (char *)__get_free_page(GFP_KERNEL); if (path) { fn = d_path(filp->f_dentry, filp->f_vfsmnt, path, PAGE_SIZE); if (IS_ERR(fn)) fn = "?"; } sprintf(buf,"'%c'", (cmd>>24) & 0x3f); if (!isprint(buf[1])) sprintf(buf, "%02x", buf[1]); printk("ioctl32(%s:%d): Unknown cmd fd(%d) " "cmd(%08x){%s} arg(%08x) on %s\n", current->comm, current->pid, (int)fd, (unsigned int)cmd, buf, (unsigned int)arg, fn); if (path) free_page((unsigned long)path); } asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) { struct file *filp; int error = -EBADF; struct ioctl_trans *t; int fput_needed; filp = fget_light(fd, &fput_needed); if (!filp) goto out; /* RED-PEN how should LSM module know it's handling 32bit? */ error = security_file_ioctl(filp, cmd, arg); if (error) goto out_fput; /* * To allow the compat_ioctl handlers to be self contained * we need to check the common ioctls here first. * Just handle them with the standard handlers below. */ switch (cmd) { case FIOCLEX: case FIONCLEX: case FIONBIO: case FIOASYNC: case FIOQSIZE: break; case FIBMAP: case FIGETBSZ: case FIONREAD: if (S_ISREG(filp->f_dentry->d_inode->i_mode)) break; /*FALL THROUGH*/ default: if (filp->f_op && filp->f_op->compat_ioctl) { error = filp->f_op->compat_ioctl(filp, cmd, arg); if (error != -ENOIOCTLCMD) goto out_fput; } if (!filp->f_op || (!filp->f_op->ioctl && !filp->f_op->unlocked_ioctl)) goto do_ioctl; break; } for (t = ioctl32_hash_table[ioctl32_hash(cmd)]; t; t = t->next) { if (t->cmd == cmd) goto found_handler; } if (S_ISSOCK(filp->f_dentry->d_inode->i_mode) && cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) { error = siocdevprivate_ioctl(fd, cmd, arg); } else { static int count; if (++count <= 50) compat_ioctl_error(filp, fd, cmd, arg); error = -EINVAL; } goto out_fput; found_handler: if (t->handler) { lock_kernel(); error = t->handler(fd, cmd, arg, filp); unlock_kernel(); goto out_fput; } do_ioctl: error = vfs_ioctl(filp, fd, cmd, arg); out_fput: fput_light(filp, fput_needed); out: return error; } static int get_compat_flock(struct flock *kfl, struct compat_flock __user *ufl) { if (!access_ok(VERIFY_READ, ufl, sizeof(*ufl)) || __get_user(kfl->l_type, &ufl->l_type) || __get_user(kfl->l_whence, &ufl->l_whence) || __get_user(kfl->l_start, &ufl->l_start) || __get_user(kfl->l_len, &ufl->l_len) || __get_user(kfl->l_pid, &ufl->l_pid)) return -EFAULT; return 0; } static int put_compat_flock(struct flock *kfl, struct compat_flock __user *ufl) { if (!access_ok(VERIFY_WRITE, ufl, sizeof(*ufl)) || __put_user(kfl->l_type, &ufl->l_type) || __put_user(kfl->l_whence, &ufl->l_whence) || __put_user(kfl->l_start, &ufl->l_start) || __put_user(kfl->l_len, &ufl->l_len) || __put_user(kfl->l_pid, &ufl->l_pid)) return -EFAULT; return 0; } #ifndef HAVE_ARCH_GET_COMPAT_FLOCK64 static int get_compat_flock64(struct flock *kfl, struct compat_flock64 __user *ufl) { if (!access_ok(VERIFY_READ, ufl, sizeof(*ufl)) || __get_user(kfl->l_type, &ufl->l_type) || __get_user(kfl->l_whence, &ufl->l_whence) || __get_user(kfl->l_start, &ufl->l_start) || __get_user(kfl->l_len, &ufl->l_len) || __get_user(kfl->l_pid, &ufl->l_pid)) return -EFAULT; return 0; } #endif #ifndef HAVE_ARCH_PUT_COMPAT_FLOCK64 static int put_compat_flock64(struct flock *kfl, struct compat_flock64 __user *ufl) { if (!access_ok(VERIFY_WRITE, ufl, sizeof(*ufl)) || __put_user(kfl->l_type, &ufl->l_type) || __put_user(kfl->l_whence, &ufl->l_whence) || __put_user(kfl->l_start, &ufl->l_start) ||