xref: /freebsd-src/sys/fs/devfs/devfs_vnops.c (revision ef9ffb8594eee294334ced627755bf5b46b48f9f)
1d167cf6fSWarner Losh /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3d63027b6SPedro F. Giffuni  *
4ff7c5a48SPoul-Henning Kamp  * Copyright (c) 2000-2004
53f54a085SPoul-Henning Kamp  *	Poul-Henning Kamp.  All rights reserved.
6ff7c5a48SPoul-Henning Kamp  * Copyright (c) 1989, 1992-1993, 1995
7ff7c5a48SPoul-Henning Kamp  *	The Regents of the University of California.  All rights reserved.
83f54a085SPoul-Henning Kamp  *
93f54a085SPoul-Henning Kamp  * This code is derived from software donated to Berkeley by
103f54a085SPoul-Henning Kamp  * Jan-Simon Pendry.
113f54a085SPoul-Henning Kamp  *
123f54a085SPoul-Henning Kamp  * Redistribution and use in source and binary forms, with or without
133f54a085SPoul-Henning Kamp  * modification, are permitted provided that the following conditions
143f54a085SPoul-Henning Kamp  * are met:
153f54a085SPoul-Henning Kamp  * 1. Redistributions of source code must retain the above copyright
163f54a085SPoul-Henning Kamp  *    notice, this list of conditions and the following disclaimer.
173f54a085SPoul-Henning Kamp  * 2. Neither the name of the University nor the names of its contributors
183f54a085SPoul-Henning Kamp  *    may be used to endorse or promote products derived from this software
193f54a085SPoul-Henning Kamp  *    without specific prior written permission.
203f54a085SPoul-Henning Kamp  *
213f54a085SPoul-Henning Kamp  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
223f54a085SPoul-Henning Kamp  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
233f54a085SPoul-Henning Kamp  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
243f54a085SPoul-Henning Kamp  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
253f54a085SPoul-Henning Kamp  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
263f54a085SPoul-Henning Kamp  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
273f54a085SPoul-Henning Kamp  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
283f54a085SPoul-Henning Kamp  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
293f54a085SPoul-Henning Kamp  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
303f54a085SPoul-Henning Kamp  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
313f54a085SPoul-Henning Kamp  * SUCH DAMAGE.
323f54a085SPoul-Henning Kamp  * From: FreeBSD: src/sys/miscfs/kernfs/kernfs_vnops.c 1.43
333f54a085SPoul-Henning Kamp  */
343f54a085SPoul-Henning Kamp 
355a9300c4SPoul-Henning Kamp /*
365a9300c4SPoul-Henning Kamp  * TODO:
375a9300c4SPoul-Henning Kamp  *	mkdir: want it ?
385a9300c4SPoul-Henning Kamp  */
395a9300c4SPoul-Henning Kamp 
403f54a085SPoul-Henning Kamp #include <sys/param.h>
413f54a085SPoul-Henning Kamp #include <sys/systm.h>
423f54a085SPoul-Henning Kamp #include <sys/conf.h>
43fb919e4dSMark Murray #include <sys/dirent.h>
44e2e050c8SConrad Meyer #include <sys/eventhandler.h>
45ff7c5a48SPoul-Henning Kamp #include <sys/fcntl.h>
4656dd3a61SPoul-Henning Kamp #include <sys/file.h>
4756dd3a61SPoul-Henning Kamp #include <sys/filedesc.h>
4856dd3a61SPoul-Henning Kamp #include <sys/filio.h>
49f8f61460SEd Schouten #include <sys/jail.h>
50fb919e4dSMark Murray #include <sys/kernel.h>
5191898857SMark Johnston #include <sys/limits.h>
52fb919e4dSMark Murray #include <sys/lock.h>
533f54a085SPoul-Henning Kamp #include <sys/malloc.h>
547077c426SJohn Baldwin #include <sys/mman.h>
553f54a085SPoul-Henning Kamp #include <sys/mount.h>
563f54a085SPoul-Henning Kamp #include <sys/namei.h>
57acd3428bSRobert Watson #include <sys/priv.h>
58fb919e4dSMark Murray #include <sys/proc.h>
59ff7c5a48SPoul-Henning Kamp #include <sys/stat.h>
60ff7c5a48SPoul-Henning Kamp #include <sys/sx.h>
614f9343fcSXin LI #include <sys/sysctl.h>
62fb919e4dSMark Murray #include <sys/time.h>
6356dd3a61SPoul-Henning Kamp #include <sys/ttycom.h>
642ab80ed8SDima Dorfman #include <sys/unistd.h>
65fb919e4dSMark Murray #include <sys/vnode.h>
663f54a085SPoul-Henning Kamp 
6721806f30SPoul-Henning Kamp static struct vop_vector devfs_vnodeops;
686e572e08SEdward Tomasz Napierala static struct vop_vector devfs_specops;
69*ef9ffb85SMark Johnston static const struct fileops devfs_ops_f;
7021806f30SPoul-Henning Kamp 
713f54a085SPoul-Henning Kamp #include <fs/devfs/devfs.h>
72e606a3c6SPoul-Henning Kamp #include <fs/devfs/devfs_int.h>
73e606a3c6SPoul-Henning Kamp 
74aed55708SRobert Watson #include <security/mac/mac_framework.h>
75aed55708SRobert Watson 
767077c426SJohn Baldwin #include <vm/vm.h>
777077c426SJohn Baldwin #include <vm/vm_extern.h>
787077c426SJohn Baldwin #include <vm/vm_object.h>
797077c426SJohn Baldwin 
8082f4d640SKonstantin Belousov static MALLOC_DEFINE(M_CDEVPDATA, "DEVFSP", "Metainfo for cdev-fp data");
8182f4d640SKonstantin Belousov 
82828d6d12SKonstantin Belousov struct mtx	devfs_de_interlock;
8356eeb277SStephan Uphoff MTX_SYSINIT(devfs_de_interlock, &devfs_de_interlock, "devfs interlock", MTX_DEF);
8482f4d640SKonstantin Belousov struct mtx	cdevpriv_mtx;
8582f4d640SKonstantin Belousov MTX_SYSINIT(cdevpriv_mtx, &cdevpriv_mtx, "cdevpriv lock", MTX_DEF);
8656eeb277SStephan Uphoff 
874f9343fcSXin LI SYSCTL_DECL(_vfs_devfs);
884f9343fcSXin LI 
894f9343fcSXin LI static int devfs_dotimes;
904f9343fcSXin LI SYSCTL_INT(_vfs_devfs, OID_AUTO, dotimes, CTLFLAG_RW,
91bda2eb9aSKonstantin Belousov     &devfs_dotimes, 0, "Update timestamps on DEVFS with default precision");
92bda2eb9aSKonstantin Belousov 
93bda2eb9aSKonstantin Belousov /*
94bda2eb9aSKonstantin Belousov  * Update devfs node timestamp.  Note that updates are unlocked and
95bda2eb9aSKonstantin Belousov  * stat(2) could see partially updated times.
96bda2eb9aSKonstantin Belousov  */
97bda2eb9aSKonstantin Belousov static void
98bda2eb9aSKonstantin Belousov devfs_timestamp(struct timespec *tsp)
99bda2eb9aSKonstantin Belousov {
100bda2eb9aSKonstantin Belousov 	time_t ts;
101bda2eb9aSKonstantin Belousov 
102bda2eb9aSKonstantin Belousov 	if (devfs_dotimes) {
103bda2eb9aSKonstantin Belousov 		vfs_timestamp(tsp);
104bda2eb9aSKonstantin Belousov 	} else {
105bda2eb9aSKonstantin Belousov 		ts = time_second;
106bda2eb9aSKonstantin Belousov 		if (tsp->tv_sec != ts) {
107bda2eb9aSKonstantin Belousov 			tsp->tv_sec = ts;
108bda2eb9aSKonstantin Belousov 			tsp->tv_nsec = 0;
109bda2eb9aSKonstantin Belousov 		}
110bda2eb9aSKonstantin Belousov 	}
111bda2eb9aSKonstantin Belousov }
1124f9343fcSXin LI 
113aac5167cSPoul-Henning Kamp static int
1143979450bSKonstantin Belousov devfs_fp_check(struct file *fp, struct cdev **devp, struct cdevsw **dswp,
1153979450bSKonstantin Belousov     int *ref)
116aac5167cSPoul-Henning Kamp {
1173979450bSKonstantin Belousov 	*dswp = devvn_refthread(fp->f_vnode, devp, ref);
118243b324fSMark Johnston 	if (*dswp == NULL || *devp != fp->f_data) {
1191663075cSKonstantin Belousov 		if (*dswp != NULL)
1203979450bSKonstantin Belousov 			dev_relthread(*devp, *ref);
121aac5167cSPoul-Henning Kamp 		return (ENXIO);
1221663075cSKonstantin Belousov 	}
123aac5167cSPoul-Henning Kamp 	KASSERT((*devp)->si_refcount > 0,
124aac5167cSPoul-Henning Kamp 	    ("devfs: un-referenced struct cdev *(%s)", devtoname(*devp)));
125aac5167cSPoul-Henning Kamp 	if (*dswp == NULL)
126aac5167cSPoul-Henning Kamp 		return (ENXIO);
12782f4d640SKonstantin Belousov 	curthread->td_fpop = fp;
128aac5167cSPoul-Henning Kamp 	return (0);
129aac5167cSPoul-Henning Kamp }
130aac5167cSPoul-Henning Kamp 
13182f4d640SKonstantin Belousov int
13282f4d640SKonstantin Belousov devfs_get_cdevpriv(void **datap)
13382f4d640SKonstantin Belousov {
13482f4d640SKonstantin Belousov 	struct file *fp;
13582f4d640SKonstantin Belousov 	struct cdev_privdata *p;
13682f4d640SKonstantin Belousov 	int error;
13782f4d640SKonstantin Belousov 
13882f4d640SKonstantin Belousov 	fp = curthread->td_fpop;
13982f4d640SKonstantin Belousov 	if (fp == NULL)
14082f4d640SKonstantin Belousov 		return (EBADF);
14182f4d640SKonstantin Belousov 	p = fp->f_cdevpriv;
14282f4d640SKonstantin Belousov 	if (p != NULL) {
14382f4d640SKonstantin Belousov 		error = 0;
14482f4d640SKonstantin Belousov 		*datap = p->cdpd_data;
14582f4d640SKonstantin Belousov 	} else
14682f4d640SKonstantin Belousov 		error = ENOENT;
14782f4d640SKonstantin Belousov 	return (error);
14882f4d640SKonstantin Belousov }
14982f4d640SKonstantin Belousov 
15082f4d640SKonstantin Belousov int
1518d7e0f58SJohn Baldwin devfs_set_cdevpriv(void *priv, d_priv_dtor_t *priv_dtr)
15282f4d640SKonstantin Belousov {
15382f4d640SKonstantin Belousov 	struct file *fp;
15482f4d640SKonstantin Belousov 	struct cdev_priv *cdp;
15582f4d640SKonstantin Belousov 	struct cdev_privdata *p;
15682f4d640SKonstantin Belousov 	int error;
15782f4d640SKonstantin Belousov 
15882f4d640SKonstantin Belousov 	fp = curthread->td_fpop;
15982f4d640SKonstantin Belousov 	if (fp == NULL)
16082f4d640SKonstantin Belousov 		return (ENOENT);
16105427aafSKonstantin Belousov 	cdp = cdev2priv((struct cdev *)fp->f_data);
16282f4d640SKonstantin Belousov 	p = malloc(sizeof(struct cdev_privdata), M_CDEVPDATA, M_WAITOK);
16382f4d640SKonstantin Belousov 	p->cdpd_data = priv;
16482f4d640SKonstantin Belousov 	p->cdpd_dtr = priv_dtr;
16582f4d640SKonstantin Belousov 	p->cdpd_fp = fp;
16682f4d640SKonstantin Belousov 	mtx_lock(&cdevpriv_mtx);
16782f4d640SKonstantin Belousov 	if (fp->f_cdevpriv == NULL) {
16882f4d640SKonstantin Belousov 		LIST_INSERT_HEAD(&cdp->cdp_fdpriv, p, cdpd_list);
16982f4d640SKonstantin Belousov 		fp->f_cdevpriv = p;
17082f4d640SKonstantin Belousov 		mtx_unlock(&cdevpriv_mtx);
17182f4d640SKonstantin Belousov 		error = 0;
17282f4d640SKonstantin Belousov 	} else {
17382f4d640SKonstantin Belousov 		mtx_unlock(&cdevpriv_mtx);
17482f4d640SKonstantin Belousov 		free(p, M_CDEVPDATA);
17582f4d640SKonstantin Belousov 		error = EBUSY;
17682f4d640SKonstantin Belousov 	}
17782f4d640SKonstantin Belousov 	return (error);
17882f4d640SKonstantin Belousov }
17982f4d640SKonstantin Belousov 
180d3efbe01SKonstantin Belousov int
181d3efbe01SKonstantin Belousov devfs_foreach_cdevpriv(struct cdev *dev, int (*cb)(void *data, void *arg),
182d3efbe01SKonstantin Belousov     void *arg)
183d3efbe01SKonstantin Belousov {
184d3efbe01SKonstantin Belousov 	struct cdev_priv *cdp;
185d3efbe01SKonstantin Belousov 	struct cdev_privdata *p;
186d3efbe01SKonstantin Belousov 	int error;
187d3efbe01SKonstantin Belousov 
188d3efbe01SKonstantin Belousov 	cdp = cdev2priv(dev);
189d3efbe01SKonstantin Belousov 	error = 0;
190d3efbe01SKonstantin Belousov 	mtx_lock(&cdevpriv_mtx);
191d3efbe01SKonstantin Belousov 	LIST_FOREACH(p, &cdp->cdp_fdpriv, cdpd_list) {
192d3efbe01SKonstantin Belousov 		error = cb(p->cdpd_data, arg);
193d3efbe01SKonstantin Belousov 		if (error != 0)
194d3efbe01SKonstantin Belousov 			break;
195d3efbe01SKonstantin Belousov 	}
196d3efbe01SKonstantin Belousov 	mtx_unlock(&cdevpriv_mtx);
197d3efbe01SKonstantin Belousov 	return (error);
198d3efbe01SKonstantin Belousov }
199d3efbe01SKonstantin Belousov 
20082f4d640SKonstantin Belousov void
20182f4d640SKonstantin Belousov devfs_destroy_cdevpriv(struct cdev_privdata *p)
20282f4d640SKonstantin Belousov {
20382f4d640SKonstantin Belousov 
20482f4d640SKonstantin Belousov 	mtx_assert(&cdevpriv_mtx, MA_OWNED);
205aeace3c3SKonstantin Belousov 	KASSERT(p->cdpd_fp->f_cdevpriv == p,
206aeace3c3SKonstantin Belousov 	    ("devfs_destoy_cdevpriv %p != %p", p->cdpd_fp->f_cdevpriv, p));
20782f4d640SKonstantin Belousov 	p->cdpd_fp->f_cdevpriv = NULL;
20882f4d640SKonstantin Belousov 	LIST_REMOVE(p, cdpd_list);
20982f4d640SKonstantin Belousov 	mtx_unlock(&cdevpriv_mtx);
21082f4d640SKonstantin Belousov 	(p->cdpd_dtr)(p->cdpd_data);
21182f4d640SKonstantin Belousov 	free(p, M_CDEVPDATA);
21282f4d640SKonstantin Belousov }
21382f4d640SKonstantin Belousov 
214a53b7c69SKonstantin Belousov static void
21582f4d640SKonstantin Belousov devfs_fpdrop(struct file *fp)
21682f4d640SKonstantin Belousov {
21782f4d640SKonstantin Belousov 	struct cdev_privdata *p;
21882f4d640SKonstantin Belousov 
21982f4d640SKonstantin Belousov 	mtx_lock(&cdevpriv_mtx);
22082f4d640SKonstantin Belousov 	if ((p = fp->f_cdevpriv) == NULL) {
22182f4d640SKonstantin Belousov 		mtx_unlock(&cdevpriv_mtx);
22282f4d640SKonstantin Belousov 		return;
22382f4d640SKonstantin Belousov 	}
22482f4d640SKonstantin Belousov 	devfs_destroy_cdevpriv(p);
22582f4d640SKonstantin Belousov }
22682f4d640SKonstantin Belousov 
22782f4d640SKonstantin Belousov void
22882f4d640SKonstantin Belousov devfs_clear_cdevpriv(void)
22982f4d640SKonstantin Belousov {
23082f4d640SKonstantin Belousov 	struct file *fp;
23182f4d640SKonstantin Belousov 
23282f4d640SKonstantin Belousov 	fp = curthread->td_fpop;
23382f4d640SKonstantin Belousov 	if (fp == NULL)
23482f4d640SKonstantin Belousov 		return;
23582f4d640SKonstantin Belousov 	devfs_fpdrop(fp);
23682f4d640SKonstantin Belousov }
23782f4d640SKonstantin Belousov 
2383b444436SMateusz Guzik static void
2393b444436SMateusz Guzik devfs_usecount_add(struct vnode *vp)
2403b444436SMateusz Guzik {
2413b444436SMateusz Guzik 	struct devfs_dirent *de;
2423b444436SMateusz Guzik 	struct cdev *dev;
2433b444436SMateusz Guzik 
2443b444436SMateusz Guzik 	mtx_lock(&devfs_de_interlock);
2453b444436SMateusz Guzik 	VI_LOCK(vp);
2463b444436SMateusz Guzik 	VNPASS(vp->v_type == VCHR || vp->v_type == VBAD, vp);
2473b444436SMateusz Guzik 	if (VN_IS_DOOMED(vp)) {
2483b444436SMateusz Guzik 		goto out_unlock;
2493b444436SMateusz Guzik 	}
2503b444436SMateusz Guzik 
2513b444436SMateusz Guzik 	de = vp->v_data;
2523b444436SMateusz Guzik 	dev = vp->v_rdev;
2533b444436SMateusz Guzik 	MPASS(de != NULL);
2543b444436SMateusz Guzik 	MPASS(dev != NULL);
2553b444436SMateusz Guzik 	dev->si_usecount++;
2563b444436SMateusz Guzik 	de->de_usecount++;
2573b444436SMateusz Guzik out_unlock:
2583b444436SMateusz Guzik 	VI_UNLOCK(vp);
2593b444436SMateusz Guzik 	mtx_unlock(&devfs_de_interlock);
2603b444436SMateusz Guzik }
2613b444436SMateusz Guzik 
2623b444436SMateusz Guzik static void
2633b444436SMateusz Guzik devfs_usecount_subl(struct vnode *vp)
2643b444436SMateusz Guzik {
2653b444436SMateusz Guzik 	struct devfs_dirent *de;
2663b444436SMateusz Guzik 	struct cdev *dev;
2673b444436SMateusz Guzik 
2683b444436SMateusz Guzik 	mtx_assert(&devfs_de_interlock, MA_OWNED);
2693b444436SMateusz Guzik 	ASSERT_VI_LOCKED(vp, __func__);
2703b444436SMateusz Guzik 	VNPASS(vp->v_type == VCHR || vp->v_type == VBAD, vp);
2713b444436SMateusz Guzik 
2723b444436SMateusz Guzik 	de = vp->v_data;
2733b444436SMateusz Guzik 	dev = vp->v_rdev;
2743b444436SMateusz Guzik 	if (de == NULL)
2753b444436SMateusz Guzik 		return;
2763b444436SMateusz Guzik 	if (dev == NULL) {
2773b444436SMateusz Guzik 		MPASS(de->de_usecount == 0);
2783b444436SMateusz Guzik 		return;
2793b444436SMateusz Guzik 	}
2803b444436SMateusz Guzik 	if (dev->si_usecount < de->de_usecount)
2813b444436SMateusz Guzik 		panic("%s: si_usecount underflow for dev %p "
2823b444436SMateusz Guzik 		    "(has %ld, dirent has %d)\n",
2833b444436SMateusz Guzik 		    __func__, dev, dev->si_usecount, de->de_usecount);
2843b444436SMateusz Guzik 	if (VN_IS_DOOMED(vp)) {
2853b444436SMateusz Guzik 		dev->si_usecount -= de->de_usecount;
2863b444436SMateusz Guzik 		de->de_usecount = 0;
2873b444436SMateusz Guzik 	} else {
2883b444436SMateusz Guzik 		if (de->de_usecount == 0)
2893b444436SMateusz Guzik 			panic("%s: de_usecount underflow for dev %p\n",
2903b444436SMateusz Guzik 			    __func__, dev);
2913b444436SMateusz Guzik 		dev->si_usecount--;
2923b444436SMateusz Guzik 		de->de_usecount--;
2933b444436SMateusz Guzik 	}
2943b444436SMateusz Guzik }
2953b444436SMateusz Guzik 
2963b444436SMateusz Guzik static void
2973b444436SMateusz Guzik devfs_usecount_sub(struct vnode *vp)
2983b444436SMateusz Guzik {
2993b444436SMateusz Guzik 
3003b444436SMateusz Guzik 	mtx_lock(&devfs_de_interlock);
3013b444436SMateusz Guzik 	VI_LOCK(vp);
3023b444436SMateusz Guzik 	devfs_usecount_subl(vp);
3033b444436SMateusz Guzik 	VI_UNLOCK(vp);
3043b444436SMateusz Guzik 	mtx_unlock(&devfs_de_interlock);
3053b444436SMateusz Guzik }
3063b444436SMateusz Guzik 
3073b444436SMateusz Guzik static int
3083b444436SMateusz Guzik devfs_usecountl(struct vnode *vp)
3093b444436SMateusz Guzik {
3103b444436SMateusz Guzik 
3113b444436SMateusz Guzik 	VNPASS(vp->v_type == VCHR, vp);
3123b444436SMateusz Guzik 	mtx_assert(&devfs_de_interlock, MA_OWNED);
3133b444436SMateusz Guzik 	ASSERT_VI_LOCKED(vp, __func__);
3143b444436SMateusz Guzik 	return (vp->v_rdev->si_usecount);
3153b444436SMateusz Guzik }
3163b444436SMateusz Guzik 
3173b444436SMateusz Guzik int
3183b444436SMateusz Guzik devfs_usecount(struct vnode *vp)
3193b444436SMateusz Guzik {
3203b444436SMateusz Guzik 	int count;
3213b444436SMateusz Guzik 
3223b444436SMateusz Guzik 	VNPASS(vp->v_type == VCHR, vp);
3233b444436SMateusz Guzik 	mtx_lock(&devfs_de_interlock);
3243b444436SMateusz Guzik 	VI_LOCK(vp);
3253b444436SMateusz Guzik 	count = devfs_usecountl(vp);
3263b444436SMateusz Guzik 	VI_UNLOCK(vp);
3273b444436SMateusz Guzik 	mtx_unlock(&devfs_de_interlock);
3283b444436SMateusz Guzik 	return (count);
3293b444436SMateusz Guzik }
3303b444436SMateusz Guzik 
3313b444436SMateusz Guzik void
3323b444436SMateusz Guzik devfs_ctty_ref(struct vnode *vp)
3333b444436SMateusz Guzik {
3343b444436SMateusz Guzik 
3353b444436SMateusz Guzik 	vrefact(vp);
3363b444436SMateusz Guzik 	devfs_usecount_add(vp);
3373b444436SMateusz Guzik }
3383b444436SMateusz Guzik 
3393b444436SMateusz Guzik void
3403b444436SMateusz Guzik devfs_ctty_unref(struct vnode *vp)
3413b444436SMateusz Guzik {
3423b444436SMateusz Guzik 
3433b444436SMateusz Guzik 	devfs_usecount_sub(vp);
3443b444436SMateusz Guzik 	vrele(vp);
3453b444436SMateusz Guzik }
3463b444436SMateusz Guzik 
3470f6bb099SJaakko Heinonen /*
3480f6bb099SJaakko Heinonen  * On success devfs_populate_vp() returns with dmp->dm_lock held.
3490f6bb099SJaakko Heinonen  */
3500f6bb099SJaakko Heinonen static int
3510f6bb099SJaakko Heinonen devfs_populate_vp(struct vnode *vp)
3520f6bb099SJaakko Heinonen {
35389d10571SJaakko Heinonen 	struct devfs_dirent *de;
3540f6bb099SJaakko Heinonen 	struct devfs_mount *dmp;
3550f6bb099SJaakko Heinonen 	int locked;
3560f6bb099SJaakko Heinonen 
3570f6bb099SJaakko Heinonen 	ASSERT_VOP_LOCKED(vp, "devfs_populate_vp");
3580f6bb099SJaakko Heinonen 
3590f6bb099SJaakko Heinonen 	dmp = VFSTODEVFS(vp->v_mount);
3607b19bddaSMateusz Guzik 	if (!devfs_populate_needed(dmp)) {
3617b19bddaSMateusz Guzik 		sx_xlock(&dmp->dm_lock);
3627b19bddaSMateusz Guzik 		goto out_nopopulate;
3637b19bddaSMateusz Guzik 	}
3647b19bddaSMateusz Guzik 
3650f6bb099SJaakko Heinonen 	locked = VOP_ISLOCKED(vp);
3660f6bb099SJaakko Heinonen 
3670f6bb099SJaakko Heinonen 	sx_xlock(&dmp->dm_lock);
3680f6bb099SJaakko Heinonen 	DEVFS_DMP_HOLD(dmp);
3690f6bb099SJaakko Heinonen 
3700f6bb099SJaakko Heinonen 	/* Can't call devfs_populate() with the vnode lock held. */
371b249ce48SMateusz Guzik 	VOP_UNLOCK(vp);
3720f6bb099SJaakko Heinonen 	devfs_populate(dmp);
3730f6bb099SJaakko Heinonen 
3740f6bb099SJaakko Heinonen 	sx_xunlock(&dmp->dm_lock);
3750f6bb099SJaakko Heinonen 	vn_lock(vp, locked | LK_RETRY);
3760f6bb099SJaakko Heinonen 	sx_xlock(&dmp->dm_lock);
3770f6bb099SJaakko Heinonen 	if (DEVFS_DMP_DROP(dmp)) {
3780f6bb099SJaakko Heinonen 		sx_xunlock(&dmp->dm_lock);
3790f6bb099SJaakko Heinonen 		devfs_unmount_final(dmp);
380fb57d63eSKonstantin Belousov 		return (ERESTART);
3810f6bb099SJaakko Heinonen 	}
3827b19bddaSMateusz Guzik out_nopopulate:
383abd80ddbSMateusz Guzik 	if (VN_IS_DOOMED(vp)) {
38489d10571SJaakko Heinonen 		sx_xunlock(&dmp->dm_lock);
385fb57d63eSKonstantin Belousov 		return (ERESTART);
38689d10571SJaakko Heinonen 	}
38789d10571SJaakko Heinonen 	de = vp->v_data;
38889d10571SJaakko Heinonen 	KASSERT(de != NULL,
38989d10571SJaakko Heinonen 	    ("devfs_populate_vp: vp->v_data == NULL but vnode not doomed"));
39089d10571SJaakko Heinonen 	if ((de->de_flags & DE_DOOMED) != 0) {
3910f6bb099SJaakko Heinonen 		sx_xunlock(&dmp->dm_lock);
392fb57d63eSKonstantin Belousov 		return (ERESTART);
3930f6bb099SJaakko Heinonen 	}
3940f6bb099SJaakko Heinonen 
3950f6bb099SJaakko Heinonen 	return (0);
3960f6bb099SJaakko Heinonen }
3970f6bb099SJaakko Heinonen 
3984c44fd37SJoe Marcus Clarke static int
3994c44fd37SJoe Marcus Clarke devfs_vptocnp(struct vop_vptocnp_args *ap)
4004c44fd37SJoe Marcus Clarke {
4014c44fd37SJoe Marcus Clarke 	struct vnode *vp = ap->a_vp;
4024c44fd37SJoe Marcus Clarke 	struct vnode **dvp = ap->a_vpp;
4034c44fd37SJoe Marcus Clarke 	struct devfs_mount *dmp;
4044c44fd37SJoe Marcus Clarke 	char *buf = ap->a_buf;
40545757984SMateusz Guzik 	size_t *buflen = ap->a_buflen;
4064c44fd37SJoe Marcus Clarke 	struct devfs_dirent *dd, *de;
4074c44fd37SJoe Marcus Clarke 	int i, error;
4084c44fd37SJoe Marcus Clarke 
4094c44fd37SJoe Marcus Clarke 	dmp = VFSTODEVFS(vp->v_mount);
41089d10571SJaakko Heinonen 
41189d10571SJaakko Heinonen 	error = devfs_populate_vp(vp);
41289d10571SJaakko Heinonen 	if (error != 0)
41389d10571SJaakko Heinonen 		return (error);
41489d10571SJaakko Heinonen 
415e3fdd051SKonstantin Belousov 	if (vp->v_type != VCHR && vp->v_type != VDIR) {
416e3fdd051SKonstantin Belousov 		error = ENOENT;
4174c44fd37SJoe Marcus Clarke 		goto finished;
4184c44fd37SJoe Marcus Clarke 	}
419e3fdd051SKonstantin Belousov 
420e3fdd051SKonstantin Belousov 	dd = vp->v_data;
421e3fdd051SKonstantin Belousov 	if (vp->v_type == VDIR && dd == dmp->dm_rootdir) {
4224c44fd37SJoe Marcus Clarke 		*dvp = vp;
423f82360acSKonstantin Belousov 		vref(*dvp);
4244c44fd37SJoe Marcus Clarke 		goto finished;
4254c44fd37SJoe Marcus Clarke 	}
426e3fdd051SKonstantin Belousov 
427e3fdd051SKonstantin Belousov 	i = *buflen;
4284c44fd37SJoe Marcus Clarke 	i -= dd->de_dirent->d_namlen;
4294c44fd37SJoe Marcus Clarke 	if (i < 0) {
4304c44fd37SJoe Marcus Clarke 		error = ENOMEM;
4314c44fd37SJoe Marcus Clarke 		goto finished;
4324c44fd37SJoe Marcus Clarke 	}
433e3fdd051SKonstantin Belousov 	bcopy(dd->de_dirent->d_name, buf + i, dd->de_dirent->d_namlen);
4344c44fd37SJoe Marcus Clarke 	*buflen = i;
435e3fdd051SKonstantin Belousov 	de = devfs_parent_dirent(dd);
436f40645c8SJaakko Heinonen 	if (de == NULL) {
437f40645c8SJaakko Heinonen 		error = ENOENT;
438f40645c8SJaakko Heinonen 		goto finished;
439f40645c8SJaakko Heinonen 	}
4404c44fd37SJoe Marcus Clarke 	mtx_lock(&devfs_de_interlock);
4414c44fd37SJoe Marcus Clarke 	*dvp = de->de_vnode;
4424c44fd37SJoe Marcus Clarke 	if (*dvp != NULL) {
4434c44fd37SJoe Marcus Clarke 		VI_LOCK(*dvp);
4444c44fd37SJoe Marcus Clarke 		mtx_unlock(&devfs_de_interlock);
4454c44fd37SJoe Marcus Clarke 		vholdl(*dvp);
4464c44fd37SJoe Marcus Clarke 		VI_UNLOCK(*dvp);
447f82360acSKonstantin Belousov 		vref(*dvp);
448f82360acSKonstantin Belousov 		vdrop(*dvp);
449c7c7520aSKonstantin Belousov 	} else {
450c7c7520aSKonstantin Belousov 		mtx_unlock(&devfs_de_interlock);
4514c44fd37SJoe Marcus Clarke 		error = ENOENT;
452c7c7520aSKonstantin Belousov 	}
4534c44fd37SJoe Marcus Clarke finished:
4544c44fd37SJoe Marcus Clarke 	sx_xunlock(&dmp->dm_lock);
4554c44fd37SJoe Marcus Clarke 	return (error);
4564c44fd37SJoe Marcus Clarke }
4574c44fd37SJoe Marcus Clarke 
4585a9300c4SPoul-Henning Kamp /*
4596adc5230SJaakko Heinonen  * Construct the fully qualified path name relative to the mountpoint.
4606adc5230SJaakko Heinonen  * If a NULL cnp is provided, no '/' is appended to the resulting path.
4615a9300c4SPoul-Henning Kamp  */
4626adc5230SJaakko Heinonen char *
4636adc5230SJaakko Heinonen devfs_fqpn(char *buf, struct devfs_mount *dmp, struct devfs_dirent *dd,
4646adc5230SJaakko Heinonen     struct componentname *cnp)
4655a9300c4SPoul-Henning Kamp {
4665a9300c4SPoul-Henning Kamp 	int i;
4676adc5230SJaakko Heinonen 	struct devfs_dirent *de;
4685a9300c4SPoul-Henning Kamp 
4696adc5230SJaakko Heinonen 	sx_assert(&dmp->dm_lock, SA_LOCKED);
4706adc5230SJaakko Heinonen 
4715a9300c4SPoul-Henning Kamp 	i = SPECNAMELEN;
4725a9300c4SPoul-Henning Kamp 	buf[i] = '\0';
4736adc5230SJaakko Heinonen 	if (cnp != NULL)
4745a9300c4SPoul-Henning Kamp 		i -= cnp->cn_namelen;
4755a9300c4SPoul-Henning Kamp 	if (i < 0)
4765a9300c4SPoul-Henning Kamp 		 return (NULL);
4776adc5230SJaakko Heinonen 	if (cnp != NULL)
4785a9300c4SPoul-Henning Kamp 		bcopy(cnp->cn_nameptr, buf + i, cnp->cn_namelen);
4795a9300c4SPoul-Henning Kamp 	de = dd;
480d785dfefSPoul-Henning Kamp 	while (de != dmp->dm_rootdir) {
4816adc5230SJaakko Heinonen 		if (cnp != NULL || i < SPECNAMELEN) {
4825a9300c4SPoul-Henning Kamp 			i--;
4835a9300c4SPoul-Henning Kamp 			if (i < 0)
4845a9300c4SPoul-Henning Kamp 				 return (NULL);
4855a9300c4SPoul-Henning Kamp 			buf[i] = '/';
4866adc5230SJaakko Heinonen 		}
4875a9300c4SPoul-Henning Kamp 		i -= de->de_dirent->d_namlen;
4885a9300c4SPoul-Henning Kamp 		if (i < 0)
4895a9300c4SPoul-Henning Kamp 			 return (NULL);
4905a9300c4SPoul-Henning Kamp 		bcopy(de->de_dirent->d_name, buf + i,
4915a9300c4SPoul-Henning Kamp 		    de->de_dirent->d_namlen);
492f40645c8SJaakko Heinonen 		de = devfs_parent_dirent(de);
493f40645c8SJaakko Heinonen 		if (de == NULL)
494f40645c8SJaakko Heinonen 			return (NULL);
4955a9300c4SPoul-Henning Kamp 	}
4965a9300c4SPoul-Henning Kamp 	return (buf + i);
4975a9300c4SPoul-Henning Kamp }
4985a9300c4SPoul-Henning Kamp 
499e7f9b744SKonstantin Belousov static int
500e7f9b744SKonstantin Belousov devfs_allocv_drop_refs(int drop_dm_lock, struct devfs_mount *dmp,
501e7f9b744SKonstantin Belousov 	struct devfs_dirent *de)
502e7f9b744SKonstantin Belousov {
503e7f9b744SKonstantin Belousov 	int not_found;
504e7f9b744SKonstantin Belousov 
505e7f9b744SKonstantin Belousov 	not_found = 0;
506e7f9b744SKonstantin Belousov 	if (de->de_flags & DE_DOOMED)
507e7f9b744SKonstantin Belousov 		not_found = 1;
508e7f9b744SKonstantin Belousov 	if (DEVFS_DE_DROP(de)) {
509e7f9b744SKonstantin Belousov 		KASSERT(not_found == 1, ("DEVFS de dropped but not doomed"));
510e7f9b744SKonstantin Belousov 		devfs_dirent_free(de);
511e7f9b744SKonstantin Belousov 	}
512e7f9b744SKonstantin Belousov 	if (DEVFS_DMP_DROP(dmp)) {
513e7f9b744SKonstantin Belousov 		KASSERT(not_found == 1,
514e7f9b744SKonstantin Belousov 			("DEVFS mount struct freed before dirent"));
515e7f9b744SKonstantin Belousov 		not_found = 2;
516e7f9b744SKonstantin Belousov 		sx_xunlock(&dmp->dm_lock);
517e7f9b744SKonstantin Belousov 		devfs_unmount_final(dmp);
518e7f9b744SKonstantin Belousov 	}
519af72db71SKonstantin Belousov 	if (not_found == 1 || (drop_dm_lock && not_found != 2))
520e7f9b744SKonstantin Belousov 		sx_unlock(&dmp->dm_lock);
521e7f9b744SKonstantin Belousov 	return (not_found);
522e7f9b744SKonstantin Belousov }
523e7f9b744SKonstantin Belousov 
524e7f9b744SKonstantin Belousov /*
525e7f9b744SKonstantin Belousov  * devfs_allocv shall be entered with dmp->dm_lock held, and it drops
526e7f9b744SKonstantin Belousov  * it on return.
527e7f9b744SKonstantin Belousov  */
528c32d0a1dSPoul-Henning Kamp int
5299968a426SKonstantin Belousov devfs_allocv(struct devfs_dirent *de, struct mount *mp, int lockmode,
5309968a426SKonstantin Belousov     struct vnode **vpp)
5313f54a085SPoul-Henning Kamp {
5323f54a085SPoul-Henning Kamp 	int error;
5333f54a085SPoul-Henning Kamp 	struct vnode *vp;
53489c9c53dSPoul-Henning Kamp 	struct cdev *dev;
535e7f9b744SKonstantin Belousov 	struct devfs_mount *dmp;
5363a6fc63cSKonstantin Belousov 	struct cdevsw *dsw;
537f9c13ab8SMateusz Guzik 	enum vgetstate vs;
5383f54a085SPoul-Henning Kamp 
539e7f9b744SKonstantin Belousov 	dmp = VFSTODEVFS(mp);
540e7f9b744SKonstantin Belousov 	if (de->de_flags & DE_DOOMED) {
541e7f9b744SKonstantin Belousov 		sx_xunlock(&dmp->dm_lock);
542e7f9b744SKonstantin Belousov 		return (ENOENT);
543e7f9b744SKonstantin Belousov 	}
544724ce55bSKonstantin Belousov loop:
545e7f9b744SKonstantin Belousov 	DEVFS_DE_HOLD(de);
546e7f9b744SKonstantin Belousov 	DEVFS_DMP_HOLD(dmp);
54756eeb277SStephan Uphoff 	mtx_lock(&devfs_de_interlock);
5483f54a085SPoul-Henning Kamp 	vp = de->de_vnode;
5493f54a085SPoul-Henning Kamp 	if (vp != NULL) {
550f9c13ab8SMateusz Guzik 		vs = vget_prep(vp);
55156eeb277SStephan Uphoff 		mtx_unlock(&devfs_de_interlock);
552e7f9b744SKonstantin Belousov 		sx_xunlock(&dmp->dm_lock);
553f9c13ab8SMateusz Guzik 		vget_finish(vp, lockmode | LK_RETRY, vs);
554e7f9b744SKonstantin Belousov 		sx_xlock(&dmp->dm_lock);
555e7f9b744SKonstantin Belousov 		if (devfs_allocv_drop_refs(0, dmp, de)) {
556e7f9b744SKonstantin Belousov 			vput(vp);
557e7f9b744SKonstantin Belousov 			return (ENOENT);
5586d79564fSKonstantin Belousov 		} else if (VN_IS_DOOMED(vp)) {
559724ce55bSKonstantin Belousov 			mtx_lock(&devfs_de_interlock);
560724ce55bSKonstantin Belousov 			if (de->de_vnode == vp) {
561724ce55bSKonstantin Belousov 				de->de_vnode = NULL;
562724ce55bSKonstantin Belousov 				vp->v_data = NULL;
563724ce55bSKonstantin Belousov 			}
564724ce55bSKonstantin Belousov 			mtx_unlock(&devfs_de_interlock);
565724ce55bSKonstantin Belousov 			vput(vp);
566724ce55bSKonstantin Belousov 			goto loop;
567772e2453SKonstantin Belousov 		}
568e7f9b744SKonstantin Belousov 		sx_xunlock(&dmp->dm_lock);
5693f54a085SPoul-Henning Kamp 		*vpp = vp;
5703f54a085SPoul-Henning Kamp 		return (0);
5713f54a085SPoul-Henning Kamp 	}
57256eeb277SStephan Uphoff 	mtx_unlock(&devfs_de_interlock);
57393bcdfe2SPoul-Henning Kamp 	if (de->de_dirent->d_type == DT_CHR) {
574e7f9b744SKonstantin Belousov 		if (!(de->de_cdp->cdp_flags & CDP_ACTIVE)) {
575e7f9b744SKonstantin Belousov 			devfs_allocv_drop_refs(1, dmp, de);
57693bcdfe2SPoul-Henning Kamp 			return (ENOENT);
577e7f9b744SKonstantin Belousov 		}
578e606a3c6SPoul-Henning Kamp 		dev = &de->de_cdp->cdp_c;
57993bcdfe2SPoul-Henning Kamp 	} else {
580f3732fd1SPoul-Henning Kamp 		dev = NULL;
58193bcdfe2SPoul-Henning Kamp 	}
582aec0fb7bSPoul-Henning Kamp 	error = getnewvnode("devfs", mp, &devfs_vnodeops, &vp);
5833f54a085SPoul-Henning Kamp 	if (error != 0) {
584e7f9b744SKonstantin Belousov 		devfs_allocv_drop_refs(1, dmp, de);
5853f54a085SPoul-Henning Kamp 		printf("devfs_allocv: failed to allocate new vnode\n");
5863f54a085SPoul-Henning Kamp 		return (error);
5873f54a085SPoul-Henning Kamp 	}
5883f54a085SPoul-Henning Kamp 
5893f54a085SPoul-Henning Kamp 	if (de->de_dirent->d_type == DT_CHR) {
5903f54a085SPoul-Henning Kamp 		vp->v_type = VCHR;
59120a92a18SPoul-Henning Kamp 		VI_LOCK(vp);
59220a92a18SPoul-Henning Kamp 		dev_lock();
593eb151cb9SPoul-Henning Kamp 		dev_refl(dev);
59457fd3d55SPawel Jakub Dawidek 		/* XXX: v_rdev should be protect by vnode lock */
59520a92a18SPoul-Henning Kamp 		vp->v_rdev = dev;
596f1fa1ba3SMateusz Guzik 		VNPASS(vp->v_usecount == 1, vp);
5973a6fc63cSKonstantin Belousov 		/* Special casing of ttys for deadfs.  Probably redundant. */
5983a6fc63cSKonstantin Belousov 		dsw = dev->si_devsw;
5993a6fc63cSKonstantin Belousov 		if (dsw != NULL && (dsw->d_flags & D_TTY) != 0)
6003a6fc63cSKonstantin Belousov 			vp->v_vflag |= VV_ISTTY;
60120a92a18SPoul-Henning Kamp 		dev_unlock();
60220a92a18SPoul-Henning Kamp 		VI_UNLOCK(vp);
6033979450bSKonstantin Belousov 		if ((dev->si_flags & SI_ETERNAL) != 0)
6043979450bSKonstantin Belousov 			vp->v_vflag |= VV_ETERNALDEV;
605aec0fb7bSPoul-Henning Kamp 		vp->v_op = &devfs_specops;
6063f54a085SPoul-Henning Kamp 	} else if (de->de_dirent->d_type == DT_DIR) {
6073f54a085SPoul-Henning Kamp 		vp->v_type = VDIR;
6083f54a085SPoul-Henning Kamp 	} else if (de->de_dirent->d_type == DT_LNK) {
6093f54a085SPoul-Henning Kamp 		vp->v_type = VLNK;
6103f54a085SPoul-Henning Kamp 	} else {
6113f54a085SPoul-Henning Kamp 		vp->v_type = VBAD;
6123f54a085SPoul-Henning Kamp 	}
6139e40a5f8SKonstantin Belousov 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY | LK_NOWITNESS);
6143634d5b2SJohn Baldwin 	VN_LOCK_ASHARE(vp);
61556eeb277SStephan Uphoff 	mtx_lock(&devfs_de_interlock);
616a481b90bSPoul-Henning Kamp 	vp->v_data = de;
6173f54a085SPoul-Henning Kamp 	de->de_vnode = vp;
61856eeb277SStephan Uphoff 	mtx_unlock(&devfs_de_interlock);
61966c5fbcaSKonstantin Belousov 	error = insmntque1(vp, mp);
62061b9d89fSTor Egge 	if (error != 0) {
62166c5fbcaSKonstantin Belousov 		mtx_lock(&devfs_de_interlock);
62266c5fbcaSKonstantin Belousov 		vp->v_data = NULL;
62366c5fbcaSKonstantin Belousov 		de->de_vnode = NULL;
62466c5fbcaSKonstantin Belousov 		mtx_unlock(&devfs_de_interlock);
62566c5fbcaSKonstantin Belousov 		vgone(vp);
62666c5fbcaSKonstantin Belousov 		vput(vp);
62761b9d89fSTor Egge 		(void) devfs_allocv_drop_refs(1, dmp, de);
62861b9d89fSTor Egge 		return (error);
62961b9d89fSTor Egge 	}
630e7f9b744SKonstantin Belousov 	if (devfs_allocv_drop_refs(0, dmp, de)) {
631829f0bcbSMateusz Guzik 		vgone(vp);
632e7f9b744SKonstantin Belousov 		vput(vp);
633e7f9b744SKonstantin Belousov 		return (ENOENT);
634e7f9b744SKonstantin Belousov 	}
6356742f328SRobert Watson #ifdef MAC
63630d239bcSRobert Watson 	mac_devfs_vnode_associate(mp, de, vp);
6376742f328SRobert Watson #endif
638e7f9b744SKonstantin Belousov 	sx_xunlock(&dmp->dm_lock);
639829f0bcbSMateusz Guzik 	vn_set_state(vp, VSTATE_CONSTRUCTED);
6403f54a085SPoul-Henning Kamp 	*vpp = vp;
6413f54a085SPoul-Henning Kamp 	return (0);
6423f54a085SPoul-Henning Kamp }
6433f54a085SPoul-Henning Kamp 
6443f54a085SPoul-Henning Kamp static int
64521806f30SPoul-Henning Kamp devfs_access(struct vop_access_args *ap)
6463f54a085SPoul-Henning Kamp {
6473f54a085SPoul-Henning Kamp 	struct vnode *vp = ap->a_vp;
648fcc9b84cSPoul-Henning Kamp 	struct devfs_dirent *de;
6494dba07b2SMateusz Guzik 	struct proc *p;
6509c71cb4bSPoul-Henning Kamp 	int error;
6513f54a085SPoul-Henning Kamp 
652fcc9b84cSPoul-Henning Kamp 	de = vp->v_data;
653a481b90bSPoul-Henning Kamp 	if (vp->v_type == VDIR)
654a481b90bSPoul-Henning Kamp 		de = de->de_dir;
6553f54a085SPoul-Henning Kamp 
6569c71cb4bSPoul-Henning Kamp 	error = vaccess(vp->v_type, de->de_mode, de->de_uid, de->de_gid,
657d292b194SMateusz Guzik 	    ap->a_accmode, ap->a_cred);
6588dc9b4cfSEd Schouten 	if (error == 0)
6598dc9b4cfSEd Schouten 		return (0);
6609c71cb4bSPoul-Henning Kamp 	if (error != EACCES)
6619c71cb4bSPoul-Henning Kamp 		return (error);
6624dba07b2SMateusz Guzik 	p = ap->a_td->td_proc;
6639c71cb4bSPoul-Henning Kamp 	/* We do, however, allow access to the controlling terminal */
6644dba07b2SMateusz Guzik 	PROC_LOCK(p);
6654dba07b2SMateusz Guzik 	if (!(p->p_flag & P_CONTROLT)) {
6664dba07b2SMateusz Guzik 		PROC_UNLOCK(p);
6679c71cb4bSPoul-Henning Kamp 		return (error);
6684dba07b2SMateusz Guzik 	}
6694dba07b2SMateusz Guzik 	if (p->p_session->s_ttydp == de->de_cdp)
6704dba07b2SMateusz Guzik 		error = 0;
6714dba07b2SMateusz Guzik 	PROC_UNLOCK(p);
6729c71cb4bSPoul-Henning Kamp 	return (error);
6733f54a085SPoul-Henning Kamp }
6743f54a085SPoul-Henning Kamp 
675cccac8a1SKonstantin Belousov _Static_assert(((FMASK | FCNTLFLAGS) & (FLASTCLOSE | FREVOKE)) == 0,
676cccac8a1SKonstantin Belousov     "devfs-only flag reuse failed");
677cccac8a1SKonstantin Belousov 
678ff7c5a48SPoul-Henning Kamp static int
67921806f30SPoul-Henning Kamp devfs_close(struct vop_close_args *ap)
680ff7c5a48SPoul-Henning Kamp {
681ff7c5a48SPoul-Henning Kamp 	struct vnode *vp = ap->a_vp, *oldvp;
682ff7c5a48SPoul-Henning Kamp 	struct thread *td = ap->a_td;
6834dba07b2SMateusz Guzik 	struct proc *p;
684ff7c5a48SPoul-Henning Kamp 	struct cdev *dev = vp->v_rdev;
685ff7c5a48SPoul-Henning Kamp 	struct cdevsw *dsw;
6863b444436SMateusz Guzik 	struct devfs_dirent *de = vp->v_data;
687cccac8a1SKonstantin Belousov 	int dflags, error, ref, vp_locked;
688ff7c5a48SPoul-Henning Kamp 
689ff7c5a48SPoul-Henning Kamp 	/*
69059e0452eSJaakko Heinonen 	 * XXX: Don't call d_close() if we were called because of
6913af3e99cSMateusz Guzik 	 * XXX: insmntque() failure.
69259e0452eSJaakko Heinonen 	 */
69359e0452eSJaakko Heinonen 	if (vp->v_data == NULL)
69459e0452eSJaakko Heinonen 		return (0);
69559e0452eSJaakko Heinonen 
69659e0452eSJaakko Heinonen 	/*
697ff7c5a48SPoul-Henning Kamp 	 * Hack: a tty device that is a controlling terminal
698ff7c5a48SPoul-Henning Kamp 	 * has a reference from the session structure.
699ff7c5a48SPoul-Henning Kamp 	 * We cannot easily tell that a character device is
700ff7c5a48SPoul-Henning Kamp 	 * a controlling terminal, unless it is the closing
701ff7c5a48SPoul-Henning Kamp 	 * process' controlling terminal.  In that case,
702ff7c5a48SPoul-Henning Kamp 	 * if the reference count is 2 (this last descriptor
703ff7c5a48SPoul-Henning Kamp 	 * plus the session), release the reference from the session.
704ff7c5a48SPoul-Henning Kamp 	 */
7053b444436SMateusz Guzik 	if (de->de_usecount == 2 && td != NULL) {
7064dba07b2SMateusz Guzik 		p = td->td_proc;
7074dba07b2SMateusz Guzik 		PROC_LOCK(p);
7084dba07b2SMateusz Guzik 		if (vp == p->p_session->s_ttyvp) {
7094dba07b2SMateusz Guzik 			PROC_UNLOCK(p);
7103a222fe0SMateusz Guzik 			oldvp = NULL;
7111d1b55fbSMateusz Guzik 			sx_xlock(&proctree_lock);
7124dba07b2SMateusz Guzik 			if (vp == p->p_session->s_ttyvp) {
7134dba07b2SMateusz Guzik 				SESS_LOCK(p->p_session);
7143b444436SMateusz Guzik 				mtx_lock(&devfs_de_interlock);
715ff7c5a48SPoul-Henning Kamp 				VI_LOCK(vp);
7163b444436SMateusz Guzik 				if (devfs_usecountl(vp) == 2 && !VN_IS_DOOMED(vp)) {
7174dba07b2SMateusz Guzik 					p->p_session->s_ttyvp = NULL;
7184dba07b2SMateusz Guzik 					p->p_session->s_ttydp = NULL;
719ff7c5a48SPoul-Henning Kamp 					oldvp = vp;
720ff7c5a48SPoul-Henning Kamp 				}
721ff7c5a48SPoul-Henning Kamp 				VI_UNLOCK(vp);
7223b444436SMateusz Guzik 				mtx_unlock(&devfs_de_interlock);
7234dba07b2SMateusz Guzik 				SESS_UNLOCK(p->p_session);
724ff7c5a48SPoul-Henning Kamp 			}
725ff7c5a48SPoul-Henning Kamp 			sx_xunlock(&proctree_lock);
726ff7c5a48SPoul-Henning Kamp 			if (oldvp != NULL)
7273b444436SMateusz Guzik 				devfs_ctty_unref(oldvp);
7284dba07b2SMateusz Guzik 		} else
7294dba07b2SMateusz Guzik 			PROC_UNLOCK(p);
7303a222fe0SMateusz Guzik 	}
731ff7c5a48SPoul-Henning Kamp 	/*
732ff7c5a48SPoul-Henning Kamp 	 * We do not want to really close the device if it
733ff7c5a48SPoul-Henning Kamp 	 * is still in use unless we are trying to close it
734ff7c5a48SPoul-Henning Kamp 	 * forcibly. Since every use (buffer, vnode, swap, cmap)
735ff7c5a48SPoul-Henning Kamp 	 * holds a reference to the vnode, and because we mark
736ff7c5a48SPoul-Henning Kamp 	 * any other vnodes that alias this device, when the
737ff7c5a48SPoul-Henning Kamp 	 * sum of the reference counts on all the aliased
738ff7c5a48SPoul-Henning Kamp 	 * vnodes descends to one, we are on last close.
739ff7c5a48SPoul-Henning Kamp 	 */
7403979450bSKonstantin Belousov 	dsw = dev_refthread(dev, &ref);
741ff7c5a48SPoul-Henning Kamp 	if (dsw == NULL)
742ff7c5a48SPoul-Henning Kamp 		return (ENXIO);
743cccac8a1SKonstantin Belousov 	dflags = 0;
7443b444436SMateusz Guzik 	mtx_lock(&devfs_de_interlock);
745ff7c5a48SPoul-Henning Kamp 	VI_LOCK(vp);
7463b444436SMateusz Guzik 	if (devfs_usecountl(vp) == 1)
7471fccb43cSMateusz Guzik 		dflags |= FLASTCLOSE;
7483b444436SMateusz Guzik 	devfs_usecount_subl(vp);
7493b444436SMateusz Guzik 	mtx_unlock(&devfs_de_interlock);
750abd80ddbSMateusz Guzik 	if (VN_IS_DOOMED(vp)) {
751ff7c5a48SPoul-Henning Kamp 		/* Forced close. */
752cccac8a1SKonstantin Belousov 		dflags |= FREVOKE | FNONBLOCK;
753ff7c5a48SPoul-Henning Kamp 	} else if (dsw->d_flags & D_TRACKCLOSE) {
754ff7c5a48SPoul-Henning Kamp 		/* Keep device updated on status. */
7551fccb43cSMateusz Guzik 	} else if ((dflags & FLASTCLOSE) == 0) {
756ff7c5a48SPoul-Henning Kamp 		VI_UNLOCK(vp);
7573979450bSKonstantin Belousov 		dev_relthread(dev, ref);
758ff7c5a48SPoul-Henning Kamp 		return (0);
759ff7c5a48SPoul-Henning Kamp 	}
7601fccb43cSMateusz Guzik 	vholdnz(vp);
761ff7c5a48SPoul-Henning Kamp 	VI_UNLOCK(vp);
76281c794f9SAttilio Rao 	vp_locked = VOP_ISLOCKED(vp);
763b249ce48SMateusz Guzik 	VOP_UNLOCK(vp);
764ff7c5a48SPoul-Henning Kamp 	KASSERT(dev->si_refcount > 0,
765ff7c5a48SPoul-Henning Kamp 	    ("devfs_close() on un-referenced struct cdev *(%s)", devtoname(dev)));
766cccac8a1SKonstantin Belousov 	error = dsw->d_close(dev, ap->a_fflag | dflags, S_IFCHR, td);
7673979450bSKonstantin Belousov 	dev_relthread(dev, ref);
768cb05b60aSAttilio Rao 	vn_lock(vp, vp_locked | LK_RETRY);
769828d6d12SKonstantin Belousov 	vdrop(vp);
770ff7c5a48SPoul-Henning Kamp 	return (error);
771ff7c5a48SPoul-Henning Kamp }
772ff7c5a48SPoul-Henning Kamp 
77356dd3a61SPoul-Henning Kamp static int
77456dd3a61SPoul-Henning Kamp devfs_close_f(struct file *fp, struct thread *td)
77556dd3a61SPoul-Henning Kamp {
77682f4d640SKonstantin Belousov 	int error;
7777818e0a5SKonstantin Belousov 	struct file *fpop;
77856dd3a61SPoul-Henning Kamp 
779e517e6f1SJohn Baldwin 	/*
780e517e6f1SJohn Baldwin 	 * NB: td may be NULL if this descriptor is closed due to
781e517e6f1SJohn Baldwin 	 * garbage collection from a closed UNIX domain socket.
782e517e6f1SJohn Baldwin 	 */
783e517e6f1SJohn Baldwin 	fpop = curthread->td_fpop;
784e517e6f1SJohn Baldwin 	curthread->td_fpop = fp;
78582f4d640SKonstantin Belousov 	error = vnops.fo_close(fp, td);
786e517e6f1SJohn Baldwin 	curthread->td_fpop = fpop;
787dccc45e4SJohn Baldwin 
788dccc45e4SJohn Baldwin 	/*
789dccc45e4SJohn Baldwin 	 * The f_cdevpriv cannot be assigned non-NULL value while we
790dccc45e4SJohn Baldwin 	 * are destroying the file.
791dccc45e4SJohn Baldwin 	 */
792dccc45e4SJohn Baldwin 	if (fp->f_cdevpriv != NULL)
793dccc45e4SJohn Baldwin 		devfs_fpdrop(fp);
79482f4d640SKonstantin Belousov 	return (error);
79556dd3a61SPoul-Henning Kamp }
79656dd3a61SPoul-Henning Kamp 
797ff7c5a48SPoul-Henning Kamp static int
79821806f30SPoul-Henning Kamp devfs_getattr(struct vop_getattr_args *ap)
7993f54a085SPoul-Henning Kamp {
8003f54a085SPoul-Henning Kamp 	struct vnode *vp = ap->a_vp;
8013f54a085SPoul-Henning Kamp 	struct vattr *vap = ap->a_vap;
8023f54a085SPoul-Henning Kamp 	struct devfs_dirent *de;
803f5efcd64SJaakko Heinonen 	struct devfs_mount *dmp;
80489c9c53dSPoul-Henning Kamp 	struct cdev *dev;
805584b675eSKonstantin Belousov 	struct timeval boottime;
806584b675eSKonstantin Belousov 	int error;
8073f54a085SPoul-Henning Kamp 
808f5efcd64SJaakko Heinonen 	error = devfs_populate_vp(vp);
809f5efcd64SJaakko Heinonen 	if (error != 0)
810f5efcd64SJaakko Heinonen 		return (error);
811f5efcd64SJaakko Heinonen 
812f5efcd64SJaakko Heinonen 	dmp = VFSTODEVFS(vp->v_mount);
813f5efcd64SJaakko Heinonen 	sx_xunlock(&dmp->dm_lock);
814f5efcd64SJaakko Heinonen 
8153f54a085SPoul-Henning Kamp 	de = vp->v_data;
8162c022012SPoul-Henning Kamp 	KASSERT(de != NULL, ("Null dirent in devfs_getattr vp=%p", vp));
8172c022012SPoul-Henning Kamp 	if (vp->v_type == VDIR) {
818a481b90bSPoul-Henning Kamp 		de = de->de_dir;
8192c022012SPoul-Henning Kamp 		KASSERT(de != NULL,
8202c022012SPoul-Henning Kamp 		    ("Null dir dirent in devfs_getattr vp=%p", vp));
8212c022012SPoul-Henning Kamp 	}
8223f54a085SPoul-Henning Kamp 	vap->va_uid = de->de_uid;
8233f54a085SPoul-Henning Kamp 	vap->va_gid = de->de_gid;
8243f54a085SPoul-Henning Kamp 	vap->va_mode = de->de_mode;
825aadf2655SPoul-Henning Kamp 	if (vp->v_type == VLNK)
826bc553559SPoul-Henning Kamp 		vap->va_size = strlen(de->de_symlink);
8272ab80ed8SDima Dorfman 	else if (vp->v_type == VDIR)
8282ab80ed8SDima Dorfman 		vap->va_size = vap->va_bytes = DEV_BSIZE;
829aadf2655SPoul-Henning Kamp 	else
8303f54a085SPoul-Henning Kamp 		vap->va_size = 0;
8312ab80ed8SDima Dorfman 	if (vp->v_type != VDIR)
8322ab80ed8SDima Dorfman 		vap->va_bytes = 0;
8333f54a085SPoul-Henning Kamp 	vap->va_blocksize = DEV_BSIZE;
83493bcdfe2SPoul-Henning Kamp 	vap->va_type = vp->v_type;
8354b1c62b3SPoul-Henning Kamp 
836584b675eSKonstantin Belousov 	getboottime(&boottime);
8374b1c62b3SPoul-Henning Kamp #define fix(aa)							\
8384b1c62b3SPoul-Henning Kamp 	do {							\
839164554deSTom Rhodes 		if ((aa).tv_sec <= 3600) {			\
8404b1c62b3SPoul-Henning Kamp 			(aa).tv_sec = boottime.tv_sec;		\
8414b1c62b3SPoul-Henning Kamp 			(aa).tv_nsec = boottime.tv_usec * 1000; \
8424b1c62b3SPoul-Henning Kamp 		}						\
8434b1c62b3SPoul-Henning Kamp 	} while (0)
8444b1c62b3SPoul-Henning Kamp 
845a481b90bSPoul-Henning Kamp 	if (vp->v_type != VCHR)  {
8464b1c62b3SPoul-Henning Kamp 		fix(de->de_atime);
8473f54a085SPoul-Henning Kamp 		vap->va_atime = de->de_atime;
8484b1c62b3SPoul-Henning Kamp 		fix(de->de_mtime);
8493f54a085SPoul-Henning Kamp 		vap->va_mtime = de->de_mtime;
8504b1c62b3SPoul-Henning Kamp 		fix(de->de_ctime);
8513f54a085SPoul-Henning Kamp 		vap->va_ctime = de->de_ctime;
852a481b90bSPoul-Henning Kamp 	} else {
853a481b90bSPoul-Henning Kamp 		dev = vp->v_rdev;
8544b1c62b3SPoul-Henning Kamp 		fix(dev->si_atime);
855a481b90bSPoul-Henning Kamp 		vap->va_atime = dev->si_atime;
8564b1c62b3SPoul-Henning Kamp 		fix(dev->si_mtime);
857a481b90bSPoul-Henning Kamp 		vap->va_mtime = dev->si_mtime;
8584b1c62b3SPoul-Henning Kamp 		fix(dev->si_ctime);
859a481b90bSPoul-Henning Kamp 		vap->va_ctime = dev->si_ctime;
860b43ab0e3SPoul-Henning Kamp 
86105427aafSKonstantin Belousov 		vap->va_rdev = cdev2priv(dev)->cdp_inode;
862a481b90bSPoul-Henning Kamp 	}
8633f54a085SPoul-Henning Kamp 	vap->va_gen = 0;
8643f54a085SPoul-Henning Kamp 	vap->va_flags = 0;
865caf8aec8SKonstantin Belousov 	vap->va_filerev = 0;
866a481b90bSPoul-Henning Kamp 	vap->va_nlink = de->de_links;
8673f54a085SPoul-Henning Kamp 	vap->va_fileid = de->de_inode;
8683f54a085SPoul-Henning Kamp 
8693f54a085SPoul-Henning Kamp 	return (error);
8703f54a085SPoul-Henning Kamp }
8713f54a085SPoul-Henning Kamp 
872ff7c5a48SPoul-Henning Kamp /* ARGSUSED */
8733f54a085SPoul-Henning Kamp static int
87456dd3a61SPoul-Henning Kamp devfs_ioctl_f(struct file *fp, u_long com, void *data, struct ucred *cred, struct thread *td)
87556dd3a61SPoul-Henning Kamp {
8767818e0a5SKonstantin Belousov 	struct file *fpop;
877af326aceSConrad Meyer 	int error;
87856dd3a61SPoul-Henning Kamp 
8797818e0a5SKonstantin Belousov 	fpop = td->td_fpop;
880af326aceSConrad Meyer 	td->td_fpop = fp;
881f12aa60cSKonstantin Belousov 	error = vnops.fo_ioctl(fp, com, data, cred, td);
882af326aceSConrad Meyer 	td->td_fpop = fpop;
883aac5167cSPoul-Henning Kamp 	return (error);
884f12aa60cSKonstantin Belousov }
88556dd3a61SPoul-Henning Kamp 
886ed34a7fcSBrooks Davis void *
887ed34a7fcSBrooks Davis fiodgname_buf_get_ptr(void *fgnp, u_long com)
888ed34a7fcSBrooks Davis {
889ed34a7fcSBrooks Davis 	union {
890ed34a7fcSBrooks Davis 		struct fiodgname_arg	fgn;
891ed34a7fcSBrooks Davis #ifdef COMPAT_FREEBSD32
892ed34a7fcSBrooks Davis 		struct fiodgname_arg32	fgn32;
893ed34a7fcSBrooks Davis #endif
894ed34a7fcSBrooks Davis 	} *fgnup;
895ed34a7fcSBrooks Davis 
896ed34a7fcSBrooks Davis 	fgnup = fgnp;
897ed34a7fcSBrooks Davis 	switch (com) {
898ed34a7fcSBrooks Davis 	case FIODGNAME:
899ed34a7fcSBrooks Davis 		return (fgnup->fgn.buf);
900ed34a7fcSBrooks Davis #ifdef COMPAT_FREEBSD32
901ed34a7fcSBrooks Davis 	case FIODGNAME_32:
902ed34a7fcSBrooks Davis 		return ((void *)(uintptr_t)fgnup->fgn32.buf);
903ed34a7fcSBrooks Davis #endif
904ed34a7fcSBrooks Davis 	default:
905ed34a7fcSBrooks Davis 		panic("Unhandled ioctl command %ld", com);
906ed34a7fcSBrooks Davis 	}
907ed34a7fcSBrooks Davis }
908ed34a7fcSBrooks Davis 
909af326aceSConrad Meyer static int
910af326aceSConrad Meyer devfs_ioctl(struct vop_ioctl_args *ap)
911af326aceSConrad Meyer {
912af326aceSConrad Meyer 	struct fiodgname_arg *fgn;
913af326aceSConrad Meyer 	struct vnode *vpold, *vp;
914af326aceSConrad Meyer 	struct cdevsw *dsw;
915af326aceSConrad Meyer 	struct thread *td;
91611c345b1SMateusz Guzik 	struct session *sess;
917af326aceSConrad Meyer 	struct cdev *dev;
918af326aceSConrad Meyer 	int error, ref, i;
919af326aceSConrad Meyer 	const char *p;
920af326aceSConrad Meyer 	u_long com;
921af326aceSConrad Meyer 
922af326aceSConrad Meyer 	vp = ap->a_vp;
923af326aceSConrad Meyer 	com = ap->a_command;
924af326aceSConrad Meyer 	td = ap->a_td;
925af326aceSConrad Meyer 
926af326aceSConrad Meyer 	dsw = devvn_refthread(vp, &dev, &ref);
927af326aceSConrad Meyer 	if (dsw == NULL)
928af326aceSConrad Meyer 		return (ENXIO);
929af326aceSConrad Meyer 	KASSERT(dev->si_refcount > 0,
930af326aceSConrad Meyer 	    ("devfs: un-referenced struct cdev *(%s)", devtoname(dev)));
931af326aceSConrad Meyer 
932ed34a7fcSBrooks Davis 	switch (com) {
933ed34a7fcSBrooks Davis 	case FIODTYPE:
934af326aceSConrad Meyer 		*(int *)ap->a_data = dsw->d_flags & D_TYPEMASK;
935af326aceSConrad Meyer 		error = 0;
936ed34a7fcSBrooks Davis 		break;
937ed34a7fcSBrooks Davis 	case FIODGNAME:
938ed34a7fcSBrooks Davis #ifdef	COMPAT_FREEBSD32
939ed34a7fcSBrooks Davis 	case FIODGNAME_32:
940ed34a7fcSBrooks Davis #endif
941af326aceSConrad Meyer 		fgn = ap->a_data;
94226474078SPoul-Henning Kamp 		p = devtoname(dev);
94326474078SPoul-Henning Kamp 		i = strlen(p) + 1;
94426474078SPoul-Henning Kamp 		if (i > fgn->len)
94580447bf7SPoul-Henning Kamp 			error = EINVAL;
94680447bf7SPoul-Henning Kamp 		else
947ed34a7fcSBrooks Davis 			error = copyout(p, fiodgname_buf_get_ptr(fgn, com), i);
948ed34a7fcSBrooks Davis 		break;
949ed34a7fcSBrooks Davis 	default:
950ed34a7fcSBrooks Davis 		error = dsw->d_ioctl(dev, com, ap->a_data, ap->a_fflag, td);
95156dd3a61SPoul-Henning Kamp 	}
952af326aceSConrad Meyer 
9533979450bSKonstantin Belousov 	dev_relthread(dev, ref);
95456dd3a61SPoul-Henning Kamp 	if (error == ENOIOCTL)
95556dd3a61SPoul-Henning Kamp 		error = ENOTTY;
95656dd3a61SPoul-Henning Kamp 
957af326aceSConrad Meyer 	if (error == 0 && com == TIOCSCTTY) {
9581b50b999SKyle Evans 		/*
9591b50b999SKyle Evans 		 * Do nothing if reassigning same control tty, or if the
9601b50b999SKyle Evans 		 * control tty has already disappeared.  If it disappeared,
9611b50b999SKyle Evans 		 * it's because we were racing with TIOCNOTTY.  TIOCNOTTY
9621b50b999SKyle Evans 		 * already took care of releasing the old vnode and we have
9631b50b999SKyle Evans 		 * nothing left to do.
9641b50b999SKyle Evans 		 */
96556dd3a61SPoul-Henning Kamp 		sx_slock(&proctree_lock);
96611c345b1SMateusz Guzik 		sess = td->td_proc->p_session;
96711c345b1SMateusz Guzik 		if (sess->s_ttyvp == vp || sess->s_ttyp == NULL) {
96856dd3a61SPoul-Henning Kamp 			sx_sunlock(&proctree_lock);
96956dd3a61SPoul-Henning Kamp 			return (0);
97056dd3a61SPoul-Henning Kamp 		}
97156dd3a61SPoul-Henning Kamp 
9723b444436SMateusz Guzik 		devfs_ctty_ref(vp);
97311c345b1SMateusz Guzik 		SESS_LOCK(sess);
97411c345b1SMateusz Guzik 		vpold = sess->s_ttyvp;
97511c345b1SMateusz Guzik 		sess->s_ttyvp = vp;
97611c345b1SMateusz Guzik 		sess->s_ttydp = cdev2priv(dev);
97711c345b1SMateusz Guzik 		SESS_UNLOCK(sess);
97856dd3a61SPoul-Henning Kamp 
97956dd3a61SPoul-Henning Kamp 		sx_sunlock(&proctree_lock);
98056dd3a61SPoul-Henning Kamp 
98156dd3a61SPoul-Henning Kamp 		/* Get rid of reference to old control tty */
98256dd3a61SPoul-Henning Kamp 		if (vpold)
9833bc17248SMateusz Guzik 			devfs_ctty_unref(vpold);
98456dd3a61SPoul-Henning Kamp 	}
98556dd3a61SPoul-Henning Kamp 	return (error);
98656dd3a61SPoul-Henning Kamp }
98756dd3a61SPoul-Henning Kamp 
988ff7c5a48SPoul-Henning Kamp /* ARGSUSED */
989ff7c5a48SPoul-Henning Kamp static int
99056dd3a61SPoul-Henning Kamp devfs_kqfilter_f(struct file *fp, struct knote *kn)
99156dd3a61SPoul-Henning Kamp {
99256dd3a61SPoul-Henning Kamp 	struct cdev *dev;
99356dd3a61SPoul-Henning Kamp 	struct cdevsw *dsw;
9943979450bSKonstantin Belousov 	int error, ref;
9957818e0a5SKonstantin Belousov 	struct file *fpop;
9967818e0a5SKonstantin Belousov 	struct thread *td;
99756dd3a61SPoul-Henning Kamp 
9987818e0a5SKonstantin Belousov 	td = curthread;
9997818e0a5SKonstantin Belousov 	fpop = td->td_fpop;
10003979450bSKonstantin Belousov 	error = devfs_fp_check(fp, &dev, &dsw, &ref);
1001aac5167cSPoul-Henning Kamp 	if (error)
1002aac5167cSPoul-Henning Kamp 		return (error);
100356dd3a61SPoul-Henning Kamp 	error = dsw->d_kqfilter(dev, kn);
10047818e0a5SKonstantin Belousov 	td->td_fpop = fpop;
10053979450bSKonstantin Belousov 	dev_relthread(dev, ref);
100656dd3a61SPoul-Henning Kamp 	return (error);
100756dd3a61SPoul-Henning Kamp }
100856dd3a61SPoul-Henning Kamp 
1009f8f61460SEd Schouten static inline int
10108dc9b4cfSEd Schouten devfs_prison_check(struct devfs_dirent *de, struct thread *td)
1011f8f61460SEd Schouten {
1012f8f61460SEd Schouten 	struct cdev_priv *cdp;
1013f8f61460SEd Schouten 	struct ucred *dcr;
10144dba07b2SMateusz Guzik 	struct proc *p;
10158dc9b4cfSEd Schouten 	int error;
1016f8f61460SEd Schouten 
1017f8f61460SEd Schouten 	cdp = de->de_cdp;
1018f8f61460SEd Schouten 	if (cdp == NULL)
1019f8f61460SEd Schouten 		return (0);
1020f8f61460SEd Schouten 	dcr = cdp->cdp_c.si_cred;
1021f8f61460SEd Schouten 	if (dcr == NULL)
1022f8f61460SEd Schouten 		return (0);
1023f8f61460SEd Schouten 
10248dc9b4cfSEd Schouten 	error = prison_check(td->td_ucred, dcr);
10258dc9b4cfSEd Schouten 	if (error == 0)
10268dc9b4cfSEd Schouten 		return (0);
10278dc9b4cfSEd Schouten 	/* We do, however, allow access to the controlling terminal */
10284dba07b2SMateusz Guzik 	p = td->td_proc;
10294dba07b2SMateusz Guzik 	PROC_LOCK(p);
10304dba07b2SMateusz Guzik 	if (!(p->p_flag & P_CONTROLT)) {
10314dba07b2SMateusz Guzik 		PROC_UNLOCK(p);
10328dc9b4cfSEd Schouten 		return (error);
10334dba07b2SMateusz Guzik 	}
10344dba07b2SMateusz Guzik 	if (p->p_session->s_ttydp == cdp)
10354dba07b2SMateusz Guzik 		error = 0;
10364dba07b2SMateusz Guzik 	PROC_UNLOCK(p);
10378dc9b4cfSEd Schouten 	return (error);
1038f8f61460SEd Schouten }
1039f8f61460SEd Schouten 
104056dd3a61SPoul-Henning Kamp static int
1041e7f9b744SKonstantin Belousov devfs_lookupx(struct vop_lookup_args *ap, int *dm_unlock)
10423f54a085SPoul-Henning Kamp {
1043c32d0a1dSPoul-Henning Kamp 	struct componentname *cnp;
1044c32d0a1dSPoul-Henning Kamp 	struct vnode *dvp, **vpp;
1045b40ce416SJulian Elischer 	struct thread *td;
1046c32d0a1dSPoul-Henning Kamp 	struct devfs_dirent *de, *dd;
10477e8766a9SPoul-Henning Kamp 	struct devfs_dirent **dde;
1048c32d0a1dSPoul-Henning Kamp 	struct devfs_mount *dmp;
104983342922SKonstantin Belousov 	struct mount *mp;
105089c9c53dSPoul-Henning Kamp 	struct cdev *cdev;
10519968a426SKonstantin Belousov 	int error, flags, nameiop, dvplocked;
1052c32d0a1dSPoul-Henning Kamp 	char specname[SPECNAMELEN + 1], *pname;
10533f54a085SPoul-Henning Kamp 
1054b4a58fbfSMateusz Guzik 	td = curthread;
1055c32d0a1dSPoul-Henning Kamp 	cnp = ap->a_cnp;
1056c32d0a1dSPoul-Henning Kamp 	vpp = ap->a_vpp;
1057c32d0a1dSPoul-Henning Kamp 	dvp = ap->a_dvp;
1058c32d0a1dSPoul-Henning Kamp 	pname = cnp->cn_nameptr;
1059c32d0a1dSPoul-Henning Kamp 	flags = cnp->cn_flags;
1060c32d0a1dSPoul-Henning Kamp 	nameiop = cnp->cn_nameiop;
106183342922SKonstantin Belousov 	mp = dvp->v_mount;
106283342922SKonstantin Belousov 	dmp = VFSTODEVFS(mp);
1063c32d0a1dSPoul-Henning Kamp 	dd = dvp->v_data;
1064c32d0a1dSPoul-Henning Kamp 	*vpp = NULLVP;
1065c32d0a1dSPoul-Henning Kamp 
10668eee3a3dSMaxime Henrion 	if ((flags & ISLASTCN) && nameiop == RENAME)
10673f54a085SPoul-Henning Kamp 		return (EOPNOTSUPP);
10683f54a085SPoul-Henning Kamp 
1069c32d0a1dSPoul-Henning Kamp 	if (dvp->v_type != VDIR)
1070c32d0a1dSPoul-Henning Kamp 		return (ENOTDIR);
1071c32d0a1dSPoul-Henning Kamp 
1072e6e370a7SJeff Roberson 	if ((flags & ISDOTDOT) && (dvp->v_vflag & VV_ROOT))
1073c32d0a1dSPoul-Henning Kamp 		return (EIO);
1074c32d0a1dSPoul-Henning Kamp 
10756a5abb1eSKyle Evans 	error = vn_dir_check_exec(dvp, cnp);
10766a5abb1eSKyle Evans 	if (error != 0)
1077c32d0a1dSPoul-Henning Kamp 		return (error);
1078c32d0a1dSPoul-Henning Kamp 
1079c32d0a1dSPoul-Henning Kamp 	if (cnp->cn_namelen == 1 && *pname == '.') {
10808eee3a3dSMaxime Henrion 		if ((flags & ISLASTCN) && nameiop != LOOKUP)
1081c32d0a1dSPoul-Henning Kamp 			return (EINVAL);
1082c32d0a1dSPoul-Henning Kamp 		*vpp = dvp;
1083c32d0a1dSPoul-Henning Kamp 		VREF(dvp);
1084c32d0a1dSPoul-Henning Kamp 		return (0);
1085c32d0a1dSPoul-Henning Kamp 	}
1086c32d0a1dSPoul-Henning Kamp 
1087c32d0a1dSPoul-Henning Kamp 	if (flags & ISDOTDOT) {
10888eee3a3dSMaxime Henrion 		if ((flags & ISLASTCN) && nameiop != LOOKUP)
1089c32d0a1dSPoul-Henning Kamp 			return (EINVAL);
1090f40645c8SJaakko Heinonen 		de = devfs_parent_dirent(dd);
1091f40645c8SJaakko Heinonen 		if (de == NULL)
1092f40645c8SJaakko Heinonen 			return (ENOENT);
10939968a426SKonstantin Belousov 		dvplocked = VOP_ISLOCKED(dvp);
1094b249ce48SMateusz Guzik 		VOP_UNLOCK(dvp);
109583342922SKonstantin Belousov 		error = devfs_allocv(de, mp, cnp->cn_lkflags & LK_TYPE_MASK,
109683342922SKonstantin Belousov 		    vpp);
1097e7f9b744SKonstantin Belousov 		*dm_unlock = 0;
10989968a426SKonstantin Belousov 		vn_lock(dvp, dvplocked | LK_RETRY);
1099c32d0a1dSPoul-Henning Kamp 		return (error);
1100c32d0a1dSPoul-Henning Kamp 	}
1101c32d0a1dSPoul-Henning Kamp 
1102c32d0a1dSPoul-Henning Kamp 	dd = dvp->v_data;
110364040d39SJaakko Heinonen 	de = devfs_find(dd, cnp->cn_nameptr, cnp->cn_namelen, 0);
1104e606a3c6SPoul-Henning Kamp 	while (de == NULL) {	/* While(...) so we can use break */
1105c32d0a1dSPoul-Henning Kamp 
11065a9300c4SPoul-Henning Kamp 		if (nameiop == DELETE)
1107e606a3c6SPoul-Henning Kamp 			return (ENOENT);
11085a9300c4SPoul-Henning Kamp 
1109c32d0a1dSPoul-Henning Kamp 		/*
1110c32d0a1dSPoul-Henning Kamp 		 * OK, we didn't have an entry for the name we were asked for
1111c32d0a1dSPoul-Henning Kamp 		 * so we try to see if anybody can create it on demand.
1112c32d0a1dSPoul-Henning Kamp 		 */
11136adc5230SJaakko Heinonen 		pname = devfs_fqpn(specname, dmp, dd, cnp);
11145a9300c4SPoul-Henning Kamp 		if (pname == NULL)
1115e606a3c6SPoul-Henning Kamp 			break;
1116c32d0a1dSPoul-Henning Kamp 
1117f3732fd1SPoul-Henning Kamp 		cdev = NULL;
1118de10ffa5SKonstantin Belousov 		DEVFS_DMP_HOLD(dmp);
1119de10ffa5SKonstantin Belousov 		sx_xunlock(&dmp->dm_lock);
1120e606a3c6SPoul-Henning Kamp 		EVENTHANDLER_INVOKE(dev_clone,
1121e606a3c6SPoul-Henning Kamp 		    td->td_ucred, pname, strlen(pname), &cdev);
11220f6bb099SJaakko Heinonen 
11230f6bb099SJaakko Heinonen 		if (cdev == NULL)
1124de10ffa5SKonstantin Belousov 			sx_xlock(&dmp->dm_lock);
11250f6bb099SJaakko Heinonen 		else if (devfs_populate_vp(dvp) != 0) {
11260f6bb099SJaakko Heinonen 			*dm_unlock = 0;
11270f6bb099SJaakko Heinonen 			sx_xlock(&dmp->dm_lock);
11280f6bb099SJaakko Heinonen 			if (DEVFS_DMP_DROP(dmp)) {
11290f6bb099SJaakko Heinonen 				sx_xunlock(&dmp->dm_lock);
11300f6bb099SJaakko Heinonen 				devfs_unmount_final(dmp);
11310f6bb099SJaakko Heinonen 			} else
11320f6bb099SJaakko Heinonen 				sx_xunlock(&dmp->dm_lock);
11330f6bb099SJaakko Heinonen 			dev_rel(cdev);
11340f6bb099SJaakko Heinonen 			return (ENOENT);
11350f6bb099SJaakko Heinonen 		}
1136de10ffa5SKonstantin Belousov 		if (DEVFS_DMP_DROP(dmp)) {
1137de10ffa5SKonstantin Belousov 			*dm_unlock = 0;
1138de10ffa5SKonstantin Belousov 			sx_xunlock(&dmp->dm_lock);
1139de10ffa5SKonstantin Belousov 			devfs_unmount_final(dmp);
114096835d61SJaakko Heinonen 			if (cdev != NULL)
114196835d61SJaakko Heinonen 				dev_rel(cdev);
1142de10ffa5SKonstantin Belousov 			return (ENOENT);
1143de10ffa5SKonstantin Belousov 		}
11440f6bb099SJaakko Heinonen 
1145f3732fd1SPoul-Henning Kamp 		if (cdev == NULL)
1146e606a3c6SPoul-Henning Kamp 			break;
1147c32d0a1dSPoul-Henning Kamp 
1148e606a3c6SPoul-Henning Kamp 		dev_lock();
114905427aafSKonstantin Belousov 		dde = &cdev2priv(cdev)->cdp_dirents[dmp->dm_idx];
1150e606a3c6SPoul-Henning Kamp 		if (dde != NULL && *dde != NULL)
11517e8766a9SPoul-Henning Kamp 			de = *dde;
1152e606a3c6SPoul-Henning Kamp 		dev_unlock();
1153e606a3c6SPoul-Henning Kamp 		dev_rel(cdev);
1154e606a3c6SPoul-Henning Kamp 		break;
1155e606a3c6SPoul-Henning Kamp 	}
1156c32d0a1dSPoul-Henning Kamp 
1157e606a3c6SPoul-Henning Kamp 	if (de == NULL || de->de_flags & DE_WHITEOUT) {
1158c32d0a1dSPoul-Henning Kamp 		if ((nameiop == CREATE || nameiop == RENAME) &&
1159c32d0a1dSPoul-Henning Kamp 		    (flags & (LOCKPARENT | WANTPARENT)) && (flags & ISLASTCN)) {
1160c32d0a1dSPoul-Henning Kamp 			return (EJUSTRETURN);
1161c32d0a1dSPoul-Henning Kamp 		}
1162c32d0a1dSPoul-Henning Kamp 		return (ENOENT);
1163e606a3c6SPoul-Henning Kamp 	}
1164c32d0a1dSPoul-Henning Kamp 
11658dc9b4cfSEd Schouten 	if (devfs_prison_check(de, td))
1166f8f61460SEd Schouten 		return (ENOENT);
1167f8f61460SEd Schouten 
1168c32d0a1dSPoul-Henning Kamp 	if ((cnp->cn_nameiop == DELETE) && (flags & ISLASTCN)) {
1169b40ce416SJulian Elischer 		error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1170c32d0a1dSPoul-Henning Kamp 		if (error)
1171c32d0a1dSPoul-Henning Kamp 			return (error);
1172c32d0a1dSPoul-Henning Kamp 		if (*vpp == dvp) {
1173c32d0a1dSPoul-Henning Kamp 			VREF(dvp);
1174c32d0a1dSPoul-Henning Kamp 			*vpp = dvp;
1175c32d0a1dSPoul-Henning Kamp 			return (0);
1176c32d0a1dSPoul-Henning Kamp 		}
1177c32d0a1dSPoul-Henning Kamp 	}
117883342922SKonstantin Belousov 	error = devfs_allocv(de, mp, cnp->cn_lkflags & LK_TYPE_MASK, vpp);
1179e7f9b744SKonstantin Belousov 	*dm_unlock = 0;
1180c32d0a1dSPoul-Henning Kamp 	return (error);
1181c32d0a1dSPoul-Henning Kamp }
1182c32d0a1dSPoul-Henning Kamp 
118393bcdfe2SPoul-Henning Kamp static int
118493bcdfe2SPoul-Henning Kamp devfs_lookup(struct vop_lookup_args *ap)
118593bcdfe2SPoul-Henning Kamp {
118693bcdfe2SPoul-Henning Kamp 	int j;
118793bcdfe2SPoul-Henning Kamp 	struct devfs_mount *dmp;
1188e7f9b744SKonstantin Belousov 	int dm_unlock;
118993bcdfe2SPoul-Henning Kamp 
11900f6bb099SJaakko Heinonen 	if (devfs_populate_vp(ap->a_dvp) != 0)
11910f6bb099SJaakko Heinonen 		return (ENOTDIR);
11920f6bb099SJaakko Heinonen 
119393bcdfe2SPoul-Henning Kamp 	dmp = VFSTODEVFS(ap->a_dvp->v_mount);
1194e7f9b744SKonstantin Belousov 	dm_unlock = 1;
1195e7f9b744SKonstantin Belousov 	j = devfs_lookupx(ap, &dm_unlock);
1196e7f9b744SKonstantin Belousov 	if (dm_unlock == 1)
1197e606a3c6SPoul-Henning Kamp 		sx_xunlock(&dmp->dm_lock);
119893bcdfe2SPoul-Henning Kamp 	return (j);
119993bcdfe2SPoul-Henning Kamp }
120093bcdfe2SPoul-Henning Kamp 
12015a9300c4SPoul-Henning Kamp static int
12025a9300c4SPoul-Henning Kamp devfs_mknod(struct vop_mknod_args *ap)
12035a9300c4SPoul-Henning Kamp {
12045a9300c4SPoul-Henning Kamp 	struct componentname *cnp;
12055a9300c4SPoul-Henning Kamp 	struct vnode *dvp, **vpp;
12065a9300c4SPoul-Henning Kamp 	struct devfs_dirent *dd, *de;
12075a9300c4SPoul-Henning Kamp 	struct devfs_mount *dmp;
12085a9300c4SPoul-Henning Kamp 	int error;
12095a9300c4SPoul-Henning Kamp 
121002a4be3fSSimon L. B. Nielsen 	/*
121102a4be3fSSimon L. B. Nielsen 	 * The only type of node we should be creating here is a
121202a4be3fSSimon L. B. Nielsen 	 * character device, for anything else return EOPNOTSUPP.
121302a4be3fSSimon L. B. Nielsen 	 */
121402a4be3fSSimon L. B. Nielsen 	if (ap->a_vap->va_type != VCHR)
121502a4be3fSSimon L. B. Nielsen 		return (EOPNOTSUPP);
12165a9300c4SPoul-Henning Kamp 	dvp = ap->a_dvp;
12175a9300c4SPoul-Henning Kamp 	dmp = VFSTODEVFS(dvp->v_mount);
12185a9300c4SPoul-Henning Kamp 
12195a9300c4SPoul-Henning Kamp 	cnp = ap->a_cnp;
12205a9300c4SPoul-Henning Kamp 	vpp = ap->a_vpp;
12215a9300c4SPoul-Henning Kamp 	dd = dvp->v_data;
12225a9300c4SPoul-Henning Kamp 
12235a9300c4SPoul-Henning Kamp 	error = ENOENT;
1224e7f9b744SKonstantin Belousov 	sx_xlock(&dmp->dm_lock);
12255a9300c4SPoul-Henning Kamp 	TAILQ_FOREACH(de, &dd->de_dlist, de_list) {
12265a9300c4SPoul-Henning Kamp 		if (cnp->cn_namelen != de->de_dirent->d_namlen)
12275a9300c4SPoul-Henning Kamp 			continue;
1228a57a934aSKonstantin Belousov 		if (de->de_dirent->d_type == DT_CHR &&
1229a57a934aSKonstantin Belousov 		    (de->de_cdp->cdp_flags & CDP_ACTIVE) == 0)
1230a57a934aSKonstantin Belousov 			continue;
12315a9300c4SPoul-Henning Kamp 		if (bcmp(cnp->cn_nameptr, de->de_dirent->d_name,
12325a9300c4SPoul-Henning Kamp 		    de->de_dirent->d_namlen) != 0)
12335a9300c4SPoul-Henning Kamp 			continue;
12345a9300c4SPoul-Henning Kamp 		if (de->de_flags & DE_WHITEOUT)
12355a9300c4SPoul-Henning Kamp 			break;
12365a9300c4SPoul-Henning Kamp 		goto notfound;
12375a9300c4SPoul-Henning Kamp 	}
12385a9300c4SPoul-Henning Kamp 	if (de == NULL)
12395a9300c4SPoul-Henning Kamp 		goto notfound;
12405a9300c4SPoul-Henning Kamp 	de->de_flags &= ~DE_WHITEOUT;
12419968a426SKonstantin Belousov 	error = devfs_allocv(de, dvp->v_mount, LK_EXCLUSIVE, vpp);
1242e7f9b744SKonstantin Belousov 	return (error);
12435a9300c4SPoul-Henning Kamp notfound:
1244e606a3c6SPoul-Henning Kamp 	sx_xunlock(&dmp->dm_lock);
12455a9300c4SPoul-Henning Kamp 	return (error);
12465a9300c4SPoul-Henning Kamp }
12475a9300c4SPoul-Henning Kamp 
1248ff7c5a48SPoul-Henning Kamp /* ARGSUSED */
1249ff7c5a48SPoul-Henning Kamp static int
125021806f30SPoul-Henning Kamp devfs_open(struct vop_open_args *ap)
1251ff7c5a48SPoul-Henning Kamp {
1252ff7c5a48SPoul-Henning Kamp 	struct thread *td = ap->a_td;
1253ff7c5a48SPoul-Henning Kamp 	struct vnode *vp = ap->a_vp;
1254ff7c5a48SPoul-Henning Kamp 	struct cdev *dev = vp->v_rdev;
12559e223287SKonstantin Belousov 	struct file *fp = ap->a_fp;
12563979450bSKonstantin Belousov 	int error, ref, vlocked;
1257ff7c5a48SPoul-Henning Kamp 	struct cdevsw *dsw;
12587818e0a5SKonstantin Belousov 	struct file *fpop;
1259ff7c5a48SPoul-Henning Kamp 
1260ff7c5a48SPoul-Henning Kamp 	if (vp->v_type == VBLK)
1261ff7c5a48SPoul-Henning Kamp 		return (ENXIO);
1262ff7c5a48SPoul-Henning Kamp 
1263ff7c5a48SPoul-Henning Kamp 	if (dev == NULL)
1264ff7c5a48SPoul-Henning Kamp 		return (ENXIO);
1265ff7c5a48SPoul-Henning Kamp 
1266ff7c5a48SPoul-Henning Kamp 	/* Make this field valid before any I/O in d_open. */
1267ff7c5a48SPoul-Henning Kamp 	if (dev->si_iosize_max == 0)
1268ff7c5a48SPoul-Henning Kamp 		dev->si_iosize_max = DFLTPHYS;
1269ff7c5a48SPoul-Henning Kamp 
12703979450bSKonstantin Belousov 	dsw = dev_refthread(dev, &ref);
1271ff7c5a48SPoul-Henning Kamp 	if (dsw == NULL)
1272ff7c5a48SPoul-Henning Kamp 		return (ENXIO);
12731fef78c3SKonstantin Belousov 	if (fp == NULL && dsw->d_fdopen != NULL) {
12741fef78c3SKonstantin Belousov 		dev_relthread(dev, ref);
12751fef78c3SKonstantin Belousov 		return (ENXIO);
12761fef78c3SKonstantin Belousov 	}
1277ff7c5a48SPoul-Henning Kamp 
12783b444436SMateusz Guzik 	if (vp->v_type == VCHR)
12793b444436SMateusz Guzik 		devfs_usecount_add(vp);
12803b444436SMateusz Guzik 
12819968a426SKonstantin Belousov 	vlocked = VOP_ISLOCKED(vp);
1282b249ce48SMateusz Guzik 	VOP_UNLOCK(vp);
1283ff7c5a48SPoul-Henning Kamp 
12847818e0a5SKonstantin Belousov 	fpop = td->td_fpop;
128582f4d640SKonstantin Belousov 	td->td_fpop = fp;
12860e9bd89dSKonstantin Belousov 	if (fp != NULL) {
128782f4d640SKonstantin Belousov 		fp->f_data = dev;
12880e9bd89dSKonstantin Belousov 		fp->f_vnode = vp;
12890e9bd89dSKonstantin Belousov 	}
1290ff7c5a48SPoul-Henning Kamp 	if (dsw->d_fdopen != NULL)
12919e223287SKonstantin Belousov 		error = dsw->d_fdopen(dev, ap->a_mode, td, fp);
1292ff7c5a48SPoul-Henning Kamp 	else
1293ff7c5a48SPoul-Henning Kamp 		error = dsw->d_open(dev, ap->a_mode, S_IFCHR, td);
1294d52aff3cSKonstantin Belousov 	/* Clean up any cdevpriv upon error. */
129507da61a6SHans Petter Selasky 	if (error != 0)
129607da61a6SHans Petter Selasky 		devfs_clear_cdevpriv();
12977818e0a5SKonstantin Belousov 	td->td_fpop = fpop;
1298ff7c5a48SPoul-Henning Kamp 
12999968a426SKonstantin Belousov 	vn_lock(vp, vlocked | LK_RETRY);
13003b444436SMateusz Guzik 	if (error != 0 && vp->v_type == VCHR)
13013b444436SMateusz Guzik 		devfs_usecount_sub(vp);
13023b444436SMateusz Guzik 
13033979450bSKonstantin Belousov 	dev_relthread(dev, ref);
13042ca49983SKonstantin Belousov 	if (error != 0) {
13052ca49983SKonstantin Belousov 		if (error == ERESTART)
13062ca49983SKonstantin Belousov 			error = EINTR;
1307ff7c5a48SPoul-Henning Kamp 		return (error);
13082ca49983SKonstantin Belousov 	}
1309ff7c5a48SPoul-Henning Kamp 
13102a9e0c32SPoul-Henning Kamp #if 0	/* /dev/console */
13119968a426SKonstantin Belousov 	KASSERT(fp != NULL, ("Could not vnode bypass device on NULL fp"));
13122a9e0c32SPoul-Henning Kamp #else
13139e223287SKonstantin Belousov 	if (fp == NULL)
13142a9e0c32SPoul-Henning Kamp 		return (error);
13152a9e0c32SPoul-Henning Kamp #endif
131634d1dcf0SEd Schouten 	if (fp->f_ops == &badfileops)
1317397c19d1SJeff Roberson 		finit(fp, fp->f_flag, DTYPE_VNODE, dev, &devfs_ops_f);
1318ff7c5a48SPoul-Henning Kamp 	return (error);
1319ff7c5a48SPoul-Henning Kamp }
1320ff7c5a48SPoul-Henning Kamp 
13212ab80ed8SDima Dorfman static int
132221806f30SPoul-Henning Kamp devfs_pathconf(struct vop_pathconf_args *ap)
13232ab80ed8SDima Dorfman {
13242ab80ed8SDima Dorfman 
13252ab80ed8SDima Dorfman 	switch (ap->a_name) {
1326418e6212SJohn Baldwin 	case _PC_FILESIZEBITS:
1327418e6212SJohn Baldwin 		*ap->a_retval = 64;
1328418e6212SJohn Baldwin 		return (0);
1329599afe53SJohn Baldwin 	case _PC_NAME_MAX:
1330599afe53SJohn Baldwin 		*ap->a_retval = NAME_MAX;
1331599afe53SJohn Baldwin 		return (0);
1332599afe53SJohn Baldwin 	case _PC_LINK_MAX:
1333f6e25ec7SJohn Baldwin 		*ap->a_retval = INT_MAX;
1334599afe53SJohn Baldwin 		return (0);
1335418e6212SJohn Baldwin 	case _PC_SYMLINK_MAX:
1336418e6212SJohn Baldwin 		*ap->a_retval = MAXPATHLEN;
1337418e6212SJohn Baldwin 		return (0);
1338e1d15b89SJohn Baldwin 	case _PC_MAX_CANON:
1339e1d15b89SJohn Baldwin 		if (ap->a_vp->v_vflag & VV_ISTTY) {
1340e1d15b89SJohn Baldwin 			*ap->a_retval = MAX_CANON;
1341e1d15b89SJohn Baldwin 			return (0);
1342e1d15b89SJohn Baldwin 		}
1343e1d15b89SJohn Baldwin 		return (EINVAL);
1344e1d15b89SJohn Baldwin 	case _PC_MAX_INPUT:
1345e1d15b89SJohn Baldwin 		if (ap->a_vp->v_vflag & VV_ISTTY) {
1346e1d15b89SJohn Baldwin 			*ap->a_retval = MAX_INPUT;
1347e1d15b89SJohn Baldwin 			return (0);
1348e1d15b89SJohn Baldwin 		}
1349e1d15b89SJohn Baldwin 		return (EINVAL);
1350e1d15b89SJohn Baldwin 	case _PC_VDISABLE:
1351e1d15b89SJohn Baldwin 		if (ap->a_vp->v_vflag & VV_ISTTY) {
1352e1d15b89SJohn Baldwin 			*ap->a_retval = _POSIX_VDISABLE;
1353e1d15b89SJohn Baldwin 			return (0);
1354e1d15b89SJohn Baldwin 		}
1355e1d15b89SJohn Baldwin 		return (EINVAL);
1356e6a5564eSRobert Watson 	case _PC_MAC_PRESENT:
135767d722edSRobert Watson #ifdef MAC
135867d722edSRobert Watson 		/*
135967d722edSRobert Watson 		 * If MAC is enabled, devfs automatically supports
13608ea3cedaSGordon Bergling 		 * trivial non-persistent label storage.
136167d722edSRobert Watson 		 */
136267d722edSRobert Watson 		*ap->a_retval = 1;
136367d722edSRobert Watson #else
136467d722edSRobert Watson 		*ap->a_retval = 0;
1365e6a5564eSRobert Watson #endif
136667d722edSRobert Watson 		return (0);
1367599afe53SJohn Baldwin 	case _PC_CHOWN_RESTRICTED:
1368599afe53SJohn Baldwin 		*ap->a_retval = 1;
1369599afe53SJohn Baldwin 		return (0);
13702ab80ed8SDima Dorfman 	default:
13712ab80ed8SDima Dorfman 		return (vop_stdpathconf(ap));
13722ab80ed8SDima Dorfman 	}
13732ab80ed8SDima Dorfman 	/* NOTREACHED */
13742ab80ed8SDima Dorfman }
13752ab80ed8SDima Dorfman 
1376ff7c5a48SPoul-Henning Kamp /* ARGSUSED */
1377ff7c5a48SPoul-Henning Kamp static int
137856dd3a61SPoul-Henning Kamp devfs_poll_f(struct file *fp, int events, struct ucred *cred, struct thread *td)
137956dd3a61SPoul-Henning Kamp {
138056dd3a61SPoul-Henning Kamp 	struct cdev *dev;
138156dd3a61SPoul-Henning Kamp 	struct cdevsw *dsw;
13823979450bSKonstantin Belousov 	int error, ref;
13837818e0a5SKonstantin Belousov 	struct file *fpop;
138456dd3a61SPoul-Henning Kamp 
13857818e0a5SKonstantin Belousov 	fpop = td->td_fpop;
13863979450bSKonstantin Belousov 	error = devfs_fp_check(fp, &dev, &dsw, &ref);
1387f12aa60cSKonstantin Belousov 	if (error != 0) {
1388f12aa60cSKonstantin Belousov 		error = vnops.fo_poll(fp, events, cred, td);
1389f12aa60cSKonstantin Belousov 		return (error);
1390f12aa60cSKonstantin Belousov 	}
139156dd3a61SPoul-Henning Kamp 	error = dsw->d_poll(dev, events, td);
13927818e0a5SKonstantin Belousov 	td->td_fpop = fpop;
13933979450bSKonstantin Belousov 	dev_relthread(dev, ref);
139456dd3a61SPoul-Henning Kamp 	return(error);
139556dd3a61SPoul-Henning Kamp }
139656dd3a61SPoul-Henning Kamp 
1397ff7c5a48SPoul-Henning Kamp /*
1398ff7c5a48SPoul-Henning Kamp  * Print out the contents of a special device vnode.
1399ff7c5a48SPoul-Henning Kamp  */
1400ff7c5a48SPoul-Henning Kamp static int
140121806f30SPoul-Henning Kamp devfs_print(struct vop_print_args *ap)
1402ff7c5a48SPoul-Henning Kamp {
1403ff7c5a48SPoul-Henning Kamp 
1404ff7c5a48SPoul-Henning Kamp 	printf("\tdev %s\n", devtoname(ap->a_vp->v_rdev));
1405ff7c5a48SPoul-Henning Kamp 	return (0);
1406ff7c5a48SPoul-Henning Kamp }
1407ff7c5a48SPoul-Henning Kamp 
14083f54a085SPoul-Henning Kamp static int
140964548150SKonstantin Belousov devfs_read_f(struct file *fp, struct uio *uio, struct ucred *cred,
141064548150SKonstantin Belousov     int flags, struct thread *td)
141156dd3a61SPoul-Henning Kamp {
141256dd3a61SPoul-Henning Kamp 	struct cdev *dev;
1413526d0bd5SKonstantin Belousov 	int ioflag, error, ref;
1414526d0bd5SKonstantin Belousov 	ssize_t resid;
141556dd3a61SPoul-Henning Kamp 	struct cdevsw *dsw;
14167818e0a5SKonstantin Belousov 	struct file *fpop;
141756dd3a61SPoul-Henning Kamp 
1418bf3e483bSKonstantin Belousov 	if (uio->uio_resid > DEVFS_IOSIZE_MAX)
1419bf3e483bSKonstantin Belousov 		return (EINVAL);
14207818e0a5SKonstantin Belousov 	fpop = td->td_fpop;
14213979450bSKonstantin Belousov 	error = devfs_fp_check(fp, &dev, &dsw, &ref);
1422f12aa60cSKonstantin Belousov 	if (error != 0) {
1423f12aa60cSKonstantin Belousov 		error = vnops.fo_read(fp, uio, cred, flags, td);
1424aac5167cSPoul-Henning Kamp 		return (error);
1425f12aa60cSKonstantin Belousov 	}
142656dd3a61SPoul-Henning Kamp 	resid = uio->uio_resid;
142750a36c11SPoul-Henning Kamp 	ioflag = fp->f_flag & (O_NONBLOCK | O_DIRECT);
142850a36c11SPoul-Henning Kamp 	if (ioflag & O_DIRECT)
142956dd3a61SPoul-Henning Kamp 		ioflag |= IO_DIRECT;
143056dd3a61SPoul-Henning Kamp 
1431c5c1199cSKonstantin Belousov 	foffset_lock_uio(fp, uio, flags | FOF_NOLOCK);
143256dd3a61SPoul-Henning Kamp 	error = dsw->d_read(dev, uio, ioflag);
1433bda2eb9aSKonstantin Belousov 	if (uio->uio_resid != resid || (error == 0 && resid != 0))
1434bda2eb9aSKonstantin Belousov 		devfs_timestamp(&dev->si_atime);
14357818e0a5SKonstantin Belousov 	td->td_fpop = fpop;
14363979450bSKonstantin Belousov 	dev_relthread(dev, ref);
143756dd3a61SPoul-Henning Kamp 
1438f2706588SThomas Munro 	foffset_unlock_uio(fp, uio, flags | FOF_NOLOCK | FOF_NEXTOFF_R);
143956dd3a61SPoul-Henning Kamp 	return (error);
144056dd3a61SPoul-Henning Kamp }
144156dd3a61SPoul-Henning Kamp 
144256dd3a61SPoul-Henning Kamp static int
144321806f30SPoul-Henning Kamp devfs_readdir(struct vop_readdir_args *ap)
14443f54a085SPoul-Henning Kamp {
1445749e1537SPoul-Henning Kamp 	int error;
1446749e1537SPoul-Henning Kamp 	struct uio *uio;
14473f54a085SPoul-Henning Kamp 	struct dirent *dp;
1448a481b90bSPoul-Henning Kamp 	struct devfs_dirent *dd;
14493f54a085SPoul-Henning Kamp 	struct devfs_dirent *de;
1450a481b90bSPoul-Henning Kamp 	struct devfs_mount *dmp;
14517956d34bSBjoern A. Zeeb 	off_t off;
145216e35dccSDoug White 	int *tmp_ncookies = NULL;
14533f54a085SPoul-Henning Kamp 
14543f54a085SPoul-Henning Kamp 	if (ap->a_vp->v_type != VDIR)
14553f54a085SPoul-Henning Kamp 		return (ENOTDIR);
14563f54a085SPoul-Henning Kamp 
1457749e1537SPoul-Henning Kamp 	uio = ap->a_uio;
1458749e1537SPoul-Henning Kamp 	if (uio->uio_offset < 0)
1459749e1537SPoul-Henning Kamp 		return (EINVAL);
1460749e1537SPoul-Henning Kamp 
146116e35dccSDoug White 	/*
146216e35dccSDoug White 	 * XXX: This is a temporary hack to get around this filesystem not
146316e35dccSDoug White 	 * supporting cookies. We store the location of the ncookies pointer
146416e35dccSDoug White 	 * in a temporary variable before calling vfs_subr.c:vfs_read_dirent()
146516e35dccSDoug White 	 * and set the number of cookies to 0. We then set the pointer to
146616e35dccSDoug White 	 * NULL so that vfs_read_dirent doesn't try to call realloc() on
146716e35dccSDoug White 	 * ap->a_cookies. Later in this function, we restore the ap->a_ncookies
146816e35dccSDoug White 	 * pointer to its original location before returning to the caller.
146916e35dccSDoug White 	 */
147016e35dccSDoug White 	if (ap->a_ncookies != NULL) {
147116e35dccSDoug White 		tmp_ncookies = ap->a_ncookies;
147216e35dccSDoug White 		*ap->a_ncookies = 0;
147316e35dccSDoug White 		ap->a_ncookies = NULL;
147416e35dccSDoug White 	}
147516e35dccSDoug White 
1476a481b90bSPoul-Henning Kamp 	dmp = VFSTODEVFS(ap->a_vp->v_mount);
14770f6bb099SJaakko Heinonen 	if (devfs_populate_vp(ap->a_vp) != 0) {
1478828d6d12SKonstantin Belousov 		if (tmp_ncookies != NULL)
1479828d6d12SKonstantin Belousov 			ap->a_ncookies = tmp_ncookies;
1480828d6d12SKonstantin Belousov 		return (EIO);
1481828d6d12SKonstantin Belousov 	}
14823f54a085SPoul-Henning Kamp 	error = 0;
1483a481b90bSPoul-Henning Kamp 	de = ap->a_vp->v_data;
14843f54a085SPoul-Henning Kamp 	off = 0;
14855a9300c4SPoul-Henning Kamp 	TAILQ_FOREACH(dd, &de->de_dlist, de_list) {
1486e606a3c6SPoul-Henning Kamp 		KASSERT(dd->de_cdp != (void *)0xdeadc0de, ("%s %d\n", __func__, __LINE__));
148764040d39SJaakko Heinonen 		if (dd->de_flags & (DE_COVERED | DE_WHITEOUT))
14885a9300c4SPoul-Henning Kamp 			continue;
14898dc9b4cfSEd Schouten 		if (devfs_prison_check(dd, uio->uio_td))
1490f8f61460SEd Schouten 			continue;
1491a481b90bSPoul-Henning Kamp 		if (dd->de_dirent->d_type == DT_DIR)
1492a481b90bSPoul-Henning Kamp 			de = dd->de_dir;
1493a481b90bSPoul-Henning Kamp 		else
1494a481b90bSPoul-Henning Kamp 			de = dd;
1495a481b90bSPoul-Henning Kamp 		dp = dd->de_dirent;
149669921123SKonstantin Belousov 		MPASS(dp->d_reclen == GENERIC_DIRSIZ(dp));
1497749e1537SPoul-Henning Kamp 		if (dp->d_reclen > uio->uio_resid)
14983f54a085SPoul-Henning Kamp 			break;
1499749e1537SPoul-Henning Kamp 		dp->d_fileno = de->de_inode;
15001c4ca778SKonstantin Belousov 		/* NOTE: d_off is the offset for the *next* entry. */
15011c4ca778SKonstantin Belousov 		dp->d_off = off + dp->d_reclen;
1502749e1537SPoul-Henning Kamp 		if (off >= uio->uio_offset) {
150321806f30SPoul-Henning Kamp 			error = vfs_read_dirent(ap, dp, off);
1504749e1537SPoul-Henning Kamp 			if (error)
1505749e1537SPoul-Henning Kamp 				break;
1506749e1537SPoul-Henning Kamp 		}
15073f54a085SPoul-Henning Kamp 		off += dp->d_reclen;
15083f54a085SPoul-Henning Kamp 	}
1509e606a3c6SPoul-Henning Kamp 	sx_xunlock(&dmp->dm_lock);
15103f54a085SPoul-Henning Kamp 	uio->uio_offset = off;
151116e35dccSDoug White 
151216e35dccSDoug White 	/*
151316e35dccSDoug White 	 * Restore ap->a_ncookies if it wasn't originally NULL in the first
151416e35dccSDoug White 	 * place.
151516e35dccSDoug White 	 */
151616e35dccSDoug White 	if (tmp_ncookies != NULL)
151716e35dccSDoug White 		ap->a_ncookies = tmp_ncookies;
151816e35dccSDoug White 
15193f54a085SPoul-Henning Kamp 	return (error);
15203f54a085SPoul-Henning Kamp }
15213f54a085SPoul-Henning Kamp 
15223f54a085SPoul-Henning Kamp static int
152321806f30SPoul-Henning Kamp devfs_readlink(struct vop_readlink_args *ap)
15243f54a085SPoul-Henning Kamp {
15253f54a085SPoul-Henning Kamp 	struct devfs_dirent *de;
15263f54a085SPoul-Henning Kamp 
15273f54a085SPoul-Henning Kamp 	de = ap->a_vp->v_data;
1528214c8ff0SPoul-Henning Kamp 	return (uiomove(de->de_symlink, strlen(de->de_symlink), ap->a_uio));
15293f54a085SPoul-Henning Kamp }
15303f54a085SPoul-Henning Kamp 
15313b444436SMateusz Guzik static void
15323b444436SMateusz Guzik devfs_reclaiml(struct vnode *vp)
15333b444436SMateusz Guzik {
15343b444436SMateusz Guzik 	struct devfs_dirent *de;
15353b444436SMateusz Guzik 
15363b444436SMateusz Guzik 	mtx_assert(&devfs_de_interlock, MA_OWNED);
15373b444436SMateusz Guzik 	de = vp->v_data;
15383b444436SMateusz Guzik 	if (de != NULL) {
15393b444436SMateusz Guzik 		MPASS(de->de_usecount == 0);
15403b444436SMateusz Guzik 		de->de_vnode = NULL;
15413b444436SMateusz Guzik 		vp->v_data = NULL;
15423b444436SMateusz Guzik 	}
15433b444436SMateusz Guzik }
15443b444436SMateusz Guzik 
15453f54a085SPoul-Henning Kamp static int
154621806f30SPoul-Henning Kamp devfs_reclaim(struct vop_reclaim_args *ap)
15473f54a085SPoul-Henning Kamp {
15482d5bba3aSKonstantin Belousov 	struct vnode *vp;
15493f54a085SPoul-Henning Kamp 
15502d5bba3aSKonstantin Belousov 	vp = ap->a_vp;
155156eeb277SStephan Uphoff 	mtx_lock(&devfs_de_interlock);
15523b444436SMateusz Guzik 	devfs_reclaiml(vp);
155356eeb277SStephan Uphoff 	mtx_unlock(&devfs_de_interlock);
15542d5bba3aSKonstantin Belousov 	return (0);
15552d5bba3aSKonstantin Belousov }
15562d5bba3aSKonstantin Belousov 
15572d5bba3aSKonstantin Belousov static int
15582d5bba3aSKonstantin Belousov devfs_reclaim_vchr(struct vop_reclaim_args *ap)
15592d5bba3aSKonstantin Belousov {
15602d5bba3aSKonstantin Belousov 	struct vnode *vp;
15612d5bba3aSKonstantin Belousov 	struct cdev *dev;
15622d5bba3aSKonstantin Belousov 
15632d5bba3aSKonstantin Belousov 	vp = ap->a_vp;
15642d5bba3aSKonstantin Belousov 	MPASS(vp->v_type == VCHR);
15652d5bba3aSKonstantin Belousov 
15663b444436SMateusz Guzik 	mtx_lock(&devfs_de_interlock);
1567314464f4SJohn Baldwin 	VI_LOCK(vp);
15683b444436SMateusz Guzik 	devfs_usecount_subl(vp);
15693b444436SMateusz Guzik 	devfs_reclaiml(vp);
15703b444436SMateusz Guzik 	mtx_unlock(&devfs_de_interlock);
15711663075cSKonstantin Belousov 	dev_lock();
1572aa2f6ddcSPoul-Henning Kamp 	dev = vp->v_rdev;
1573aa2f6ddcSPoul-Henning Kamp 	vp->v_rdev = NULL;
1574aa2f6ddcSPoul-Henning Kamp 	dev_unlock();
1575314464f4SJohn Baldwin 	VI_UNLOCK(vp);
15762d5bba3aSKonstantin Belousov 	if (dev != NULL)
1577aa2f6ddcSPoul-Henning Kamp 		dev_rel(dev);
15783f54a085SPoul-Henning Kamp 	return (0);
15793f54a085SPoul-Henning Kamp }
15803f54a085SPoul-Henning Kamp 
15813f54a085SPoul-Henning Kamp static int
158221806f30SPoul-Henning Kamp devfs_remove(struct vop_remove_args *ap)
15833f54a085SPoul-Henning Kamp {
158489d10571SJaakko Heinonen 	struct vnode *dvp = ap->a_dvp;
15853f54a085SPoul-Henning Kamp 	struct vnode *vp = ap->a_vp;
1586a481b90bSPoul-Henning Kamp 	struct devfs_dirent *dd;
158764040d39SJaakko Heinonen 	struct devfs_dirent *de, *de_covered;
158893bcdfe2SPoul-Henning Kamp 	struct devfs_mount *dmp = VFSTODEVFS(vp->v_mount);
15893f54a085SPoul-Henning Kamp 
159089d10571SJaakko Heinonen 	ASSERT_VOP_ELOCKED(dvp, "devfs_remove");
159189d10571SJaakko Heinonen 	ASSERT_VOP_ELOCKED(vp, "devfs_remove");
159289d10571SJaakko Heinonen 
1593e606a3c6SPoul-Henning Kamp 	sx_xlock(&dmp->dm_lock);
15943f54a085SPoul-Henning Kamp 	dd = ap->a_dvp->v_data;
15953f54a085SPoul-Henning Kamp 	de = vp->v_data;
15963b72f38bSPoul-Henning Kamp 	if (de->de_cdp == NULL) {
159740739c02SPoul-Henning Kamp 		TAILQ_REMOVE(&dd->de_dlist, de, de_list);
159864040d39SJaakko Heinonen 		if (de->de_dirent->d_type == DT_LNK) {
159964040d39SJaakko Heinonen 			de_covered = devfs_find(dd, de->de_dirent->d_name,
160064040d39SJaakko Heinonen 			    de->de_dirent->d_namlen, 0);
160164040d39SJaakko Heinonen 			if (de_covered != NULL)
160264040d39SJaakko Heinonen 				de_covered->de_flags &= ~DE_COVERED;
160364040d39SJaakko Heinonen 		}
160489d10571SJaakko Heinonen 		/* We need to unlock dvp because devfs_delete() may lock it. */
1605b249ce48SMateusz Guzik 		VOP_UNLOCK(vp);
160689d10571SJaakko Heinonen 		if (dvp != vp)
1607b249ce48SMateusz Guzik 			VOP_UNLOCK(dvp);
160889d10571SJaakko Heinonen 		devfs_delete(dmp, de, 0);
160989d10571SJaakko Heinonen 		sx_xunlock(&dmp->dm_lock);
161089d10571SJaakko Heinonen 		if (dvp != vp)
161189d10571SJaakko Heinonen 			vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
161289d10571SJaakko Heinonen 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
161340739c02SPoul-Henning Kamp 	} else {
16145a9300c4SPoul-Henning Kamp 		de->de_flags |= DE_WHITEOUT;
1615e606a3c6SPoul-Henning Kamp 		sx_xunlock(&dmp->dm_lock);
161689d10571SJaakko Heinonen 	}
16173f54a085SPoul-Henning Kamp 	return (0);
16183f54a085SPoul-Henning Kamp }
16193f54a085SPoul-Henning Kamp 
16203f54a085SPoul-Henning Kamp /*
16213f54a085SPoul-Henning Kamp  * Revoke is called on a tty when a terminal session ends.  The vnode
16223f54a085SPoul-Henning Kamp  * is orphaned by setting v_op to deadfs so we need to let go of it
16233f54a085SPoul-Henning Kamp  * as well so that we create a new one next time around.
1624e606a3c6SPoul-Henning Kamp  *
16253f54a085SPoul-Henning Kamp  */
16263f54a085SPoul-Henning Kamp static int
162721806f30SPoul-Henning Kamp devfs_revoke(struct vop_revoke_args *ap)
16283f54a085SPoul-Henning Kamp {
1629e606a3c6SPoul-Henning Kamp 	struct vnode *vp = ap->a_vp, *vp2;
163049b7607eSPoul-Henning Kamp 	struct cdev *dev;
1631e606a3c6SPoul-Henning Kamp 	struct cdev_priv *cdp;
1632aa2f6ddcSPoul-Henning Kamp 	struct devfs_dirent *de;
1633f9c13ab8SMateusz Guzik 	enum vgetstate vs;
1634b114da42SPedro F. Giffuni 	u_int i;
16353f54a085SPoul-Henning Kamp 
163649b7607eSPoul-Henning Kamp 	KASSERT((ap->a_flags & REVOKEALL) != 0, ("devfs_revoke !REVOKEALL"));
163749b7607eSPoul-Henning Kamp 
163849b7607eSPoul-Henning Kamp 	dev = vp->v_rdev;
163905427aafSKonstantin Belousov 	cdp = cdev2priv(dev);
1640828d6d12SKonstantin Belousov 
1641828d6d12SKonstantin Belousov 	dev_lock();
1642828d6d12SKonstantin Belousov 	cdp->cdp_inuse++;
1643828d6d12SKonstantin Belousov 	dev_unlock();
1644828d6d12SKonstantin Belousov 
1645828d6d12SKonstantin Belousov 	vhold(vp);
1646828d6d12SKonstantin Belousov 	vgone(vp);
1647828d6d12SKonstantin Belousov 	vdrop(vp);
1648828d6d12SKonstantin Belousov 
1649b249ce48SMateusz Guzik 	VOP_UNLOCK(vp);
1650828d6d12SKonstantin Belousov  loop:
165149b7607eSPoul-Henning Kamp 	for (;;) {
165256eeb277SStephan Uphoff 		mtx_lock(&devfs_de_interlock);
165349b7607eSPoul-Henning Kamp 		dev_lock();
1654e606a3c6SPoul-Henning Kamp 		vp2 = NULL;
1655e606a3c6SPoul-Henning Kamp 		for (i = 0; i <= cdp->cdp_maxdirent; i++) {
1656e606a3c6SPoul-Henning Kamp 			de = cdp->cdp_dirents[i];
1657aa2f6ddcSPoul-Henning Kamp 			if (de == NULL)
1658e606a3c6SPoul-Henning Kamp 				continue;
165956eeb277SStephan Uphoff 
1660e606a3c6SPoul-Henning Kamp 			vp2 = de->de_vnode;
1661e606a3c6SPoul-Henning Kamp 			if (vp2 != NULL) {
166256eeb277SStephan Uphoff 				dev_unlock();
1663f9c13ab8SMateusz Guzik 				vs = vget_prep(vp2);
166456eeb277SStephan Uphoff 				mtx_unlock(&devfs_de_interlock);
1665f9c13ab8SMateusz Guzik 				if (vget_finish(vp2, LK_EXCLUSIVE, vs) != 0)
1666828d6d12SKonstantin Belousov 					goto loop;
1667828d6d12SKonstantin Belousov 				vhold(vp2);
1668e606a3c6SPoul-Henning Kamp 				vgone(vp2);
166923b77994SJeff Roberson 				vdrop(vp2);
1670828d6d12SKonstantin Belousov 				vput(vp2);
167156eeb277SStephan Uphoff 				break;
167256eeb277SStephan Uphoff 			}
167356eeb277SStephan Uphoff 		}
167456eeb277SStephan Uphoff 		if (vp2 != NULL) {
1675e606a3c6SPoul-Henning Kamp 			continue;
1676e606a3c6SPoul-Henning Kamp 		}
167756eeb277SStephan Uphoff 		dev_unlock();
167856eeb277SStephan Uphoff 		mtx_unlock(&devfs_de_interlock);
1679e606a3c6SPoul-Henning Kamp 		break;
168049b7607eSPoul-Henning Kamp 	}
1681828d6d12SKonstantin Belousov 	dev_lock();
1682828d6d12SKonstantin Belousov 	cdp->cdp_inuse--;
1683828d6d12SKonstantin Belousov 	if (!(cdp->cdp_flags & CDP_ACTIVE) && cdp->cdp_inuse == 0) {
168467864268SJason A. Harmening 		KASSERT((cdp->cdp_flags & CDP_ON_ACTIVE_LIST) != 0,
168567864268SJason A. Harmening 		    ("%s: cdp %p (%s) not on active list",
168667864268SJason A. Harmening 		    __func__, cdp, dev->si_name));
168767864268SJason A. Harmening 		cdp->cdp_flags &= ~CDP_ON_ACTIVE_LIST;
1688828d6d12SKonstantin Belousov 		TAILQ_REMOVE(&cdevp_list, cdp, cdp_list);
1689828d6d12SKonstantin Belousov 		dev_unlock();
1690828d6d12SKonstantin Belousov 		dev_rel(&cdp->cdp_c);
1691828d6d12SKonstantin Belousov 	} else
1692828d6d12SKonstantin Belousov 		dev_unlock();
1693828d6d12SKonstantin Belousov 
1694cb05b60aSAttilio Rao 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
16953f54a085SPoul-Henning Kamp 	return (0);
16963f54a085SPoul-Henning Kamp }
16973f54a085SPoul-Henning Kamp 
16983f54a085SPoul-Henning Kamp static int
169921806f30SPoul-Henning Kamp devfs_rioctl(struct vop_ioctl_args *ap)
1700ff7c5a48SPoul-Henning Kamp {
1701c4df27d5SKonstantin Belousov 	struct vnode *vp;
1702ff7c5a48SPoul-Henning Kamp 	struct devfs_mount *dmp;
1703c4df27d5SKonstantin Belousov 	int error;
1704ff7c5a48SPoul-Henning Kamp 
1705c4df27d5SKonstantin Belousov 	vp = ap->a_vp;
1706c4df27d5SKonstantin Belousov 	vn_lock(vp, LK_SHARED | LK_RETRY);
1707abd80ddbSMateusz Guzik 	if (VN_IS_DOOMED(vp)) {
1708b249ce48SMateusz Guzik 		VOP_UNLOCK(vp);
1709c4df27d5SKonstantin Belousov 		return (EBADF);
1710c4df27d5SKonstantin Belousov 	}
1711c4df27d5SKonstantin Belousov 	dmp = VFSTODEVFS(vp->v_mount);
1712e606a3c6SPoul-Henning Kamp 	sx_xlock(&dmp->dm_lock);
1713b249ce48SMateusz Guzik 	VOP_UNLOCK(vp);
1714828d6d12SKonstantin Belousov 	DEVFS_DMP_HOLD(dmp);
1715ff7c5a48SPoul-Henning Kamp 	devfs_populate(dmp);
1716828d6d12SKonstantin Belousov 	if (DEVFS_DMP_DROP(dmp)) {
1717828d6d12SKonstantin Belousov 		sx_xunlock(&dmp->dm_lock);
1718828d6d12SKonstantin Belousov 		devfs_unmount_final(dmp);
1719828d6d12SKonstantin Belousov 		return (ENOENT);
1720828d6d12SKonstantin Belousov 	}
1721214c8ff0SPoul-Henning Kamp 	error = devfs_rules_ioctl(dmp, ap->a_command, ap->a_data, ap->a_td);
1722e606a3c6SPoul-Henning Kamp 	sx_xunlock(&dmp->dm_lock);
1723ff7c5a48SPoul-Henning Kamp 	return (error);
1724ff7c5a48SPoul-Henning Kamp }
1725ff7c5a48SPoul-Henning Kamp 
1726ff7c5a48SPoul-Henning Kamp static int
172721806f30SPoul-Henning Kamp devfs_rread(struct vop_read_args *ap)
1728ff7c5a48SPoul-Henning Kamp {
1729ff7c5a48SPoul-Henning Kamp 
1730ff7c5a48SPoul-Henning Kamp 	if (ap->a_vp->v_type != VDIR)
1731ff7c5a48SPoul-Henning Kamp 		return (EINVAL);
1732ff7c5a48SPoul-Henning Kamp 	return (VOP_READDIR(ap->a_vp, ap->a_uio, ap->a_cred, NULL, NULL, NULL));
1733ff7c5a48SPoul-Henning Kamp }
1734ff7c5a48SPoul-Henning Kamp 
1735ff7c5a48SPoul-Henning Kamp static int
173621806f30SPoul-Henning Kamp devfs_setattr(struct vop_setattr_args *ap)
1737c32d0a1dSPoul-Henning Kamp {
1738c32d0a1dSPoul-Henning Kamp 	struct devfs_dirent *de;
1739c80d2913SPoul-Henning Kamp 	struct vattr *vap;
174096070273SPoul-Henning Kamp 	struct vnode *vp;
17410359a12eSAttilio Rao 	struct thread *td;
1742c80d2913SPoul-Henning Kamp 	int c, error;
1743c80d2913SPoul-Henning Kamp 	uid_t uid;
1744c80d2913SPoul-Henning Kamp 	gid_t gid;
1745c80d2913SPoul-Henning Kamp 
1746c80d2913SPoul-Henning Kamp 	vap = ap->a_vap;
174796070273SPoul-Henning Kamp 	vp = ap->a_vp;
17480359a12eSAttilio Rao 	td = curthread;
1749c80d2913SPoul-Henning Kamp 	if ((vap->va_type != VNON) ||
1750c80d2913SPoul-Henning Kamp 	    (vap->va_nlink != VNOVAL) ||
1751c80d2913SPoul-Henning Kamp 	    (vap->va_fsid != VNOVAL) ||
1752c80d2913SPoul-Henning Kamp 	    (vap->va_fileid != VNOVAL) ||
1753c80d2913SPoul-Henning Kamp 	    (vap->va_blocksize != VNOVAL) ||
1754eb7ba7f9SPoul-Henning Kamp 	    (vap->va_flags != VNOVAL && vap->va_flags != 0) ||
1755c80d2913SPoul-Henning Kamp 	    (vap->va_rdev != VNOVAL) ||
1756c80d2913SPoul-Henning Kamp 	    ((int)vap->va_bytes != VNOVAL) ||
1757c80d2913SPoul-Henning Kamp 	    (vap->va_gen != VNOVAL)) {
1758c80d2913SPoul-Henning Kamp 		return (EINVAL);
1759c80d2913SPoul-Henning Kamp 	}
1760c32d0a1dSPoul-Henning Kamp 
1761b63d070aSKonstantin Belousov 	error = devfs_populate_vp(vp);
1762b63d070aSKonstantin Belousov 	if (error != 0)
1763b63d070aSKonstantin Belousov 		return (error);
1764b63d070aSKonstantin Belousov 
176596070273SPoul-Henning Kamp 	de = vp->v_data;
176696070273SPoul-Henning Kamp 	if (vp->v_type == VDIR)
1767c32d0a1dSPoul-Henning Kamp 		de = de->de_dir;
1768c32d0a1dSPoul-Henning Kamp 
1769b63d070aSKonstantin Belousov 	c = 0;
1770c80d2913SPoul-Henning Kamp 	if (vap->va_uid == (uid_t)VNOVAL)
1771c80d2913SPoul-Henning Kamp 		uid = de->de_uid;
1772c80d2913SPoul-Henning Kamp 	else
1773c80d2913SPoul-Henning Kamp 		uid = vap->va_uid;
1774c80d2913SPoul-Henning Kamp 	if (vap->va_gid == (gid_t)VNOVAL)
1775c80d2913SPoul-Henning Kamp 		gid = de->de_gid;
1776c80d2913SPoul-Henning Kamp 	else
1777c80d2913SPoul-Henning Kamp 		gid = vap->va_gid;
1778c80d2913SPoul-Henning Kamp 	if (uid != de->de_uid || gid != de->de_gid) {
177916151645SEd Schouten 		if ((ap->a_cred->cr_uid != de->de_uid) || uid != de->de_uid ||
178016151645SEd Schouten 		    (gid != de->de_gid && !groupmember(gid, ap->a_cred))) {
17810359a12eSAttilio Rao 			error = priv_check(td, PRIV_VFS_CHOWN);
1782b63d070aSKonstantin Belousov 			if (error != 0)
1783b63d070aSKonstantin Belousov 				goto ret;
1784acd3428bSRobert Watson 		}
1785c80d2913SPoul-Henning Kamp 		de->de_uid = uid;
1786c80d2913SPoul-Henning Kamp 		de->de_gid = gid;
1787c32d0a1dSPoul-Henning Kamp 		c = 1;
1788c32d0a1dSPoul-Henning Kamp 	}
178996070273SPoul-Henning Kamp 
1790c80d2913SPoul-Henning Kamp 	if (vap->va_mode != (mode_t)VNOVAL) {
179116151645SEd Schouten 		if (ap->a_cred->cr_uid != de->de_uid) {
17920359a12eSAttilio Rao 			error = priv_check(td, PRIV_VFS_ADMIN);
1793b63d070aSKonstantin Belousov 			if (error != 0)
1794b63d070aSKonstantin Belousov 				goto ret;
1795acd3428bSRobert Watson 		}
1796c80d2913SPoul-Henning Kamp 		de->de_mode = vap->va_mode;
1797c32d0a1dSPoul-Henning Kamp 		c = 1;
1798c32d0a1dSPoul-Henning Kamp 	}
179911257f4dSBruce Evans 
180011257f4dSBruce Evans 	if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
18017b81a399SKonstantin Belousov 		error = vn_utimes_perm(vp, vap, ap->a_cred, td);
18027b81a399SKonstantin Belousov 		if (error != 0)
1803b63d070aSKonstantin Belousov 			goto ret;
180411257f4dSBruce Evans 		if (vap->va_atime.tv_sec != VNOVAL) {
180511257f4dSBruce Evans 			if (vp->v_type == VCHR)
180611257f4dSBruce Evans 				vp->v_rdev->si_atime = vap->va_atime;
180711257f4dSBruce Evans 			else
1808c80d2913SPoul-Henning Kamp 				de->de_atime = vap->va_atime;
1809c32d0a1dSPoul-Henning Kamp 		}
1810c80d2913SPoul-Henning Kamp 		if (vap->va_mtime.tv_sec != VNOVAL) {
181111257f4dSBruce Evans 			if (vp->v_type == VCHR)
181211257f4dSBruce Evans 				vp->v_rdev->si_mtime = vap->va_mtime;
181311257f4dSBruce Evans 			else
1814c80d2913SPoul-Henning Kamp 				de->de_mtime = vap->va_mtime;
181511257f4dSBruce Evans 		}
1816c80d2913SPoul-Henning Kamp 		c = 1;
1817c80d2913SPoul-Henning Kamp 	}
1818c32d0a1dSPoul-Henning Kamp 
181911257f4dSBruce Evans 	if (c) {
182011257f4dSBruce Evans 		if (vp->v_type == VCHR)
182111257f4dSBruce Evans 			vfs_timestamp(&vp->v_rdev->si_ctime);
182211257f4dSBruce Evans 		else
182311257f4dSBruce Evans 			vfs_timestamp(&de->de_mtime);
182411257f4dSBruce Evans 	}
1825b63d070aSKonstantin Belousov 
1826b63d070aSKonstantin Belousov ret:
1827b63d070aSKonstantin Belousov 	sx_xunlock(&VFSTODEVFS(vp->v_mount)->dm_lock);
1828b63d070aSKonstantin Belousov 	return (error);
1829c32d0a1dSPoul-Henning Kamp }
1830c32d0a1dSPoul-Henning Kamp 
18316742f328SRobert Watson #ifdef MAC
18326742f328SRobert Watson static int
183321806f30SPoul-Henning Kamp devfs_setlabel(struct vop_setlabel_args *ap)
18346742f328SRobert Watson {
18356742f328SRobert Watson 	struct vnode *vp;
18366742f328SRobert Watson 	struct devfs_dirent *de;
18376742f328SRobert Watson 
18386742f328SRobert Watson 	vp = ap->a_vp;
18396742f328SRobert Watson 	de = vp->v_data;
18406742f328SRobert Watson 
184130d239bcSRobert Watson 	mac_vnode_relabel(ap->a_cred, vp, ap->a_label);
184230d239bcSRobert Watson 	mac_devfs_update(vp->v_mount, de, vp);
18436742f328SRobert Watson 
18446742f328SRobert Watson 	return (0);
18456742f328SRobert Watson }
18466742f328SRobert Watson #endif
18476742f328SRobert Watson 
1848c32d0a1dSPoul-Henning Kamp static int
18492b68eb8eSMateusz Guzik devfs_stat_f(struct file *fp, struct stat *sb, struct ucred *cred)
185056dd3a61SPoul-Henning Kamp {
185156dd3a61SPoul-Henning Kamp 
18522b68eb8eSMateusz Guzik 	return (vnops.fo_stat(fp, sb, cred));
185356dd3a61SPoul-Henning Kamp }
185456dd3a61SPoul-Henning Kamp 
185556dd3a61SPoul-Henning Kamp static int
185621806f30SPoul-Henning Kamp devfs_symlink(struct vop_symlink_args *ap)
18573f54a085SPoul-Henning Kamp {
1858ecde9a6dSPoul-Henning Kamp 	int i, error;
1859a481b90bSPoul-Henning Kamp 	struct devfs_dirent *dd;
186064040d39SJaakko Heinonen 	struct devfs_dirent *de, *de_covered, *de_dotdot;
1861a481b90bSPoul-Henning Kamp 	struct devfs_mount *dmp;
18623f54a085SPoul-Henning Kamp 
1863dfd233edSAttilio Rao 	error = priv_check(curthread, PRIV_DEVFS_SYMLINK);
1864ecde9a6dSPoul-Henning Kamp 	if (error)
1865ecde9a6dSPoul-Henning Kamp 		return(error);
1866c32d0a1dSPoul-Henning Kamp 	dmp = VFSTODEVFS(ap->a_dvp->v_mount);
186789d10571SJaakko Heinonen 	if (devfs_populate_vp(ap->a_dvp) != 0)
186889d10571SJaakko Heinonen 		return (ENOENT);
186989d10571SJaakko Heinonen 
18703f54a085SPoul-Henning Kamp 	dd = ap->a_dvp->v_data;
18713f54a085SPoul-Henning Kamp 	de = devfs_newdirent(ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen);
1872d318c565SJaakko Heinonen 	de->de_flags = DE_USER;
18733f54a085SPoul-Henning Kamp 	de->de_uid = 0;
18743f54a085SPoul-Henning Kamp 	de->de_gid = 0;
1875ecde9a6dSPoul-Henning Kamp 	de->de_mode = 0755;
1876e606a3c6SPoul-Henning Kamp 	de->de_inode = alloc_unr(devfs_inos);
18774136388aSJaakko Heinonen 	de->de_dir = dd;
18783f54a085SPoul-Henning Kamp 	de->de_dirent->d_type = DT_LNK;
18793f54a085SPoul-Henning Kamp 	i = strlen(ap->a_target) + 1;
1880214c8ff0SPoul-Henning Kamp 	de->de_symlink = malloc(i, M_DEVFS, M_WAITOK);
18813f54a085SPoul-Henning Kamp 	bcopy(ap->a_target, de->de_symlink, i);
188274e62b1bSRobert Watson #ifdef MAC
188330d239bcSRobert Watson 	mac_devfs_create_symlink(ap->a_cnp->cn_cred, dmp->dm_mount, dd, de);
188474e62b1bSRobert Watson #endif
188564040d39SJaakko Heinonen 	de_covered = devfs_find(dd, de->de_dirent->d_name,
188664040d39SJaakko Heinonen 	    de->de_dirent->d_namlen, 0);
188764040d39SJaakko Heinonen 	if (de_covered != NULL) {
18882d843e7dSJaakko Heinonen 		if ((de_covered->de_flags & DE_USER) != 0) {
18892d843e7dSJaakko Heinonen 			devfs_delete(dmp, de, DEVFS_DEL_NORECURSE);
18902d843e7dSJaakko Heinonen 			sx_xunlock(&dmp->dm_lock);
18912d843e7dSJaakko Heinonen 			return (EEXIST);
18922d843e7dSJaakko Heinonen 		}
189364040d39SJaakko Heinonen 		KASSERT((de_covered->de_flags & DE_COVERED) == 0,
189464040d39SJaakko Heinonen 		    ("devfs_symlink: entry %p already covered", de_covered));
189564040d39SJaakko Heinonen 		de_covered->de_flags |= DE_COVERED;
189664040d39SJaakko Heinonen 	}
189764040d39SJaakko Heinonen 
189864040d39SJaakko Heinonen 	de_dotdot = TAILQ_FIRST(&dd->de_dlist);		/* "." */
189964040d39SJaakko Heinonen 	de_dotdot = TAILQ_NEXT(de_dotdot, de_list);	/* ".." */
190064040d39SJaakko Heinonen 	TAILQ_INSERT_AFTER(&dd->de_dlist, de_dotdot, de, de_list);
1901d318c565SJaakko Heinonen 	devfs_dir_ref_de(dmp, dd);
1902ef456eecSJaakko Heinonen 	devfs_rules_apply(dmp, de);
190364040d39SJaakko Heinonen 
19049968a426SKonstantin Belousov 	return (devfs_allocv(de, ap->a_dvp->v_mount, LK_EXCLUSIVE, ap->a_vpp));
19053f54a085SPoul-Henning Kamp }
19063f54a085SPoul-Henning Kamp 
1907e4650294SJohn Baldwin static int
1908e4650294SJohn Baldwin devfs_truncate_f(struct file *fp, off_t length, struct ucred *cred, struct thread *td)
1909e4650294SJohn Baldwin {
1910e4650294SJohn Baldwin 
1911e4650294SJohn Baldwin 	return (vnops.fo_truncate(fp, length, cred, td));
1912e4650294SJohn Baldwin }
1913e4650294SJohn Baldwin 
1914ff7c5a48SPoul-Henning Kamp static int
191564548150SKonstantin Belousov devfs_write_f(struct file *fp, struct uio *uio, struct ucred *cred,
191664548150SKonstantin Belousov     int flags, struct thread *td)
191756dd3a61SPoul-Henning Kamp {
191856dd3a61SPoul-Henning Kamp 	struct cdev *dev;
1919526d0bd5SKonstantin Belousov 	int error, ioflag, ref;
1920526d0bd5SKonstantin Belousov 	ssize_t resid;
192156dd3a61SPoul-Henning Kamp 	struct cdevsw *dsw;
19227818e0a5SKonstantin Belousov 	struct file *fpop;
192356dd3a61SPoul-Henning Kamp 
1924bf3e483bSKonstantin Belousov 	if (uio->uio_resid > DEVFS_IOSIZE_MAX)
1925bf3e483bSKonstantin Belousov 		return (EINVAL);
19267818e0a5SKonstantin Belousov 	fpop = td->td_fpop;
19273979450bSKonstantin Belousov 	error = devfs_fp_check(fp, &dev, &dsw, &ref);
1928f12aa60cSKonstantin Belousov 	if (error != 0) {
1929f12aa60cSKonstantin Belousov 		error = vnops.fo_write(fp, uio, cred, flags, td);
1930aac5167cSPoul-Henning Kamp 		return (error);
1931f12aa60cSKonstantin Belousov 	}
193256dd3a61SPoul-Henning Kamp 	KASSERT(uio->uio_td == td, ("uio_td %p is not td %p", uio->uio_td, td));
193350a36c11SPoul-Henning Kamp 	ioflag = fp->f_flag & (O_NONBLOCK | O_DIRECT | O_FSYNC);
193450a36c11SPoul-Henning Kamp 	if (ioflag & O_DIRECT)
193556dd3a61SPoul-Henning Kamp 		ioflag |= IO_DIRECT;
1936c5c1199cSKonstantin Belousov 	foffset_lock_uio(fp, uio, flags | FOF_NOLOCK);
193756dd3a61SPoul-Henning Kamp 
193856dd3a61SPoul-Henning Kamp 	resid = uio->uio_resid;
193956dd3a61SPoul-Henning Kamp 
194056dd3a61SPoul-Henning Kamp 	error = dsw->d_write(dev, uio, ioflag);
1941bda2eb9aSKonstantin Belousov 	if (uio->uio_resid != resid || (error == 0 && resid != 0)) {
1942bda2eb9aSKonstantin Belousov 		devfs_timestamp(&dev->si_ctime);
194356dd3a61SPoul-Henning Kamp 		dev->si_mtime = dev->si_ctime;
194456dd3a61SPoul-Henning Kamp 	}
19457818e0a5SKonstantin Belousov 	td->td_fpop = fpop;
19463979450bSKonstantin Belousov 	dev_relthread(dev, ref);
194756dd3a61SPoul-Henning Kamp 
1948f2706588SThomas Munro 	foffset_unlock_uio(fp, uio, flags | FOF_NOLOCK | FOF_NEXTOFF_W);
194956dd3a61SPoul-Henning Kamp 	return (error);
195056dd3a61SPoul-Henning Kamp }
1951ff7c5a48SPoul-Henning Kamp 
19527077c426SJohn Baldwin static int
19537077c426SJohn Baldwin devfs_mmap_f(struct file *fp, vm_map_t map, vm_offset_t *addr, vm_size_t size,
19547077c426SJohn Baldwin     vm_prot_t prot, vm_prot_t cap_maxprot, int flags, vm_ooffset_t foff,
19557077c426SJohn Baldwin     struct thread *td)
19567077c426SJohn Baldwin {
19577077c426SJohn Baldwin 	struct cdev *dev;
19587077c426SJohn Baldwin 	struct cdevsw *dsw;
19597077c426SJohn Baldwin 	struct mount *mp;
19607077c426SJohn Baldwin 	struct vnode *vp;
19617077c426SJohn Baldwin 	struct file *fpop;
19627077c426SJohn Baldwin 	vm_object_t object;
19637077c426SJohn Baldwin 	vm_prot_t maxprot;
19647077c426SJohn Baldwin 	int error, ref;
19657077c426SJohn Baldwin 
19667077c426SJohn Baldwin 	vp = fp->f_vnode;
19677077c426SJohn Baldwin 
19687077c426SJohn Baldwin 	/*
19697077c426SJohn Baldwin 	 * Ensure that file and memory protections are
19707077c426SJohn Baldwin 	 * compatible.
19717077c426SJohn Baldwin 	 */
19727077c426SJohn Baldwin 	mp = vp->v_mount;
1973ecc6c515SKonstantin Belousov 	if (mp != NULL && (mp->mnt_flag & MNT_NOEXEC) != 0) {
19747077c426SJohn Baldwin 		maxprot = VM_PROT_NONE;
1975ecc6c515SKonstantin Belousov 		if ((prot & VM_PROT_EXECUTE) != 0)
1976ecc6c515SKonstantin Belousov 			return (EACCES);
1977ecc6c515SKonstantin Belousov 	} else
19787077c426SJohn Baldwin 		maxprot = VM_PROT_EXECUTE;
19797077c426SJohn Baldwin 	if ((fp->f_flag & FREAD) != 0)
19807077c426SJohn Baldwin 		maxprot |= VM_PROT_READ;
19817077c426SJohn Baldwin 	else if ((prot & VM_PROT_READ) != 0)
19827077c426SJohn Baldwin 		return (EACCES);
19837077c426SJohn Baldwin 
19847077c426SJohn Baldwin 	/*
1985fada4adfSJohn Baldwin 	 * If we are sharing potential changes via MAP_SHARED and we
1986fada4adfSJohn Baldwin 	 * are trying to get write permission although we opened it
1987fada4adfSJohn Baldwin 	 * without asking for it, bail out.
1988fada4adfSJohn Baldwin 	 *
1989fada4adfSJohn Baldwin 	 * Note that most character devices always share mappings.
1990fada4adfSJohn Baldwin 	 * The one exception is that D_MMAP_ANON devices
1991fada4adfSJohn Baldwin 	 * (i.e. /dev/zero) permit private writable mappings.
1992fada4adfSJohn Baldwin 	 *
1993fada4adfSJohn Baldwin 	 * Rely on vm_mmap_cdev() to fail invalid MAP_PRIVATE requests
1994fada4adfSJohn Baldwin 	 * as well as updating maxprot to permit writing for
1995fada4adfSJohn Baldwin 	 * D_MMAP_ANON devices rather than doing that here.
19967077c426SJohn Baldwin 	 */
1997fada4adfSJohn Baldwin 	if ((flags & MAP_SHARED) != 0) {
19987077c426SJohn Baldwin 		if ((fp->f_flag & FWRITE) != 0)
19997077c426SJohn Baldwin 			maxprot |= VM_PROT_WRITE;
20007077c426SJohn Baldwin 		else if ((prot & VM_PROT_WRITE) != 0)
20017077c426SJohn Baldwin 			return (EACCES);
2002fada4adfSJohn Baldwin 	}
20037077c426SJohn Baldwin 	maxprot &= cap_maxprot;
20047077c426SJohn Baldwin 
20057077c426SJohn Baldwin 	fpop = td->td_fpop;
20067077c426SJohn Baldwin 	error = devfs_fp_check(fp, &dev, &dsw, &ref);
20077077c426SJohn Baldwin 	if (error != 0)
20087077c426SJohn Baldwin 		return (error);
20097077c426SJohn Baldwin 
20107077c426SJohn Baldwin 	error = vm_mmap_cdev(td, size, prot, &maxprot, &flags, dev, dsw, &foff,
20117077c426SJohn Baldwin 	    &object);
20127077c426SJohn Baldwin 	td->td_fpop = fpop;
20137077c426SJohn Baldwin 	dev_relthread(dev, ref);
20147077c426SJohn Baldwin 	if (error != 0)
20157077c426SJohn Baldwin 		return (error);
20167077c426SJohn Baldwin 
20177077c426SJohn Baldwin 	error = vm_mmap_object(map, addr, size, prot, maxprot, flags, object,
20187077c426SJohn Baldwin 	    foff, FALSE, td);
20197077c426SJohn Baldwin 	if (error != 0)
20207077c426SJohn Baldwin 		vm_object_deallocate(object);
20217077c426SJohn Baldwin 	return (error);
20227077c426SJohn Baldwin }
20237077c426SJohn Baldwin 
2024b43ab0e3SPoul-Henning Kamp dev_t
2025b43ab0e3SPoul-Henning Kamp dev2udev(struct cdev *x)
2026b43ab0e3SPoul-Henning Kamp {
2027b43ab0e3SPoul-Henning Kamp 	if (x == NULL)
2028b43ab0e3SPoul-Henning Kamp 		return (NODEV);
202905427aafSKonstantin Belousov 	return (cdev2priv(x)->cdp_inode);
2030b43ab0e3SPoul-Henning Kamp }
2031b43ab0e3SPoul-Henning Kamp 
20325c41d888SKonstantin Belousov static int
20335c41d888SKonstantin Belousov devfs_cmp_f(struct file *fp1, struct file *fp2, struct thread *td)
20345c41d888SKonstantin Belousov {
20355c41d888SKonstantin Belousov 	if (fp2->f_type != DTYPE_VNODE || fp2->f_ops != &devfs_ops_f)
20365c41d888SKonstantin Belousov 		return (3);
20375c41d888SKonstantin Belousov 	return (kcmp_cmp((uintptr_t)fp1->f_data, (uintptr_t)fp2->f_data));
20385c41d888SKonstantin Belousov }
20395c41d888SKonstantin Belousov 
2040*ef9ffb85SMark Johnston static const struct fileops devfs_ops_f = {
204121806f30SPoul-Henning Kamp 	.fo_read =	devfs_read_f,
204221806f30SPoul-Henning Kamp 	.fo_write =	devfs_write_f,
2043e4650294SJohn Baldwin 	.fo_truncate =	devfs_truncate_f,
204421806f30SPoul-Henning Kamp 	.fo_ioctl =	devfs_ioctl_f,
204521806f30SPoul-Henning Kamp 	.fo_poll =	devfs_poll_f,
204621806f30SPoul-Henning Kamp 	.fo_kqfilter =	devfs_kqfilter_f,
204721806f30SPoul-Henning Kamp 	.fo_stat =	devfs_stat_f,
204821806f30SPoul-Henning Kamp 	.fo_close =	devfs_close_f,
20499c00bb91SKonstantin Belousov 	.fo_chmod =	vn_chmod,
20509c00bb91SKonstantin Belousov 	.fo_chown =	vn_chown,
2051b1dd38f4SKonstantin Belousov 	.fo_sendfile =	vn_sendfile,
2052c0a46535SKonstantin Belousov 	.fo_seek =	vn_seek,
20539696feebSJohn Baldwin 	.fo_fill_kinfo = vn_fill_kinfo,
20547077c426SJohn Baldwin 	.fo_mmap =	devfs_mmap_f,
20555c41d888SKonstantin Belousov 	.fo_cmp =	devfs_cmp_f,
205621806f30SPoul-Henning Kamp 	.fo_flags =	DFLAG_PASSABLE | DFLAG_SEEKABLE
205721806f30SPoul-Henning Kamp };
205821806f30SPoul-Henning Kamp 
2059af326aceSConrad Meyer /* Vops for non-CHR vnodes in /dev. */
206021806f30SPoul-Henning Kamp static struct vop_vector devfs_vnodeops = {
206121806f30SPoul-Henning Kamp 	.vop_default =		&default_vnodeops,
206221806f30SPoul-Henning Kamp 
206321806f30SPoul-Henning Kamp 	.vop_access =		devfs_access,
206421806f30SPoul-Henning Kamp 	.vop_getattr =		devfs_getattr,
206521806f30SPoul-Henning Kamp 	.vop_ioctl =		devfs_rioctl,
206621806f30SPoul-Henning Kamp 	.vop_lookup =		devfs_lookup,
206721806f30SPoul-Henning Kamp 	.vop_mknod =		devfs_mknod,
206821806f30SPoul-Henning Kamp 	.vop_pathconf =		devfs_pathconf,
206921806f30SPoul-Henning Kamp 	.vop_read =		devfs_rread,
207021806f30SPoul-Henning Kamp 	.vop_readdir =		devfs_readdir,
207121806f30SPoul-Henning Kamp 	.vop_readlink =		devfs_readlink,
207221806f30SPoul-Henning Kamp 	.vop_reclaim =		devfs_reclaim,
207321806f30SPoul-Henning Kamp 	.vop_remove =		devfs_remove,
207421806f30SPoul-Henning Kamp 	.vop_revoke =		devfs_revoke,
207521806f30SPoul-Henning Kamp 	.vop_setattr =		devfs_setattr,
207621806f30SPoul-Henning Kamp #ifdef MAC
207721806f30SPoul-Henning Kamp 	.vop_setlabel =		devfs_setlabel,
207821806f30SPoul-Henning Kamp #endif
207921806f30SPoul-Henning Kamp 	.vop_symlink =		devfs_symlink,
20804c44fd37SJoe Marcus Clarke 	.vop_vptocnp =		devfs_vptocnp,
2081f8935a96SMateusz Guzik 	.vop_lock1 =		vop_lock,
2082f8935a96SMateusz Guzik 	.vop_unlock =		vop_unlock,
2083f8935a96SMateusz Guzik 	.vop_islocked =		vop_islocked,
20843ffcfa59SMateusz Guzik 	.vop_add_writecount =	vop_stdadd_writecount_nomsync,
208521806f30SPoul-Henning Kamp };
20866fa079fcSMateusz Guzik VFS_VOP_VECTOR_REGISTER(devfs_vnodeops);
208721806f30SPoul-Henning Kamp 
2088af326aceSConrad Meyer /* Vops for VCHR vnodes in /dev. */
20896e572e08SEdward Tomasz Napierala static struct vop_vector devfs_specops = {
209021806f30SPoul-Henning Kamp 	.vop_default =		&default_vnodeops,
209121806f30SPoul-Henning Kamp 
209221806f30SPoul-Henning Kamp 	.vop_access =		devfs_access,
209321806f30SPoul-Henning Kamp 	.vop_bmap =		VOP_PANIC,
209421806f30SPoul-Henning Kamp 	.vop_close =		devfs_close,
209521806f30SPoul-Henning Kamp 	.vop_create =		VOP_PANIC,
20961a2dbd1aSEdward Tomasz Napierala 	.vop_fsync =		vop_stdfsync,
209721806f30SPoul-Henning Kamp 	.vop_getattr =		devfs_getattr,
2098af326aceSConrad Meyer 	.vop_ioctl =		devfs_ioctl,
209921806f30SPoul-Henning Kamp 	.vop_link =		VOP_PANIC,
210021806f30SPoul-Henning Kamp 	.vop_mkdir =		VOP_PANIC,
210121806f30SPoul-Henning Kamp 	.vop_mknod =		VOP_PANIC,
210221806f30SPoul-Henning Kamp 	.vop_open =		devfs_open,
210321806f30SPoul-Henning Kamp 	.vop_pathconf =		devfs_pathconf,
2104f12aa60cSKonstantin Belousov 	.vop_poll =		dead_poll,
210521806f30SPoul-Henning Kamp 	.vop_print =		devfs_print,
2106f12aa60cSKonstantin Belousov 	.vop_read =		dead_read,
210721806f30SPoul-Henning Kamp 	.vop_readdir =		VOP_PANIC,
210821806f30SPoul-Henning Kamp 	.vop_readlink =		VOP_PANIC,
210921806f30SPoul-Henning Kamp 	.vop_reallocblks =	VOP_PANIC,
21102d5bba3aSKonstantin Belousov 	.vop_reclaim =		devfs_reclaim_vchr,
211121806f30SPoul-Henning Kamp 	.vop_remove =		devfs_remove,
211221806f30SPoul-Henning Kamp 	.vop_rename =		VOP_PANIC,
211321806f30SPoul-Henning Kamp 	.vop_revoke =		devfs_revoke,
211421806f30SPoul-Henning Kamp 	.vop_rmdir =		VOP_PANIC,
211521806f30SPoul-Henning Kamp 	.vop_setattr =		devfs_setattr,
211621806f30SPoul-Henning Kamp #ifdef MAC
211721806f30SPoul-Henning Kamp 	.vop_setlabel =		devfs_setlabel,
211821806f30SPoul-Henning Kamp #endif
211921806f30SPoul-Henning Kamp 	.vop_strategy =		VOP_PANIC,
212021806f30SPoul-Henning Kamp 	.vop_symlink =		VOP_PANIC,
21214c44fd37SJoe Marcus Clarke 	.vop_vptocnp =		devfs_vptocnp,
2122f12aa60cSKonstantin Belousov 	.vop_write =		dead_write,
2123f8935a96SMateusz Guzik 	.vop_lock1 =		vop_lock,
2124f8935a96SMateusz Guzik 	.vop_unlock =		vop_unlock,
2125f8935a96SMateusz Guzik 	.vop_islocked =		vop_islocked,
21263ffcfa59SMateusz Guzik 	.vop_add_writecount =	vop_stdadd_writecount_nomsync,
212721806f30SPoul-Henning Kamp };
21286fa079fcSMateusz Guzik VFS_VOP_VECTOR_REGISTER(devfs_specops);
212921806f30SPoul-Henning Kamp 
213010eee285SPoul-Henning Kamp /*
213110eee285SPoul-Henning Kamp  * Our calling convention to the device drivers used to be that we passed
213210eee285SPoul-Henning Kamp  * vnode.h IO_* flags to read()/write(), but we're moving to fcntl.h O_
213310eee285SPoul-Henning Kamp  * flags instead since that's what open(), close() and ioctl() takes and
213410eee285SPoul-Henning Kamp  * we don't really want vnode.h in device drivers.
213510eee285SPoul-Henning Kamp  * We solved the source compatibility by redefining some vnode flags to
213610eee285SPoul-Henning Kamp  * be the same as the fcntl ones and by sending down the bitwise OR of
213710eee285SPoul-Henning Kamp  * the respective fcntl/vnode flags.  These CTASSERTS make sure nobody
213810eee285SPoul-Henning Kamp  * pulls the rug out under this.
213910eee285SPoul-Henning Kamp  */
214010eee285SPoul-Henning Kamp CTASSERT(O_NONBLOCK == IO_NDELAY);
214110eee285SPoul-Henning Kamp CTASSERT(O_FSYNC == IO_SYNC);
2142