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()
This commit is contained in:
commit
204f144c9f
|
@ -14,7 +14,6 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef CONFIG_COMPAT
|
||||
#define __ARCH_WANT_COMPAT_SYS_GETDENTS64
|
||||
#define __ARCH_WANT_COMPAT_STAT64
|
||||
#define __ARCH_WANT_SYS_GETHOSTNAME
|
||||
#define __ARCH_WANT_SYS_PAUSE
|
||||
|
|
|
@ -456,7 +456,7 @@ __SYSCALL(__NR_setfsuid32, sys_setfsuid)
|
|||
#define __NR_setfsgid32 216
|
||||
__SYSCALL(__NR_setfsgid32, sys_setfsgid)
|
||||
#define __NR_getdents64 217
|
||||
__SYSCALL(__NR_getdents64, compat_sys_getdents64)
|
||||
__SYSCALL(__NR_getdents64, sys_getdents64)
|
||||
#define __NR_pivot_root 218
|
||||
__SYSCALL(__NR_pivot_root, sys_pivot_root)
|
||||
#define __NR_mincore 219
|
||||
|
|
|
@ -226,7 +226,7 @@
|
|||
217 i386 pivot_root sys_pivot_root
|
||||
218 i386 mincore sys_mincore
|
||||
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
|
||||
# 222 is unused
|
||||
# 223 is unused
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
# include <asm/unistd_64.h>
|
||||
# include <asm/unistd_64_x32.h>
|
||||
# define __ARCH_WANT_COMPAT_SYS_TIME
|
||||
# define __ARCH_WANT_COMPAT_SYS_GETDENTS64
|
||||
# define __ARCH_WANT_COMPAT_SYS_PREADV64
|
||||
# define __ARCH_WANT_COMPAT_SYS_PWRITEV64
|
||||
# define __ARCH_WANT_COMPAT_SYS_PREADV64V2
|
||||
|
|
1191
fs/compat.c
1191
fs/compat.c
File diff suppressed because it is too large
Load Diff
157
fs/fcntl.c
157
fs/fcntl.c
|
@ -23,6 +23,7 @@
|
|||
#include <linux/pid_namespace.h>
|
||||
#include <linux/user_namespace.h>
|
||||
#include <linux/shmem_fs.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
#include <asm/poll.h>
|
||||
#include <asm/siginfo.h>
|
||||
|
@ -420,6 +421,162 @@ out:
|
|||
}
|
||||
#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 */
|
||||
|
||||
static const long band_table[NSIGPOLL] = {
|
||||
|
|
13
fs/fhandle.c
13
fs/fhandle.c
|
@ -9,6 +9,7 @@
|
|||
#include <linux/fsnotify.h>
|
||||
#include <linux/personality.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/compat.h>
|
||||
#include "internal.h"
|
||||
#include "mount.h"
|
||||
|
||||
|
@ -264,3 +265,15 @@ SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,
|
|||
ret = do_handle_open(mountdirfd, handle, flags);
|
||||
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,
|
|||
extern struct file *do_file_open_root(struct dentry *, struct vfsmount *,
|
||||
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 vfs_open(const struct path *, struct file *, const struct cred *);
|
||||
extern struct file *filp_clone_open(struct file *);
|
||||
|
|
20
fs/open.c
20
fs/open.c
|
@ -1078,6 +1078,26 @@ SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags,
|
|||
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__
|
||||
|
||||
/*
|
||||
|
|
|
@ -841,6 +841,81 @@ out:
|
|||
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,
|
||||
struct iov_iter *iter, loff_t *pos, int flags)
|
||||
{
|
||||
|
|
165
fs/readdir.c
165
fs/readdir.c
|
@ -18,6 +18,7 @@
|
|||
#include <linux/security.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
|
@ -324,3 +325,167 @@ SYSCALL_DEFINE3(getdents64, unsigned int, fd,
|
|||
fdput_pos(f);
|
||||
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
|
||||
|
|
421
fs/select.c
421
fs/select.c
|
@ -338,6 +338,53 @@ sticky:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 FDS_IN(fds, n) (fds->in + n)
|
||||
#define FDS_OUT(fds, n) (fds->out + n)
|
||||
#define FDS_EX(fds, n) (fds->ex + n)
|
||||
|
@ -401,7 +448,7 @@ static inline void wait_key_set(poll_table *wait, unsigned long in,
|
|||
wait->_key |= POLLOUT_SET;
|
||||
}
|
||||
|
||||
int do_select(int n, fd_set_bits *fds, struct timespec64 *end_time)
|
||||
static int do_select(int n, fd_set_bits *fds, struct timespec64 *end_time)
|
||||
{
|
||||
ktime_t expire, *to = NULL;
|
||||
struct poll_wqueues table;
|
||||
|
@ -881,7 +928,7 @@ static int do_poll(struct poll_list *list, struct poll_wqueues *wait,
|
|||
#define N_STACK_PPS ((sizeof(stack_pps) - sizeof(struct poll_list)) / \
|
||||
sizeof(struct pollfd))
|
||||
|
||||
int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds,
|
||||
static int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds,
|
||||
struct timespec64 *end_time)
|
||||
{
|
||||
struct poll_wqueues table;
|
||||
|
@ -1053,3 +1100,373 @@ SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
#define __COMPAT_NFDBITS (8 * sizeof(compat_ulong_t))
|
||||
|
||||
static
|
||||
int compat_poll_select_copy_remaining(struct timespec *end_time, void __user *p,
|
||||
int timeval, int ret)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
if (!p)
|
||||
return ret;
|
||||
|
||||
if (current->personality & STICKY_TIMEOUTS)
|
||||
goto sticky;
|
||||
|
||||
/* No update for zero timeout */
|
||||
if (!end_time->tv_sec && !end_time->tv_nsec)
|
||||
return ret;
|
||||
|
||||
ktime_get_ts(&ts);
|
||||
ts = timespec_sub(*end_time, ts);
|
||||
if (ts.tv_sec < 0)
|
||||
ts.tv_sec = ts.tv_nsec = 0;
|
||||
|
||||
if (timeval) {
|
||||
struct compat_timeval rtv;
|
||||
|
||||
rtv.tv_sec = ts.tv_sec;
|
||||
rtv.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
|
||||
|
||||
if (!copy_to_user(p, &rtv, sizeof(rtv)))
|
||||
return ret;
|
||||
} else {
|
||||
struct compat_timespec rts;
|
||||
|
||||
rts.tv_sec = ts.tv_sec;
|
||||
rts.tv_nsec = ts.tv_nsec;
|
||||
|
||||
if (!copy_to_user(p, &rts, sizeof(rts)))
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* If an application puts its timeval in read-only memory, we
|
||||
* don't want the Linux-specific update to the timeval to
|
||||
* cause a fault after the select has completed
|
||||
* successfully. However, because we're not updating the
|
||||
* timeval, we can't restart the system call.
|
||||
*/
|
||||
|
||||
sticky:
|
||||
if (ret == -ERESTARTNOHAND)
|
||||
ret = -EINTR;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ooo, nasty. We need here to frob 32-bit unsigned longs to
|
||||
* 64-bit unsigned longs.
|
||||
*/
|
||||
static
|
||||
int compat_get_fd_set(unsigned long nr, compat_ulong_t __user *ufdset,
|
||||
unsigned long *fdset)
|
||||
{
|
||||
nr = DIV_ROUND_UP(nr, __COMPAT_NFDBITS);
|
||||
if (ufdset) {
|
||||
unsigned long odd;
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, ufdset, nr*sizeof(compat_ulong_t)))
|
||||
return -EFAULT;
|
||||
|
||||
odd = nr & 1UL;
|
||||
nr &= ~1UL;
|
||||
while (nr) {
|
||||
unsigned long h, l;
|
||||
if (__get_user(l, ufdset) || __get_user(h, ufdset+1))
|
||||
return -EFAULT;
|
||||
ufdset += 2;
|
||||
*fdset++ = h << 32 | l;
|
||||
nr -= 2;
|
||||
}
|
||||
if (odd && __get_user(*fdset, ufdset))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
/* Tricky, must clear full unsigned long in the
|
||||
* kernel fdset at the end, this makes sure that
|
||||
* actually happens.
|
||||
*/
|
||||
memset(fdset, 0, ((nr + 1) & ~1)*sizeof(compat_ulong_t));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int compat_set_fd_set(unsigned long nr, compat_ulong_t __user *ufdset,
|
||||
unsigned long *fdset)
|
||||
{
|
||||
unsigned long odd;
|
||||
nr = DIV_ROUND_UP(nr, __COMPAT_NFDBITS);
|
||||
|
||||
if (!ufdset)
|
||||
return 0;
|
||||
|
||||
odd = nr & 1UL;
|
||||
nr &= ~1UL;
|
||||
while (nr) {
|
||||
unsigned long h, l;
|
||||
l = *fdset++;
|
||||
h = l >> 32;
|
||||
if (__put_user(l, ufdset) || __put_user(h, ufdset+1))
|
||||
return -EFAULT;
|
||||
ufdset += 2;
|
||||
nr -= 2;
|
||||
}
|
||||
if (odd && __put_user(*fdset, ufdset))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This is a virtual copy of sys_select from fs/select.c and probably
|
||||
* should be compared to it from time to time
|
||||
*/
|
||||
|
||||
/*
|
||||
* We can actually return ERESTARTSYS instead of EINTR, but I'd
|
||||
* like to be certain this leads to no problems. So I return
|
||||
* EINTR just for safety.
|
||||
*
|
||||
* Update: ERESTARTSYS breaks at least the xview clock binary, so
|
||||
* I'm trying ERESTARTNOHAND which restart only when you want to.
|
||||
*/
|
||||
static int compat_core_sys_select(int n, compat_ulong_t __user *inp,
|
||||
compat_ulong_t __user *outp, compat_ulong_t __user *exp,
|
||||
struct timespec *end_time)
|
||||
{
|
||||
fd_set_bits fds;
|
||||
void *bits;
|
||||
int size, max_fds, ret = -EINVAL;
|
||||
struct fdtable *fdt;
|
||||
long stack_fds[SELECT_STACK_ALLOC/sizeof(long)];
|
||||
|
||||
if (n < 0)
|
||||
goto out_nofds;
|
||||
|
||||
/* max_fds can increase, so grab it once to avoid race */
|
||||
rcu_read_lock();
|
||||
fdt = files_fdtable(current->files);
|
||||
max_fds = fdt->max_fds;
|
||||
rcu_read_unlock();
|
||||
if (n > max_fds)
|
||||
n = max_fds;
|
||||
|
||||
/*
|
||||
* We need 6 bitmaps (in/out/ex for both incoming and outgoing),
|
||||
* since we used fdset we need to allocate memory in units of
|
||||
* long-words.
|
||||
*/
|
||||
size = FDS_BYTES(n);
|
||||
bits = stack_fds;
|
||||
if (size > sizeof(stack_fds) / 6) {
|
||||
bits = kmalloc(6 * size, GFP_KERNEL);
|
||||
ret = -ENOMEM;
|
||||
if (!bits)
|
||||
goto out_nofds;
|
||||
}
|
||||
fds.in = (unsigned long *) bits;
|
||||
fds.out = (unsigned long *) (bits + size);
|
||||
fds.ex = (unsigned long *) (bits + 2*size);
|
||||
fds.res_in = (unsigned long *) (bits + 3*size);
|
||||
fds.res_out = (unsigned long *) (bits + 4*size);
|
||||
fds.res_ex = (unsigned long *) (bits + 5*size);
|
||||
|
||||
if ((ret = compat_get_fd_set(n, inp, fds.in)) ||
|
||||
(ret = compat_get_fd_set(n, outp, fds.out)) ||
|
||||
(ret = compat_get_fd_set(n, exp, fds.ex)))
|
||||
goto out;
|
||||
zero_fd_set(n, fds.res_in);
|
||||
zero_fd_set(n, fds.res_out);
|
||||
zero_fd_set(n, fds.res_ex);
|
||||
|
||||
ret = do_select(n, &fds, end_time);
|
||||
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (!ret) {
|
||||
ret = -ERESTARTNOHAND;
|
||||
if (signal_pending(current))
|
||||
goto out;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (compat_set_fd_set(n, inp, fds.res_in) ||
|
||||
compat_set_fd_set(n, outp, fds.res_out) ||
|
||||
compat_set_fd_set(n, exp, fds.res_ex))
|
||||
ret = -EFAULT;
|
||||
out:
|
||||
if (bits != stack_fds)
|
||||
kfree(bits);
|
||||
out_nofds:
|
||||
return ret;
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE5(select, int, n, compat_ulong_t __user *, inp,
|
||||
compat_ulong_t __user *, outp, compat_ulong_t __user *, exp,
|
||||
struct compat_timeval __user *, tvp)
|
||||
{
|
||||
struct timespec end_time, *to = NULL;
|
||||
struct compat_timeval tv;
|
||||
int ret;
|
||||
|
||||
if (tvp) {
|
||||
if (copy_from_user(&tv, tvp, sizeof(tv)))
|
||||
return -EFAULT;
|
||||
|
||||
to = &end_time;
|
||||
if (poll_select_set_timeout(to,
|
||||
tv.tv_sec + (tv.tv_usec / USEC_PER_SEC),
|
||||
(tv.tv_usec % USEC_PER_SEC) * NSEC_PER_USEC))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = compat_core_sys_select(n, inp, outp, exp, to);
|
||||
ret = compat_poll_select_copy_remaining(&end_time, tvp, 1, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct compat_sel_arg_struct {
|
||||
compat_ulong_t n;
|
||||
compat_uptr_t inp;
|
||||
compat_uptr_t outp;
|
||||
compat_uptr_t exp;
|
||||
compat_uptr_t tvp;
|
||||
};
|
||||
|
||||
COMPAT_SYSCALL_DEFINE1(old_select, struct compat_sel_arg_struct __user *, arg)
|
||||
{
|
||||
struct compat_sel_arg_struct a;
|
||||
|
||||
if (copy_from_user(&a, arg, sizeof(a)))
|
||||
return -EFAULT;
|
||||
return compat_sys_select(a.n, compat_ptr(a.inp), compat_ptr(a.outp),
|
||||
compat_ptr(a.exp), compat_ptr(a.tvp));
|
||||
}
|
||||
|
||||
static long do_compat_pselect(int n, compat_ulong_t __user *inp,
|
||||
compat_ulong_t __user *outp, compat_ulong_t __user *exp,
|
||||
struct compat_timespec __user *tsp, compat_sigset_t __user *sigmask,
|
||||
compat_size_t sigsetsize)
|
||||
{
|
||||
compat_sigset_t ss32;
|
||||
sigset_t ksigmask, sigsaved;
|
||||
struct compat_timespec ts;
|
||||
struct timespec end_time, *to = NULL;
|
||||
int ret;
|
||||
|
||||
if (tsp) {
|
||||
if (copy_from_user(&ts, tsp, sizeof(ts)))
|
||||
return -EFAULT;
|
||||
|
||||
to = &end_time;
|
||||
if (poll_select_set_timeout(to, ts.tv_sec, ts.tv_nsec))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (sigmask) {
|
||||
if (sigsetsize != sizeof(compat_sigset_t))
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&ss32, sigmask, sizeof(ss32)))
|
||||
return -EFAULT;
|
||||
sigset_from_compat(&ksigmask, &ss32);
|
||||
|
||||
sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP));
|
||||
sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
|
||||
}
|
||||
|
||||
ret = compat_core_sys_select(n, inp, outp, exp, to);
|
||||
ret = compat_poll_select_copy_remaining(&end_time, tsp, 0, ret);
|
||||
|
||||
if (ret == -ERESTARTNOHAND) {
|
||||
/*
|
||||
* Don't restore the signal mask yet. Let do_signal() deliver
|
||||
* the signal on the way back to userspace, before the signal
|
||||
* mask is restored.
|
||||
*/
|
||||
if (sigmask) {
|
||||
memcpy(¤t->saved_sigmask, &sigsaved,
|
||||
sizeof(sigsaved));
|
||||
set_restore_sigmask();
|
||||
}
|
||||
} else if (sigmask)
|
||||
sigprocmask(SIG_SETMASK, &sigsaved, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE6(pselect6, int, n, compat_ulong_t __user *, inp,
|
||||
compat_ulong_t __user *, outp, compat_ulong_t __user *, exp,
|
||||
struct compat_timespec __user *, tsp, void __user *, sig)
|
||||
{
|
||||
compat_size_t sigsetsize = 0;
|
||||
compat_uptr_t up = 0;
|
||||
|
||||
if (sig) {
|
||||
if (!access_ok(VERIFY_READ, sig,
|
||||
sizeof(compat_uptr_t)+sizeof(compat_size_t)) ||
|
||||
__get_user(up, (compat_uptr_t __user *)sig) ||
|
||||
__get_user(sigsetsize,
|
||||
(compat_size_t __user *)(sig+sizeof(up))))
|
||||
return -EFAULT;
|
||||
}
|
||||
return do_compat_pselect(n, inp, outp, exp, tsp, compat_ptr(up),
|
||||
sigsetsize);
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds,
|
||||
unsigned int, nfds, struct compat_timespec __user *, tsp,
|
||||
const compat_sigset_t __user *, sigmask, compat_size_t, sigsetsize)
|
||||
{
|
||||
compat_sigset_t ss32;
|
||||
sigset_t ksigmask, sigsaved;
|
||||
struct compat_timespec ts;
|
||||
struct timespec end_time, *to = NULL;
|
||||
int ret;
|
||||
|
||||
if (tsp) {
|
||||
if (copy_from_user(&ts, tsp, sizeof(ts)))
|
||||
return -EFAULT;
|
||||
|
||||
to = &end_time;
|
||||
if (poll_select_set_timeout(to, ts.tv_sec, ts.tv_nsec))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (sigmask) {
|
||||
if (sigsetsize != sizeof(compat_sigset_t))
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&ss32, sigmask, sizeof(ss32)))
|
||||
return -EFAULT;
|
||||
sigset_from_compat(&ksigmask, &ss32);
|
||||
|
||||
sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP));
|
||||
sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
|
||||
}
|
||||
|
||||
ret = do_sys_poll(ufds, nfds, to);
|
||||
|
||||
/* We can restart this syscall, usually */
|
||||
if (ret == -EINTR) {
|
||||
/*
|
||||
* Don't restore the signal mask yet. Let do_signal() deliver
|
||||
* the signal on the way back to userspace, before the signal
|
||||
* mask is restored.
|
||||
*/
|
||||
if (sigmask) {
|
||||
memcpy(¤t->saved_sigmask, &sigsaved,
|
||||
sizeof(sigsaved));
|
||||
set_restore_sigmask();
|
||||
}
|
||||
ret = -ERESTARTNOHAND;
|
||||
} else if (sigmask)
|
||||
sigprocmask(SIG_SETMASK, &sigsaved, NULL);
|
||||
|
||||
ret = compat_poll_select_copy_remaining(&end_time, tsp, 0, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
|
86
fs/stat.c
86
fs/stat.c
|
@ -15,6 +15,7 @@
|
|||
#include <linux/cred.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/unistd.h>
|
||||
|
@ -575,6 +576,91 @@ SYSCALL_DEFINE5(statx,
|
|||
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) */
|
||||
void __inode_add_bytes(struct inode *inode, loff_t bytes)
|
||||
{
|
||||
|
|
140
fs/statfs.c
140
fs/statfs.c
|
@ -7,6 +7,7 @@
|
|||
#include <linux/statfs.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/compat.h>
|
||||
#include "internal.h"
|
||||
|
||||
static int flags_by_mnt(int mnt_flags)
|
||||
|
@ -239,3 +240,142 @@ SYSCALL_DEFINE2(ustat, unsigned, dev, struct ustat __user *, ubuf)
|
|||
|
||||
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
|
||||
|
|
66
fs/utimes.c
66
fs/utimes.c
|
@ -1,14 +1,10 @@
|
|||
#include <linux/compiler.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/linkage.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/utime.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/compat.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
#ifdef __ARCH_WANT_SYS_UTIME
|
||||
|
@ -219,3 +215,63 @@ SYSCALL_DEFINE2(utimes, char __user *, filename,
|
|||
{
|
||||
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,
|
|||
asmlinkage long compat_sys_getdents(unsigned int fd,
|
||||
struct compat_linux_dirent __user *dirent,
|
||||
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 *,
|
||||
unsigned int nr_segs, unsigned 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,
|
|||
ktime_t *expires, unsigned long slack);
|
||||
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)
|
||||
|
||||
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,
|
||||
fd_set __user *exp, struct timespec64 *end_time);
|
||||
|
||||
|
|
|
@ -194,8 +194,7 @@ __SYSCALL(__NR_quotactl, sys_quotactl)
|
|||
|
||||
/* fs/readdir.c */
|
||||
#define __NR_getdents64 61
|
||||
#define __ARCH_WANT_COMPAT_SYS_GETDENTS64
|
||||
__SC_COMP(__NR_getdents64, sys_getdents64, compat_sys_getdents64)
|
||||
__SYSCALL(__NR_getdents64, sys_getdents64)
|
||||
|
||||
/* fs/read_write.c */
|
||||
#define __NR3264_lseek 62
|
||||
|
|
Loading…
Reference in New Issue