| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- /*
- * linux/fs/compat.c
- *
- * Kernel compatibililty routines for e.g. 32 bit syscall support
- * on 64 bit kernels.
- *
- * Copyright (C) 2002 Stephen Rothwell, IBM Corporation
- * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com)
- * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
- * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs
- * Copyright (C) 2003 Pavel Machek (pavel@ucw.cz)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
- #include <linux/stddef.h>
- #include <linux/kernel.h>
- #include <linux/linkage.h>
- #include <linux/compat.h>
- #include <linux/errno.h>
- #include <linux/time.h>
- #include <linux/cred.h>
- #include <linux/fs.h>
- #include <linux/fcntl.h>
- #include <linux/namei.h>
- #include <linux/file.h>
- #include <linux/fdtable.h>
- #include <linux/vfs.h>
- #include <linux/ioctl.h>
- #include <linux/init.h>
- #include <linux/ncp_mount.h>
- #include <linux/nfs4_mount.h>
- #include <linux/syscalls.h>
- #include <linux/ctype.h>
- #include <linux/dirent.h>
- #include <linux/fsnotify.h>
- #include <linux/highuid.h>
- #include <linux/personality.h>
- #include <linux/rwsem.h>
- #include <linux/tsacct_kern.h>
- #include <linux/security.h>
- #include <linux/highmem.h>
- #include <linux/signal.h>
- #include <linux/mm.h>
- #include <linux/fs_struct.h>
- #include <linux/slab.h>
- #include <linux/pagemap.h>
- #include <linux/aio.h>
- #include <linux/uaccess.h>
- #include <asm/mmu_context.h>
- #include <asm/ioctls.h>
- #include "internal.h"
- 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;
- }
- /* A write operation does a read from user space and vice versa */
- #define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ)
- 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;
- }
- struct compat_ncp_mount_data {
- compat_int_t version;
- compat_uint_t ncp_fd;
- __compat_uid_t mounted_uid;
- compat_pid_t wdog_pid;
- unsigned char mounted_vol[NCP_VOLNAME_LEN + 1];
- compat_uint_t time_out;
- compat_uint_t retry_count;
- compat_uint_t flags;
- __compat_uid_t uid;
- __compat_gid_t gid;
- compat_mode_t file_mode;
- compat_mode_t dir_mode;
- };
- struct compat_ncp_mount_data_v4 {
- compat_int_t version;
- compat_ulong_t flags;
- compat_ulong_t mounted_uid;
- compat_long_t wdog_pid;
- compat_uint_t ncp_fd;
- compat_uint_t time_out;
- compat_uint_t retry_count;
- compat_ulong_t uid;
- compat_ulong_t gid;
- compat_ulong_t file_mode;
- compat_ulong_t dir_mode;
- };
- static void *do_ncp_super_data_conv(void *raw_data)
- {
- int version = *(unsigned int *)raw_data;
- if (version == 3) {
- struct compat_ncp_mount_data *c_n = raw_data;
- struct ncp_mount_data *n = raw_data;
- n->dir_mode = c_n->dir_mode;
- n->file_mode = c_n->file_mode;
- n->gid = c_n->gid;
- n->uid = c_n->uid;
- memmove (n->mounted_vol, c_n->mounted_vol, (sizeof (c_n->mounted_vol) + 3 * sizeof (unsigned int)));
- n->wdog_pid = c_n->wdog_pid;
- n->mounted_uid = c_n->mounted_uid;
- } else if (version == 4) {
- struct compat_ncp_mount_data_v4 *c_n = raw_data;
- struct ncp_mount_data_v4 *n = raw_data;
- n->dir_mode = c_n->dir_mode;
- n->file_mode = c_n->file_mode;
- n->gid = c_n->gid;
- n->uid = c_n->uid;
- n->retry_count = c_n->retry_count;
- n->time_out = c_n->time_out;
- n->ncp_fd = c_n->ncp_fd;
- n->wdog_pid = c_n->wdog_pid;
- n->mounted_uid = c_n->mounted_uid;
- n->flags = c_n->flags;
- } else if (version != 5) {
- return NULL;
- }
- return raw_data;
- }
- struct compat_nfs_string {
- compat_uint_t len;
- compat_uptr_t data;
- };
- static inline void compat_nfs_string(struct nfs_string *dst,
- struct compat_nfs_string *src)
- {
- dst->data = compat_ptr(src->data);
- dst->len = src->len;
- }
- struct compat_nfs4_mount_data_v1 {
- compat_int_t version;
- compat_int_t flags;
- compat_int_t rsize;
- compat_int_t wsize;
- compat_int_t timeo;
- compat_int_t retrans;
- compat_int_t acregmin;
- compat_int_t acregmax;
- compat_int_t acdirmin;
- compat_int_t acdirmax;
- struct compat_nfs_string client_addr;
- struct compat_nfs_string mnt_path;
- struct compat_nfs_string hostname;
- compat_uint_t host_addrlen;
- compat_uptr_t host_addr;
- compat_int_t proto;
- compat_int_t auth_flavourlen;
- compat_uptr_t auth_flavours;
- };
- static int do_nfs4_super_data_conv(void *raw_data)
- {
- int version = *(compat_uint_t *) raw_data;
- if (version == 1) {
- struct compat_nfs4_mount_data_v1 *raw = raw_data;
- struct nfs4_mount_data *real = raw_data;
- /* copy the fields backwards */
- real->auth_flavours = compat_ptr(raw->auth_flavours);
- real->auth_flavourlen = raw->auth_flavourlen;
- real->proto = raw->proto;
- real->host_addr = compat_ptr(raw->host_addr);
- real->host_addrlen = raw->host_addrlen;
- compat_nfs_string(&real->hostname, &raw->hostname);
- compat_nfs_string(&real->mnt_path, &raw->mnt_path);
- compat_nfs_string(&real->client_addr, &raw->client_addr);
- real->acdirmax = raw->acdirmax;
- real->acdirmin = raw->acdirmin;
- real->acregmax = raw->acregmax;
- real->acregmin = raw->acregmin;
- real->retrans = raw->retrans;
- real->timeo = raw->timeo;
- real->wsize = raw->wsize;
- real->rsize = raw->rsize;
- real->flags = raw->flags;
- real->version = raw->version;
- }
- return 0;
- }
- #define NCPFS_NAME "ncpfs"
- #define NFS4_NAME "nfs4"
- COMPAT_SYSCALL_DEFINE5(mount, const char __user *, dev_name,
- const char __user *, dir_name,
- const char __user *, type, compat_ulong_t, flags,
- const void __user *, data)
- {
- char *kernel_type;
- void *options;
- char *kernel_dev;
- int retval;
- kernel_type = copy_mount_string(type);
- retval = PTR_ERR(kernel_type);
- if (IS_ERR(kernel_type))
- goto out;
- kernel_dev = copy_mount_string(dev_name);
- retval = PTR_ERR(kernel_dev);
- if (IS_ERR(kernel_dev))
- goto out1;
- options = copy_mount_options(data);
- retval = PTR_ERR(options);
- if (IS_ERR(options))
- goto out2;
- if (kernel_type && options) {
- if (!strcmp(kernel_type, NCPFS_NAME)) {
- do_ncp_super_data_conv(options);
- } else if (!strcmp(kernel_type, NFS4_NAME)) {
- retval = -EINVAL;
- if (do_nfs4_super_data_conv(options))
- goto out3;
- }
- }
- retval = do_mount(kernel_dev, dir_name, kernel_type, flags, options);
- out3:
- kfree(options);
- out2:
- kfree(kernel_dev);
- out1:
- kfree(kernel_type);
- out:
- return retval;
- }
- /*
- * Exactly like fs/open.c: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 fs/open.c: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);
- }
- #ifdef CONFIG_FHANDLE
- /*
- * 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
|