diff --git a/fs/read_write.c b/fs/read_write.c index 23443657fd57..709dc129b052 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -1076,107 +1076,226 @@ SYSCALL_DEFINE6(pwritev2, unsigned long, fd, const struct iovec __user *, vec, return do_pwritev(fd, vec, vlen, pos, flags); } -/* - * Various compat syscalls. Note that they all pretend to take a native - * iovec - import_iovec will properly treat those as compat_iovecs based on - * in_compat_syscall(). - */ #ifdef CONFIG_COMPAT +static size_t compat_readv(struct file *file, + const struct compat_iovec __user *vec, + unsigned long vlen, loff_t *pos, rwf_t flags) +{ + struct iovec iovstack[UIO_FASTIOV]; + struct iovec *iov = iovstack; + struct iov_iter iter; + ssize_t ret; + + ret = import_iovec(READ, (const struct iovec __user *)vec, vlen, + UIO_FASTIOV, &iov, &iter); + if (ret >= 0) { + ret = do_iter_read(file, &iter, pos, flags); + kfree(iov); + } + if (ret > 0) + add_rchar(current, ret); + inc_syscr(current); + return ret; +} + +static size_t do_compat_readv(compat_ulong_t fd, + const struct compat_iovec __user *vec, + compat_ulong_t vlen, rwf_t flags) +{ + struct fd f = fdget_pos(fd); + ssize_t ret; + loff_t pos; + + if (!f.file) + return -EBADF; + pos = f.file->f_pos; + ret = compat_readv(f.file, vec, vlen, &pos, flags); + if (ret >= 0) + f.file->f_pos = pos; + fdput_pos(f); + return ret; + +} + COMPAT_SYSCALL_DEFINE3(readv, compat_ulong_t, fd, - const struct iovec __user *, vec, + const struct compat_iovec __user *,vec, compat_ulong_t, vlen) { - return do_readv(fd, vec, vlen, 0); + return do_compat_readv(fd, vec, vlen, 0); +} + +static long do_compat_preadv64(unsigned long fd, + const struct compat_iovec __user *vec, + unsigned long vlen, loff_t pos, rwf_t flags) +{ + struct fd f; + ssize_t ret; + + if (pos < 0) + return -EINVAL; + f = fdget(fd); + if (!f.file) + return -EBADF; + ret = -ESPIPE; + if (f.file->f_mode & FMODE_PREAD) + ret = compat_readv(f.file, vec, vlen, &pos, flags); + fdput(f); + return ret; } #ifdef __ARCH_WANT_COMPAT_SYS_PREADV64 COMPAT_SYSCALL_DEFINE4(preadv64, unsigned long, fd, - const struct iovec __user *, vec, + const struct compat_iovec __user *,vec, unsigned long, vlen, loff_t, pos) { - return do_preadv(fd, vec, vlen, pos, 0); + return do_compat_preadv64(fd, vec, vlen, pos, 0); } #endif COMPAT_SYSCALL_DEFINE5(preadv, compat_ulong_t, fd, - const struct iovec __user *, vec, + const struct compat_iovec __user *,vec, compat_ulong_t, vlen, u32, pos_low, u32, pos_high) { loff_t pos = ((loff_t)pos_high << 32) | pos_low; - return do_preadv(fd, vec, vlen, pos, 0); + return do_compat_preadv64(fd, vec, vlen, pos, 0); } #ifdef __ARCH_WANT_COMPAT_SYS_PREADV64V2 COMPAT_SYSCALL_DEFINE5(preadv64v2, unsigned long, fd, - const struct iovec __user *, vec, + const struct compat_iovec __user *,vec, unsigned long, vlen, loff_t, pos, rwf_t, flags) { if (pos == -1) - return do_readv(fd, vec, vlen, flags); - return do_preadv(fd, vec, vlen, pos, flags); + return do_compat_readv(fd, vec, vlen, flags); + + return do_compat_preadv64(fd, vec, vlen, pos, flags); } #endif COMPAT_SYSCALL_DEFINE6(preadv2, compat_ulong_t, fd, - const struct iovec __user *, vec, + const struct compat_iovec __user *,vec, compat_ulong_t, vlen, u32, pos_low, u32, pos_high, rwf_t, flags) { loff_t pos = ((loff_t)pos_high << 32) | pos_low; if (pos == -1) - return do_readv(fd, vec, vlen, flags); - return do_preadv(fd, vec, vlen, pos, flags); + return do_compat_readv(fd, vec, vlen, flags); + + return do_compat_preadv64(fd, vec, vlen, pos, flags); +} + +static size_t compat_writev(struct file *file, + const struct compat_iovec __user *vec, + unsigned long vlen, loff_t *pos, rwf_t flags) +{ + struct iovec iovstack[UIO_FASTIOV]; + struct iovec *iov = iovstack; + struct iov_iter iter; + ssize_t ret; + + ret = import_iovec(WRITE, (const struct iovec __user *)vec, vlen, + UIO_FASTIOV, &iov, &iter); + if (ret >= 0) { + file_start_write(file); + ret = do_iter_write(file, &iter, pos, flags); + file_end_write(file); + kfree(iov); + } + if (ret > 0) + add_wchar(current, ret); + inc_syscw(current); + return ret; +} + +static size_t do_compat_writev(compat_ulong_t fd, + const struct compat_iovec __user* vec, + compat_ulong_t vlen, rwf_t flags) +{ + struct fd f = fdget_pos(fd); + ssize_t ret; + loff_t pos; + + if (!f.file) + return -EBADF; + pos = f.file->f_pos; + ret = compat_writev(f.file, vec, vlen, &pos, flags); + if (ret >= 0) + f.file->f_pos = pos; + fdput_pos(f); + return ret; } COMPAT_SYSCALL_DEFINE3(writev, compat_ulong_t, fd, - const struct iovec __user *, vec, + const struct compat_iovec __user *, vec, compat_ulong_t, vlen) { - return do_writev(fd, vec, vlen, 0); + return do_compat_writev(fd, vec, vlen, 0); +} + +static long do_compat_pwritev64(unsigned long fd, + const struct compat_iovec __user *vec, + unsigned long vlen, loff_t pos, rwf_t flags) +{ + struct fd f; + ssize_t ret; + + if (pos < 0) + return -EINVAL; + f = fdget(fd); + if (!f.file) + return -EBADF; + ret = -ESPIPE; + if (f.file->f_mode & FMODE_PWRITE) + ret = compat_writev(f.file, vec, vlen, &pos, flags); + fdput(f); + return ret; } #ifdef __ARCH_WANT_COMPAT_SYS_PWRITEV64 COMPAT_SYSCALL_DEFINE4(pwritev64, unsigned long, fd, - const struct iovec __user *, vec, + const struct compat_iovec __user *,vec, unsigned long, vlen, loff_t, pos) { - return do_pwritev(fd, vec, vlen, pos, 0); + return do_compat_pwritev64(fd, vec, vlen, pos, 0); } #endif COMPAT_SYSCALL_DEFINE5(pwritev, compat_ulong_t, fd, - const struct iovec __user *,vec, + const struct compat_iovec __user *,vec, compat_ulong_t, vlen, u32, pos_low, u32, pos_high) { loff_t pos = ((loff_t)pos_high << 32) | pos_low; - return do_pwritev(fd, vec, vlen, pos, 0); + return do_compat_pwritev64(fd, vec, vlen, pos, 0); } #ifdef __ARCH_WANT_COMPAT_SYS_PWRITEV64V2 COMPAT_SYSCALL_DEFINE5(pwritev64v2, unsigned long, fd, - const struct iovec __user *, vec, + const struct compat_iovec __user *,vec, unsigned long, vlen, loff_t, pos, rwf_t, flags) { if (pos == -1) - return do_writev(fd, vec, vlen, flags); - return do_pwritev(fd, vec, vlen, pos, flags); + return do_compat_writev(fd, vec, vlen, flags); + + return do_compat_pwritev64(fd, vec, vlen, pos, flags); } #endif COMPAT_SYSCALL_DEFINE6(pwritev2, compat_ulong_t, fd, - const struct iovec __user *,vec, + const struct compat_iovec __user *,vec, compat_ulong_t, vlen, u32, pos_low, u32, pos_high, rwf_t, flags) { loff_t pos = ((loff_t)pos_high << 32) | pos_low; if (pos == -1) - return do_writev(fd, vec, vlen, flags); - return do_pwritev(fd, vec, vlen, pos, flags); + return do_compat_writev(fd, vec, vlen, flags); + + return do_compat_pwritev64(fd, vec, vlen, pos, flags); } -#endif /* CONFIG_COMPAT */ + +#endif static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, size_t count, loff_t max) diff --git a/include/linux/compat.h b/include/linux/compat.h index bebeb0f5e825..7b02522ddf8e 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -546,25 +546,25 @@ asmlinkage long compat_sys_getdents(unsigned int fd, /* fs/read_write.c */ asmlinkage long compat_sys_lseek(unsigned int, compat_off_t, unsigned int); asmlinkage ssize_t compat_sys_readv(compat_ulong_t fd, - const struct iovec __user *vec, compat_ulong_t vlen); + const struct compat_iovec __user *vec, compat_ulong_t vlen); asmlinkage ssize_t compat_sys_writev(compat_ulong_t fd, - const struct iovec __user *vec, compat_ulong_t vlen); + const struct compat_iovec __user *vec, compat_ulong_t vlen); /* No generic prototype for pread64 and pwrite64 */ asmlinkage ssize_t compat_sys_preadv(compat_ulong_t fd, - const struct iovec __user *vec, + const struct compat_iovec __user *vec, compat_ulong_t vlen, u32 pos_low, u32 pos_high); asmlinkage ssize_t compat_sys_pwritev(compat_ulong_t fd, - const struct iovec __user *vec, + const struct compat_iovec __user *vec, compat_ulong_t vlen, u32 pos_low, u32 pos_high); #ifdef __ARCH_WANT_COMPAT_SYS_PREADV64 asmlinkage long compat_sys_preadv64(unsigned long fd, - const struct iovec __user *vec, + const struct compat_iovec __user *vec, unsigned long vlen, loff_t pos); #endif #ifdef __ARCH_WANT_COMPAT_SYS_PWRITEV64 asmlinkage long compat_sys_pwritev64(unsigned long fd, - const struct iovec __user *vec, + const struct compat_iovec __user *vec, unsigned long vlen, loff_t pos); #endif @@ -800,20 +800,20 @@ asmlinkage long compat_sys_execveat(int dfd, const char __user *filename, const compat_uptr_t __user *argv, const compat_uptr_t __user *envp, int flags); asmlinkage ssize_t compat_sys_preadv2(compat_ulong_t fd, - const struct iovec __user *vec, + const struct compat_iovec __user *vec, compat_ulong_t vlen, u32 pos_low, u32 pos_high, rwf_t flags); asmlinkage ssize_t compat_sys_pwritev2(compat_ulong_t fd, - const struct iovec __user *vec, + const struct compat_iovec __user *vec, compat_ulong_t vlen, u32 pos_low, u32 pos_high, rwf_t flags); #ifdef __ARCH_WANT_COMPAT_SYS_PREADV64V2 asmlinkage long compat_sys_preadv64v2(unsigned long fd, - const struct iovec __user *vec, + const struct compat_iovec __user *vec, unsigned long vlen, loff_t pos, rwf_t flags); #endif #ifdef __ARCH_WANT_COMPAT_SYS_PWRITEV64V2 asmlinkage long compat_sys_pwritev64v2(unsigned long fd, - const struct iovec __user *vec, + const struct compat_iovec __user *vec, unsigned long vlen, loff_t pos, rwf_t flags); #endif