Skip to content
Snippets Groups Projects
compat.c 55.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	if (!access_ok(VERIFY_READ, &arg->ca32_svc, sizeof(arg->ca32_svc)) ||
    		get_user(karg->ca_version, &arg->ca32_version) ||
    		__get_user(karg->ca_svc.svc_port, &arg->ca32_svc.svc32_port) ||
    		__get_user(karg->ca_svc.svc_nthreads,
    				&arg->ca32_svc.svc32_nthreads))
    		return -EFAULT;
    	return 0;
    
    static int compat_nfs_clnt_trans(struct nfsctl_arg *karg,
    				struct compat_nfsctl_arg __user *arg)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	if (!access_ok(VERIFY_READ, &arg->ca32_client,
    			sizeof(arg->ca32_client)) ||
    		get_user(karg->ca_version, &arg->ca32_version) ||
    		__copy_from_user(&karg->ca_client.cl_ident[0],
    				&arg->ca32_client.cl32_ident[0],
    				NFSCLNT_IDMAX) ||
    		__get_user(karg->ca_client.cl_naddr,
    				&arg->ca32_client.cl32_naddr) ||
    		__copy_from_user(&karg->ca_client.cl_addrlist[0],
    				&arg->ca32_client.cl32_addrlist[0],
    				(sizeof(struct in_addr) * NFSCLNT_ADDRMAX)) ||
    		__get_user(karg->ca_client.cl_fhkeytype,
    				&arg->ca32_client.cl32_fhkeytype) ||
    		__get_user(karg->ca_client.cl_fhkeylen,
    				&arg->ca32_client.cl32_fhkeylen) ||
    		__copy_from_user(&karg->ca_client.cl_fhkey[0],
    				&arg->ca32_client.cl32_fhkey[0],
    				NFSCLNT_KEYMAX))
    		return -EFAULT;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    static int compat_nfs_exp_trans(struct nfsctl_arg *karg,
    				struct compat_nfsctl_arg __user *arg)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	if (!access_ok(VERIFY_READ, &arg->ca32_export,
    				sizeof(arg->ca32_export)) ||
    		get_user(karg->ca_version, &arg->ca32_version) ||
    		__copy_from_user(&karg->ca_export.ex_client[0],
    				&arg->ca32_export.ex32_client[0],
    				NFSCLNT_IDMAX) ||
    		__copy_from_user(&karg->ca_export.ex_path[0],
    				&arg->ca32_export.ex32_path[0],
    				NFS_MAXPATHLEN) ||
    		__get_user(karg->ca_export.ex_dev,
    				&arg->ca32_export.ex32_dev) ||
    		__get_user(karg->ca_export.ex_ino,
    				&arg->ca32_export.ex32_ino) ||
    		__get_user(karg->ca_export.ex_flags,
    				&arg->ca32_export.ex32_flags) ||
    		__get_user(karg->ca_export.ex_anon_uid,
    				&arg->ca32_export.ex32_anon_uid) ||
    		__get_user(karg->ca_export.ex_anon_gid,
    				&arg->ca32_export.ex32_anon_gid))
    		return -EFAULT;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	SET_UID(karg->ca_export.ex_anon_uid, karg->ca_export.ex_anon_uid);
    	SET_GID(karg->ca_export.ex_anon_gid, karg->ca_export.ex_anon_gid);
    
    
    static int compat_nfs_getfd_trans(struct nfsctl_arg *karg,
    				struct compat_nfsctl_arg __user *arg)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	if (!access_ok(VERIFY_READ, &arg->ca32_getfd,
    			sizeof(arg->ca32_getfd)) ||
    		get_user(karg->ca_version, &arg->ca32_version) ||
    		__copy_from_user(&karg->ca_getfd.gd_addr,
    				&arg->ca32_getfd.gd32_addr,
    				(sizeof(struct sockaddr))) ||
    		__copy_from_user(&karg->ca_getfd.gd_path,
    				&arg->ca32_getfd.gd32_path,
    				(NFS_MAXPATHLEN+1)) ||
    		__get_user(karg->ca_getfd.gd_version,
    				&arg->ca32_getfd.gd32_version))
    		return -EFAULT;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    static int compat_nfs_getfs_trans(struct nfsctl_arg *karg,
    				struct compat_nfsctl_arg __user *arg)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	if (!access_ok(VERIFY_READ,&arg->ca32_getfs,sizeof(arg->ca32_getfs)) ||
    		get_user(karg->ca_version, &arg->ca32_version) ||
    		__copy_from_user(&karg->ca_getfs.gd_addr,
    				&arg->ca32_getfs.gd32_addr,
    				(sizeof(struct sockaddr))) ||
    		__copy_from_user(&karg->ca_getfs.gd_path,
    				&arg->ca32_getfs.gd32_path,
    				(NFS_MAXPATHLEN+1)) ||
    		__get_user(karg->ca_getfs.gd_maxlen,
    				&arg->ca32_getfs.gd32_maxlen))
    		return -EFAULT;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /* This really doesn't need translations, we are only passing
     * back a union which contains opaque nfs file handle data.
     */
    
    static int compat_nfs_getfh_res_trans(union nfsctl_res *kres,
    				union compat_nfsctl_res __user *res)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	int err;
    
    	err = copy_to_user(res, kres, sizeof(*res));
    
    	return (err) ? -EFAULT : 0;
    }
    
    
    asmlinkage long compat_sys_nfsservctl(int cmd,
    				struct compat_nfsctl_arg __user *arg,
    				union compat_nfsctl_res __user *res)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct nfsctl_arg *karg;
    	union nfsctl_res *kres;
    	mm_segment_t oldfs;
    	int err;
    
    	karg = kmalloc(sizeof(*karg), GFP_USER);
    	kres = kmalloc(sizeof(*kres), GFP_USER);
    	if(!karg || !kres) {
    		err = -ENOMEM;
    		goto done;
    	}
    
    	switch(cmd) {
    	case NFSCTL_SVC:
    		err = compat_nfs_svc_trans(karg, arg);
    		break;
    
    	case NFSCTL_ADDCLIENT:
    		err = compat_nfs_clnt_trans(karg, arg);
    		break;
    
    	case NFSCTL_DELCLIENT:
    		err = compat_nfs_clnt_trans(karg, arg);
    		break;
    
    	case NFSCTL_EXPORT:
    	case NFSCTL_UNEXPORT:
    		err = compat_nfs_exp_trans(karg, arg);
    		break;
    
    	case NFSCTL_GETFD:
    		err = compat_nfs_getfd_trans(karg, arg);
    		break;
    
    	case NFSCTL_GETFS:
    		err = compat_nfs_getfs_trans(karg, arg);
    		break;
    
    	default:
    		err = -EINVAL;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	oldfs = get_fs();
    	set_fs(KERNEL_DS);
    	/* The __user pointer casts are valid because of the set_fs() */
    	err = sys_nfsservctl(cmd, (void __user *) karg, (void __user *) kres);
    	set_fs(oldfs);
    
    	if (err)
    		goto done;
    
    	if((cmd == NFSCTL_GETFD) ||
    	   (cmd == NFSCTL_GETFS))
    		err = compat_nfs_getfh_res_trans(kres, res);
    
    done:
    	kfree(karg);
    	kfree(kres);
    	return err;
    }
    #else /* !NFSD */
    long asmlinkage compat_sys_nfsservctl(int cmd, void *notused, void *notused2)
    {
    	return sys_ni_syscall();
    }
    #endif
    
    #ifdef HAVE_SET_RESTORE_SIGMASK
    
    asmlinkage long compat_sys_epoll_pwait(int epfd,
    			struct compat_epoll_event __user *events,
    			int maxevents, int timeout,
    			const compat_sigset_t __user *sigmask,
    			compat_size_t sigsetsize)
    {
    	long err;
    	compat_sigset_t csigmask;
    	sigset_t ksigmask, sigsaved;
    
    	/*
    	 * If the caller wants a certain signal mask to be set during the wait,
    	 * we apply it here.
    	 */
    	if (sigmask) {
    		if (sigsetsize != sizeof(compat_sigset_t))
    			return -EINVAL;
    		if (copy_from_user(&csigmask, sigmask, sizeof(csigmask)))
    			return -EFAULT;
    		sigset_from_compat(&ksigmask, &csigmask);
    		sigdelsetmask(&ksigmask, sigmask(SIGKILL) | sigmask(SIGSTOP));
    		sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
    	}
    
    	err = sys_epoll_wait(epfd, events, maxevents, timeout);
    
    	/*
    	 * If we changed the signal mask, we need to restore the original one.
    	 * In case we've got a signal while waiting, we do not restore the
    	 * signal mask yet, and we allow do_signal() to deliver the signal on
    	 * the way back to userspace, before the signal mask is restored.
    	 */
    	if (sigmask) {
    		if (err == -EINTR) {
    			memcpy(&current->saved_sigmask, &sigsaved,
    			       sizeof(sigsaved));
    
    			set_restore_sigmask();
    
    		} else
    			sigprocmask(SIG_SETMASK, &sigsaved, NULL);
    	}
    
    	return err;
    }
    
    #endif /* HAVE_SET_RESTORE_SIGMASK */
    
    
    #endif /* CONFIG_EPOLL */
    
    asmlinkage long compat_sys_signalfd4(int ufd,
    				     const compat_sigset_t __user *sigmask,
    				     compat_size_t sigsetsize, int flags)
    
    {
    	compat_sigset_t ss32;
    	sigset_t tmp;
    	sigset_t __user *ksigmask;
    
    	if (sigsetsize != sizeof(compat_sigset_t))
    		return -EINVAL;
    	if (copy_from_user(&ss32, sigmask, sizeof(ss32)))
    		return -EFAULT;
    	sigset_from_compat(&tmp, &ss32);
    	ksigmask = compat_alloc_user_space(sizeof(sigset_t));
    	if (copy_to_user(ksigmask, &tmp, sizeof(sigset_t)))
    		return -EFAULT;
    
    
    	return sys_signalfd4(ufd, ksigmask, sizeof(sigset_t), flags);
    
    asmlinkage long compat_sys_signalfd(int ufd,
    				    const compat_sigset_t __user *sigmask,
    				    compat_size_t sigsetsize)
    {
    	return compat_sys_signalfd4(ufd, sigmask, sigsetsize, 0);
    }
    
    #endif /* CONFIG_SIGNALFD */
    
    
    #ifdef CONFIG_TIMERFD
    
    
    Davide Libenzi's avatar
    Davide Libenzi committed
    asmlinkage long compat_sys_timerfd_settime(int ufd, int flags,
    				   const struct compat_itimerspec __user *utmr,
    				   struct compat_itimerspec __user *otmr)
    
    Davide Libenzi's avatar
    Davide Libenzi committed
    	int error;
    
    	struct itimerspec t;
    	struct itimerspec __user *ut;
    
    	if (get_compat_itimerspec(&t, utmr))
    
    		return -EFAULT;
    
    Davide Libenzi's avatar
    Davide Libenzi committed
    	ut = compat_alloc_user_space(2 * sizeof(struct itimerspec));
    	if (copy_to_user(&ut[0], &t, sizeof(t)))
    
    		return -EFAULT;
    
    Davide Libenzi's avatar
    Davide Libenzi committed
    	error = sys_timerfd_settime(ufd, flags, &ut[0], &ut[1]);
    	if (!error && otmr)
    		error = (copy_from_user(&t, &ut[1], sizeof(struct itimerspec)) ||
    			 put_compat_itimerspec(otmr, &t)) ? -EFAULT: 0;
    
    	return error;
    }
    
    asmlinkage long compat_sys_timerfd_gettime(int ufd,
    				   struct compat_itimerspec __user *otmr)
    {
    	int error;
    	struct itimerspec t;
    	struct itimerspec __user *ut;
    
    Davide Libenzi's avatar
    Davide Libenzi committed
    	ut = compat_alloc_user_space(sizeof(struct itimerspec));
    	error = sys_timerfd_gettime(ufd, ut);
    	if (!error)
    		error = (copy_from_user(&t, ut, sizeof(struct itimerspec)) ||
    			 put_compat_itimerspec(otmr, &t)) ? -EFAULT: 0;
    
    	return error;
    
    }
    
    #endif /* CONFIG_TIMERFD */