| /* |
| * 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 |
| * |
| * 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 <asm/uaccess.h> |
| |
| /* |
| * Not all architectures have sys_utime, so implement this in terms |
| * of sys_utimes. |
| */ |
| asmlinkage long compat_sys_utime(char *filename, struct compat_utimbuf *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(filename, t ? tv : NULL); |
| } |
| |
| |
| asmlinkage long compat_sys_newstat(char * filename, |
| struct compat_stat *statbuf) |
| { |
| struct kstat stat; |
| int error = vfs_stat(filename, &stat); |
| |
| if (!error) |
| error = cp_compat_stat(&stat, statbuf); |
| return error; |
| } |
| |
| asmlinkage long compat_sys_newlstat(char * filename, |
| struct compat_stat *statbuf) |
| { |
| struct kstat stat; |
| int error = vfs_lstat(filename, &stat); |
| |
| if (!error) |
| error = cp_compat_stat(&stat, statbuf); |
| return error; |
| } |
| |
| asmlinkage long compat_sys_newfstat(unsigned int fd, |
| struct compat_stat * 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 *ubuf, struct statfs *kbuf) |
| { |
| if (verify_area(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])) |
| 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 *path, struct compat_statfs *buf) |
| { |
| struct nameidata nd; |
| int error; |
| |
| error = user_path_walk(path, &nd); |
| if (!error) { |
| struct statfs tmp; |
| error = vfs_statfs(nd.dentry->d_inode->i_sb, &tmp); |
| if (!error && put_compat_statfs(buf, &tmp)) |
| error = -EFAULT; |
| path_release(&nd); |
| } |
| return error; |
| } |
| |
| asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs *buf) |
| { |
| struct file * file; |
| struct statfs 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 && put_compat_statfs(buf, &tmp)) |
| error = -EFAULT; |
| fput(file); |
| out: |
| return error; |
| } |
| |
| static int get_compat_flock(struct flock *kfl, struct compat_flock *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 *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; |
| } |
| |
| static int get_compat_flock64(struct flock *kfl, struct compat_flock64 *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_flock64(struct flock *kfl, struct compat_flock64 *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; |
| } |
| |
| extern asmlinkage long sys_fcntl(unsigned int, unsigned int, unsigned long); |
| |
| 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)) { |
| if ((f.l_start >= COMPAT_OFF_T_MAX) || |
| ((f.l_start + f.l_len) >= COMPAT_OFF_T_MAX)) |
| ret = -EOVERFLOW; |
| 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, F_GETLK, (unsigned long)&f); |
| 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)) { |
| if ((f.l_start >= COMPAT_LOFF_T_MAX) || |
| ((f.l_start + f.l_len) >= COMPAT_LOFF_T_MAX)) |
| ret = -EOVERFLOW; |
| 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); |
| } |
| |