/* * 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@ucw.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/stddef.h>#include <linux/kernel.h>#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/fdtable.h>#include <linux/vfs.h>#include <linux/ioctl.h>#include <linux/init.h>#include <linux/ncp_mount.h>#include <linux/nfs4_mount.h>#include <linux/syscalls.h>#include <linux/ctype.h>#include <linux/dirent.h>#include <linux/fsnotify.h>#include <linux/highuid.h>#include <linux/personality.h>#include <linux/rwsem.h>#include <linux/tsacct_kern.h>#include <linux/security.h>#include <linux/highmem.h>#include <linux/signal.h>#include <linux/poll.h>#include <linux/mm.h>#include <linux/fs_struct.h>#include <linux/slab.h>#include <linux/pagemap.h>#include <linux/aio.h> #include <asm/uaccess.h>#include <asm/mmu_context.h>#include <asm/ioctls.h>#include "internal.h" int compat_log = 1; int compat_printk(const char *fmt, ...){ va_list ap; int ret; if (!compat_log) return 0; va_start(ap, fmt); ret = vprintk(fmt, ap); va_end(ap); return ret;} /* * Not all architectures have sys_utime, so implement this in terms * of sys_utimes. */asmlinkage long compat_sys_utime(const char __user *filename, struct compat_utimbuf __user *t){ struct timespec 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_nsec = 0; tv[1].tv_nsec = 0; } return do_utimes(AT_FDCWD, filename, t ? tv : NULL, 0);} asmlinkage long compat_sys_utimensat(unsigned int dfd, const char __user *filename, struct compat_timespec __user *t, int flags){ struct timespec tv[2]; if (t) { if (get_compat_timespec(&tv[0], &t[0]) || get_compat_timespec(&tv[1], &t[1])) return -EFAULT; if (tv[0].tv_nsec == UTIME_OMIT && tv[1].tv_nsec == UTIME_OMIT) return 0; } return do_utimes(dfd, filename, t ? tv : NULL, flags);} asmlinkage long compat_sys_futimesat(unsigned int dfd, const char __user *filename, struct compat_timeval __user *t){ struct timespec tv[2]; if (t) { if (get_user(tv[0].tv_sec, &t[0].tv_sec) || get_user(tv[0].tv_nsec, &t[0].tv_usec) || get_user(tv[1].tv_sec, &t[1].tv_sec) || get_user(tv[1].tv_nsec, &t[1].tv_usec)) return -EFAULT; if (tv[0].tv_nsec >= 1000000 || tv[0].tv_nsec < 0 || tv[1].tv_nsec >= 1000000 || tv[1].tv_nsec < 0) return -EINVAL; tv[0].tv_nsec *= 1000; tv[1].tv_nsec *= 1000; } return do_utimes(dfd, filename, t ? tv : NULL, 0);} asmlinkage long compat_sys_utimes(const char __user *filename, struct compat_timeval __user *t){ return compat_sys_futimesat(AT_FDCWD, filename, t);} static int cp_compat_stat(struct kstat *stat, struct compat_stat __user *ubuf){ struct compat_stat tmp; if (!old_valid_dev(stat->dev) || !old_valid_dev(stat->rdev)) return -EOVERFLOW; memset(&tmp, 0, sizeof(tmp)); tmp.st_dev = old_encode_dev(stat->dev); tmp.st_ino = stat->ino; if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino) return -EOVERFLOW; tmp.st_mode = stat->mode; tmp.st_nlink = stat->nlink; if (tmp.st_nlink != stat->nlink) return -EOVERFLOW; SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid)); SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid)); tmp.st_rdev = old_encode_dev(stat->rdev); if ((u64) stat->size > MAX_NON_LFS) return -EOVERFLOW; tmp.st_size = stat->size; tmp.st_atime = stat->atime.tv_sec; tmp.st_atime_nsec = stat->atime.tv_nsec; tmp.st_mtime = stat->mtime.tv_sec; tmp.st_mtime_nsec = stat->mtime.tv_nsec; tmp.st_ctime = stat->ctime.tv_sec; tmp.st_ctime_nsec = stat->ctime.tv_nsec; tmp.st_blocks = stat->blocks; tmp.st_blksize = stat->blksize; return copy_to_user(ubuf, &tmp, sizeof(tmp)) ? -EFAULT : 0;} asmlinkage long compat_sys_newstat(const char __user * filename, struct compat_stat __user *statbuf){ struct kstat stat; int error; error = vfs_stat(filename, &stat); if (error) return error; return cp_compat_stat(&stat, statbuf);} asmlinkage long compat_sys_newlstat(const char __user * filename, struct compat_stat __user *statbuf){ struct kstat stat; int error; error = vfs_lstat(filename, &stat); if (error) return error; return cp_compat_stat(&stat, statbuf);} #ifndef __ARCH_WANT_STAT64asmlinkage long compat_sys_newfstatat(unsigned int dfd, const char __user *filename, struct compat_stat __user *statbuf, int flag){ struct kstat stat; int error; error = vfs_fstatat(dfd, filename, &stat, flag); if (error) return error; return cp_compat_stat(&stat, statbuf);}#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 | kbuf->f_bsize | kbuf->f_frsize) & 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(kbuf->f_flags, &ubuf->f_flags) || __clear_user(ubuf->f_spare, sizeof(ubuf->f_spare))) return -EFAULT; return 0;} /* * The following statfs calls are copies of code from fs/statfs.c and * should be checked against those from time to time */asmlinkage long compat_sys_statfs(const char __user *pathname, struct compat_statfs __user *buf){ struct kstatfs tmp; int error = user_statfs(pathname, &tmp); if (!error) error = put_compat_statfs(buf, &tmp); return error;} asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs __user *buf){ struct kstatfs tmp; int error = fd_statfs(fd, &tmp); if (!error) error = put_compat_statfs(buf, &tmp); 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 | kbuf->f_bsize | kbuf->f_frsize) & 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(kbuf->f_flags, &ubuf->f_flags) || __clear_user(ubuf->f_spare, sizeof(ubuf->f_spare))) return -EFAULT; return 0;} asmlinkage long compat_sys_statfs64(const char __user *pathname, compat_size_t sz, struct compat_statfs64 __user *buf){ struct kstatfs tmp; int error; if (sz != sizeof(*buf)) return -EINVAL; error = user_statfs(pathname, &tmp); if (!error) error = put_compat_statfs64(buf, &tmp); return error;} asmlinkage long compat_sys_fstatfs64(unsigned int fd, compat_size_t sz, struct compat_statfs64 __user *buf){ struct kstatfs tmp; int error; if (sz != sizeof(*buf)) return -EINVAL; error = fd_statfs(fd, &tmp); if (!error) error = put_compat_statfs64(buf, &tmp); return error;} /* * This is a copy of sys_ustat, just dealing with a structure layout. * Given how simple this syscall is that apporach is more maintainable * than the various conversion hacks. */asmlinkage long compat_sys_ustat(unsigned dev, struct compat_ustat __user *u){ struct compat_ustat tmp; struct kstatfs sbuf; int err = vfs_ustat(new_decode_dev(dev), &sbuf); if (err) return err; memset(&tmp, 0, sizeof(struct compat_ustat)); tmp.f_tfree = sbuf.f_bfree; tmp.f_tinode = sbuf.f_ffree; if (copy_to_user(u, &tmp, sizeof(struct compat_ustat))) return -EFAULT; return 0;} 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) || __put_user(kfl->l_len, &ufl->l_len) || __put_user(kfl->l_pid, &ufl->l_pid)) return -EFAULT; return 0;}#endif asmlinkage long compat_sys_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg){ mm_segment_t old_fs; struct flock f; long ret; switch (cmd) { case F_GETLK: case F_SETLK: case F_SETLKW: ret = get_compat_flock(&f, compat_ptr(arg)); if (ret != 0) break; old_fs = get_fs(); set_fs(KERNEL_DS); ret = sys_fcntl(fd, cmd, (unsigned long)&f); set_fs(old_fs); if (cmd == F_GETLK && ret == 0) { /* GETLK was successful and we need to return the data... * but it needs to fit in the compat structure. * l_start shouldn't be too big, unless the original * start + end is greater than COMPAT_OFF_T_MAX, in which * case the app was asking for trouble, so we return * -EOVERFLOW in that case. * l_len could be too big, in which case we just truncate it, * and only allow the app to see that part of the conflicting * lock that might make sense to it anyway */ if (f.l_start > COMPAT_OFF_T_MAX) ret = -EOVERFLOW; if (f.l_len > COMPAT_OFF_T_MAX) f.l_len = COMPAT_OFF_T_MAX; if (ret == 0) ret = put_compat_flock(&f, compat_ptr(arg)); } break; case F_GETLK64: case F_SETLK64: case F_SETLKW64: ret = get_compat_flock64(&f, compat_ptr(arg)); if (ret != 0) break; old_fs = get_fs(); set_fs(KERNEL_DS); ret = sys_fcntl(fd, (cmd == F_GETLK64) ? F_GETLK : ((cmd == F_SETLK64) ? F_SETLK : F_SETLKW), (unsigned long)&f); set_fs(old_fs); if (cmd == F_GETLK64 && ret == 0) { /* need to return lock information - see above for commentary */ if (f.l_start > COMPAT_LOFF_T_MAX) ret = -EOVERFLOW; if (f.l_len > COMPAT_LOFF_T_MAX) f.l_len = COMPAT_LOFF_T_MAX; if (ret == 0) ret = put_compat_flock64(&f, compat_ptr(arg)); } break; default: ret = sys_fcntl(fd, cmd, arg); break; } return ret;} asmlinkage long compat_sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg){ if ((cmd == F_GETLK64) || (cmd == F_SETLK64) || (cmd == F_SETLKW64)) return -EINVAL; return compat_sys_fcntl64(fd, cmd, arg);} asmlinkage longcompat_sys_io_setup(unsigned nr_reqs, u32 __user *ctx32p){ long ret; aio_context_t ctx64; mm_segment_t oldfs = get_fs(); if (unlikely(get_user(ctx64, ctx32p))) return -EFAULT; set_fs(KERNEL_DS); /* The __user pointer cast is valid because of the set_fs() */ ret = sys_io_setup(nr_reqs, (aio_context_t __user *) &ctx64); set_fs(oldfs); /* truncating is ok because it's a user address */ if (!ret) ret = put_user((u32) ctx64, ctx32p); return ret;} asmlinkage longcompat_sys_io_getevents(aio_context_t ctx_id,Lines 1–500 of 1466. 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@ucw.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/stddef.h> #include <linux/kernel.h> #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/fdtable.h> #include <linux/vfs.h> #include <linux/ioctl.h> #include <linux/init.h> #include <linux/ncp_mount.h> #include <linux/nfs4_mount.h> #include <linux/syscalls.h> #include <linux/ctype.h> #include <linux/dirent.h> #include <linux/fsnotify.h> #include <linux/highuid.h> #include <linux/personality.h> #include <linux/rwsem.h> #include <linux/tsacct_kern.h> #include <linux/security.h> #include <linux/highmem.h> #include <linux/signal.h> #include <linux/poll.h> #include <linux/mm.h> #include <linux/fs_struct.h> #include <linux/slab.h> #include <linux/pagemap.h> #include <linux/aio.h> #include <asm/uaccess.h> #include <asm/mmu_context.h> #include <asm/ioctls.h> #include "internal.h" int compat_log = 1; int compat_printk(const char *fmt, ...) { va_list ap; int ret; if (!compat_log) return 0; va_start(ap, fmt); ret = vprintk(fmt, ap); va_end(ap); return ret; } /* * Not all architectures have sys_utime, so implement this in terms * of sys_utimes. */ asmlinkage long compat_sys_utime(const char __user *filename, struct compat_utimbuf __user *t) { struct timespec 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_nsec = 0; tv[1].tv_nsec = 0; } return do_utimes(AT_FDCWD, filename, t ? tv : NULL, 0); } asmlinkage long compat_sys_utimensat(unsigned int dfd, const char __user *filename, struct compat_timespec __user *t, int flags) { struct timespec tv[2]; if (t) { if (get_compat_timespec(&tv[0], &t[0]) || get_compat_timespec(&tv[1], &t[1])) return -EFAULT; if (tv[0].tv_nsec == UTIME_OMIT && tv[1].tv_nsec == UTIME_OMIT) return 0; } return do_utimes(dfd, filename, t ? tv : NULL, flags); } asmlinkage long compat_sys_futimesat(unsigned int dfd, const char __user *filename, struct compat_timeval __user *t) { struct timespec tv[2]; if (t) { if (get_user(tv[0].tv_sec, &t[0].tv_sec) || get_user(tv[0].tv_nsec, &t[0].tv_usec) || get_user(tv[1].tv_sec, &t[1].tv_sec) || get_user(tv[1].tv_nsec, &t[1].tv_usec)) return -EFAULT; if (tv[0].tv_nsec >= 1000000 || tv[0].tv_nsec < 0 || tv[1].tv_nsec >= 1000000 || tv[1].tv_nsec < 0) return -EINVAL; tv[0].tv_nsec *= 1000; tv[1].tv_nsec *= 1000; } return do_utimes(dfd, filename, t ? tv : NULL, 0); } asmlinkage long compat_sys_utimes(const char __user *filename, struct compat_timeval __user *t) { return compat_sys_futimesat(AT_FDCWD, filename, t); } static int cp_compat_stat(struct kstat *stat, struct compat_stat __user *ubuf) { struct compat_stat tmp; if (!old_valid_dev(stat->dev) || !old_valid_dev(stat->rdev)) return -EOVERFLOW; memset(&tmp, 0, sizeof(tmp)); tmp.st_dev = old_encode_dev(stat->dev); tmp.st_ino = stat->ino; if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino) return -EOVERFLOW; tmp.st_mode = stat->mode; tmp.st_nlink = stat->nlink; if (tmp.st_nlink != stat->nlink) return -EOVERFLOW; SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid)); SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid)); tmp.st_rdev = old_encode_dev(stat->rdev); if ((u64) stat->size > MAX_NON_LFS) return -EOVERFLOW; tmp.st_size = stat->size; tmp.st_atime = stat->atime.tv_sec; tmp.st_atime_nsec = stat->atime.tv_nsec; tmp.st_mtime = stat->mtime.tv_sec; tmp.st_mtime_nsec = stat->mtime.tv_nsec; tmp.st_ctime = stat->ctime.tv_sec; tmp.st_ctime_nsec = stat->ctime.tv_nsec; tmp.st_blocks = stat->blocks; tmp.st_blksize = stat->blksize; return copy_to_user(ubuf, &tmp, sizeof(tmp)) ? -EFAULT : 0; } asmlinkage long compat_sys_newstat(const char __user * filename, struct compat_stat __user *statbuf) { struct kstat stat; int error; error = vfs_stat(filename, &stat); if (error) return error; return cp_compat_stat(&stat, statbuf); } asmlinkage long compat_sys_newlstat(const char __user * filename, struct compat_stat __user *statbuf) { struct kstat stat; int error; error = vfs_lstat(filename, &stat); if (error) return error; return cp_compat_stat(&stat, statbuf); } #ifndef __ARCH_WANT_STAT64 asmlinkage long compat_sys_newfstatat(unsigned int dfd, const char __user *filename, struct compat_stat __user *statbuf, int flag) { struct kstat stat; int error; error = vfs_fstatat(dfd, filename, &stat, flag); if (error) return error; return cp_compat_stat(&stat, statbuf); } #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 | kbuf->f_bsize | kbuf->f_frsize) & 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(kbuf->f_flags, &ubuf->f_flags) || __clear_user(ubuf->f_spare, sizeof(ubuf->f_spare))) return -EFAULT; return 0; } /* * The following statfs calls are copies of code from fs/statfs.c and * should be checked against those from time to time */ asmlinkage long compat_sys_statfs(const char __user *pathname, struct compat_statfs __user *buf) { struct kstatfs tmp; int error = user_statfs(pathname, &tmp); if (!error) error = put_compat_statfs(buf, &tmp); return error; } asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs __user *buf) { struct kstatfs tmp; int error = fd_statfs(fd, &tmp); if (!error) error = put_compat_statfs(buf, &tmp); 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 | kbuf->f_bsize | kbuf->f_frsize) & 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(kbuf->f_flags, &ubuf->f_flags) || __clear_user(ubuf->f_spare, sizeof(ubuf->f_spare))) return -EFAULT; return 0; } asmlinkage long compat_sys_statfs64(const char __user *pathname, compat_size_t sz, struct compat_statfs64 __user *buf) { struct kstatfs tmp; int error; if (sz != sizeof(*buf)) return -EINVAL; error = user_statfs(pathname, &tmp); if (!error) error = put_compat_statfs64(buf, &tmp); return error; } asmlinkage long compat_sys_fstatfs64(unsigned int fd, compat_size_t sz, struct compat_statfs64 __user *buf) { struct kstatfs tmp; int error; if (sz != sizeof(*buf)) return -EINVAL; error = fd_statfs(fd, &tmp); if (!error) error = put_compat_statfs64(buf, &tmp); return error; } /* * This is a copy of sys_ustat, just dealing with a structure layout. * Given how simple this syscall is that apporach is more maintainable * than the various conversion hacks. */ asmlinkage long compat_sys_ustat(unsigned dev, struct compat_ustat __user *u) { struct compat_ustat tmp; struct kstatfs sbuf; int err = vfs_ustat(new_decode_dev(dev), &sbuf); if (err) return err; memset(&tmp, 0, sizeof(struct compat_ustat)); tmp.f_tfree = sbuf.f_bfree; tmp.f_tinode = sbuf.f_ffree; if (copy_to_user(u, &tmp, sizeof(struct compat_ustat))) return -EFAULT; return 0; } 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) || __put_user(kfl->l_len, &ufl->l_len) || __put_user(kfl->l_pid, &ufl->l_pid)) return -EFAULT; return 0; } #endif asmlinkage long compat_sys_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg) { mm_segment_t old_fs; struct flock f; long ret; switch (cmd) { case F_GETLK: case F_SETLK: case F_SETLKW: ret = get_compat_flock(&f, compat_ptr(arg)); if (ret != 0) break; old_fs = get_fs(); set_fs(KERNEL_DS); ret = sys_fcntl(fd, cmd, (unsigned long)&f); set_fs(old_fs); if (cmd == F_GETLK && ret == 0) { /* GETLK was successful and we need to return the data... * but it needs to fit in the compat structure. * l_start shouldn't be too big, unless the original * start + end is greater than COMPAT_OFF_T_MAX, in which * case the app was asking for trouble, so we return * -EOVERFLOW in that case. * l_len could be too big, in which case we just truncate it, * and only allow the app to see that part of the conflicting * lock that might make sense to it anyway */ if (f.l_start > COMPAT_OFF_T_MAX) ret = -EOVERFLOW; if (f.l_len > COMPAT_OFF_T_MAX) f.l_len = COMPAT_OFF_T_MAX; if (ret == 0) ret = put_compat_flock(&f, compat_ptr(arg)); } break; case F_GETLK64: case F_SETLK64: case F_SETLKW64: ret = get_compat_flock64(&f, compat_ptr(arg)); if (ret != 0) break; old_fs = get_fs(); set_fs(KERNEL_DS); ret = sys_fcntl(fd, (cmd == F_GETLK64) ? F_GETLK : ((cmd == F_SETLK64) ? F_SETLK : F_SETLKW), (unsigned long)&f); set_fs(old_fs); if (cmd == F_GETLK64 && ret == 0) { /* need to return lock information - see above for commentary */ if (f.l_start > COMPAT_LOFF_T_MAX) ret = -EOVERFLOW; if (f.l_len > COMPAT_LOFF_T_MAX) f.l_len = COMPAT_LOFF_T_MAX; if (ret == 0) ret = put_compat_flock64(&f, compat_ptr(arg)); } break; default: ret = sys_fcntl(fd, cmd, arg); break; } return ret; } asmlinkage long compat_sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) { if ((cmd == F_GETLK64) || (cmd == F_SETLK64) || (cmd == F_SETLKW64)) return -EINVAL; return compat_sys_fcntl64(fd, cmd, arg); } asmlinkage long compat_sys_io_setup(unsigned nr_reqs, u32 __user *ctx32p) { long ret; aio_context_t ctx64; mm_segment_t oldfs = get_fs(); if (unlikely(get_user(ctx64, ctx32p))) return -EFAULT; set_fs(KERNEL_DS); /* The __user pointer cast is valid because of the set_fs() */ ret = sys_io_setup(nr_reqs, (aio_context_t __user *) &ctx64); set_fs(oldfs); /* truncating is ok because it's a user address */ if (!ret) ret = put_user((u32) ctx64, ctx32p); return ret; } asmlinkage long compat_sys_io_getevents(aio_context_t ctx_id,