Commit 204f144c authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'work.compat' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull fs/compat.c cleanups from Al Viro:
 "More moving of compat syscalls from fs/compat.c to fs/*.c where the
  native counterparts live.

  And death to compat_sys_getdents64() - the only architecture that used
  to need it was ia64, and _that_ has lost biarch support quite a few
  years ago"

* 'work.compat' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  fs/compat.c: trim unused includes
  move compat_rw_copy_check_uvector() over to fs/read_write.c
  fhandle: move compat syscalls from compat.c
  open: move compat syscalls from compat.c
  stat: move compat syscalls from compat.c
  fcntl: move compat syscalls from compat.c
  readdir: move compat syscalls from compat.c
  statfs: move compat syscalls from compat.c
  utimes: move compat syscalls from compat.c
  move compat select-related syscalls to fs/select.c
  Remove compat_sys_getdents64()
parents da7b66ff 801b25f1
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
#define __ARCH_WANT_COMPAT_SYS_GETDENTS64
#define __ARCH_WANT_COMPAT_STAT64 #define __ARCH_WANT_COMPAT_STAT64
#define __ARCH_WANT_SYS_GETHOSTNAME #define __ARCH_WANT_SYS_GETHOSTNAME
#define __ARCH_WANT_SYS_PAUSE #define __ARCH_WANT_SYS_PAUSE
......
...@@ -456,7 +456,7 @@ __SYSCALL(__NR_setfsuid32, sys_setfsuid) ...@@ -456,7 +456,7 @@ __SYSCALL(__NR_setfsuid32, sys_setfsuid)
#define __NR_setfsgid32 216 #define __NR_setfsgid32 216
__SYSCALL(__NR_setfsgid32, sys_setfsgid) __SYSCALL(__NR_setfsgid32, sys_setfsgid)
#define __NR_getdents64 217 #define __NR_getdents64 217
__SYSCALL(__NR_getdents64, compat_sys_getdents64) __SYSCALL(__NR_getdents64, sys_getdents64)
#define __NR_pivot_root 218 #define __NR_pivot_root 218
__SYSCALL(__NR_pivot_root, sys_pivot_root) __SYSCALL(__NR_pivot_root, sys_pivot_root)
#define __NR_mincore 219 #define __NR_mincore 219
......
...@@ -226,7 +226,7 @@ ...@@ -226,7 +226,7 @@
217 i386 pivot_root sys_pivot_root 217 i386 pivot_root sys_pivot_root
218 i386 mincore sys_mincore 218 i386 mincore sys_mincore
219 i386 madvise sys_madvise 219 i386 madvise sys_madvise
220 i386 getdents64 sys_getdents64 compat_sys_getdents64 220 i386 getdents64 sys_getdents64
221 i386 fcntl64 sys_fcntl64 compat_sys_fcntl64 221 i386 fcntl64 sys_fcntl64 compat_sys_fcntl64
# 222 is unused # 222 is unused
# 223 is unused # 223 is unused
......
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
# include <asm/unistd_64.h> # include <asm/unistd_64.h>
# include <asm/unistd_64_x32.h> # include <asm/unistd_64_x32.h>
# define __ARCH_WANT_COMPAT_SYS_TIME # define __ARCH_WANT_COMPAT_SYS_TIME
# define __ARCH_WANT_COMPAT_SYS_GETDENTS64
# define __ARCH_WANT_COMPAT_SYS_PREADV64 # define __ARCH_WANT_COMPAT_SYS_PREADV64
# define __ARCH_WANT_COMPAT_SYS_PWRITEV64 # define __ARCH_WANT_COMPAT_SYS_PWRITEV64
# define __ARCH_WANT_COMPAT_SYS_PREADV64V2 # define __ARCH_WANT_COMPAT_SYS_PREADV64V2
......
This diff is collapsed.
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/pid_namespace.h> #include <linux/pid_namespace.h>
#include <linux/user_namespace.h> #include <linux/user_namespace.h>
#include <linux/shmem_fs.h> #include <linux/shmem_fs.h>
#include <linux/compat.h>
#include <asm/poll.h> #include <asm/poll.h>
#include <asm/siginfo.h> #include <asm/siginfo.h>
...@@ -420,6 +421,162 @@ SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd, ...@@ -420,6 +421,162 @@ SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd,
} }
#endif #endif
#ifdef CONFIG_COMPAT
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
static unsigned int
convert_fcntl_cmd(unsigned int cmd)
{
switch (cmd) {
case F_GETLK64:
return F_GETLK;
case F_SETLK64:
return F_SETLK;
case F_SETLKW64:
return F_SETLKW;
}
return cmd;
}
COMPAT_SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd,
compat_ulong_t, arg)
{
mm_segment_t old_fs;
struct flock f;
long ret;
unsigned int conv_cmd;
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:
case F_OFD_GETLK:
case F_OFD_SETLK:
case F_OFD_SETLKW:
ret = get_compat_flock64(&f, compat_ptr(arg));
if (ret != 0)
break;
old_fs = get_fs();
set_fs(KERNEL_DS);
conv_cmd = convert_fcntl_cmd(cmd);
ret = sys_fcntl(fd, conv_cmd, (unsigned long)&f);
set_fs(old_fs);
if ((conv_cmd == F_GETLK || conv_cmd == F_OFD_GETLK) && 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;
}
COMPAT_SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd,
compat_ulong_t, arg)
{
switch (cmd) {
case F_GETLK64:
case F_SETLK64:
case F_SETLKW64:
case F_OFD_GETLK:
case F_OFD_SETLK:
case F_OFD_SETLKW:
return -EINVAL;
}
return compat_sys_fcntl64(fd, cmd, arg);
}
#endif
/* Table to convert sigio signal codes into poll band bitmaps */ /* Table to convert sigio signal codes into poll band bitmaps */
static const long band_table[NSIGPOLL] = { static const long band_table[NSIGPOLL] = {
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/fsnotify.h> #include <linux/fsnotify.h>
#include <linux/personality.h> #include <linux/personality.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/compat.h>
#include "internal.h" #include "internal.h"
#include "mount.h" #include "mount.h"
...@@ -264,3 +265,15 @@ SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd, ...@@ -264,3 +265,15 @@ SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,
ret = do_handle_open(mountdirfd, handle, flags); ret = do_handle_open(mountdirfd, handle, flags);
return ret; return ret;
} }
#ifdef CONFIG_COMPAT
/*
* Exactly like fs/open.c:sys_open_by_handle_at(), except that it
* doesn't set the O_LARGEFILE flag.
*/
COMPAT_SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,
struct file_handle __user *, handle, int, flags)
{
return do_handle_open(mountdirfd, handle, flags);
}
#endif
...@@ -108,8 +108,6 @@ extern struct file *do_filp_open(int dfd, struct filename *pathname, ...@@ -108,8 +108,6 @@ extern struct file *do_filp_open(int dfd, struct filename *pathname,
extern struct file *do_file_open_root(struct dentry *, struct vfsmount *, extern struct file *do_file_open_root(struct dentry *, struct vfsmount *,
const char *, const struct open_flags *); const char *, const struct open_flags *);
extern long do_handle_open(int mountdirfd,
struct file_handle __user *ufh, int open_flag);
extern int open_check_o_direct(struct file *f); extern int open_check_o_direct(struct file *f);
extern int vfs_open(const struct path *, struct file *, const struct cred *); extern int vfs_open(const struct path *, struct file *, const struct cred *);
extern struct file *filp_clone_open(struct file *); extern struct file *filp_clone_open(struct file *);
......
...@@ -1078,6 +1078,26 @@ SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags, ...@@ -1078,6 +1078,26 @@ SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags,
return do_sys_open(dfd, filename, flags, mode); return do_sys_open(dfd, filename, flags, mode);
} }
#ifdef CONFIG_COMPAT
/*
* Exactly like sys_open(), except that it doesn't set the
* O_LARGEFILE flag.
*/
COMPAT_SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{
return do_sys_open(AT_FDCWD, filename, flags, mode);
}
/*
* Exactly like sys_openat(), except that it doesn't set the
* O_LARGEFILE flag.
*/
COMPAT_SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags, umode_t, mode)
{
return do_sys_open(dfd, filename, flags, mode);
}
#endif
#ifndef __alpha__ #ifndef __alpha__
/* /*
......
...@@ -841,6 +841,81 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector, ...@@ -841,6 +841,81 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
return ret; return ret;
} }
#ifdef CONFIG_COMPAT
ssize_t compat_rw_copy_check_uvector(int type,
const struct compat_iovec __user *uvector, unsigned long nr_segs,
unsigned long fast_segs, struct iovec *fast_pointer,
struct iovec **ret_pointer)
{
compat_ssize_t tot_len;
struct iovec *iov = *ret_pointer = fast_pointer;
ssize_t ret = 0;
int seg;
/*
* SuS says "The readv() function *may* fail if the iovcnt argument
* was less than or equal to 0, or greater than {IOV_MAX}. Linux has
* traditionally returned zero for zero segments, so...
*/
if (nr_segs == 0)
goto out;
ret = -EINVAL;
if (nr_segs > UIO_MAXIOV)
goto out;
if (nr_segs > fast_segs) {
ret = -ENOMEM;
iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
if (iov == NULL)
goto out;
}
*ret_pointer = iov;
ret = -EFAULT;
if (!access_ok(VERIFY_READ, uvector, nr_segs*sizeof(*uvector)))
goto out;
/*
* Single unix specification:
* We should -EINVAL if an element length is not >= 0 and fitting an
* ssize_t.
*
* In Linux, the total length is limited to MAX_RW_COUNT, there is
* no overflow possibility.
*/
tot_len = 0;
ret = -EINVAL;
for (seg = 0; seg < nr_segs; seg++) {
compat_uptr_t buf;
compat_ssize_t len;
if (__get_user(len, &uvector->iov_len) ||
__get_user(buf, &uvector->iov_base)) {
ret = -EFAULT;
goto out;
}
if (len < 0) /* size_t not fitting in compat_ssize_t .. */
goto out;
if (type >= 0 &&
!access_ok(vrfy_dir(type), compat_ptr(buf), len)) {
ret = -EFAULT;
goto out;
}
if (len > MAX_RW_COUNT - tot_len)
len = MAX_RW_COUNT - tot_len;
tot_len += len;
iov->iov_base = compat_ptr(buf);
iov->iov_len = (compat_size_t) len;
uvector++;
iov++;
}
ret = tot_len;
out:
return ret;
}
#endif
static ssize_t __do_readv_writev(int type, struct file *file, static ssize_t __do_readv_writev(int type, struct file *file,
struct iov_iter *iter, loff_t *pos, int flags) struct iov_iter *iter, loff_t *pos, int flags)
{ {
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/security.h> #include <linux/security.h>
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/unistd.h> #include <linux/unistd.h>
#include <linux/compat.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
...@@ -324,3 +325,167 @@ SYSCALL_DEFINE3(getdents64, unsigned int, fd, ...@@ -324,3 +325,167 @@ SYSCALL_DEFINE3(getdents64, unsigned int, fd,
fdput_pos(f); fdput_pos(f);
return error; return error;
} }
#ifdef CONFIG_COMPAT
struct compat_old_linux_dirent {
compat_ulong_t d_ino;
compat_ulong_t d_offset;
unsigned short d_namlen;
char d_name[1];
};
struct compat_readdir_callback {
struct dir_context ctx;
struct compat_old_linux_dirent __user *dirent;
int result;
};
static int compat_fillonedir(struct dir_context *ctx, const char *name,
int namlen, loff_t offset, u64 ino,
unsigned int d_type)
{
struct compat_readdir_callback *buf =
container_of(ctx, struct compat_readdir_callback, ctx);
struct compat_old_linux_dirent __user *dirent;
compat_ulong_t d_ino;
if (buf->result)
return -EINVAL;
d_ino = ino;
if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
buf->result = -EOVERFLOW;
return -EOVERFLOW;
}
buf->result++;
dirent = buf->dirent;
if (!access_ok(VERIFY_WRITE, dirent,
(unsigned long)(dirent->d_name + namlen + 1) -
(unsigned long)dirent))
goto efault;
if ( __put_user(d_ino, &dirent->d_ino) ||
__put_user(offset, &dirent->d_offset) ||
__put_user(namlen, &dirent->d_namlen) ||
__copy_to_user(dirent->d_name, name, namlen) ||
__put_user(0, dirent->d_name + namlen))
goto efault;
return 0;
efault:
buf->result = -EFAULT;
return -EFAULT;
}
COMPAT_SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
struct compat_old_linux_dirent __user *, dirent, unsigned int, count)
{
int error;
struct fd f = fdget_pos(fd);
struct compat_readdir_callback buf = {
.ctx.actor = compat_fillonedir,
.dirent = dirent
};
if (!f.file)
return -EBADF;
error = iterate_dir(f.file, &buf.ctx);
if (buf.result)
error = buf.result;
fdput_pos(f);
return error;
}
struct compat_linux_dirent {
compat_ulong_t d_ino;
compat_ulong_t d_off;
unsigned short d_reclen;
char d_name[1];
};
struct compat_getdents_callback {
struct dir_context ctx;
struct compat_linux_dirent __user *current_dir;
struct compat_linux_dirent __user *previous;
int count;
int error;
};
static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
loff_t offset, u64 ino, unsigned int d_type)
{
struct compat_linux_dirent __user * dirent;
struct compat_getdents_callback *buf =
container_of(ctx, struct compat_getdents_callback, ctx);
compat_ulong_t d_ino;
int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) +
namlen + 2, sizeof(compat_long_t));
buf->error = -EINVAL; /* only used if we fail.. */
if (reclen > buf->count)
return -EINVAL;
d_ino = ino;
if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
buf->error = -EOVERFLOW;
return -EOVERFLOW;
}
dirent = buf->previous;
if (dirent) {
if (signal_pending(current))
return -EINTR;
if (__put_user(offset, &dirent->d_off))
goto efault;
}
dirent = buf->current_dir;
if (__put_user(d_ino, &dirent->d_ino))
goto efault;
if (__put_user(reclen, &dirent->d_reclen))
goto efault;
if (copy_to_user(dirent->d_name, name, namlen))
goto efault;
if (__put_user(0, dirent->d_name + namlen))
goto efault;
if (__put_user(d_type, (char __user *) dirent + reclen - 1))
goto efault;
buf->previous = dirent;
dirent = (void __user *)dirent + reclen;
buf->current_dir = dirent;
buf->count -= reclen;
return 0;
efault:
buf->error = -EFAULT;
return -EFAULT;
}
COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
struct compat_linux_dirent __user *, dirent, unsigned int, count)
{
struct fd f;
struct compat_linux_dirent __user * lastdirent;
struct compat_getdents_callback buf = {
.ctx.actor = compat_filldir,
.current_dir = dirent,
.count = count
};
int error;
if (!access_ok(VERIFY_WRITE, dirent, count))
return -EFAULT;
f = fdget_pos(fd);
if (!f.file)
return -EBADF;
error = iterate_dir(f.file, &buf.ctx);
if (error >= 0)
error = buf.error;
lastdirent = buf.previous;
if (lastdirent) {
if (put_user(buf.ctx.pos, &lastdirent->d_off))
error = -EFAULT;
else
error = count - buf.count;
}
fdput_pos(f);
return error;
}
#endif
This diff is collapsed.
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/cred.h> #include <linux/cred.h>
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/compat.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <asm/unistd.h> #include <asm/unistd.h>
...@@ -575,6 +576,91 @@ SYSCALL_DEFINE5(statx, ...@@ -575,6 +576,91 @@ SYSCALL_DEFINE5(statx,
return cp_statx(&stat, buffer); return cp_statx(&stat, buffer);
} }
#ifdef CONFIG_COMPAT
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;
}
COMPAT_SYSCALL_DEFINE2(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);
}
COMPAT_SYSCALL_DEFINE2(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
COMPAT_SYSCALL_DEFINE4(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
COMPAT_SYSCALL_DEFINE2(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;
}
#endif
/* Caller is here responsible for sufficient locking (ie. inode->i_lock) */ /* Caller is here responsible for sufficient locking (ie. inode->i_lock) */
void __inode_add_bytes(struct inode *inode, loff_t bytes) void __inode_add_bytes(struct inode *inode, loff_t bytes)
{ {
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/statfs.h> #include <linux/statfs.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/compat.h>
#include "internal.h" #include "internal.h"
static int flags_by_mnt(int mnt_flags) static int flags_by_mnt(int mnt_flags)
...@@ -239,3 +240,142 @@ SYSCALL_DEFINE2(ustat, unsigned, dev, struct ustat __user *, ubuf) ...@@ -239,3 +240,142 @@ SYSCALL_DEFINE2(ustat, unsigned, dev, struct ustat __user *, ubuf)
return copy_to_user(ubuf, &tmp, sizeof(struct ustat)) ? -EFAULT : 0; return copy_to_user(ubuf, &tmp, sizeof(struct ustat)) ? -EFAULT : 0;
} }
#ifdef CONFIG_COMPAT
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
*/
COMPAT_SYSCALL_DEFINE2(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;
}
COMPAT_SYSCALL_DEFINE2(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_bsize) == 4) {
if ((kbuf->f_type | kbuf->f_bsize | kbuf->f_namelen |
kbuf->f_frsize | kbuf->f_flags) & 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;
}
COMPAT_SYSCALL_DEFINE3(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;
}
COMPAT_SYSCALL_DEFINE3(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.
*/
COMPAT_SYSCALL_DEFINE2(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;
}
#endif
#include <linux/compiler.h>
#include <linux/file.h> #include <linux/file.h>
#include <linux/fs.h>
#include <linux/linkage.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/utime.h> #include <linux/utime.h>
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/compat.h>
#include <asm/unistd.h> #include <asm/unistd.h>
#ifdef __ARCH_WANT_SYS_UTIME #ifdef __ARCH_WANT_SYS_UTIME
...@@ -219,3 +215,63 @@ SYSCALL_DEFINE2(utimes, char __user *, filename, ...@@ -219,3 +215,63 @@ SYSCALL_DEFINE2(utimes, char __user *, filename,
{ {
return sys_futimesat(AT_FDCWD, filename, utimes); return sys_futimesat(AT_FDCWD, filename, utimes);
} }
#ifdef CONFIG_COMPAT
/*
* Not all architectures have sys_utime, so implement this in terms
* of sys_utimes.
*/
COMPAT_SYSCALL_DEFINE2(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);
}
COMPAT_SYSCALL_DEFINE4(utimensat, unsigned int, dfd, const char __user *, filename, struct compat_timespec __user *, t, int, flags)
{
struct timespec tv[2];
if (t) {
if (compat_get_timespec(&tv[0], &t[0]) ||
compat_get_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);
}
COMPAT_SYSCALL_DEFINE3(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);
}
COMPAT_SYSCALL_DEFINE2(utimes, const char __user *, filename, struct compat_timeval __user *, t)
{
return compat_sys_futimesat(AT_FDCWD, filename, t);
}
#endif
...@@ -528,11 +528,6 @@ asmlinkage long compat_sys_old_readdir(unsigned int fd, ...@@ -528,11 +528,6 @@ asmlinkage long compat_sys_old_readdir(unsigned int fd,
asmlinkage long compat_sys_getdents(unsigned int fd, asmlinkage long compat_sys_getdents(unsigned int fd,
struct compat_linux_dirent __user *dirent, struct compat_linux_dirent __user *dirent,
unsigned int count); unsigned int count);
#ifdef __ARCH_WANT_COMPAT_SYS_GETDENTS64
asmlinkage long compat_sys_getdents64(unsigned int fd,
struct linux_dirent64 __user *dirent,
unsigned int count);
#endif
asmlinkage long compat_sys_vmsplice(int fd, const struct compat_iovec __user *, asmlinkage long compat_sys_vmsplice(int fd, const struct compat_iovec __user *,
unsigned int nr_segs, unsigned int flags); unsigned int nr_segs, unsigned int flags);
asmlinkage long compat_sys_open(const char __user *filename, int flags, asmlinkage long compat_sys_open(const char __user *filename, int flags,
......
...@@ -98,64 +98,8 @@ extern int poll_schedule_timeout(struct poll_wqueues *pwq, int state, ...@@ -98,64 +98,8 @@ extern int poll_schedule_timeout(struct poll_wqueues *pwq, int state,
ktime_t *expires, unsigned long slack); ktime_t *expires, unsigned long slack);
extern u64 select_estimate_accuracy(struct timespec64 *tv); extern u64 select_estimate_accuracy(struct timespec64 *tv);
static inline int poll_schedule(struct poll_wqueues *pwq, int state)
{
return poll_schedule_timeout(pwq, state, NULL, 0);
}
/*
* Scalable version of the fd_set.
*/
typedef struct {
unsigned long *in, *out, *ex;
unsigned long *res_in, *res_out, *res_ex;
} fd_set_bits;
/*
* How many longwords for "nr" bits?
*/
#define FDS_BITPERLONG (8*sizeof(long))
#define FDS_LONGS(nr) (((nr)+FDS_BITPERLONG-1)/FDS_BITPERLONG)
#define FDS_BYTES(nr) (FDS_LONGS(nr)*sizeof(long))
/*
* We do a VERIFY_WRITE here even though we are only reading this time:
* we'll write to it eventually..
*
* Use "unsigned long" accesses to let user-mode fd_set's be long-aligned.
*/
static inline
int get_fd_set(unsigned long nr, void __user *ufdset, unsigned long *fdset)
{
nr = FDS_BYTES(nr);
if (ufdset)
return copy_from_user(fdset, ufdset, nr) ? -EFAULT : 0;
memset(fdset, 0, nr);
return 0;
}
static inline unsigned long __must_check
set_fd_set(unsigned long nr, void __user *ufdset, unsigned long *fdset)
{
if (ufdset)
return __copy_to_user(ufdset, fdset, FDS_BYTES(nr));
return 0;
}
static inline
void zero_fd_set(unsigned long nr, unsigned long *fdset)
{
memset(fdset, 0, FDS_BYTES(nr));
}
#define MAX_INT64_SECONDS (((s64)(~((u64)0)>>1)/HZ)-1) #define MAX_INT64_SECONDS (((s64)(~((u64)0)>>1)/HZ)-1)
extern int do_select(int n, fd_set_bits *fds, struct timespec64 *end_time);
extern int do_sys_poll(struct pollfd __user * ufds, unsigned int nfds,
struct timespec64 *end_time);
extern int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp, extern int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp,
fd_set __user *exp, struct timespec64 *end_time); fd_set __user *exp, struct timespec64 *end_time);
......
...@@ -194,8 +194,7 @@ __SYSCALL(__NR_quotactl, sys_quotactl) ...@@ -194,8 +194,7 @@ __SYSCALL(__NR_quotactl, sys_quotactl)
/* fs/readdir.c */ /* fs/readdir.c */
#define __NR_getdents64 61 #define __NR_getdents64 61
#define __ARCH_WANT_COMPAT_SYS_GETDENTS64 __SYSCALL(__NR_getdents64, sys_getdents64)
__SC_COMP(__NR_getdents64, sys_getdents64, compat_sys_getdents64)
/* fs/read_write.c */ /* fs/read_write.c */
#define __NR3264_lseek 62 #define __NR3264_lseek 62
......
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