1139776Simp/*-
21541Srgrimes * Copyright (c) 1992, 1993
31541Srgrimes *	The Regents of the University of California.  All rights reserved.
41541Srgrimes *
51541Srgrimes * This code is derived from software donated to Berkeley by
61541Srgrimes * Jan-Simon Pendry.
71541Srgrimes *
81541Srgrimes * Redistribution and use in source and binary forms, with or without
91541Srgrimes * modification, are permitted provided that the following conditions
101541Srgrimes * are met:
111541Srgrimes * 1. Redistributions of source code must retain the above copyright
121541Srgrimes *    notice, this list of conditions and the following disclaimer.
131541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141541Srgrimes *    notice, this list of conditions and the following disclaimer in the
151541Srgrimes *    documentation and/or other materials provided with the distribution.
161541Srgrimes * 4. Neither the name of the University nor the names of its contributors
171541Srgrimes *    may be used to endorse or promote products derived from this software
181541Srgrimes *    without specific prior written permission.
191541Srgrimes *
201541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301541Srgrimes * SUCH DAMAGE.
311541Srgrimes *
321541Srgrimes *	@(#)fdesc_vnops.c	8.9 (Berkeley) 1/21/94
331541Srgrimes *
3450477Speter * $FreeBSD$
351541Srgrimes */
361541Srgrimes
371541Srgrimes/*
381541Srgrimes * /dev/fd Filesystem
391541Srgrimes */
401541Srgrimes
411541Srgrimes#include <sys/param.h>
421541Srgrimes#include <sys/systm.h>
43224778Srwatson#include <sys/capability.h>
4476166Smarkm#include <sys/conf.h>
4576166Smarkm#include <sys/dirent.h>
4676166Smarkm#include <sys/filedesc.h>
471541Srgrimes#include <sys/kernel.h>	/* boottime */
4876166Smarkm#include <sys/lock.h>
4989316Salfred#include <sys/mutex.h>
501541Srgrimes#include <sys/malloc.h>
5176166Smarkm#include <sys/file.h>	/* Must come after sys/malloc.h */
521541Srgrimes#include <sys/mount.h>
531541Srgrimes#include <sys/namei.h>
5476166Smarkm#include <sys/proc.h>
5576166Smarkm#include <sys/stat.h>
5676166Smarkm#include <sys/vnode.h>
5776166Smarkm
5877031Sru#include <fs/fdescfs/fdesc.h>
591541Srgrimes
602609Sdg#define	NFDCACHE 4
6122521Sdyson#define FD_NHASH(ix) \
6222521Sdyson	(&fdhashtbl[(ix) & fdhash])
6360938Sjakestatic LIST_HEAD(fdhashhead, fdescnode) *fdhashtbl;
6433181Seivindstatic u_long fdhash;
651541Srgrimes
66179288Slulfstruct mtx fdesc_hashmtx;
67179288Slulf
68138270Sphkstatic vop_getattr_t	fdesc_getattr;
69138270Sphkstatic vop_lookup_t	fdesc_lookup;
70138270Sphkstatic vop_open_t	fdesc_open;
71138270Sphkstatic vop_readdir_t	fdesc_readdir;
72138270Sphkstatic vop_reclaim_t	fdesc_reclaim;
73138270Sphkstatic vop_setattr_t	fdesc_setattr;
7412595Sbde
75148919Sobrienstatic struct vop_vector fdesc_vnodeops = {
76148919Sobrien	.vop_default =		&default_vnodeops,
77138290Sphk
78148919Sobrien	.vop_access =		VOP_NULL,
79148919Sobrien	.vop_getattr =		fdesc_getattr,
80148919Sobrien	.vop_lookup =		fdesc_lookup,
81148919Sobrien	.vop_open =		fdesc_open,
82148919Sobrien	.vop_pathconf =		vop_stdpathconf,
83148919Sobrien	.vop_readdir =		fdesc_readdir,
84148919Sobrien	.vop_reclaim =		fdesc_reclaim,
85148919Sobrien	.vop_setattr =		fdesc_setattr,
86148919Sobrien};
87148919Sobrien
88179288Slulfstatic void fdesc_insmntque_dtr(struct vnode *, void *);
89179288Slulfstatic void fdesc_remove_entry(struct fdescnode *);
90179288Slulf
911541Srgrimes/*
921541Srgrimes * Initialise cache headers
931541Srgrimes */
941549Srgrimesint
9522521Sdysonfdesc_init(vfsp)
9622521Sdyson	struct vfsconf *vfsp;
971541Srgrimes{
981541Srgrimes
99179288Slulf	mtx_init(&fdesc_hashmtx, "fdescfs_hash", NULL, MTX_DEF);
10022521Sdyson	fdhashtbl = hashinit(NFDCACHE, M_CACHE, &fdhash);
1011549Srgrimes	return (0);
1021541Srgrimes}
1031541Srgrimes
104179288Slulf/*
105179288Slulf * Uninit ready for unload.
106179288Slulf */
1071541Srgrimesint
108179288Slulffdesc_uninit(vfsp)
109179288Slulf	struct vfsconf *vfsp;
110179288Slulf{
111179288Slulf
112179288Slulf	hashdestroy(fdhashtbl, M_CACHE, fdhash);
113179288Slulf	mtx_destroy(&fdesc_hashmtx);
114179288Slulf	return (0);
115179288Slulf}
116179288Slulf
117179288Slulf/*
118179288Slulf * If allocating vnode fails, call this.
119179288Slulf */
120179288Slulfstatic void
121179288Slulffdesc_insmntque_dtr(struct vnode *vp, void *arg)
122179288Slulf{
123179288Slulf
124179288Slulf	vgone(vp);
125179288Slulf	vput(vp);
126179288Slulf}
127179288Slulf
128179288Slulf/*
129179288Slulf * Remove an entry from the hash if it exists.
130179288Slulf */
131179288Slulfstatic void
132179288Slulffdesc_remove_entry(struct fdescnode *fd)
133179288Slulf{
134179288Slulf	struct fdhashhead *fc;
135179288Slulf	struct fdescnode *fd2;
136179288Slulf
137179288Slulf	fc = FD_NHASH(fd->fd_ix);
138179288Slulf	mtx_lock(&fdesc_hashmtx);
139179288Slulf	LIST_FOREACH(fd2, fc, fd_hash) {
140179288Slulf		if (fd == fd2) {
141179288Slulf			LIST_REMOVE(fd, fd_hash);
142179288Slulf			break;
143179288Slulf		}
144179288Slulf	}
145179288Slulf	mtx_unlock(&fdesc_hashmtx);
146179288Slulf}
147179288Slulf
148179288Slulfint
149191990Sattiliofdesc_allocvp(ftype, fd_fd, ix, mp, vpp)
1501541Srgrimes	fdntype ftype;
151179288Slulf	unsigned fd_fd;
1521541Srgrimes	int ix;
1531541Srgrimes	struct mount *mp;
1541541Srgrimes	struct vnode **vpp;
1551541Srgrimes{
156179288Slulf	struct fdescmount *fmp;
15722521Sdyson	struct fdhashhead *fc;
158179288Slulf	struct fdescnode *fd, *fd2;
159179288Slulf	struct vnode *vp, *vp2;
160191990Sattilio	struct thread *td;
1611541Srgrimes	int error = 0;
1621541Srgrimes
163191990Sattilio	td = curthread;
16422521Sdyson	fc = FD_NHASH(ix);
1651541Srgrimesloop:
166179288Slulf	mtx_lock(&fdesc_hashmtx);
167179288Slulf	/*
168179288Slulf	 * If a forced unmount is progressing, we need to drop it. The flags are
169179288Slulf	 * protected by the hashmtx.
170179288Slulf	 */
171179288Slulf	fmp = (struct fdescmount *)mp->mnt_data;
172179288Slulf	if (fmp == NULL || fmp->flags & FMNT_UNMOUNTF) {
173179288Slulf		mtx_unlock(&fdesc_hashmtx);
174179288Slulf		return (-1);
175179288Slulf	}
176179288Slulf
17771999Sphk	LIST_FOREACH(fd, fc, fd_hash) {
1781541Srgrimes		if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) {
179179288Slulf			/* Get reference to vnode in case it's being free'd */
180179288Slulf			vp = fd->fd_vnode;
181179288Slulf			VI_LOCK(vp);
182179288Slulf			mtx_unlock(&fdesc_hashmtx);
183179288Slulf			if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td))
1841541Srgrimes				goto loop;
185179288Slulf			*vpp = vp;
186179288Slulf			return (0);
1871541Srgrimes		}
1881541Srgrimes	}
189179288Slulf	mtx_unlock(&fdesc_hashmtx);
1901541Srgrimes
191184205Sdes	fd = malloc(sizeof(struct fdescnode), M_TEMP, M_WAITOK);
19216312Sdg
193179288Slulf	error = getnewvnode("fdescfs", mp, &fdesc_vnodeops, &vp);
19416312Sdg	if (error) {
195184205Sdes		free(fd, M_TEMP);
196179288Slulf		return (error);
19716312Sdg	}
198179288Slulf	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
199179288Slulf	vp->v_data = fd;
200179288Slulf	fd->fd_vnode = vp;
2011541Srgrimes	fd->fd_type = ftype;
202179288Slulf	fd->fd_fd = fd_fd;
2031541Srgrimes	fd->fd_ix = ix;
204179288Slulf	error = insmntque1(vp, mp, fdesc_insmntque_dtr, NULL);
205167497Stegge	if (error != 0) {
206167497Stegge		*vpp = NULLVP;
207179288Slulf		return (error);
208167497Stegge	}
2091541Srgrimes
210179288Slulf	/* Make sure that someone didn't beat us when inserting the vnode. */
211179288Slulf	mtx_lock(&fdesc_hashmtx);
212179288Slulf	/*
213179288Slulf	 * If a forced unmount is progressing, we need to drop it. The flags are
214179288Slulf	 * protected by the hashmtx.
215179288Slulf	 */
216179288Slulf	fmp = (struct fdescmount *)mp->mnt_data;
217179288Slulf	if (fmp == NULL || fmp->flags & FMNT_UNMOUNTF) {
218179288Slulf		mtx_unlock(&fdesc_hashmtx);
219179288Slulf		vgone(vp);
220179288Slulf		vput(vp);
221179288Slulf		*vpp = NULLVP;
222179288Slulf		return (-1);
223179288Slulf	}
2241541Srgrimes
225179288Slulf	LIST_FOREACH(fd2, fc, fd_hash) {
226179288Slulf		if (fd2->fd_ix == ix && fd2->fd_vnode->v_mount == mp) {
227179288Slulf			/* Get reference to vnode in case it's being free'd */
228179288Slulf			vp2 = fd2->fd_vnode;
229179288Slulf			VI_LOCK(vp2);
230179288Slulf			mtx_unlock(&fdesc_hashmtx);
231179288Slulf			error = vget(vp2, LK_EXCLUSIVE | LK_INTERLOCK, td);
232179288Slulf			/* Someone beat us, dec use count and wait for reclaim */
233179288Slulf			vgone(vp);
234179288Slulf			vput(vp);
235179288Slulf			/* If we didn't get it, return no vnode. */
236179288Slulf			if (error)
237179288Slulf				vp2 = NULLVP;
238179288Slulf			*vpp = vp2;
239179288Slulf			return (error);
240179288Slulf		}
2411541Srgrimes	}
2421541Srgrimes
243179288Slulf	/* If we came here, we can insert it safely. */
244179288Slulf	LIST_INSERT_HEAD(fc, fd, fd_hash);
245179288Slulf	mtx_unlock(&fdesc_hashmtx);
246179288Slulf	*vpp = vp;
247179288Slulf	return (0);
2481541Srgrimes}
2491541Srgrimes
2501541Srgrimes/*
2511541Srgrimes * vp is the current namei directory
2521541Srgrimes * ndp is the name to locate in that directory...
2531541Srgrimes */
25412143Sphkstatic int
2551541Srgrimesfdesc_lookup(ap)
2561541Srgrimes	struct vop_lookup_args /* {
2571541Srgrimes		struct vnode * a_dvp;
2581541Srgrimes		struct vnode ** a_vpp;
2591541Srgrimes		struct componentname * a_cnp;
2601541Srgrimes	} */ *ap;
2611541Srgrimes{
2621541Srgrimes	struct vnode **vpp = ap->a_vpp;
2631541Srgrimes	struct vnode *dvp = ap->a_dvp;
26422521Sdyson	struct componentname *cnp = ap->a_cnp;
26522521Sdyson	char *pname = cnp->cn_nameptr;
26683366Sjulian	struct thread *td = cnp->cn_thread;
26789306Salfred	struct file *fp;
26860406Schris	int nlen = cnp->cn_namelen;
269192012Skib	u_int fd, fd1;
2701541Srgrimes	int error;
2711541Srgrimes	struct vnode *fvp;
2721541Srgrimes
273105998Smux	if ((cnp->cn_flags & ISLASTCN) &&
274105998Smux	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
27510534Smpp		error = EROFS;
27610534Smpp		goto bad;
27710534Smpp	}
27810534Smpp
27922521Sdyson	if (cnp->cn_namelen == 1 && *pname == '.') {
2801541Srgrimes		*vpp = dvp;
281111742Sdes		VREF(dvp);
2821541Srgrimes		return (0);
2831541Srgrimes	}
2841541Srgrimes
28560406Schris	if (VTOFDESC(dvp)->fd_type != Froot) {
2861541Srgrimes		error = ENOTDIR;
2871541Srgrimes		goto bad;
28860406Schris	}
2891541Srgrimes
29060406Schris	fd = 0;
29162182Salfred	/* the only time a leading 0 is acceptable is if it's "0" */
29262182Salfred	if (*pname == '0' && nlen != 1) {
29362182Salfred		error = ENOENT;
29462182Salfred		goto bad;
29562182Salfred	}
29660406Schris	while (nlen--) {
29760406Schris		if (*pname < '0' || *pname > '9') {
2981541Srgrimes			error = ENOENT;
2991541Srgrimes			goto bad;
3001541Srgrimes		}
301192012Skib		fd1 = 10 * fd + *pname++ - '0';
302192012Skib		if (fd1 < fd) {
303192012Skib			error = ENOENT;
304192012Skib			goto bad;
305192012Skib		}
306192012Skib		fd = fd1;
30760406Schris	}
3081541Srgrimes
309224778Srwatson	/*
310224778Srwatson	 * No rights to check since 'fp' isn't actually used.
311224778Srwatson	 */
312224778Srwatson	if ((error = fget(td, fd, 0, &fp)) != 0)
31360406Schris		goto bad;
3141541Srgrimes
315179288Slulf	/* Check if we're looking up ourselves. */
316179288Slulf	if (VTOFDESC(dvp)->fd_ix == FD_DESC + fd) {
317179288Slulf		/*
318179288Slulf		 * In case we're holding the last reference to the file, the dvp
319179288Slulf		 * will be re-acquired.
320179288Slulf		 */
321179288Slulf		vhold(dvp);
322179288Slulf		VOP_UNLOCK(dvp, 0);
323179288Slulf		fdrop(fp, td);
324179288Slulf
325179288Slulf		/* Re-aquire the lock afterwards. */
326179288Slulf		vn_lock(dvp, LK_RETRY | LK_EXCLUSIVE);
327179288Slulf		vdrop(dvp);
328179288Slulf		fvp = dvp;
329179288Slulf	} else {
330179288Slulf		/*
331179288Slulf		 * Unlock our root node (dvp) when doing this, since we might
332179288Slulf		 * deadlock since the vnode might be locked by another thread
333179288Slulf		 * and the root vnode lock will be obtained afterwards (in case
334179288Slulf		 * we're looking up the fd of the root vnode), which will be the
335179288Slulf		 * opposite lock order. Vhold the root vnode first so we don't
336258218Smav		 * lose it.
337179288Slulf		 */
338179288Slulf		vhold(dvp);
339179288Slulf		VOP_UNLOCK(dvp, 0);
340179288Slulf		error = fdesc_allocvp(Fdesc, fd, FD_DESC + fd, dvp->v_mount,
341191990Sattilio		    &fvp);
342179288Slulf		fdrop(fp, td);
343179288Slulf		/*
344179288Slulf		 * The root vnode must be locked last to prevent deadlock condition.
345179288Slulf		 */
346179288Slulf		vn_lock(dvp, LK_RETRY | LK_EXCLUSIVE);
347179288Slulf		vdrop(dvp);
348179288Slulf	}
349179288Slulf
35060406Schris	if (error)
35160406Schris		goto bad;
35260406Schris	*vpp = fvp;
35360406Schris	return (0);
3541541Srgrimes
35560406Schrisbad:
3561541Srgrimes	*vpp = NULL;
3571541Srgrimes	return (error);
3581541Srgrimes}
3591541Srgrimes
36012143Sphkstatic int
3611541Srgrimesfdesc_open(ap)
3621541Srgrimes	struct vop_open_args /* {
3631541Srgrimes		struct vnode *a_vp;
3641541Srgrimes		int  a_mode;
3651541Srgrimes		struct ucred *a_cred;
36683366Sjulian		struct thread *a_td;
3671541Srgrimes	} */ *ap;
3681541Srgrimes{
3691541Srgrimes	struct vnode *vp = ap->a_vp;
3701541Srgrimes
37160406Schris	if (VTOFDESC(vp)->fd_type == Froot)
37260406Schris		return (0);
3731541Srgrimes
37460406Schris	/*
375218909Sbrucec	 * XXX Kludge: set td->td_proc->p_dupfd to contain the value of the file
37660406Schris	 * descriptor being sought for duplication. The error return ensures
37760406Schris	 * that the vnode for this device will be released by vn_open. Open
37860406Schris	 * will detect this special error and take the actions in dupfdopen.
37960406Schris	 * Other callers of vn_open or VOP_OPEN will simply report the
38060406Schris	 * error.
38160406Schris	 */
38283366Sjulian	ap->a_td->td_dupfd = VTOFDESC(vp)->fd_fd;	/* XXX */
38360406Schris	return (ENODEV);
3841541Srgrimes}
3851541Srgrimes
3861541Srgrimesstatic int
3871541Srgrimesfdesc_getattr(ap)
3881541Srgrimes	struct vop_getattr_args /* {
3891541Srgrimes		struct vnode *a_vp;
3901541Srgrimes		struct vattr *a_vap;
3911541Srgrimes		struct ucred *a_cred;
3921541Srgrimes	} */ *ap;
3931541Srgrimes{
3941541Srgrimes	struct vnode *vp = ap->a_vp;
3951541Srgrimes	struct vattr *vap = ap->a_vap;
3961541Srgrimes
397192013Skib	vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
398192013Skib	vap->va_fileid = VTOFDESC(vp)->fd_ix;
399192013Skib	vap->va_uid = 0;
400192013Skib	vap->va_gid = 0;
401192013Skib	vap->va_blocksize = DEV_BSIZE;
402192013Skib	vap->va_atime.tv_sec = boottime.tv_sec;
403192013Skib	vap->va_atime.tv_nsec = 0;
404192013Skib	vap->va_mtime = vap->va_atime;
405192013Skib	vap->va_ctime = vap->va_mtime;
406192013Skib	vap->va_gen = 0;
407192013Skib	vap->va_flags = 0;
408192013Skib	vap->va_bytes = 0;
409192013Skib	vap->va_filerev = 0;
410192013Skib
4111541Srgrimes	switch (VTOFDESC(vp)->fd_type) {
4121541Srgrimes	case Froot:
41360406Schris		vap->va_type = VDIR;
41460406Schris		vap->va_nlink = 2;
41560406Schris		vap->va_size = DEV_BSIZE;
416183214Skib		vap->va_rdev = NODEV;
4171541Srgrimes		break;
4181541Srgrimes
4191541Srgrimes	case Fdesc:
420192013Skib		vap->va_type = VCHR;
421192013Skib		vap->va_nlink = 1;
422192013Skib		vap->va_size = 0;
423192013Skib		vap->va_rdev = makedev(0, vap->va_fileid);
4241541Srgrimes		break;
4251541Srgrimes
4261541Srgrimes	default:
4271541Srgrimes		panic("fdesc_getattr");
4288876Srgrimes		break;
4291541Srgrimes	}
4301541Srgrimes
431192013Skib	vp->v_type = vap->va_type;
432192013Skib	return (0);
4331541Srgrimes}
4341541Srgrimes
43512143Sphkstatic int
4361541Srgrimesfdesc_setattr(ap)
4371541Srgrimes	struct vop_setattr_args /* {
4381541Srgrimes		struct vnode *a_vp;
4391541Srgrimes		struct vattr *a_vap;
4401541Srgrimes		struct ucred *a_cred;
4411541Srgrimes	} */ *ap;
4421541Srgrimes{
44336873Sdfr	struct vattr *vap = ap->a_vap;
44462976Smckusick	struct vnode *vp;
44562976Smckusick	struct mount *mp;
4461541Srgrimes	struct file *fp;
447182371Sattilio	struct thread *td = curthread;
4481541Srgrimes	unsigned fd;
4491541Srgrimes	int error;
4501541Srgrimes
4511541Srgrimes	/*
4521541Srgrimes	 * Can't mess with the root vnode
4531541Srgrimes	 */
45460406Schris	if (VTOFDESC(ap->a_vp)->fd_type == Froot)
4551541Srgrimes		return (EACCES);
4561541Srgrimes
4571541Srgrimes	fd = VTOFDESC(ap->a_vp)->fd_fd;
4581541Srgrimes
4591541Srgrimes	/*
46061315Schris	 * Allow setattr where there is an underlying vnode.
4611541Srgrimes	 */
462224778Srwatson	error = getvnode(td->td_proc->p_fd, fd, CAP_EXTATTR_SET, &fp);
46366894Schris	if (error) {
46466894Schris		/*
46566894Schris		 * getvnode() returns EINVAL if the file descriptor is not
46666894Schris		 * backed by a vnode.  Silently drop all changes except
46766894Schris		 * chflags(2) in this case.
46866894Schris		 */
46966894Schris		if (error == EINVAL) {
47066894Schris			if (vap->va_flags != VNOVAL)
47166894Schris				error = EOPNOTSUPP;
47266894Schris			else
47366894Schris				error = 0;
47466894Schris		}
47566894Schris		return (error);
4761541Srgrimes	}
477116678Sphk	vp = fp->f_vnode;
478122893Skan	if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) == 0) {
479175202Sattilio		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
480182371Sattilio		error = VOP_SETATTR(vp, ap->a_vap, ap->a_cred);
481175294Sattilio		VOP_UNLOCK(vp, 0);
482122893Skan		vn_finished_write(mp);
48389306Salfred	}
484182371Sattilio	fdrop(fp, td);
4851541Srgrimes	return (error);
4861541Srgrimes}
4871541Srgrimes
4881541Srgrimes#define UIO_MX 16
4891541Srgrimes
49012143Sphkstatic int
4911541Srgrimesfdesc_readdir(ap)
4921541Srgrimes	struct vop_readdir_args /* {
4931541Srgrimes		struct vnode *a_vp;
4941541Srgrimes		struct uio *a_uio;
4951541Srgrimes		struct ucred *a_cred;
49622521Sdyson		int *a_eofflag;
49722521Sdyson		u_long *a_cookies;
49822521Sdyson		int a_ncookies;
4991541Srgrimes	} */ *ap;
5001541Srgrimes{
5011541Srgrimes	struct uio *uio = ap->a_uio;
5021541Srgrimes	struct filedesc *fdp;
50360406Schris	struct dirent d;
50460406Schris	struct dirent *dp = &d;
50560406Schris	int error, i, off, fcnt;
5061541Srgrimes
50760406Schris	if (VTOFDESC(ap->a_vp)->fd_type != Froot)
50836963Sbde		panic("fdesc_readdir: not dir");
5091541Srgrimes
510220506Skib	if (ap->a_ncookies != NULL)
511220506Skib		*ap->a_ncookies = 0;
512220506Skib
51336963Sbde	off = (int)uio->uio_offset;
51436963Sbde	if (off != uio->uio_offset || off < 0 || (u_int)off % UIO_MX != 0 ||
51536963Sbde	    uio->uio_resid < UIO_MX)
51636963Sbde		return (EINVAL);
51736963Sbde	i = (u_int)off / UIO_MX;
51883366Sjulian	fdp = uio->uio_td->td_proc->p_fd;
51960406Schris	error = 0;
5201541Srgrimes
52160406Schris	fcnt = i - 2;		/* The first two nodes are `.' and `..' */
5221541Srgrimes
523168355Srwatson	FILEDESC_SLOCK(fdp);
52460406Schris	while (i < fdp->fd_nfiles + 2 && uio->uio_resid >= UIO_MX) {
525205223Sjkim		bzero((caddr_t)dp, UIO_MX);
52660406Schris		switch (i) {
52760406Schris		case 0:	/* `.' */
52860406Schris		case 1: /* `..' */
52960406Schris			dp->d_fileno = i + FD_ROOT;
53060406Schris			dp->d_namlen = i + 1;
53160406Schris			dp->d_reclen = UIO_MX;
53260406Schris			bcopy("..", dp->d_name, dp->d_namlen);
53360406Schris			dp->d_name[i + 1] = '\0';
53460406Schris			dp->d_type = DT_DIR;
53560406Schris			break;
53660406Schris		default:
537205223Sjkim			if (fdp->fd_ofiles[fcnt] == NULL)
538205223Sjkim				break;
53960406Schris			dp->d_namlen = sprintf(dp->d_name, "%d", fcnt);
5401541Srgrimes			dp->d_reclen = UIO_MX;
541252885Sjilles			dp->d_type = DT_CHR;
54260406Schris			dp->d_fileno = i + FD_DESC;
54360406Schris			break;
5441541Srgrimes		}
545205223Sjkim		if (dp->d_namlen != 0) {
546205223Sjkim			/*
547205223Sjkim			 * And ship to userland
548205223Sjkim			 */
549205223Sjkim			FILEDESC_SUNLOCK(fdp);
550205223Sjkim			error = uiomove(dp, UIO_MX, uio);
551205223Sjkim			if (error)
552205223Sjkim				goto done;
553205223Sjkim			FILEDESC_SLOCK(fdp);
554205223Sjkim		}
5551541Srgrimes		i++;
55660406Schris		fcnt++;
5571541Srgrimes	}
558168355Srwatson	FILEDESC_SUNLOCK(fdp);
5591541Srgrimes
56060406Schrisdone:
5611541Srgrimes	uio->uio_offset = i * UIO_MX;
5621541Srgrimes	return (error);
5631541Srgrimes}
5641541Srgrimes
56512143Sphkstatic int
5661541Srgrimesfdesc_reclaim(ap)
5671541Srgrimes	struct vop_reclaim_args /* {
5681541Srgrimes		struct vnode *a_vp;
5691541Srgrimes	} */ *ap;
5701541Srgrimes{
571179288Slulf	struct vnode *vp;
572179288Slulf	struct fdescnode *fd;
5731541Srgrimes
574179288Slulf 	vp = ap->a_vp;
575179288Slulf 	fd = VTOFDESC(vp);
576179288Slulf	fdesc_remove_entry(fd);
577184205Sdes	free(vp->v_data, M_TEMP);
578179288Slulf	vp->v_data = NULL;
5791541Srgrimes	return (0);
5801541Srgrimes}
581