devfs_vnops.c revision 273462
1224762Smarius/*-
2214921Scognet * Copyright (c) 2000-2004
3214921Scognet *	Poul-Henning Kamp.  All rights reserved.
4214921Scognet * Copyright (c) 1989, 1992-1993, 1995
5214921Scognet *	The Regents of the University of California.  All rights reserved.
6214921Scognet *
7214921Scognet * This code is derived from software donated to Berkeley by
8214921Scognet * Jan-Simon Pendry.
9214921Scognet *
10214921Scognet * Redistribution and use in source and binary forms, with or without
11214921Scognet * modification, are permitted provided that the following conditions
12214921Scognet * are met:
13214921Scognet * 1. Redistributions of source code must retain the above copyright
14214921Scognet *    notice, this list of conditions and the following disclaimer.
15214921Scognet * 2. Neither the name of the University nor the names of its contributors
16214921Scognet *    may be used to endorse or promote products derived from this software
17214921Scognet *    without specific prior written permission.
18214921Scognet *
19214921Scognet * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20214921Scognet * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21214921Scognet * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22214921Scognet * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23214921Scognet * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24214921Scognet * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25214921Scognet * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26214921Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27214921Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28214921Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29214921Scognet * SUCH DAMAGE.
30214921Scognet *
31214921Scognet *	@(#)kernfs_vnops.c	8.15 (Berkeley) 5/21/95
32214921Scognet * From: FreeBSD: src/sys/miscfs/kernfs/kernfs_vnops.c 1.43
33214921Scognet *
34214921Scognet * $FreeBSD: stable/10/sys/fs/devfs/devfs_vnops.c 273462 2014-10-22 09:09:41Z kib $
35214921Scognet */
36214921Scognet
37214921Scognet/*
38214921Scognet * TODO:
39214921Scognet *	mkdir: want it ?
40214921Scognet */
41214921Scognet
42214921Scognet#include <sys/param.h>
43214921Scognet#include <sys/systm.h>
44214921Scognet#include <sys/conf.h>
45214921Scognet#include <sys/dirent.h>
46214921Scognet#include <sys/fcntl.h>
47214921Scognet#include <sys/file.h>
48214921Scognet#include <sys/filedesc.h>
49214921Scognet#include <sys/filio.h>
50214921Scognet#include <sys/jail.h>
51214921Scognet#include <sys/kernel.h>
52214921Scognet#include <sys/lock.h>
53214921Scognet#include <sys/malloc.h>
54214921Scognet#include <sys/mount.h>
55214921Scognet#include <sys/namei.h>
56214921Scognet#include <sys/priv.h>
57214921Scognet#include <sys/proc.h>
58214921Scognet#include <sys/stat.h>
59214921Scognet#include <sys/sx.h>
60214921Scognet#include <sys/time.h>
61214921Scognet#include <sys/ttycom.h>
62214921Scognet#include <sys/unistd.h>
63214921Scognet#include <sys/vnode.h>
64214921Scognet
65214921Scognetstatic struct vop_vector devfs_vnodeops;
66214921Scognetstatic struct vop_vector devfs_specops;
67214921Scognetstatic struct fileops devfs_ops_f;
68214921Scognet
69214921Scognet#include <fs/devfs/devfs.h>
70214921Scognet#include <fs/devfs/devfs_int.h>
71214921Scognet
72214921Scognet#include <security/mac/mac_framework.h>
73214921Scognet
74214921Scognetstatic MALLOC_DEFINE(M_CDEVPDATA, "DEVFSP", "Metainfo for cdev-fp data");
75214921Scognet
76214921Scognetstruct mtx	devfs_de_interlock;
77214921ScognetMTX_SYSINIT(devfs_de_interlock, &devfs_de_interlock, "devfs interlock", MTX_DEF);
78214921Scognetstruct sx	clone_drain_lock;
79214921ScognetSX_SYSINIT(clone_drain_lock, &clone_drain_lock, "clone events drain lock");
80214921Scognetstruct mtx	cdevpriv_mtx;
81214921ScognetMTX_SYSINIT(cdevpriv_mtx, &cdevpriv_mtx, "cdevpriv lock", MTX_DEF);
82214921Scognet
83214921Scognetstatic int
84214921Scognetdevfs_fp_check(struct file *fp, struct cdev **devp, struct cdevsw **dswp,
85214921Scognet    int *ref)
86214921Scognet{
87214921Scognet
88214921Scognet	*dswp = devvn_refthread(fp->f_vnode, devp, ref);
89214921Scognet	if (*devp != fp->f_data) {
90214921Scognet		if (*dswp != NULL)
91214921Scognet			dev_relthread(*devp, *ref);
92214921Scognet		return (ENXIO);
93214921Scognet	}
94214921Scognet	KASSERT((*devp)->si_refcount > 0,
95214921Scognet	    ("devfs: un-referenced struct cdev *(%s)", devtoname(*devp)));
96214921Scognet	if (*dswp == NULL)
97214921Scognet		return (ENXIO);
98214921Scognet	curthread->td_fpop = fp;
99214921Scognet	return (0);
100214921Scognet}
101214921Scognet
102214921Scognetint
103214921Scognetdevfs_get_cdevpriv(void **datap)
104214921Scognet{
105214921Scognet	struct file *fp;
106214921Scognet	struct cdev_privdata *p;
107214921Scognet	int error;
108214921Scognet
109214921Scognet	fp = curthread->td_fpop;
110214921Scognet	if (fp == NULL)
111214921Scognet		return (EBADF);
112214921Scognet	p = fp->f_cdevpriv;
113214921Scognet	if (p != NULL) {
114214921Scognet		error = 0;
115214921Scognet		*datap = p->cdpd_data;
116214921Scognet	} else
117214921Scognet		error = ENOENT;
118214921Scognet	return (error);
119214921Scognet}
120214921Scognet
121214921Scognetint
122214921Scognetdevfs_set_cdevpriv(void *priv, cdevpriv_dtr_t priv_dtr)
123214921Scognet{
124214921Scognet	struct file *fp;
125214921Scognet	struct cdev_priv *cdp;
126214921Scognet	struct cdev_privdata *p;
127214921Scognet	int error;
128214921Scognet
129214921Scognet	fp = curthread->td_fpop;
130224762Smarius	if (fp == NULL)
131214921Scognet		return (ENOENT);
132214921Scognet	cdp = cdev2priv((struct cdev *)fp->f_data);
133214921Scognet	p = malloc(sizeof(struct cdev_privdata), M_CDEVPDATA, M_WAITOK);
134214921Scognet	p->cdpd_data = priv;
135214921Scognet	p->cdpd_dtr = priv_dtr;
136214921Scognet	p->cdpd_fp = fp;
137214921Scognet	mtx_lock(&cdevpriv_mtx);
138214921Scognet	if (fp->f_cdevpriv == NULL) {
139214921Scognet		LIST_INSERT_HEAD(&cdp->cdp_fdpriv, p, cdpd_list);
140214921Scognet		fp->f_cdevpriv = p;
141214921Scognet		mtx_unlock(&cdevpriv_mtx);
142214921Scognet		error = 0;
143214921Scognet	} else {
144214921Scognet		mtx_unlock(&cdevpriv_mtx);
145214921Scognet		free(p, M_CDEVPDATA);
146214921Scognet		error = EBUSY;
147214921Scognet	}
148214921Scognet	return (error);
149214921Scognet}
150214921Scognet
151214921Scognetvoid
152214921Scognetdevfs_destroy_cdevpriv(struct cdev_privdata *p)
153214921Scognet{
154214921Scognet
155214921Scognet	mtx_assert(&cdevpriv_mtx, MA_OWNED);
156214921Scognet	p->cdpd_fp->f_cdevpriv = NULL;
157214921Scognet	LIST_REMOVE(p, cdpd_list);
158214921Scognet	mtx_unlock(&cdevpriv_mtx);
159214921Scognet	(p->cdpd_dtr)(p->cdpd_data);
160214921Scognet	free(p, M_CDEVPDATA);
161214921Scognet}
162214921Scognet
163214921Scognetvoid
164214921Scognetdevfs_fpdrop(struct file *fp)
165214921Scognet{
166293290Sbdrewery	struct cdev_privdata *p;
167214921Scognet
168214921Scognet	mtx_lock(&cdevpriv_mtx);
169214921Scognet	if ((p = fp->f_cdevpriv) == NULL) {
170214921Scognet		mtx_unlock(&cdevpriv_mtx);
171214921Scognet		return;
172214921Scognet	}
173214921Scognet	devfs_destroy_cdevpriv(p);
174214921Scognet}
175214921Scognet
176214921Scognetvoid
177214921Scognetdevfs_clear_cdevpriv(void)
178214921Scognet{
179214921Scognet	struct file *fp;
180214921Scognet
181214921Scognet	fp = curthread->td_fpop;
182214921Scognet	if (fp == NULL)
183214921Scognet		return;
184214921Scognet	devfs_fpdrop(fp);
185214921Scognet}
186214921Scognet
187214921Scognet/*
188214921Scognet * On success devfs_populate_vp() returns with dmp->dm_lock held.
189214921Scognet */
190214921Scognetstatic int
191214921Scognetdevfs_populate_vp(struct vnode *vp)
192214921Scognet{
193214921Scognet	struct devfs_dirent *de;
194214921Scognet	struct devfs_mount *dmp;
195214921Scognet	int locked;
196214921Scognet
197214921Scognet	ASSERT_VOP_LOCKED(vp, "devfs_populate_vp");
198214921Scognet
199214921Scognet	dmp = VFSTODEVFS(vp->v_mount);
200214921Scognet	locked = VOP_ISLOCKED(vp);
201214921Scognet
202214921Scognet	sx_xlock(&dmp->dm_lock);
203214921Scognet	DEVFS_DMP_HOLD(dmp);
204214921Scognet
205214921Scognet	/* Can't call devfs_populate() with the vnode lock held. */
206214921Scognet	VOP_UNLOCK(vp, 0);
207214921Scognet	devfs_populate(dmp);
208214921Scognet
209214921Scognet	sx_xunlock(&dmp->dm_lock);
210222191Snwhitehorn	vn_lock(vp, locked | LK_RETRY);
211214921Scognet	sx_xlock(&dmp->dm_lock);
212214921Scognet	if (DEVFS_DMP_DROP(dmp)) {
213214921Scognet		sx_xunlock(&dmp->dm_lock);
214214921Scognet		devfs_unmount_final(dmp);
215214921Scognet		return (EBADF);
216214921Scognet	}
217214921Scognet	if ((vp->v_iflag & VI_DOOMED) != 0) {
218214921Scognet		sx_xunlock(&dmp->dm_lock);
219214921Scognet		return (EBADF);
220214921Scognet	}
221214921Scognet	de = vp->v_data;
222214921Scognet	KASSERT(de != NULL,
223214921Scognet	    ("devfs_populate_vp: vp->v_data == NULL but vnode not doomed"));
224214921Scognet	if ((de->de_flags & DE_DOOMED) != 0) {
225214921Scognet		sx_xunlock(&dmp->dm_lock);
226214921Scognet		return (EBADF);
227214921Scognet	}
228214921Scognet
229224690Smm	return (0);
230224690Smm}
231224690Smm
232214921Scognetstatic int
233253707Smariusdevfs_vptocnp(struct vop_vptocnp_args *ap)
234214921Scognet{
235214921Scognet	struct vnode *vp = ap->a_vp;
236214921Scognet	struct vnode **dvp = ap->a_vpp;
237214921Scognet	struct devfs_mount *dmp;
238214921Scognet	char *buf = ap->a_buf;
239214921Scognet	int *buflen = ap->a_buflen;
240214921Scognet	struct devfs_dirent *dd, *de;
241214921Scognet	int i, error;
242214921Scognet
243214921Scognet	dmp = VFSTODEVFS(vp->v_mount);
244214921Scognet
245214921Scognet	error = devfs_populate_vp(vp);
246214921Scognet	if (error != 0)
247214921Scognet		return (error);
248214921Scognet
249214921Scognet	i = *buflen;
250214921Scognet	dd = vp->v_data;
251214921Scognet
252214921Scognet	if (vp->v_type == VCHR) {
253214921Scognet		i -= strlen(dd->de_cdp->cdp_c.si_name);
254214921Scognet		if (i < 0) {
255214921Scognet			error = ENOMEM;
256214921Scognet			goto finished;
257214921Scognet		}
258214921Scognet		bcopy(dd->de_cdp->cdp_c.si_name, buf + i,
259214921Scognet		    strlen(dd->de_cdp->cdp_c.si_name));
260214921Scognet		de = dd->de_dir;
261214921Scognet	} else if (vp->v_type == VDIR) {
262214921Scognet		if (dd == dmp->dm_rootdir) {
263214921Scognet			*dvp = vp;
264214921Scognet			vref(*dvp);
265214921Scognet			goto finished;
266214921Scognet		}
267214921Scognet		i -= dd->de_dirent->d_namlen;
268214921Scognet		if (i < 0) {
269214921Scognet			error = ENOMEM;
270214921Scognet			goto finished;
271214921Scognet		}
272214921Scognet		bcopy(dd->de_dirent->d_name, buf + i,
273214921Scognet		    dd->de_dirent->d_namlen);
274214921Scognet		de = dd;
275214921Scognet	} else {
276214921Scognet		error = ENOENT;
277214921Scognet		goto finished;
278214921Scognet	}
279214921Scognet	*buflen = i;
280214921Scognet	de = devfs_parent_dirent(de);
281214921Scognet	if (de == NULL) {
282214921Scognet		error = ENOENT;
283214921Scognet		goto finished;
284214921Scognet	}
285214921Scognet	mtx_lock(&devfs_de_interlock);
286214921Scognet	*dvp = de->de_vnode;
287214921Scognet	if (*dvp != NULL) {
288214921Scognet		VI_LOCK(*dvp);
289214921Scognet		mtx_unlock(&devfs_de_interlock);
290214921Scognet		vholdl(*dvp);
291214921Scognet		VI_UNLOCK(*dvp);
292214921Scognet		vref(*dvp);
293214921Scognet		vdrop(*dvp);
294214921Scognet	} else {
295214921Scognet		mtx_unlock(&devfs_de_interlock);
296214921Scognet		error = ENOENT;
297214921Scognet	}
298214921Scognetfinished:
299290595Sngie	sx_xunlock(&dmp->dm_lock);
300290595Sngie	return (error);
301214921Scognet}
302214921Scognet
303214921Scognet/*
304214921Scognet * Construct the fully qualified path name relative to the mountpoint.
305214921Scognet * If a NULL cnp is provided, no '/' is appended to the resulting path.
306214921Scognet */
307214921Scognetchar *
308214921Scognetdevfs_fqpn(char *buf, struct devfs_mount *dmp, struct devfs_dirent *dd,
309214921Scognet    struct componentname *cnp)
310214921Scognet{
311214921Scognet	int i;
312214921Scognet	struct devfs_dirent *de;
313214921Scognet
314214921Scognet	sx_assert(&dmp->dm_lock, SA_LOCKED);
315214921Scognet
316214921Scognet	i = SPECNAMELEN;
317214921Scognet	buf[i] = '\0';
318214921Scognet	if (cnp != NULL)
319214921Scognet		i -= cnp->cn_namelen;
320214921Scognet	if (i < 0)
321214921Scognet		 return (NULL);
322214921Scognet	if (cnp != NULL)
323214921Scognet		bcopy(cnp->cn_nameptr, buf + i, cnp->cn_namelen);
324214921Scognet	de = dd;
325214921Scognet	while (de != dmp->dm_rootdir) {
326214921Scognet		if (cnp != NULL || i < SPECNAMELEN) {
327214921Scognet			i--;
328214921Scognet			if (i < 0)
329214921Scognet				 return (NULL);
330214921Scognet			buf[i] = '/';
331214921Scognet		}
332214921Scognet		i -= de->de_dirent->d_namlen;
333214921Scognet		if (i < 0)
334214921Scognet			 return (NULL);
335214921Scognet		bcopy(de->de_dirent->d_name, buf + i,
336214921Scognet		    de->de_dirent->d_namlen);
337214921Scognet		de = devfs_parent_dirent(de);
338214921Scognet		if (de == NULL)
339214921Scognet			return (NULL);
340214921Scognet	}
341214921Scognet	return (buf + i);
342214921Scognet}
343214921Scognet
344214921Scognetstatic int
345214921Scognetdevfs_allocv_drop_refs(int drop_dm_lock, struct devfs_mount *dmp,
346214921Scognet	struct devfs_dirent *de)
347214921Scognet{
348214921Scognet	int not_found;
349214921Scognet
350214921Scognet	not_found = 0;
351214921Scognet	if (de->de_flags & DE_DOOMED)
352214921Scognet		not_found = 1;
353214921Scognet	if (DEVFS_DE_DROP(de)) {
354214921Scognet		KASSERT(not_found == 1, ("DEVFS de dropped but not doomed"));
355214921Scognet		devfs_dirent_free(de);
356214921Scognet	}
357214921Scognet	if (DEVFS_DMP_DROP(dmp)) {
358214921Scognet		KASSERT(not_found == 1,
359214921Scognet			("DEVFS mount struct freed before dirent"));
360214921Scognet		not_found = 2;
361214921Scognet		sx_xunlock(&dmp->dm_lock);
362214921Scognet		devfs_unmount_final(dmp);
363214921Scognet	}
364214921Scognet	if (not_found == 1 || (drop_dm_lock && not_found != 2))
365214921Scognet		sx_unlock(&dmp->dm_lock);
366214921Scognet	return (not_found);
367214921Scognet}
368214921Scognet
369214921Scognetstatic void
370214921Scognetdevfs_insmntque_dtr(struct vnode *vp, void *arg)
371214921Scognet{
372214921Scognet	struct devfs_dirent *de;
373214921Scognet
374214921Scognet	de = (struct devfs_dirent *)arg;
375214921Scognet	mtx_lock(&devfs_de_interlock);
376214921Scognet	vp->v_data = NULL;
377214921Scognet	de->de_vnode = NULL;
378214921Scognet	mtx_unlock(&devfs_de_interlock);
379214921Scognet	vgone(vp);
380214921Scognet	vput(vp);
381214921Scognet}
382214921Scognet
383214921Scognet/*
384214921Scognet * devfs_allocv shall be entered with dmp->dm_lock held, and it drops
385214921Scognet * it on return.
386214921Scognet */
387214921Scognetint
388214921Scognetdevfs_allocv(struct devfs_dirent *de, struct mount *mp, int lockmode,
389214921Scognet    struct vnode **vpp)
390214921Scognet{
391214921Scognet	int error;
392214921Scognet	struct vnode *vp;
393214921Scognet	struct cdev *dev;
394222191Snwhitehorn	struct devfs_mount *dmp;
395222191Snwhitehorn	struct cdevsw *dsw;
396214921Scognet
397214921Scognet	dmp = VFSTODEVFS(mp);
398214921Scognet	if (de->de_flags & DE_DOOMED) {
399214921Scognet		sx_xunlock(&dmp->dm_lock);
400214921Scognet		return (ENOENT);
401214921Scognet	}
402214921Scognetloop:
403214921Scognet	DEVFS_DE_HOLD(de);
404214921Scognet	DEVFS_DMP_HOLD(dmp);
405214921Scognet	mtx_lock(&devfs_de_interlock);
406214921Scognet	vp = de->de_vnode;
407214921Scognet	if (vp != NULL) {
408214921Scognet		VI_LOCK(vp);
409214921Scognet		mtx_unlock(&devfs_de_interlock);
410214921Scognet		sx_xunlock(&dmp->dm_lock);
411214921Scognet		vget(vp, lockmode | LK_INTERLOCK | LK_RETRY, curthread);
412214921Scognet		sx_xlock(&dmp->dm_lock);
413214921Scognet		if (devfs_allocv_drop_refs(0, dmp, de)) {
414214921Scognet			vput(vp);
415214921Scognet			return (ENOENT);
416214921Scognet		}
417214921Scognet		else if ((vp->v_iflag & VI_DOOMED) != 0) {
418214921Scognet			mtx_lock(&devfs_de_interlock);
419214921Scognet			if (de->de_vnode == vp) {
420214921Scognet				de->de_vnode = NULL;
421214921Scognet				vp->v_data = NULL;
422214921Scognet			}
423214921Scognet			mtx_unlock(&devfs_de_interlock);
424214921Scognet			vput(vp);
425214921Scognet			goto loop;
426214921Scognet		}
427214921Scognet		sx_xunlock(&dmp->dm_lock);
428214921Scognet		*vpp = vp;
429214921Scognet		return (0);
430214921Scognet	}
431289989Sngie	mtx_unlock(&devfs_de_interlock);
432214921Scognet	if (de->de_dirent->d_type == DT_CHR) {
433214921Scognet		if (!(de->de_cdp->cdp_flags & CDP_ACTIVE)) {
434214921Scognet			devfs_allocv_drop_refs(1, dmp, de);
435214921Scognet			return (ENOENT);
436214921Scognet		}
437214921Scognet		dev = &de->de_cdp->cdp_c;
438214921Scognet	} else {
439214921Scognet		dev = NULL;
440214921Scognet	}
441214921Scognet	error = getnewvnode("devfs", mp, &devfs_vnodeops, &vp);
442214921Scognet	if (error != 0) {
443214921Scognet		devfs_allocv_drop_refs(1, dmp, de);
444214921Scognet		printf("devfs_allocv: failed to allocate new vnode\n");
445214921Scognet		return (error);
446214921Scognet	}
447214921Scognet
448214921Scognet	if (de->de_dirent->d_type == DT_CHR) {
449214921Scognet		vp->v_type = VCHR;
450214921Scognet		VI_LOCK(vp);
451214921Scognet		dev_lock();
452214921Scognet		dev_refl(dev);
453214921Scognet		/* XXX: v_rdev should be protect by vnode lock */
454214921Scognet		vp->v_rdev = dev;
455214921Scognet		KASSERT(vp->v_usecount == 1,
456214921Scognet		    ("%s %d (%d)\n", __func__, __LINE__, vp->v_usecount));
457214921Scognet		dev->si_usecount += vp->v_usecount;
458214921Scognet		/* Special casing of ttys for deadfs.  Probably redundant. */
459214921Scognet		dsw = dev->si_devsw;
460214921Scognet		if (dsw != NULL && (dsw->d_flags & D_TTY) != 0)
461214921Scognet			vp->v_vflag |= VV_ISTTY;
462214921Scognet		dev_unlock();
463214921Scognet		VI_UNLOCK(vp);
464214921Scognet		if ((dev->si_flags & SI_ETERNAL) != 0)
465214921Scognet			vp->v_vflag |= VV_ETERNALDEV;
466214921Scognet		vp->v_op = &devfs_specops;
467214921Scognet	} else if (de->de_dirent->d_type == DT_DIR) {
468214921Scognet		vp->v_type = VDIR;
469214921Scognet	} else if (de->de_dirent->d_type == DT_LNK) {
470214921Scognet		vp->v_type = VLNK;
471214921Scognet	} else {
472214921Scognet		vp->v_type = VBAD;
473214921Scognet	}
474214921Scognet	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY | LK_NOWITNESS);
475214921Scognet	VN_LOCK_ASHARE(vp);
476214921Scognet	mtx_lock(&devfs_de_interlock);
477214921Scognet	vp->v_data = de;
478214921Scognet	de->de_vnode = vp;
479214921Scognet	mtx_unlock(&devfs_de_interlock);
480214921Scognet	error = insmntque1(vp, mp, devfs_insmntque_dtr, de);
481214921Scognet	if (error != 0) {
482214921Scognet		(void) devfs_allocv_drop_refs(1, dmp, de);
483214921Scognet		return (error);
484214921Scognet	}
485214921Scognet	if (devfs_allocv_drop_refs(0, dmp, de)) {
486214921Scognet		vput(vp);
487214921Scognet		return (ENOENT);
488214921Scognet	}
489214921Scognet#ifdef MAC
490214921Scognet	mac_devfs_vnode_associate(mp, de, vp);
491214921Scognet#endif
492214921Scognet	sx_xunlock(&dmp->dm_lock);
493214921Scognet	*vpp = vp;
494214921Scognet	return (0);
495214921Scognet}
496214921Scognet
497214921Scognetstatic int
498214921Scognetdevfs_access(struct vop_access_args *ap)
499214921Scognet{
500214921Scognet	struct vnode *vp = ap->a_vp;
501214921Scognet	struct devfs_dirent *de;
502214921Scognet	int error;
503214921Scognet
504214921Scognet	de = vp->v_data;
505214921Scognet	if (vp->v_type == VDIR)
506214921Scognet		de = de->de_dir;
507214921Scognet
508214921Scognet	error = vaccess(vp->v_type, de->de_mode, de->de_uid, de->de_gid,
509214921Scognet	    ap->a_accmode, ap->a_cred, NULL);
510214921Scognet	if (error == 0)
511214921Scognet		return (0);
512214921Scognet	if (error != EACCES)
513214921Scognet		return (error);
514214921Scognet	/* We do, however, allow access to the controlling terminal */
515214921Scognet	if (!(ap->a_td->td_proc->p_flag & P_CONTROLT))
516214921Scognet		return (error);
517214921Scognet	if (ap->a_td->td_proc->p_session->s_ttydp == de->de_cdp)
518214921Scognet		return (0);
519214921Scognet	return (error);
520214921Scognet}
521214921Scognet
522214921Scognet/* ARGSUSED */
523214921Scognetstatic int
524214921Scognetdevfs_close(struct vop_close_args *ap)
525214921Scognet{
526214921Scognet	struct vnode *vp = ap->a_vp, *oldvp;
527214921Scognet	struct thread *td = ap->a_td;
528214921Scognet	struct cdev *dev = vp->v_rdev;
529214921Scognet	struct cdevsw *dsw;
530214921Scognet	int vp_locked, error, ref;
531214921Scognet
532214921Scognet	/*
533214921Scognet	 * XXX: Don't call d_close() if we were called because of
534214921Scognet	 * XXX: insmntque1() failure.
535214921Scognet	 */
536214921Scognet	if (vp->v_data == NULL)
537214921Scognet		return (0);
538214921Scognet
539214921Scognet	/*
540214921Scognet	 * Hack: a tty device that is a controlling terminal
541214921Scognet	 * has a reference from the session structure.
542214921Scognet	 * We cannot easily tell that a character device is
543214921Scognet	 * a controlling terminal, unless it is the closing
544214921Scognet	 * process' controlling terminal.  In that case,
545214921Scognet	 * if the reference count is 2 (this last descriptor
546214921Scognet	 * plus the session), release the reference from the session.
547214921Scognet	 */
548214921Scognet	oldvp = NULL;
549214921Scognet	sx_xlock(&proctree_lock);
550214921Scognet	if (td && vp == td->td_proc->p_session->s_ttyvp) {
551214921Scognet		SESS_LOCK(td->td_proc->p_session);
552214921Scognet		VI_LOCK(vp);
553214921Scognet		if (count_dev(dev) == 2 && (vp->v_iflag & VI_DOOMED) == 0) {
554214921Scognet			td->td_proc->p_session->s_ttyvp = NULL;
555214921Scognet			td->td_proc->p_session->s_ttydp = NULL;
556214921Scognet			oldvp = vp;
557214921Scognet		}
558214921Scognet		VI_UNLOCK(vp);
559214921Scognet		SESS_UNLOCK(td->td_proc->p_session);
560214921Scognet	}
561214921Scognet	sx_xunlock(&proctree_lock);
562214921Scognet	if (oldvp != NULL)
563214921Scognet		vrele(oldvp);
564214921Scognet	/*
565214921Scognet	 * We do not want to really close the device if it
566214921Scognet	 * is still in use unless we are trying to close it
567214921Scognet	 * forcibly. Since every use (buffer, vnode, swap, cmap)
568214921Scognet	 * holds a reference to the vnode, and because we mark
569214921Scognet	 * any other vnodes that alias this device, when the
570214921Scognet	 * sum of the reference counts on all the aliased
571214921Scognet	 * vnodes descends to one, we are on last close.
572214921Scognet	 */
573214921Scognet	dsw = dev_refthread(dev, &ref);
574214921Scognet	if (dsw == NULL)
575214921Scognet		return (ENXIO);
576214921Scognet	VI_LOCK(vp);
577214921Scognet	if (vp->v_iflag & VI_DOOMED) {
578214921Scognet		/* Forced close. */
579214921Scognet	} else if (dsw->d_flags & D_TRACKCLOSE) {
580214921Scognet		/* Keep device updated on status. */
581214921Scognet	} else if (count_dev(dev) > 1) {
582214921Scognet		VI_UNLOCK(vp);
583214921Scognet		dev_relthread(dev, ref);
584214921Scognet		return (0);
585214921Scognet	}
586214921Scognet	vholdl(vp);
587214921Scognet	VI_UNLOCK(vp);
588214921Scognet	vp_locked = VOP_ISLOCKED(vp);
589214921Scognet	VOP_UNLOCK(vp, 0);
590214921Scognet	KASSERT(dev->si_refcount > 0,
591214921Scognet	    ("devfs_close() on un-referenced struct cdev *(%s)", devtoname(dev)));
592214921Scognet	error = dsw->d_close(dev, ap->a_fflag, S_IFCHR, td);
593214921Scognet	dev_relthread(dev, ref);
594214921Scognet	vn_lock(vp, vp_locked | LK_RETRY);
595214921Scognet	vdrop(vp);
596214921Scognet	return (error);
597214921Scognet}
598214921Scognet
599214921Scognetstatic int
600214921Scognetdevfs_close_f(struct file *fp, struct thread *td)
601214921Scognet{
602214921Scognet	int error;
603214921Scognet	struct file *fpop;
604214921Scognet
605214921Scognet	/*
606214921Scognet	 * NB: td may be NULL if this descriptor is closed due to
607214921Scognet	 * garbage collection from a closed UNIX domain socket.
608214921Scognet	 */
609214921Scognet	fpop = curthread->td_fpop;
610214921Scognet	curthread->td_fpop = fp;
611214921Scognet	error = vnops.fo_close(fp, td);
612214921Scognet	curthread->td_fpop = fpop;
613214921Scognet
614214921Scognet	/*
615214921Scognet	 * The f_cdevpriv cannot be assigned non-NULL value while we
616214921Scognet	 * are destroying the file.
617214921Scognet	 */
618214921Scognet	if (fp->f_cdevpriv != NULL)
619214921Scognet		devfs_fpdrop(fp);
620214921Scognet	return (error);
621214921Scognet}
622214921Scognet
623214921Scognetstatic int
624214921Scognetdevfs_fsync(struct vop_fsync_args *ap)
625214921Scognet{
626214921Scognet	int error;
627214921Scognet	struct bufobj *bo;
628214921Scognet	struct devfs_dirent *de;
629214921Scognet
630214921Scognet	if (!vn_isdisk(ap->a_vp, &error)) {
631214921Scognet		bo = &ap->a_vp->v_bufobj;
632214921Scognet		de = ap->a_vp->v_data;
633214921Scognet		if (error == ENXIO && bo->bo_dirty.bv_cnt > 0) {
634214921Scognet			printf("Device %s went missing before all of the data "
635214921Scognet			    "could be written to it; expect data loss.\n",
636214921Scognet			    de->de_dirent->d_name);
637214921Scognet
638214921Scognet			error = vop_stdfsync(ap);
639214921Scognet			if (bo->bo_dirty.bv_cnt != 0 || error != 0)
640214921Scognet				panic("devfs_fsync: vop_stdfsync failed.");
641214921Scognet		}
642214921Scognet
643214921Scognet		return (0);
644214921Scognet	}
645214921Scognet
646214921Scognet	return (vop_stdfsync(ap));
647214921Scognet}
648214921Scognet
649214921Scognetstatic int
650214921Scognetdevfs_getattr(struct vop_getattr_args *ap)
651214921Scognet{
652214921Scognet	struct vnode *vp = ap->a_vp;
653214921Scognet	struct vattr *vap = ap->a_vap;
654214921Scognet	int error;
655214921Scognet	struct devfs_dirent *de;
656214921Scognet	struct devfs_mount *dmp;
657214921Scognet	struct cdev *dev;
658214921Scognet
659214921Scognet	error = devfs_populate_vp(vp);
660214921Scognet	if (error != 0)
661214921Scognet		return (error);
662214921Scognet
663214921Scognet	dmp = VFSTODEVFS(vp->v_mount);
664224690Smm	sx_xunlock(&dmp->dm_lock);
665214921Scognet
666224690Smm	de = vp->v_data;
667214921Scognet	KASSERT(de != NULL, ("Null dirent in devfs_getattr vp=%p", vp));
668224690Smm	if (vp->v_type == VDIR) {
669214921Scognet		de = de->de_dir;
670214921Scognet		KASSERT(de != NULL,
671214921Scognet		    ("Null dir dirent in devfs_getattr vp=%p", vp));
672214921Scognet	}
673214921Scognet	vap->va_uid = de->de_uid;
674214921Scognet	vap->va_gid = de->de_gid;
675214921Scognet	vap->va_mode = de->de_mode;
676214921Scognet	if (vp->v_type == VLNK)
677214921Scognet		vap->va_size = strlen(de->de_symlink);
678214921Scognet	else if (vp->v_type == VDIR)
679214921Scognet		vap->va_size = vap->va_bytes = DEV_BSIZE;
680214921Scognet	else
681214921Scognet		vap->va_size = 0;
682214921Scognet	if (vp->v_type != VDIR)
683253707Smarius		vap->va_bytes = 0;
684253707Smarius	vap->va_blocksize = DEV_BSIZE;
685214921Scognet	vap->va_type = vp->v_type;
686214921Scognet
687214921Scognet#define fix(aa)							\
688214921Scognet	do {							\
689214921Scognet		if ((aa).tv_sec <= 3600) {			\
690214921Scognet			(aa).tv_sec = boottime.tv_sec;		\
691214921Scognet			(aa).tv_nsec = boottime.tv_usec * 1000; \
692214921Scognet		}						\
693214921Scognet	} while (0)
694214921Scognet
695214921Scognet	if (vp->v_type != VCHR)  {
696214921Scognet		fix(de->de_atime);
697214921Scognet		vap->va_atime = de->de_atime;
698214921Scognet		fix(de->de_mtime);
699214921Scognet		vap->va_mtime = de->de_mtime;
700214921Scognet		fix(de->de_ctime);
701214921Scognet		vap->va_ctime = de->de_ctime;
702214921Scognet	} else {
703214921Scognet		dev = vp->v_rdev;
704214921Scognet		fix(dev->si_atime);
705214921Scognet		vap->va_atime = dev->si_atime;
706214921Scognet		fix(dev->si_mtime);
707214921Scognet		vap->va_mtime = dev->si_mtime;
708214921Scognet		fix(dev->si_ctime);
709214921Scognet		vap->va_ctime = dev->si_ctime;
710214921Scognet
711214921Scognet		vap->va_rdev = cdev2priv(dev)->cdp_inode;
712214921Scognet	}
713214921Scognet	vap->va_gen = 0;
714214921Scognet	vap->va_flags = 0;
715214921Scognet	vap->va_filerev = 0;
716214921Scognet	vap->va_nlink = de->de_links;
717214921Scognet	vap->va_fileid = de->de_inode;
718214921Scognet
719214921Scognet	return (error);
720214921Scognet}
721214921Scognet
722214921Scognet/* ARGSUSED */
723214921Scognetstatic int
724214921Scognetdevfs_ioctl_f(struct file *fp, u_long com, void *data, struct ucred *cred, struct thread *td)
725214921Scognet{
726214921Scognet	struct cdev *dev;
727214921Scognet	struct cdevsw *dsw;
728214921Scognet	struct vnode *vp;
729214921Scognet	struct vnode *vpold;
730214921Scognet	int error, i, ref;
731214921Scognet	const char *p;
732214921Scognet	struct fiodgname_arg *fgn;
733214921Scognet	struct file *fpop;
734214921Scognet
735214921Scognet	fpop = td->td_fpop;
736214921Scognet	error = devfs_fp_check(fp, &dev, &dsw, &ref);
737214921Scognet	if (error != 0) {
738214921Scognet		error = vnops.fo_ioctl(fp, com, data, cred, td);
739214921Scognet		return (error);
740214921Scognet	}
741214921Scognet
742214921Scognet	if (com == FIODTYPE) {
743214921Scognet		*(int *)data = dsw->d_flags & D_TYPEMASK;
744214921Scognet		td->td_fpop = fpop;
745214921Scognet		dev_relthread(dev, ref);
746214921Scognet		return (0);
747214921Scognet	} else if (com == FIODGNAME) {
748214921Scognet		fgn = data;
749214921Scognet		p = devtoname(dev);
750214921Scognet		i = strlen(p) + 1;
751214921Scognet		if (i > fgn->len)
752214921Scognet			error = EINVAL;
753214921Scognet		else
754214921Scognet			error = copyout(p, fgn->buf, i);
755214921Scognet		td->td_fpop = fpop;
756214921Scognet		dev_relthread(dev, ref);
757214921Scognet		return (error);
758214921Scognet	}
759214921Scognet	error = dsw->d_ioctl(dev, com, data, fp->f_flag, td);
760214921Scognet	td->td_fpop = NULL;
761214921Scognet	dev_relthread(dev, ref);
762214921Scognet	if (error == ENOIOCTL)
763214921Scognet		error = ENOTTY;
764214921Scognet	if (error == 0 && com == TIOCSCTTY) {
765214921Scognet		vp = fp->f_vnode;
766214921Scognet
767214921Scognet		/* Do nothing if reassigning same control tty */
768214921Scognet		sx_slock(&proctree_lock);
769214921Scognet		if (td->td_proc->p_session->s_ttyvp == vp) {
770214921Scognet			sx_sunlock(&proctree_lock);
771214921Scognet			return (0);
772214921Scognet		}
773214921Scognet
774214921Scognet		vpold = td->td_proc->p_session->s_ttyvp;
775214921Scognet		VREF(vp);
776214921Scognet		SESS_LOCK(td->td_proc->p_session);
777214921Scognet		td->td_proc->p_session->s_ttyvp = vp;
778214921Scognet		td->td_proc->p_session->s_ttydp = cdev2priv(dev);
779214921Scognet		SESS_UNLOCK(td->td_proc->p_session);
780214921Scognet
781214921Scognet		sx_sunlock(&proctree_lock);
782214921Scognet
783214921Scognet		/* Get rid of reference to old control tty */
784214921Scognet		if (vpold)
785214921Scognet			vrele(vpold);
786214921Scognet	}
787214921Scognet	return (error);
788214921Scognet}
789214921Scognet
790214921Scognet/* ARGSUSED */
791214921Scognetstatic int
792214921Scognetdevfs_kqfilter_f(struct file *fp, struct knote *kn)
793214921Scognet{
794214921Scognet	struct cdev *dev;
795214921Scognet	struct cdevsw *dsw;
796214921Scognet	int error, ref;
797214921Scognet	struct file *fpop;
798214921Scognet	struct thread *td;
799214921Scognet
800214921Scognet	td = curthread;
801214921Scognet	fpop = td->td_fpop;
802214921Scognet	error = devfs_fp_check(fp, &dev, &dsw, &ref);
803214921Scognet	if (error)
804214921Scognet		return (error);
805214921Scognet	error = dsw->d_kqfilter(dev, kn);
806224762Smarius	td->td_fpop = fpop;
807214921Scognet	dev_relthread(dev, ref);
808214921Scognet	return (error);
809214921Scognet}
810214921Scognet
811214921Scognetstatic inline int
812214921Scognetdevfs_prison_check(struct devfs_dirent *de, struct thread *td)
813214921Scognet{
814214921Scognet	struct cdev_priv *cdp;
815214921Scognet	struct ucred *dcr;
816214921Scognet	int error;
817214921Scognet
818214921Scognet	cdp = de->de_cdp;
819214921Scognet	if (cdp == NULL)
820214921Scognet		return (0);
821214921Scognet	dcr = cdp->cdp_c.si_cred;
822214921Scognet	if (dcr == NULL)
823214921Scognet		return (0);
824214921Scognet
825214921Scognet	error = prison_check(td->td_ucred, dcr);
826214921Scognet	if (error == 0)
827214921Scognet		return (0);
828214921Scognet	/* We do, however, allow access to the controlling terminal */
829214921Scognet	if (!(td->td_proc->p_flag & P_CONTROLT))
830214921Scognet		return (error);
831214921Scognet	if (td->td_proc->p_session->s_ttydp == cdp)
832214921Scognet		return (0);
833214921Scognet	return (error);
834214921Scognet}
835214921Scognet
836214921Scognetstatic int
837214921Scognetdevfs_lookupx(struct vop_lookup_args *ap, int *dm_unlock)
838214921Scognet{
839214921Scognet	struct componentname *cnp;
840214921Scognet	struct vnode *dvp, **vpp;
841214921Scognet	struct thread *td;
842214921Scognet	struct devfs_dirent *de, *dd;
843224762Smarius	struct devfs_dirent **dde;
844214921Scognet	struct devfs_mount *dmp;
845214921Scognet	struct cdev *cdev;
846214921Scognet	int error, flags, nameiop, dvplocked;
847214921Scognet	char specname[SPECNAMELEN + 1], *pname;
848214921Scognet
849214921Scognet	cnp = ap->a_cnp;
850214921Scognet	vpp = ap->a_vpp;
851214921Scognet	dvp = ap->a_dvp;
852214921Scognet	pname = cnp->cn_nameptr;
853214921Scognet	td = cnp->cn_thread;
854214921Scognet	flags = cnp->cn_flags;
855214921Scognet	nameiop = cnp->cn_nameiop;
856214921Scognet	dmp = VFSTODEVFS(dvp->v_mount);
857214921Scognet	dd = dvp->v_data;
858214921Scognet	*vpp = NULLVP;
859214921Scognet
860214921Scognet	if ((flags & ISLASTCN) && nameiop == RENAME)
861214921Scognet		return (EOPNOTSUPP);
862214921Scognet
863214921Scognet	if (dvp->v_type != VDIR)
864214921Scognet		return (ENOTDIR);
865214921Scognet
866214921Scognet	if ((flags & ISDOTDOT) && (dvp->v_vflag & VV_ROOT))
867214921Scognet		return (EIO);
868214921Scognet
869214921Scognet	error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td);
870214921Scognet	if (error)
871214921Scognet		return (error);
872224762Smarius
873214921Scognet	if (cnp->cn_namelen == 1 && *pname == '.') {
874214921Scognet		if ((flags & ISLASTCN) && nameiop != LOOKUP)
875214921Scognet			return (EINVAL);
876214921Scognet		*vpp = dvp;
877214921Scognet		VREF(dvp);
878214921Scognet		return (0);
879214921Scognet	}
880214921Scognet
881214921Scognet	if (flags & ISDOTDOT) {
882214921Scognet		if ((flags & ISLASTCN) && nameiop != LOOKUP)
883214921Scognet			return (EINVAL);
884214921Scognet		de = devfs_parent_dirent(dd);
885214921Scognet		if (de == NULL)
886214921Scognet			return (ENOENT);
887214921Scognet		dvplocked = VOP_ISLOCKED(dvp);
888214921Scognet		VOP_UNLOCK(dvp, 0);
889214921Scognet		error = devfs_allocv(de, dvp->v_mount,
890214921Scognet		    cnp->cn_lkflags & LK_TYPE_MASK, vpp);
891214921Scognet		*dm_unlock = 0;
892214921Scognet		vn_lock(dvp, dvplocked | LK_RETRY);
893214921Scognet		return (error);
894214921Scognet	}
895214921Scognet
896214921Scognet	dd = dvp->v_data;
897214921Scognet	de = devfs_find(dd, cnp->cn_nameptr, cnp->cn_namelen, 0);
898214921Scognet	while (de == NULL) {	/* While(...) so we can use break */
899214921Scognet
900214921Scognet		if (nameiop == DELETE)
901214921Scognet			return (ENOENT);
902214921Scognet
903214921Scognet		/*
904214921Scognet		 * OK, we didn't have an entry for the name we were asked for
905214921Scognet		 * so we try to see if anybody can create it on demand.
906214921Scognet		 */
907214921Scognet		pname = devfs_fqpn(specname, dmp, dd, cnp);
908214921Scognet		if (pname == NULL)
909214921Scognet			break;
910214921Scognet
911214921Scognet		cdev = NULL;
912214921Scognet		DEVFS_DMP_HOLD(dmp);
913214921Scognet		sx_xunlock(&dmp->dm_lock);
914214921Scognet		sx_slock(&clone_drain_lock);
915214921Scognet		EVENTHANDLER_INVOKE(dev_clone,
916214921Scognet		    td->td_ucred, pname, strlen(pname), &cdev);
917214921Scognet		sx_sunlock(&clone_drain_lock);
918214921Scognet
919214921Scognet		if (cdev == NULL)
920214921Scognet			sx_xlock(&dmp->dm_lock);
921214921Scognet		else if (devfs_populate_vp(dvp) != 0) {
922214921Scognet			*dm_unlock = 0;
923214921Scognet			sx_xlock(&dmp->dm_lock);
924214921Scognet			if (DEVFS_DMP_DROP(dmp)) {
925214921Scognet				sx_xunlock(&dmp->dm_lock);
926214921Scognet				devfs_unmount_final(dmp);
927214921Scognet			} else
928214921Scognet				sx_xunlock(&dmp->dm_lock);
929214921Scognet			dev_rel(cdev);
930214921Scognet			return (ENOENT);
931214921Scognet		}
932214921Scognet		if (DEVFS_DMP_DROP(dmp)) {
933214921Scognet			*dm_unlock = 0;
934214921Scognet			sx_xunlock(&dmp->dm_lock);
935214921Scognet			devfs_unmount_final(dmp);
936214921Scognet			if (cdev != NULL)
937214921Scognet				dev_rel(cdev);
938214921Scognet			return (ENOENT);
939214921Scognet		}
940214921Scognet
941214921Scognet		if (cdev == NULL)
942214921Scognet			break;
943214921Scognet
944214921Scognet		dev_lock();
945214921Scognet		dde = &cdev2priv(cdev)->cdp_dirents[dmp->dm_idx];
946214921Scognet		if (dde != NULL && *dde != NULL)
947214921Scognet			de = *dde;
948214921Scognet		dev_unlock();
949214921Scognet		dev_rel(cdev);
950214921Scognet		break;
951214921Scognet	}
952214921Scognet
953214921Scognet	if (de == NULL || de->de_flags & DE_WHITEOUT) {
954214921Scognet		if ((nameiop == CREATE || nameiop == RENAME) &&
955214921Scognet		    (flags & (LOCKPARENT | WANTPARENT)) && (flags & ISLASTCN)) {
956214921Scognet			cnp->cn_flags |= SAVENAME;
957214921Scognet			return (EJUSTRETURN);
958214921Scognet		}
959214921Scognet		return (ENOENT);
960214921Scognet	}
961214921Scognet
962214921Scognet	if (devfs_prison_check(de, td))
963214921Scognet		return (ENOENT);
964214921Scognet
965214921Scognet	if ((cnp->cn_nameiop == DELETE) && (flags & ISLASTCN)) {
966214921Scognet		error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
967214921Scognet		if (error)
968214921Scognet			return (error);
969214921Scognet		if (*vpp == dvp) {
970214921Scognet			VREF(dvp);
971214921Scognet			*vpp = dvp;
972214921Scognet			return (0);
973214921Scognet		}
974214921Scognet	}
975214921Scognet	error = devfs_allocv(de, dvp->v_mount, cnp->cn_lkflags & LK_TYPE_MASK,
976214921Scognet	    vpp);
977214921Scognet	*dm_unlock = 0;
978214921Scognet	return (error);
979214921Scognet}
980214921Scognet
981214921Scognetstatic int
982214921Scognetdevfs_lookup(struct vop_lookup_args *ap)
983214921Scognet{
984214921Scognet	int j;
985214921Scognet	struct devfs_mount *dmp;
986214921Scognet	int dm_unlock;
987214921Scognet
988214921Scognet	if (devfs_populate_vp(ap->a_dvp) != 0)
989214921Scognet		return (ENOTDIR);
990214921Scognet
991214921Scognet	dmp = VFSTODEVFS(ap->a_dvp->v_mount);
992214921Scognet	dm_unlock = 1;
993214921Scognet	j = devfs_lookupx(ap, &dm_unlock);
994214921Scognet	if (dm_unlock == 1)
995214921Scognet		sx_xunlock(&dmp->dm_lock);
996214921Scognet	return (j);
997214921Scognet}
998214921Scognet
999214921Scognetstatic int
1000214921Scognetdevfs_mknod(struct vop_mknod_args *ap)
1001214921Scognet{
1002214921Scognet	struct componentname *cnp;
1003214921Scognet	struct vnode *dvp, **vpp;
1004214921Scognet	struct devfs_dirent *dd, *de;
1005214921Scognet	struct devfs_mount *dmp;
1006214921Scognet	int error;
1007214921Scognet
1008214921Scognet	/*
1009214921Scognet	 * The only type of node we should be creating here is a
1010214921Scognet	 * character device, for anything else return EOPNOTSUPP.
1011214921Scognet	 */
1012214921Scognet	if (ap->a_vap->va_type != VCHR)
1013214921Scognet		return (EOPNOTSUPP);
1014214921Scognet	dvp = ap->a_dvp;
1015214921Scognet	dmp = VFSTODEVFS(dvp->v_mount);
1016214921Scognet
1017214921Scognet	cnp = ap->a_cnp;
1018214921Scognet	vpp = ap->a_vpp;
1019214921Scognet	dd = dvp->v_data;
1020214921Scognet
1021214921Scognet	error = ENOENT;
1022214921Scognet	sx_xlock(&dmp->dm_lock);
1023214921Scognet	TAILQ_FOREACH(de, &dd->de_dlist, de_list) {
1024214921Scognet		if (cnp->cn_namelen != de->de_dirent->d_namlen)
1025214921Scognet			continue;
1026214921Scognet		if (bcmp(cnp->cn_nameptr, de->de_dirent->d_name,
1027214921Scognet		    de->de_dirent->d_namlen) != 0)
1028214921Scognet			continue;
1029214921Scognet		if (de->de_flags & DE_WHITEOUT)
1030214921Scognet			break;
1031214921Scognet		goto notfound;
1032214921Scognet	}
1033214921Scognet	if (de == NULL)
1034214921Scognet		goto notfound;
1035214921Scognet	de->de_flags &= ~DE_WHITEOUT;
1036214921Scognet	error = devfs_allocv(de, dvp->v_mount, LK_EXCLUSIVE, vpp);
1037214921Scognet	return (error);
1038214921Scognetnotfound:
1039214921Scognet	sx_xunlock(&dmp->dm_lock);
1040214921Scognet	return (error);
1041214921Scognet}
1042214921Scognet
1043214921Scognet/* ARGSUSED */
1044214921Scognetstatic int
1045214921Scognetdevfs_open(struct vop_open_args *ap)
1046214921Scognet{
1047214921Scognet	struct thread *td = ap->a_td;
1048214921Scognet	struct vnode *vp = ap->a_vp;
1049214921Scognet	struct cdev *dev = vp->v_rdev;
1050230354Seadler	struct file *fp = ap->a_fp;
1051214921Scognet	int error, ref, vlocked;
1052214921Scognet	struct cdevsw *dsw;
1053214921Scognet	struct file *fpop;
1054214921Scognet	struct mtx *mtxp;
1055214921Scognet
1056214921Scognet	if (vp->v_type == VBLK)
1057214921Scognet		return (ENXIO);
1058290595Sngie
1059214921Scognet	if (dev == NULL)
1060214921Scognet		return (ENXIO);
1061214921Scognet
1062214921Scognet	/* Make this field valid before any I/O in d_open. */
1063214921Scognet	if (dev->si_iosize_max == 0)
1064214921Scognet		dev->si_iosize_max = DFLTPHYS;
1065214921Scognet
1066214921Scognet	dsw = dev_refthread(dev, &ref);
1067214921Scognet	if (dsw == NULL)
1068214921Scognet		return (ENXIO);
1069316198Ssevan	if (fp == NULL && dsw->d_fdopen != NULL) {
1070214921Scognet		dev_relthread(dev, ref);
1071214921Scognet		return (ENXIO);
1072214921Scognet	}
1073214921Scognet
1074214921Scognet	vlocked = VOP_ISLOCKED(vp);
1075214921Scognet	VOP_UNLOCK(vp, 0);
1076214921Scognet
1077214921Scognet	fpop = td->td_fpop;
1078214921Scognet	td->td_fpop = fp;
1079214921Scognet	if (fp != NULL) {
1080214921Scognet		fp->f_data = dev;
1081214921Scognet		fp->f_vnode = vp;
1082214921Scognet	}
1083214921Scognet	if (dsw->d_fdopen != NULL)
1084214921Scognet		error = dsw->d_fdopen(dev, ap->a_mode, td, fp);
1085214921Scognet	else
1086214921Scognet		error = dsw->d_open(dev, ap->a_mode, S_IFCHR, td);
1087214921Scognet	/* cleanup any cdevpriv upon error */
1088230529Shrs	if (error != 0)
1089230529Shrs		devfs_clear_cdevpriv();
1090214921Scognet	td->td_fpop = fpop;
1091214921Scognet
1092214921Scognet	vn_lock(vp, vlocked | LK_RETRY);
1093214921Scognet	dev_relthread(dev, ref);
1094214921Scognet	if (error != 0) {
1095214921Scognet		if (error == ERESTART)
1096214921Scognet			error = EINTR;
1097214921Scognet		return (error);
1098214921Scognet	}
1099214921Scognet
1100214921Scognet#if 0	/* /dev/console */
1101214921Scognet	KASSERT(fp != NULL, ("Could not vnode bypass device on NULL fp"));
1102214921Scognet#else
1103214921Scognet	if (fp == NULL)
1104214921Scognet		return (error);
1105214921Scognet#endif
1106214921Scognet	if (fp->f_ops == &badfileops)
1107214921Scognet		finit(fp, fp->f_flag, DTYPE_VNODE, dev, &devfs_ops_f);
1108214921Scognet	mtxp = mtx_pool_find(mtxpool_sleep, fp);
1109214921Scognet
1110214921Scognet	/*
1111214921Scognet	 * Hint to the dofilewrite() to not force the buffer draining
1112214921Scognet	 * on the writer to the file.  Most likely, the write would
1113214921Scognet	 * not need normal buffers.
1114214921Scognet	 */
1115214921Scognet	mtx_lock(mtxp);
1116214921Scognet	fp->f_vnread_flags |= FDEVFS_VNODE;
1117214921Scognet	mtx_unlock(mtxp);
1118214921Scognet	return (error);
1119214921Scognet}
1120214921Scognet
1121214921Scognetstatic int
1122214921Scognetdevfs_pathconf(struct vop_pathconf_args *ap)
1123214921Scognet{
1124214921Scognet
1125214921Scognet	switch (ap->a_name) {
1126214921Scognet	case _PC_MAC_PRESENT:
1127214921Scognet#ifdef MAC
1128214921Scognet		/*
1129214921Scognet		 * If MAC is enabled, devfs automatically supports
1130214921Scognet		 * trivial non-persistant label storage.
1131214921Scognet		 */
1132214921Scognet		*ap->a_retval = 1;
1133214921Scognet#else
1134214921Scognet		*ap->a_retval = 0;
1135214921Scognet#endif
1136214921Scognet		return (0);
1137214921Scognet	default:
1138214921Scognet		return (vop_stdpathconf(ap));
1139214921Scognet	}
1140214921Scognet	/* NOTREACHED */
1141214921Scognet}
1142214921Scognet
1143214921Scognet/* ARGSUSED */
1144214921Scognetstatic int
1145214921Scognetdevfs_poll_f(struct file *fp, int events, struct ucred *cred, struct thread *td)
1146214921Scognet{
1147214921Scognet	struct cdev *dev;
1148214921Scognet	struct cdevsw *dsw;
1149214921Scognet	int error, ref;
1150214921Scognet	struct file *fpop;
1151214921Scognet
1152214921Scognet	fpop = td->td_fpop;
1153214921Scognet	error = devfs_fp_check(fp, &dev, &dsw, &ref);
1154214921Scognet	if (error != 0) {
1155214921Scognet		error = vnops.fo_poll(fp, events, cred, td);
1156214921Scognet		return (error);
1157214921Scognet	}
1158214921Scognet	error = dsw->d_poll(dev, events, td);
1159214921Scognet	td->td_fpop = fpop;
1160214921Scognet	dev_relthread(dev, ref);
1161214921Scognet	return(error);
1162214921Scognet}
1163214921Scognet
1164214921Scognet/*
1165214921Scognet * Print out the contents of a special device vnode.
1166214921Scognet */
1167214921Scognetstatic int
1168214921Scognetdevfs_print(struct vop_print_args *ap)
1169214921Scognet{
1170214921Scognet
1171214921Scognet	printf("\tdev %s\n", devtoname(ap->a_vp->v_rdev));
1172214921Scognet	return (0);
1173214921Scognet}
1174214921Scognet
1175214921Scognetstatic int
1176214921Scognetdevfs_read_f(struct file *fp, struct uio *uio, struct ucred *cred,
1177214921Scognet    int flags, struct thread *td)
1178214921Scognet{
1179214921Scognet	struct cdev *dev;
1180214921Scognet	int ioflag, error, ref;
1181214921Scognet	ssize_t resid;
1182214921Scognet	struct cdevsw *dsw;
1183214921Scognet	struct file *fpop;
1184214921Scognet
1185214921Scognet	if (uio->uio_resid > DEVFS_IOSIZE_MAX)
1186214921Scognet		return (EINVAL);
1187214921Scognet	fpop = td->td_fpop;
1188214921Scognet	error = devfs_fp_check(fp, &dev, &dsw, &ref);
1189214921Scognet	if (error != 0) {
1190214921Scognet		error = vnops.fo_read(fp, uio, cred, flags, td);
1191214921Scognet		return (error);
1192214921Scognet	}
1193214921Scognet	resid = uio->uio_resid;
1194214921Scognet	ioflag = fp->f_flag & (O_NONBLOCK | O_DIRECT);
1195214921Scognet	if (ioflag & O_DIRECT)
1196214921Scognet		ioflag |= IO_DIRECT;
1197214921Scognet
1198214921Scognet	foffset_lock_uio(fp, uio, flags | FOF_NOLOCK);
1199214921Scognet	error = dsw->d_read(dev, uio, ioflag);
1200214921Scognet	if (uio->uio_resid != resid || (error == 0 && resid != 0))
1201214921Scognet		vfs_timestamp(&dev->si_atime);
1202214921Scognet	td->td_fpop = fpop;
1203214921Scognet	dev_relthread(dev, ref);
1204214921Scognet
1205214921Scognet	foffset_unlock_uio(fp, uio, flags | FOF_NOLOCK | FOF_NEXTOFF);
1206214921Scognet	return (error);
1207214921Scognet}
1208214921Scognet
1209214921Scognetstatic int
1210214921Scognetdevfs_readdir(struct vop_readdir_args *ap)
1211214921Scognet{
1212214921Scognet	int error;
1213214921Scognet	struct uio *uio;
1214214921Scognet	struct dirent *dp;
1215214921Scognet	struct devfs_dirent *dd;
1216214921Scognet	struct devfs_dirent *de;
1217214921Scognet	struct devfs_mount *dmp;
1218214921Scognet	off_t off;
1219214921Scognet	int *tmp_ncookies = NULL;
1220214921Scognet
1221214921Scognet	if (ap->a_vp->v_type != VDIR)
1222214921Scognet		return (ENOTDIR);
1223214921Scognet
1224214921Scognet	uio = ap->a_uio;
1225214921Scognet	if (uio->uio_offset < 0)
1226214921Scognet		return (EINVAL);
1227214921Scognet
1228214921Scognet	/*
1229214921Scognet	 * XXX: This is a temporary hack to get around this filesystem not
1230214921Scognet	 * supporting cookies. We store the location of the ncookies pointer
1231214921Scognet	 * in a temporary variable before calling vfs_subr.c:vfs_read_dirent()
1232214921Scognet	 * and set the number of cookies to 0. We then set the pointer to
1233214921Scognet	 * NULL so that vfs_read_dirent doesn't try to call realloc() on
1234214921Scognet	 * ap->a_cookies. Later in this function, we restore the ap->a_ncookies
1235214921Scognet	 * pointer to its original location before returning to the caller.
1236214921Scognet	 */
1237214921Scognet	if (ap->a_ncookies != NULL) {
1238214921Scognet		tmp_ncookies = ap->a_ncookies;
1239214921Scognet		*ap->a_ncookies = 0;
1240214921Scognet		ap->a_ncookies = NULL;
1241214921Scognet	}
1242214921Scognet
1243214921Scognet	dmp = VFSTODEVFS(ap->a_vp->v_mount);
1244214921Scognet	if (devfs_populate_vp(ap->a_vp) != 0) {
1245214921Scognet		if (tmp_ncookies != NULL)
1246214921Scognet			ap->a_ncookies = tmp_ncookies;
1247214921Scognet		return (EIO);
1248214921Scognet	}
1249214921Scognet	error = 0;
1250214921Scognet	de = ap->a_vp->v_data;
1251214921Scognet	off = 0;
1252214921Scognet	TAILQ_FOREACH(dd, &de->de_dlist, de_list) {
1253214921Scognet		KASSERT(dd->de_cdp != (void *)0xdeadc0de, ("%s %d\n", __func__, __LINE__));
1254214921Scognet		if (dd->de_flags & (DE_COVERED | DE_WHITEOUT))
1255214921Scognet			continue;
1256214921Scognet		if (devfs_prison_check(dd, uio->uio_td))
1257214921Scognet			continue;
1258214921Scognet		if (dd->de_dirent->d_type == DT_DIR)
1259214921Scognet			de = dd->de_dir;
1260214921Scognet		else
1261214921Scognet			de = dd;
1262214921Scognet		dp = dd->de_dirent;
1263214921Scognet		if (dp->d_reclen > uio->uio_resid)
1264214921Scognet			break;
1265214921Scognet		dp->d_fileno = de->de_inode;
1266214921Scognet		if (off >= uio->uio_offset) {
1267214921Scognet			error = vfs_read_dirent(ap, dp, off);
1268214921Scognet			if (error)
1269214921Scognet				break;
1270214921Scognet		}
1271214921Scognet		off += dp->d_reclen;
1272214921Scognet	}
1273214921Scognet	sx_xunlock(&dmp->dm_lock);
1274214921Scognet	uio->uio_offset = off;
1275214921Scognet
1276214921Scognet	/*
1277214921Scognet	 * Restore ap->a_ncookies if it wasn't originally NULL in the first
1278214921Scognet	 * place.
1279214921Scognet	 */
1280214921Scognet	if (tmp_ncookies != NULL)
1281214921Scognet		ap->a_ncookies = tmp_ncookies;
1282214921Scognet
1283214921Scognet	return (error);
1284214921Scognet}
1285214921Scognet
1286214921Scognetstatic int
1287214921Scognetdevfs_readlink(struct vop_readlink_args *ap)
1288214921Scognet{
1289214921Scognet	struct devfs_dirent *de;
1290214921Scognet
1291214921Scognet	de = ap->a_vp->v_data;
1292214921Scognet	return (uiomove(de->de_symlink, strlen(de->de_symlink), ap->a_uio));
1293214921Scognet}
1294214921Scognet
1295214921Scognetstatic int
1296214921Scognetdevfs_reclaim(struct vop_reclaim_args *ap)
1297224690Smm{
1298224690Smm	struct vnode *vp = ap->a_vp;
1299214921Scognet	struct devfs_dirent *de;
1300214921Scognet	struct cdev *dev;
1301214921Scognet
1302214921Scognet	mtx_lock(&devfs_de_interlock);
1303214921Scognet	de = vp->v_data;
1304214921Scognet	if (de != NULL) {
1305214921Scognet		de->de_vnode = NULL;
1306214921Scognet		vp->v_data = NULL;
1307214921Scognet	}
1308214921Scognet	mtx_unlock(&devfs_de_interlock);
1309214921Scognet
1310214921Scognet	vnode_destroy_vobject(vp);
1311214921Scognet
1312214921Scognet	VI_LOCK(vp);
1313214921Scognet	dev_lock();
1314214921Scognet	dev = vp->v_rdev;
1315214921Scognet	vp->v_rdev = NULL;
1316214921Scognet
1317214921Scognet	if (dev == NULL) {
1318214921Scognet		dev_unlock();
1319214921Scognet		VI_UNLOCK(vp);
1320214921Scognet		return (0);
1321214921Scognet	}
1322214921Scognet
1323214921Scognet	dev->si_usecount -= vp->v_usecount;
1324214921Scognet	dev_unlock();
1325214921Scognet	VI_UNLOCK(vp);
1326214921Scognet	dev_rel(dev);
1327214921Scognet	return (0);
1328214921Scognet}
1329214921Scognet
1330214921Scognetstatic int
1331214921Scognetdevfs_remove(struct vop_remove_args *ap)
1332214921Scognet{
1333214921Scognet	struct vnode *dvp = ap->a_dvp;
1334214921Scognet	struct vnode *vp = ap->a_vp;
1335214921Scognet	struct devfs_dirent *dd;
1336214921Scognet	struct devfs_dirent *de, *de_covered;
1337214921Scognet	struct devfs_mount *dmp = VFSTODEVFS(vp->v_mount);
1338214921Scognet
1339214921Scognet	ASSERT_VOP_ELOCKED(dvp, "devfs_remove");
1340214921Scognet	ASSERT_VOP_ELOCKED(vp, "devfs_remove");
1341214921Scognet
1342214921Scognet	sx_xlock(&dmp->dm_lock);
1343214921Scognet	dd = ap->a_dvp->v_data;
1344214921Scognet	de = vp->v_data;
1345214921Scognet	if (de->de_cdp == NULL) {
1346214921Scognet		TAILQ_REMOVE(&dd->de_dlist, de, de_list);
1347214921Scognet		if (de->de_dirent->d_type == DT_LNK) {
1348214921Scognet			de_covered = devfs_find(dd, de->de_dirent->d_name,
1349214921Scognet			    de->de_dirent->d_namlen, 0);
1350214921Scognet			if (de_covered != NULL)
1351214921Scognet				de_covered->de_flags &= ~DE_COVERED;
1352214921Scognet		}
1353214921Scognet		/* We need to unlock dvp because devfs_delete() may lock it. */
1354214921Scognet		VOP_UNLOCK(vp, 0);
1355214921Scognet		if (dvp != vp)
1356214921Scognet			VOP_UNLOCK(dvp, 0);
1357214921Scognet		devfs_delete(dmp, de, 0);
1358214921Scognet		sx_xunlock(&dmp->dm_lock);
1359214921Scognet		if (dvp != vp)
1360214921Scognet			vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
1361214921Scognet		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
1362214921Scognet	} else {
1363214921Scognet		de->de_flags |= DE_WHITEOUT;
1364214921Scognet		sx_xunlock(&dmp->dm_lock);
1365214921Scognet	}
1366214921Scognet	return (0);
1367214921Scognet}
1368214921Scognet
1369214921Scognet/*
1370214921Scognet * Revoke is called on a tty when a terminal session ends.  The vnode
1371214921Scognet * is orphaned by setting v_op to deadfs so we need to let go of it
1372214921Scognet * as well so that we create a new one next time around.
1373214921Scognet *
1374214921Scognet */
1375214921Scognetstatic int
1376214921Scognetdevfs_revoke(struct vop_revoke_args *ap)
1377214921Scognet{
1378214921Scognet	struct vnode *vp = ap->a_vp, *vp2;
1379214921Scognet	struct cdev *dev;
1380214921Scognet	struct cdev_priv *cdp;
1381214921Scognet	struct devfs_dirent *de;
1382214921Scognet	int i;
1383214921Scognet
1384214921Scognet	KASSERT((ap->a_flags & REVOKEALL) != 0, ("devfs_revoke !REVOKEALL"));
1385214921Scognet
1386214921Scognet	dev = vp->v_rdev;
1387214921Scognet	cdp = cdev2priv(dev);
1388214921Scognet
1389214921Scognet	dev_lock();
1390214921Scognet	cdp->cdp_inuse++;
1391214921Scognet	dev_unlock();
1392214921Scognet
1393214921Scognet	vhold(vp);
1394214921Scognet	vgone(vp);
1395214921Scognet	vdrop(vp);
1396214921Scognet
1397214921Scognet	VOP_UNLOCK(vp,0);
1398214921Scognet loop:
1399214921Scognet	for (;;) {
1400214921Scognet		mtx_lock(&devfs_de_interlock);
1401214921Scognet		dev_lock();
1402214921Scognet		vp2 = NULL;
1403214921Scognet		for (i = 0; i <= cdp->cdp_maxdirent; i++) {
1404214921Scognet			de = cdp->cdp_dirents[i];
1405214921Scognet			if (de == NULL)
1406214921Scognet				continue;
1407214921Scognet
1408214921Scognet			vp2 = de->de_vnode;
1409214921Scognet			if (vp2 != NULL) {
1410214921Scognet				dev_unlock();
1411214921Scognet				VI_LOCK(vp2);
1412214921Scognet				mtx_unlock(&devfs_de_interlock);
1413214921Scognet				if (vget(vp2, LK_EXCLUSIVE | LK_INTERLOCK,
1414214921Scognet				    curthread))
1415214921Scognet					goto loop;
1416214921Scognet				vhold(vp2);
1417214921Scognet				vgone(vp2);
1418214921Scognet				vdrop(vp2);
1419214921Scognet				vput(vp2);
1420214921Scognet				break;
1421214921Scognet			}
1422214921Scognet		}
1423214921Scognet		if (vp2 != NULL) {
1424214921Scognet			continue;
1425214921Scognet		}
1426214921Scognet		dev_unlock();
1427214921Scognet		mtx_unlock(&devfs_de_interlock);
1428214921Scognet		break;
1429214921Scognet	}
1430214921Scognet	dev_lock();
1431214921Scognet	cdp->cdp_inuse--;
1432214921Scognet	if (!(cdp->cdp_flags & CDP_ACTIVE) && cdp->cdp_inuse == 0) {
1433214921Scognet		TAILQ_REMOVE(&cdevp_list, cdp, cdp_list);
1434214921Scognet		dev_unlock();
1435214921Scognet		dev_rel(&cdp->cdp_c);
1436214921Scognet	} else
1437214921Scognet		dev_unlock();
1438214921Scognet
1439214921Scognet	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
1440214921Scognet	return (0);
1441214921Scognet}
1442214921Scognet
1443214921Scognetstatic int
1444214921Scognetdevfs_rioctl(struct vop_ioctl_args *ap)
1445214921Scognet{
1446214921Scognet	struct vnode *vp;
1447214921Scognet	struct devfs_mount *dmp;
1448214921Scognet	int error;
1449214921Scognet
1450214921Scognet	vp = ap->a_vp;
1451214921Scognet	vn_lock(vp, LK_SHARED | LK_RETRY);
1452214921Scognet	if (vp->v_iflag & VI_DOOMED) {
1453214921Scognet		VOP_UNLOCK(vp, 0);
1454214921Scognet		return (EBADF);
1455214921Scognet	}
1456214921Scognet	dmp = VFSTODEVFS(vp->v_mount);
1457214921Scognet	sx_xlock(&dmp->dm_lock);
1458214921Scognet	VOP_UNLOCK(vp, 0);
1459214921Scognet	DEVFS_DMP_HOLD(dmp);
1460214921Scognet	devfs_populate(dmp);
1461214921Scognet	if (DEVFS_DMP_DROP(dmp)) {
1462214921Scognet		sx_xunlock(&dmp->dm_lock);
1463214921Scognet		devfs_unmount_final(dmp);
1464214921Scognet		return (ENOENT);
1465214921Scognet	}
1466214921Scognet	error = devfs_rules_ioctl(dmp, ap->a_command, ap->a_data, ap->a_td);
1467214921Scognet	sx_xunlock(&dmp->dm_lock);
1468214921Scognet	return (error);
1469214921Scognet}
1470214921Scognet
1471214921Scognetstatic int
1472214921Scognetdevfs_rread(struct vop_read_args *ap)
1473214921Scognet{
1474214921Scognet
1475214921Scognet	if (ap->a_vp->v_type != VDIR)
1476214921Scognet		return (EINVAL);
1477214921Scognet	return (VOP_READDIR(ap->a_vp, ap->a_uio, ap->a_cred, NULL, NULL, NULL));
1478214921Scognet}
1479214921Scognet
1480214921Scognetstatic int
1481214921Scognetdevfs_setattr(struct vop_setattr_args *ap)
1482214921Scognet{
1483214921Scognet	struct devfs_dirent *de;
1484214921Scognet	struct vattr *vap;
1485214921Scognet	struct vnode *vp;
1486214921Scognet	struct thread *td;
1487214921Scognet	int c, error;
1488214921Scognet	uid_t uid;
1489214921Scognet	gid_t gid;
1490214921Scognet
1491214921Scognet	vap = ap->a_vap;
1492214921Scognet	vp = ap->a_vp;
1493214921Scognet	td = curthread;
1494214921Scognet	if ((vap->va_type != VNON) ||
1495214921Scognet	    (vap->va_nlink != VNOVAL) ||
1496214921Scognet	    (vap->va_fsid != VNOVAL) ||
1497214921Scognet	    (vap->va_fileid != VNOVAL) ||
1498214921Scognet	    (vap->va_blocksize != VNOVAL) ||
1499214921Scognet	    (vap->va_flags != VNOVAL && vap->va_flags != 0) ||
1500214921Scognet	    (vap->va_rdev != VNOVAL) ||
1501214921Scognet	    ((int)vap->va_bytes != VNOVAL) ||
1502214921Scognet	    (vap->va_gen != VNOVAL)) {
1503214921Scognet		return (EINVAL);
1504214921Scognet	}
1505214921Scognet
1506214921Scognet	de = vp->v_data;
1507214921Scognet	if (vp->v_type == VDIR)
1508214921Scognet		de = de->de_dir;
1509214921Scognet
1510214921Scognet	error = c = 0;
1511214921Scognet	if (vap->va_uid == (uid_t)VNOVAL)
1512214921Scognet		uid = de->de_uid;
1513214921Scognet	else
1514214921Scognet		uid = vap->va_uid;
1515214921Scognet	if (vap->va_gid == (gid_t)VNOVAL)
1516214921Scognet		gid = de->de_gid;
1517214921Scognet	else
1518214921Scognet		gid = vap->va_gid;
1519214921Scognet	if (uid != de->de_uid || gid != de->de_gid) {
1520214921Scognet		if ((ap->a_cred->cr_uid != de->de_uid) || uid != de->de_uid ||
1521214921Scognet		    (gid != de->de_gid && !groupmember(gid, ap->a_cred))) {
1522214921Scognet			error = priv_check(td, PRIV_VFS_CHOWN);
1523214921Scognet			if (error)
1524214921Scognet				return (error);
1525214921Scognet		}
1526214921Scognet		de->de_uid = uid;
1527214921Scognet		de->de_gid = gid;
1528214921Scognet		c = 1;
1529214921Scognet	}
1530214921Scognet
1531214921Scognet	if (vap->va_mode != (mode_t)VNOVAL) {
1532214921Scognet		if (ap->a_cred->cr_uid != de->de_uid) {
1533214921Scognet			error = priv_check(td, PRIV_VFS_ADMIN);
1534214921Scognet			if (error)
1535214921Scognet				return (error);
1536214921Scognet		}
1537214921Scognet		de->de_mode = vap->va_mode;
1538214921Scognet		c = 1;
1539214921Scognet	}
1540214921Scognet
1541214921Scognet	if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
1542214921Scognet		error = vn_utimes_perm(vp, vap, ap->a_cred, td);
1543214921Scognet		if (error != 0)
1544214921Scognet			return (error);
1545214921Scognet		if (vap->va_atime.tv_sec != VNOVAL) {
1546214921Scognet			if (vp->v_type == VCHR)
1547214921Scognet				vp->v_rdev->si_atime = vap->va_atime;
1548214921Scognet			else
1549214921Scognet				de->de_atime = vap->va_atime;
1550214921Scognet		}
1551214921Scognet		if (vap->va_mtime.tv_sec != VNOVAL) {
1552214921Scognet			if (vp->v_type == VCHR)
1553214921Scognet				vp->v_rdev->si_mtime = vap->va_mtime;
1554214921Scognet			else
1555214921Scognet				de->de_mtime = vap->va_mtime;
1556214921Scognet		}
1557214921Scognet		c = 1;
1558214921Scognet	}
1559214921Scognet
1560214921Scognet	if (c) {
1561214921Scognet		if (vp->v_type == VCHR)
1562214921Scognet			vfs_timestamp(&vp->v_rdev->si_ctime);
1563214921Scognet		else
1564214921Scognet			vfs_timestamp(&de->de_mtime);
1565214921Scognet	}
1566214921Scognet	return (0);
1567214921Scognet}
1568214921Scognet
1569214921Scognet#ifdef MAC
1570230795Sjkimstatic int
1571214921Scognetdevfs_setlabel(struct vop_setlabel_args *ap)
1572230795Sjkim{
1573214921Scognet	struct vnode *vp;
1574230795Sjkim	struct devfs_dirent *de;
1575230795Sjkim
1576230795Sjkim	vp = ap->a_vp;
1577230795Sjkim	de = vp->v_data;
1578230795Sjkim
1579214921Scognet	mac_vnode_relabel(ap->a_cred, vp, ap->a_label);
1580214921Scognet	mac_devfs_update(vp->v_mount, de, vp);
1581214921Scognet
1582214921Scognet	return (0);
1583214921Scognet}
1584214921Scognet#endif
1585214921Scognet
1586214921Scognetstatic int
1587214921Scognetdevfs_stat_f(struct file *fp, struct stat *sb, struct ucred *cred, struct thread *td)
1588214921Scognet{
1589214921Scognet
1590214921Scognet	return (vnops.fo_stat(fp, sb, cred, td));
1591214921Scognet}
1592214921Scognet
1593214921Scognetstatic int
1594214921Scognetdevfs_symlink(struct vop_symlink_args *ap)
1595214921Scognet{
1596214921Scognet	int i, error;
1597214921Scognet	struct devfs_dirent *dd;
1598214921Scognet	struct devfs_dirent *de, *de_covered, *de_dotdot;
1599214921Scognet	struct devfs_mount *dmp;
1600214921Scognet
1601214921Scognet	error = priv_check(curthread, PRIV_DEVFS_SYMLINK);
1602214921Scognet	if (error)
1603214921Scognet		return(error);
1604225121Smm	dmp = VFSTODEVFS(ap->a_dvp->v_mount);
1605214921Scognet	if (devfs_populate_vp(ap->a_dvp) != 0)
1606214921Scognet		return (ENOENT);
1607214921Scognet
1608214921Scognet	dd = ap->a_dvp->v_data;
1609214921Scognet	de = devfs_newdirent(ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen);
1610214921Scognet	de->de_flags = DE_USER;
1611214921Scognet	de->de_uid = 0;
1612214921Scognet	de->de_gid = 0;
1613214921Scognet	de->de_mode = 0755;
1614214921Scognet	de->de_inode = alloc_unr(devfs_inos);
1615214921Scognet	de->de_dir = dd;
1616214921Scognet	de->de_dirent->d_type = DT_LNK;
1617214921Scognet	i = strlen(ap->a_target) + 1;
1618214921Scognet	de->de_symlink = malloc(i, M_DEVFS, M_WAITOK);
1619214921Scognet	bcopy(ap->a_target, de->de_symlink, i);
1620214921Scognet#ifdef MAC
1621225121Smm	mac_devfs_create_symlink(ap->a_cnp->cn_cred, dmp->dm_mount, dd, de);
1622214921Scognet#endif
1623214921Scognet	de_covered = devfs_find(dd, de->de_dirent->d_name,
1624214921Scognet	    de->de_dirent->d_namlen, 0);
1625214921Scognet	if (de_covered != NULL) {
1626214921Scognet		if ((de_covered->de_flags & DE_USER) != 0) {
1627214921Scognet			devfs_delete(dmp, de, DEVFS_DEL_NORECURSE);
1628214921Scognet			sx_xunlock(&dmp->dm_lock);
1629214921Scognet			return (EEXIST);
1630214921Scognet		}
1631214921Scognet		KASSERT((de_covered->de_flags & DE_COVERED) == 0,
1632214921Scognet		    ("devfs_symlink: entry %p already covered", de_covered));
1633214921Scognet		de_covered->de_flags |= DE_COVERED;
1634214921Scognet	}
1635214921Scognet
1636214921Scognet	de_dotdot = TAILQ_FIRST(&dd->de_dlist);		/* "." */
1637214921Scognet	de_dotdot = TAILQ_NEXT(de_dotdot, de_list);	/* ".." */
1638214921Scognet	TAILQ_INSERT_AFTER(&dd->de_dlist, de_dotdot, de, de_list);
1639214921Scognet	devfs_dir_ref_de(dmp, dd);
1640214921Scognet	devfs_rules_apply(dmp, de);
1641214921Scognet
1642214921Scognet	return (devfs_allocv(de, ap->a_dvp->v_mount, LK_EXCLUSIVE, ap->a_vpp));
1643214921Scognet}
1644214921Scognet
1645214921Scognetstatic int
1646214921Scognetdevfs_truncate_f(struct file *fp, off_t length, struct ucred *cred, struct thread *td)
1647214921Scognet{
1648214921Scognet
1649214921Scognet	return (vnops.fo_truncate(fp, length, cred, td));
1650214921Scognet}
1651214921Scognet
1652214921Scognetstatic int
1653214921Scognetdevfs_write_f(struct file *fp, struct uio *uio, struct ucred *cred,
1654214921Scognet    int flags, struct thread *td)
1655214921Scognet{
1656214921Scognet	struct cdev *dev;
1657214921Scognet	int error, ioflag, ref;
1658214921Scognet	ssize_t resid;
1659214921Scognet	struct cdevsw *dsw;
1660214921Scognet	struct file *fpop;
1661214921Scognet
1662214921Scognet	if (uio->uio_resid > DEVFS_IOSIZE_MAX)
1663214921Scognet		return (EINVAL);
1664214921Scognet	fpop = td->td_fpop;
1665225121Smm	error = devfs_fp_check(fp, &dev, &dsw, &ref);
1666214921Scognet	if (error != 0) {
1667214921Scognet		error = vnops.fo_write(fp, uio, cred, flags, td);
1668214921Scognet		return (error);
1669214921Scognet	}
1670214921Scognet	KASSERT(uio->uio_td == td, ("uio_td %p is not td %p", uio->uio_td, td));
1671214921Scognet	ioflag = fp->f_flag & (O_NONBLOCK | O_DIRECT | O_FSYNC);
1672214921Scognet	if (ioflag & O_DIRECT)
1673214921Scognet		ioflag |= IO_DIRECT;
1674214921Scognet	foffset_lock_uio(fp, uio, flags | FOF_NOLOCK);
1675214921Scognet
1676214921Scognet	resid = uio->uio_resid;
1677214921Scognet
1678214921Scognet	error = dsw->d_write(dev, uio, ioflag);
1679214921Scognet	if (uio->uio_resid != resid || (error == 0 && resid != 0)) {
1680214921Scognet		vfs_timestamp(&dev->si_ctime);
1681214921Scognet		dev->si_mtime = dev->si_ctime;
1682214921Scognet	}
1683214921Scognet	td->td_fpop = fpop;
1684214921Scognet	dev_relthread(dev, ref);
1685214921Scognet
1686214921Scognet	foffset_unlock_uio(fp, uio, flags | FOF_NOLOCK | FOF_NEXTOFF);
1687214921Scognet	return (error);
1688214921Scognet}
1689214921Scognet
1690214921Scognetdev_t
1691214921Scognetdev2udev(struct cdev *x)
1692214921Scognet{
1693214921Scognet	if (x == NULL)
1694214921Scognet		return (NODEV);
1695214921Scognet	return (cdev2priv(x)->cdp_inode);
1696214921Scognet}
1697214921Scognet
1698214921Scognetstatic struct fileops devfs_ops_f = {
1699214921Scognet	.fo_read =	devfs_read_f,
1700214921Scognet	.fo_write =	devfs_write_f,
1701214921Scognet	.fo_truncate =	devfs_truncate_f,
1702214921Scognet	.fo_ioctl =	devfs_ioctl_f,
1703214921Scognet	.fo_poll =	devfs_poll_f,
1704214921Scognet	.fo_kqfilter =	devfs_kqfilter_f,
1705214921Scognet	.fo_stat =	devfs_stat_f,
1706214921Scognet	.fo_close =	devfs_close_f,
1707214921Scognet	.fo_chmod =	vn_chmod,
1708214921Scognet	.fo_chown =	vn_chown,
1709214921Scognet	.fo_sendfile =	vn_sendfile,
1710214921Scognet	.fo_seek =	vn_seek,
1711214921Scognet	.fo_flags =	DFLAG_PASSABLE | DFLAG_SEEKABLE
1712214921Scognet};
1713214921Scognet
1714214921Scognetstatic struct vop_vector devfs_vnodeops = {
1715214921Scognet	.vop_default =		&default_vnodeops,
1716214921Scognet
1717214921Scognet	.vop_access =		devfs_access,
1718214921Scognet	.vop_getattr =		devfs_getattr,
1719214921Scognet	.vop_ioctl =		devfs_rioctl,
1720214921Scognet	.vop_lookup =		devfs_lookup,
1721214921Scognet	.vop_mknod =		devfs_mknod,
1722214921Scognet	.vop_pathconf =		devfs_pathconf,
1723214921Scognet	.vop_read =		devfs_rread,
1724214921Scognet	.vop_readdir =		devfs_readdir,
1725214921Scognet	.vop_readlink =		devfs_readlink,
1726214921Scognet	.vop_reclaim =		devfs_reclaim,
1727214921Scognet	.vop_remove =		devfs_remove,
1728214921Scognet	.vop_revoke =		devfs_revoke,
1729214921Scognet	.vop_setattr =		devfs_setattr,
1730214921Scognet#ifdef MAC
1731214921Scognet	.vop_setlabel =		devfs_setlabel,
1732214921Scognet#endif
1733214921Scognet	.vop_symlink =		devfs_symlink,
1734290595Sngie	.vop_vptocnp =		devfs_vptocnp,
1735214921Scognet};
1736214921Scognet
1737214921Scognetstatic struct vop_vector devfs_specops = {
1738214921Scognet	.vop_default =		&default_vnodeops,
1739214921Scognet
1740214921Scognet	.vop_access =		devfs_access,
1741214921Scognet	.vop_bmap =		VOP_PANIC,
1742214921Scognet	.vop_close =		devfs_close,
1743214921Scognet	.vop_create =		VOP_PANIC,
1744214921Scognet	.vop_fsync =		devfs_fsync,
1745214921Scognet	.vop_getattr =		devfs_getattr,
1746214921Scognet	.vop_link =		VOP_PANIC,
1747214921Scognet	.vop_mkdir =		VOP_PANIC,
1748214921Scognet	.vop_mknod =		VOP_PANIC,
1749214921Scognet	.vop_open =		devfs_open,
1750214921Scognet	.vop_pathconf =		devfs_pathconf,
1751214921Scognet	.vop_poll =		dead_poll,
1752214921Scognet	.vop_print =		devfs_print,
1753214921Scognet	.vop_read =		dead_read,
1754214921Scognet	.vop_readdir =		VOP_PANIC,
1755214921Scognet	.vop_readlink =		VOP_PANIC,
1756214921Scognet	.vop_reallocblks =	VOP_PANIC,
1757214921Scognet	.vop_reclaim =		devfs_reclaim,
1758214921Scognet	.vop_remove =		devfs_remove,
1759214921Scognet	.vop_rename =		VOP_PANIC,
1760214921Scognet	.vop_revoke =		devfs_revoke,
1761214921Scognet	.vop_rmdir =		VOP_PANIC,
1762214921Scognet	.vop_setattr =		devfs_setattr,
1763214921Scognet#ifdef MAC
1764214921Scognet	.vop_setlabel =		devfs_setlabel,
1765214921Scognet#endif
1766214921Scognet	.vop_strategy =		VOP_PANIC,
1767214921Scognet	.vop_symlink =		VOP_PANIC,
1768214921Scognet	.vop_vptocnp =		devfs_vptocnp,
1769214921Scognet	.vop_write =		dead_write,
1770214921Scognet};
1771214921Scognet
1772214921Scognet/*
1773214921Scognet * Our calling convention to the device drivers used to be that we passed
1774214921Scognet * vnode.h IO_* flags to read()/write(), but we're moving to fcntl.h O_
1775214921Scognet * flags instead since that's what open(), close() and ioctl() takes and
1776214921Scognet * we don't really want vnode.h in device drivers.
1777214921Scognet * We solved the source compatibility by redefining some vnode flags to
1778214921Scognet * be the same as the fcntl ones and by sending down the bitwise OR of
1779214921Scognet * the respective fcntl/vnode flags.  These CTASSERTS make sure nobody
1780214921Scognet * pulls the rug out under this.
1781214921Scognet */
1782214921ScognetCTASSERT(O_NONBLOCK == IO_NDELAY);
1783214921ScognetCTASSERT(O_FSYNC == IO_SYNC);
1784214921Scognet