예제 #1
0
int
ptmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
	dev_t newdev, error;
	struct pt_softc * pti;
	struct nameidata cnd, snd;
	struct filedesc *fdp = p->p_fd;
	struct file *cfp = NULL, *sfp = NULL;
	int cindx, sindx;
	uid_t uid;
	gid_t gid;
	struct vattr vattr;
	struct ucred *cred;
	struct ptmget *ptm = (struct ptmget *)data;

	error = 0;
	switch (cmd) {
	case PTMGET:
		fdplock(fdp);
		/* Grab two filedescriptors. */
		if ((error = falloc(p, &cfp, &cindx)) != 0) {
			fdpunlock(fdp);
			break;
		}
		if ((error = falloc(p, &sfp, &sindx)) != 0) {
			fdremove(fdp, cindx);
			closef(cfp, p);
			fdpunlock(fdp);
			break;
		}

retry:
		/* Find and open a free master pty. */
		newdev = pty_getfree();
		if ((error = check_pty(minor(newdev))))
			goto bad;
		pti = pt_softc[minor(newdev)];
		NDINIT(&cnd, LOOKUP, NOFOLLOW|LOCKLEAF, UIO_SYSSPACE,
		    pti->pty_pn, p);
		if ((error = ptm_vn_open(&cnd)) != 0) {
			/*
			 * Check if the master open failed because we lost
			 * the race to grab it.
			 */
			if (error == EIO && !pty_isfree(minor(newdev)))
				goto retry;
			goto bad;
		}
		cfp->f_flag = FREAD|FWRITE;
		cfp->f_type = DTYPE_VNODE;
		cfp->f_ops = &vnops;
		cfp->f_data = (caddr_t) cnd.ni_vp;
		VOP_UNLOCK(cnd.ni_vp, 0, p);

		/*
		 * Open the slave.
		 * namei -> setattr -> unlock -> revoke -> vrele ->
		 * namei -> open -> unlock
		 * Three stage rocket:
		 * 1. Change the owner and permissions on the slave.
		 * 2. Revoke all the users of the slave.
		 * 3. open the slave.
		 */
		NDINIT(&snd, LOOKUP, NOFOLLOW|LOCKLEAF, UIO_SYSSPACE,
		    pti->pty_sn, p);
		if ((error = namei(&snd)) != 0)
			goto bad;
		if ((snd.ni_vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
			gid = tty_gid;
			/* get real uid */
			uid = p->p_cred->p_ruid;

			VATTR_NULL(&vattr);
			vattr.va_uid = uid;
			vattr.va_gid = gid;
			vattr.va_mode = (S_IRUSR|S_IWUSR|S_IWGRP) & ALLPERMS;
			/* Get a fake cred to pretend we're root. */
			cred = crget();
			error = VOP_SETATTR(snd.ni_vp, &vattr, cred, p);
			crfree(cred);
			if (error) {
				vput(snd.ni_vp);
				goto bad;
			}
		}
		VOP_UNLOCK(snd.ni_vp, 0, p);
		if (snd.ni_vp->v_usecount > 1 ||
		    (snd.ni_vp->v_flag & (VALIASED)))
			VOP_REVOKE(snd.ni_vp, REVOKEALL);

		/*
		 * The vnode is useless after the revoke, we need to
		 * namei again.
		 */
		vrele(snd.ni_vp);

		NDINIT(&snd, LOOKUP, NOFOLLOW|LOCKLEAF, UIO_SYSSPACE,
		    pti->pty_sn, p);
		/* now open it */
		if ((error = ptm_vn_open(&snd)) != 0)
			goto bad;
		sfp->f_flag = FREAD|FWRITE;
		sfp->f_type = DTYPE_VNODE;
		sfp->f_ops = &vnops;
		sfp->f_data = (caddr_t) snd.ni_vp;
		VOP_UNLOCK(snd.ni_vp, 0, p);

		/* now, put the indexen and names into struct ptmget */
		ptm->cfd = cindx;
		ptm->sfd = sindx;
		memcpy(ptm->cn, pti->pty_pn, sizeof(pti->pty_pn));
		memcpy(ptm->sn, pti->pty_sn, sizeof(pti->pty_sn));

		/* mark the files mature now that we've passed all errors */
		FILE_SET_MATURE(cfp);
		FILE_SET_MATURE(sfp);

		fdpunlock(fdp);
		break;
	default:
		error = EINVAL;
		break;
	}
	return (error);
bad:
	fdremove(fdp, cindx);
	closef(cfp, p);
	fdremove(fdp, sindx);
	closef(sfp, p);
	fdpunlock(fdp);
	return (error);
}
예제 #2
0
static int
pty_alloc_master(struct lwp *l, int *fd, dev_t *dev, struct mount *mp)
{
	int error;
	struct file *fp;
	struct vnode *vp;
	int md;

	if ((error = fd_allocfile(&fp, fd)) != 0) {
		DPRINTF(("fd_allocfile %d\n", error));
		return error;
	}
retry:
	/* Find and open a free master pty. */
	*dev = pty_getfree();
	md = minor(*dev);
	if ((error = pty_check(md)) != 0) {
		DPRINTF(("pty_check %d\n", error));
		goto bad;
	}
	if (ptm == NULL) {
		DPRINTF(("no ptm\n"));
		error = EOPNOTSUPP;
		goto bad;
	}
	/*
	 * XXX Since PTYFS has now multiple instance support, if we mounted
	 * more than one PTYFS we must check here the ptyfs_used_tbl, to find
	 * out if the ptyfsnode is under the appropriate mount and skip the
	 * node if not, because the pty could has been released, but
	 * ptyfs_reclaim didn't get a chance to release the corresponding
	 * node other mount point yet.
	 *
	 * It's important to have only one mount point's ptyfsnode for each
	 * appropriate device in ptyfs_used_tbl, else we will have a security 
	 * problem, because every entry will have access to this device.
	 *
	 * Also we will not have not efficient vnode and memory usage.
	 * You can test this by changing a_recycle from true to false
	 * in ptyfs_inactive.
	 */
	if ((error = (*ptm->allocvp)(mp, l, &vp, *dev, 'p')) != 0) {
		DPRINTF(("pty_allocvp %d\n", error));
		goto bad;
	}

	if ((error = pty_vn_open(vp, l)) != 0) {
		DPRINTF(("pty_vn_open %d\n", error));
		/*
		 * Check if the master open failed because we lost
		 * the race to grab it.
		 */
		if (error != EIO)
			goto bad;
		error = !pty_isfree(md, 1);
		DPRINTF(("pty_isfree %d\n", error));
		if (error)
			goto retry;
		else
			goto bad;
	}
	fp->f_flag = FREAD|FWRITE;
	fp->f_type = DTYPE_VNODE;
	fp->f_ops = &vnops;
	fp->f_data = vp;
	VOP_UNLOCK(vp);
	fd_affix(curproc, fp, *fd);
	return 0;
bad:
	fd_abort(curproc, fp, *fd);
	return error;
}