int
linux32_sys_fcntl(struct lwp *l, const struct linux32_sys_fcntl_args *uap, register_t *retval)
{
	/* {
		syscallarg(int) fd;
                syscallarg(int) cmd;
		syscallarg(netbsd32_voidp) arg;
	} */
	struct linux_sys_fcntl_args ua;
	int cmd =  SCARG(uap, cmd);

	switch (cmd) {
	case LINUX_F_GETLK64:
		do_linux_getlk(SCARG(uap, fd), cmd, SCARG_P32(uap, arg),
		    linux32, flock64);
	case LINUX_F_SETLK64:
	case LINUX_F_SETLKW64:
		do_linux_setlk(SCARG(uap, fd), cmd, SCARG_P32(uap, arg),
		    linux32, flock64, LINUX_F_SETLK64);
	case LINUX_F_GETLK:
		do_linux_getlk(SCARG(uap, fd), cmd, SCARG_P32(uap, arg),
		    linux32, flock);
	case LINUX_F_SETLK:
	case LINUX_F_SETLKW:
		do_linux_setlk(SCARG(uap, fd), cmd, SCARG_P32(uap, arg),
		    linux32, flock, LINUX_F_SETLK);
	default:
		break;
	}

	NETBSD32TO64_UAP(fd);
	SCARG(&ua, cmd) = cmd;
	NETBSD32TOP_UAP(arg, void);

	return linux_sys_fcntl(l, &ua, retval);
}
Example #2
0
/*
 * Most actions in the fcntl() call are straightforward; simply
 * pass control to the NetBSD system call. A few commands need
 * conversions after the actual system call has done its work,
 * because the flag values and lock structure are different.
 */
int
linux_sys_fcntl(struct lwp *l, const struct linux_sys_fcntl_args *uap, register_t *retval)
{
	/* {
		syscallarg(int) fd;
		syscallarg(int) cmd;
		syscallarg(void *) arg;
	} */
	struct proc *p = l->l_proc;
	int fd, cmd, error;
	u_long val;
	void *arg;
	struct sys_fcntl_args fca;
	file_t *fp;
	struct vnode *vp;
	struct vattr va;
	long pgid;
	struct pgrp *pgrp;
	struct tty *tp;

	fd = SCARG(uap, fd);
	cmd = SCARG(uap, cmd);
	arg = SCARG(uap, arg);

	switch (cmd) {

	case LINUX_F_DUPFD:
		cmd = F_DUPFD;
		break;

	case LINUX_F_GETFD:
		cmd = F_GETFD;
		break;

	case LINUX_F_SETFD:
		cmd = F_SETFD;
		break;

	case LINUX_F_GETFL:
		SCARG(&fca, fd) = fd;
		SCARG(&fca, cmd) = F_GETFL;
		SCARG(&fca, arg) = arg;
		if ((error = sys_fcntl(l, &fca, retval)))
			return error;
		retval[0] = bsd_to_linux_ioflags(retval[0]);
		return 0;

	case LINUX_F_SETFL: {
		file_t	*fp1 = NULL;

		val = linux_to_bsd_ioflags((unsigned long)SCARG(uap, arg));
		/*
		 * Linux seems to have same semantics for sending SIGIO to the
		 * read side of socket, but slightly different semantics
		 * for SIGIO to the write side.  Rather than sending the SIGIO
		 * every time it's possible to write (directly) more data, it
		 * only sends SIGIO if last write(2) failed due to insufficient
		 * memory to hold the data. This is compatible enough
		 * with NetBSD semantics to not do anything about the
		 * difference.
		 *
		 * Linux does NOT send SIGIO for pipes. Deal with socketpair
		 * ones and DTYPE_PIPE ones. For these, we don't set
		 * the underlying flags (we don't pass O_ASYNC flag down
		 * to sys_fcntl()), but set the FASYNC flag for file descriptor,
		 * so that F_GETFL would report the ASYNC i/o is on.
		 */
		if (val & O_ASYNC) {
			if (((fp1 = fd_getfile(fd)) == NULL))
			    return (EBADF);
			if (((fp1->f_type == DTYPE_SOCKET) && fp1->f_data
			      && ((struct socket *)fp1->f_data)->so_state & SS_ISAPIPE)
			    || (fp1->f_type == DTYPE_PIPE))
				val &= ~O_ASYNC;
			else {
				/* not a pipe, do not modify anything */
				fd_putfile(fd);
				fp1 = NULL;
			}
		}

		SCARG(&fca, fd) = fd;
		SCARG(&fca, cmd) = F_SETFL;
		SCARG(&fca, arg) = (void *) val;

		error = sys_fcntl(l, &fca, retval);

		/* Now set the FASYNC flag for pipes */
		if (fp1) {
			if (!error) {
				mutex_enter(&fp1->f_lock);
				fp1->f_flag |= FASYNC;
				mutex_exit(&fp1->f_lock);
			}
			fd_putfile(fd);
		}

		return (error);
	    }

	case LINUX_F_GETLK:
		do_linux_getlk(fd, cmd, arg, linux, flock);

	case LINUX_F_SETLK:
	case LINUX_F_SETLKW:
		do_linux_setlk(fd, cmd, arg, linux, flock, LINUX_F_SETLK);

	case LINUX_F_SETOWN:
	case LINUX_F_GETOWN:
		/*
		 * We need to route fcntl() for tty descriptors around normal
		 * fcntl(), since NetBSD tty TIOC{G,S}PGRP semantics is too
		 * restrictive for Linux F_{G,S}ETOWN. For non-tty descriptors,
		 * this is not a problem.
		 */
		if ((fp = fd_getfile(fd)) == NULL)
			return EBADF;

		/* Check it's a character device vnode */
		if (fp->f_type != DTYPE_VNODE
		    || (vp = (struct vnode *)fp->f_data) == NULL
		    || vp->v_type != VCHR) {
			fd_putfile(fd);

	    not_tty:
			/* Not a tty, proceed with common fcntl() */
			cmd = cmd == LINUX_F_SETOWN ? F_SETOWN : F_GETOWN;
			break;
		}

		vn_lock(vp, LK_SHARED | LK_RETRY);
		error = VOP_GETATTR(vp, &va, l->l_cred);
		VOP_UNLOCK(vp);

		fd_putfile(fd);

		if (error)
			return error;

		if ((tp = cdev_tty(va.va_rdev)) == NULL)
			goto not_tty;

		/* set tty pg_id appropriately */
		mutex_enter(proc_lock);
		if (cmd == LINUX_F_GETOWN) {
			retval[0] = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PGID;
			mutex_exit(proc_lock);
			return 0;
		}
		if ((long)arg <= 0) {
			pgid = -(long)arg;
		} else {
			struct proc *p1 = proc_find((long)arg);
			if (p1 == NULL) {
				mutex_exit(proc_lock);
				return (ESRCH);
			}
			pgid = (long)p1->p_pgrp->pg_id;
		}
		pgrp = pgrp_find(pgid);
		if (pgrp == NULL || pgrp->pg_session != p->p_session) {
			mutex_exit(proc_lock);
			return EPERM;
		}
		tp->t_pgrp = pgrp;
		mutex_exit(proc_lock);
		return 0;

	default:
		return EOPNOTSUPP;
	}

	SCARG(&fca, fd) = fd;
	SCARG(&fca, cmd) = cmd;
	SCARG(&fca, arg) = arg;

	return sys_fcntl(l, &fca, retval);
}