Home | History | Annotate | Line # | Download | only in fdesc
fdesc_vnops.c revision 1.31
      1 /*	$NetBSD: fdesc_vnops.c,v 1.31 1996/02/13 13:12:52 mycroft Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1992, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * This code is derived from software donated to Berkeley by
      8  * Jan-Simon Pendry.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *	This product includes software developed by the University of
     21  *	California, Berkeley and its contributors.
     22  * 4. Neither the name of the University nor the names of its contributors
     23  *    may be used to endorse or promote products derived from this software
     24  *    without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     36  * SUCH DAMAGE.
     37  *
     38  *	@(#)fdesc_vnops.c	8.12 (Berkeley) 8/20/94
     39  *
     40  * #Id: fdesc_vnops.c,v 1.12 1993/04/06 16:17:17 jsp Exp #
     41  */
     42 
     43 /*
     44  * /dev/fd Filesystem
     45  */
     46 
     47 #include <sys/param.h>
     48 #include <sys/systm.h>
     49 #include <sys/types.h>
     50 #include <sys/time.h>
     51 #include <sys/proc.h>
     52 #include <sys/kernel.h>	/* boottime */
     53 #include <sys/resourcevar.h>
     54 #include <sys/socketvar.h>
     55 #include <sys/filedesc.h>
     56 #include <sys/vnode.h>
     57 #include <sys/malloc.h>
     58 #include <sys/file.h>
     59 #include <sys/stat.h>
     60 #include <sys/mount.h>
     61 #include <sys/namei.h>
     62 #include <sys/buf.h>
     63 #include <sys/dirent.h>
     64 #include <sys/tty.h>
     65 #include <miscfs/fdesc/fdesc.h>
     66 
     67 #define cttyvp(p) ((p)->p_flag & P_CONTROLT ? (p)->p_session->s_ttyvp : NULL)
     68 
     69 #define FDL_WANT	0x01
     70 #define FDL_LOCKED	0x02
     71 static int fdcache_lock;
     72 
     73 dev_t devctty;
     74 
     75 #if (FD_STDIN != FD_STDOUT-1) || (FD_STDOUT != FD_STDERR-1)
     76 FD_STDIN, FD_STDOUT, FD_STDERR must be a sequence n, n+1, n+2
     77 #endif
     78 
     79 #define	NFDCACHE 4
     80 
     81 #define FD_NHASH(ix) \
     82 	(&fdhashtbl[(ix) & fdhash])
     83 LIST_HEAD(fdhashhead, fdescnode) *fdhashtbl;
     84 u_long fdhash;
     85 
     86 int	fdesc_badop	__P((void *));
     87 int	fdesc_enotsupp	__P((void *));
     88 
     89 int	fdesc_lookup	__P((void *));
     90 #define	fdesc_create	fdesc_enotsupp
     91 #define	fdesc_mknod	fdesc_enotsupp
     92 int	fdesc_open	__P((void *));
     93 #define	fdesc_close	nullop
     94 #define	fdesc_access	nullop
     95 int	fdesc_getattr	__P((void *));
     96 int	fdesc_setattr	__P((void *));
     97 int	fdesc_read	__P((void *));
     98 int	fdesc_write	__P((void *));
     99 int	fdesc_ioctl	__P((void *));
    100 int	fdesc_select	__P((void *));
    101 #define	fdesc_mmap	fdesc_enotsupp
    102 #define	fdesc_fsync	nullop
    103 #define	fdesc_seek	nullop
    104 #define	fdesc_remove	fdesc_enotsupp
    105 int	fdesc_link	__P((void *));
    106 #define	fdesc_rename	fdesc_enotsupp
    107 #define	fdesc_mkdir	fdesc_enotsupp
    108 #define	fdesc_rmdir	fdesc_enotsupp
    109 int	fdesc_symlink	__P((void *));
    110 int	fdesc_readdir	__P((void *));
    111 int	fdesc_readlink	__P((void *));
    112 int	fdesc_abortop	__P((void *));
    113 int	fdesc_inactive	__P((void *));
    114 int	fdesc_reclaim	__P((void *));
    115 #define	fdesc_lock	nullop
    116 #define	fdesc_unlock	nullop
    117 #define	fdesc_bmap	fdesc_badop
    118 #define	fdesc_strategy	fdesc_badop
    119 int	fdesc_print	__P((void *));
    120 int	fdesc_pathconf	__P((void *));
    121 #define	fdesc_islocked	nullop
    122 #define	fdesc_advlock	fdesc_enotsupp
    123 #define	fdesc_blkatoff	fdesc_enotsupp
    124 #define	fdesc_valloc	fdesc_enotsupp
    125 int	fdesc_vfree	__P((void *));
    126 #define	fdesc_truncate	fdesc_enotsupp
    127 #define	fdesc_update	fdesc_enotsupp
    128 #define	fdesc_bwrite	fdesc_enotsupp
    129 
    130 static int fdesc_attr __P((int, struct vattr *, struct ucred *, struct proc *));
    131 
    132 int (**fdesc_vnodeop_p) __P((void *));
    133 struct vnodeopv_entry_desc fdesc_vnodeop_entries[] = {
    134 	{ &vop_default_desc, vn_default_error },
    135 	{ &vop_lookup_desc, fdesc_lookup },	/* lookup */
    136 	{ &vop_create_desc, fdesc_create },	/* create */
    137 	{ &vop_mknod_desc, fdesc_mknod },	/* mknod */
    138 	{ &vop_open_desc, fdesc_open },		/* open */
    139 	{ &vop_close_desc, fdesc_close },	/* close */
    140 	{ &vop_access_desc, fdesc_access },	/* access */
    141 	{ &vop_getattr_desc, fdesc_getattr },	/* getattr */
    142 	{ &vop_setattr_desc, fdesc_setattr },	/* setattr */
    143 	{ &vop_read_desc, fdesc_read },		/* read */
    144 	{ &vop_write_desc, fdesc_write },	/* write */
    145 	{ &vop_ioctl_desc, fdesc_ioctl },	/* ioctl */
    146 	{ &vop_select_desc, fdesc_select },	/* select */
    147 	{ &vop_mmap_desc, fdesc_mmap },		/* mmap */
    148 	{ &vop_fsync_desc, fdesc_fsync },	/* fsync */
    149 	{ &vop_seek_desc, fdesc_seek },		/* seek */
    150 	{ &vop_remove_desc, fdesc_remove },	/* remove */
    151 	{ &vop_link_desc, fdesc_link },		/* link */
    152 	{ &vop_rename_desc, fdesc_rename },	/* rename */
    153 	{ &vop_mkdir_desc, fdesc_mkdir },	/* mkdir */
    154 	{ &vop_rmdir_desc, fdesc_rmdir },	/* rmdir */
    155 	{ &vop_symlink_desc, fdesc_symlink },	/* symlink */
    156 	{ &vop_readdir_desc, fdesc_readdir },	/* readdir */
    157 	{ &vop_readlink_desc, fdesc_readlink },	/* readlink */
    158 	{ &vop_abortop_desc, fdesc_abortop },	/* abortop */
    159 	{ &vop_inactive_desc, fdesc_inactive },	/* inactive */
    160 	{ &vop_reclaim_desc, fdesc_reclaim },	/* reclaim */
    161 	{ &vop_lock_desc, fdesc_lock },		/* lock */
    162 	{ &vop_unlock_desc, fdesc_unlock },	/* unlock */
    163 	{ &vop_bmap_desc, fdesc_bmap },		/* bmap */
    164 	{ &vop_strategy_desc, fdesc_strategy },	/* strategy */
    165 	{ &vop_print_desc, fdesc_print },	/* print */
    166 	{ &vop_islocked_desc, fdesc_islocked },	/* islocked */
    167 	{ &vop_pathconf_desc, fdesc_pathconf },	/* pathconf */
    168 	{ &vop_advlock_desc, fdesc_advlock },	/* advlock */
    169 	{ &vop_blkatoff_desc, fdesc_blkatoff },	/* blkatoff */
    170 	{ &vop_valloc_desc, fdesc_valloc },	/* valloc */
    171 	{ &vop_vfree_desc, fdesc_vfree },	/* vfree */
    172 	{ &vop_truncate_desc, fdesc_truncate },	/* truncate */
    173 	{ &vop_update_desc, fdesc_update },	/* update */
    174 	{ &vop_bwrite_desc, fdesc_bwrite },	/* bwrite */
    175 	{ (struct vnodeop_desc*)NULL, (int(*) __P((void *)))NULL }
    176 };
    177 
    178 struct vnodeopv_desc fdesc_vnodeop_opv_desc =
    179 	{ &fdesc_vnodeop_p, fdesc_vnodeop_entries };
    180 
    181 /*
    182  * Initialise cache headers
    183  */
    184 void
    185 fdesc_init()
    186 {
    187 
    188 	devctty = makedev(nchrdev, 0);
    189 	fdhashtbl = hashinit(NFDCACHE, M_CACHE, &fdhash);
    190 }
    191 
    192 int
    193 fdesc_allocvp(ftype, ix, mp, vpp)
    194 	fdntype ftype;
    195 	int ix;
    196 	struct mount *mp;
    197 	struct vnode **vpp;
    198 {
    199 	struct fdhashhead *fc;
    200 	struct fdescnode *fd;
    201 	int error = 0;
    202 
    203 	fc = FD_NHASH(ix);
    204 loop:
    205 	for (fd = fc->lh_first; fd != 0; fd = fd->fd_hash.le_next) {
    206 		if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) {
    207 			if (vget(fd->fd_vnode, 0))
    208 				goto loop;
    209 			*vpp = fd->fd_vnode;
    210 			return (error);
    211 		}
    212 	}
    213 
    214 	/*
    215 	 * otherwise lock the array while we call getnewvnode
    216 	 * since that can block.
    217 	 */
    218 	if (fdcache_lock & FDL_LOCKED) {
    219 		fdcache_lock |= FDL_WANT;
    220 		sleep((caddr_t) &fdcache_lock, PINOD);
    221 		goto loop;
    222 	}
    223 	fdcache_lock |= FDL_LOCKED;
    224 
    225 	error = getnewvnode(VT_FDESC, mp, fdesc_vnodeop_p, vpp);
    226 	if (error)
    227 		goto out;
    228 	MALLOC(fd, void *, sizeof(struct fdescnode), M_TEMP, M_WAITOK);
    229 	(*vpp)->v_data = fd;
    230 	fd->fd_vnode = *vpp;
    231 	fd->fd_type = ftype;
    232 	fd->fd_fd = -1;
    233 	fd->fd_link = 0;
    234 	fd->fd_ix = ix;
    235 	LIST_INSERT_HEAD(fc, fd, fd_hash);
    236 
    237 out:;
    238 	fdcache_lock &= ~FDL_LOCKED;
    239 
    240 	if (fdcache_lock & FDL_WANT) {
    241 		fdcache_lock &= ~FDL_WANT;
    242 		wakeup((caddr_t) &fdcache_lock);
    243 	}
    244 
    245 	return (error);
    246 }
    247 
    248 /*
    249  * vp is the current namei directory
    250  * ndp is the name to locate in that directory...
    251  */
    252 int
    253 fdesc_lookup(v)
    254 	void *v;
    255 {
    256 	struct vop_lookup_args /* {
    257 		struct vnode * a_dvp;
    258 		struct vnode ** a_vpp;
    259 		struct componentname * a_cnp;
    260 	} */ *ap = v;
    261 	struct vnode **vpp = ap->a_vpp;
    262 	struct vnode *dvp = ap->a_dvp;
    263 	char *pname;
    264 	struct proc *p;
    265 	int nfiles;
    266 	unsigned fd = 0;
    267 	int error;
    268 	struct vnode *fvp;
    269 	char *ln;
    270 
    271 	pname = ap->a_cnp->cn_nameptr;
    272 	if (ap->a_cnp->cn_namelen == 1 && *pname == '.') {
    273 		*vpp = dvp;
    274 		VREF(dvp);
    275 		VOP_LOCK(dvp);
    276 		return (0);
    277 	}
    278 
    279 	p = ap->a_cnp->cn_proc;
    280 	nfiles = p->p_fd->fd_nfiles;
    281 
    282 	switch (VTOFDESC(dvp)->fd_type) {
    283 	default:
    284 	case Flink:
    285 	case Fdesc:
    286 	case Fctty:
    287 		error = ENOTDIR;
    288 		goto bad;
    289 
    290 	case Froot:
    291 		if (ap->a_cnp->cn_namelen == 2 && bcmp(pname, "fd", 2) == 0) {
    292 			error = fdesc_allocvp(Fdevfd, FD_DEVFD, dvp->v_mount, &fvp);
    293 			if (error)
    294 				goto bad;
    295 			*vpp = fvp;
    296 			fvp->v_type = VDIR;
    297 			VOP_LOCK(fvp);
    298 			return (0);
    299 		}
    300 
    301 		if (ap->a_cnp->cn_namelen == 3 && bcmp(pname, "tty", 3) == 0) {
    302 			struct vnode *ttyvp = cttyvp(p);
    303 			if (ttyvp == NULL) {
    304 				error = ENXIO;
    305 				goto bad;
    306 			}
    307 			error = fdesc_allocvp(Fctty, FD_CTTY, dvp->v_mount, &fvp);
    308 			if (error)
    309 				goto bad;
    310 			*vpp = fvp;
    311 			fvp->v_type = VFIFO;
    312 			VOP_LOCK(fvp);
    313 			return (0);
    314 		}
    315 
    316 		ln = 0;
    317 		switch (ap->a_cnp->cn_namelen) {
    318 		case 5:
    319 			if (bcmp(pname, "stdin", 5) == 0) {
    320 				ln = "fd/0";
    321 				fd = FD_STDIN;
    322 			}
    323 			break;
    324 		case 6:
    325 			if (bcmp(pname, "stdout", 6) == 0) {
    326 				ln = "fd/1";
    327 				fd = FD_STDOUT;
    328 			} else
    329 			if (bcmp(pname, "stderr", 6) == 0) {
    330 				ln = "fd/2";
    331 				fd = FD_STDERR;
    332 			}
    333 			break;
    334 		}
    335 
    336 		if (ln) {
    337 			error = fdesc_allocvp(Flink, fd, dvp->v_mount, &fvp);
    338 			if (error)
    339 				goto bad;
    340 			VTOFDESC(fvp)->fd_link = ln;
    341 			*vpp = fvp;
    342 			fvp->v_type = VLNK;
    343 			VOP_LOCK(fvp);
    344 			return (0);
    345 		} else {
    346 			error = ENOENT;
    347 			goto bad;
    348 		}
    349 
    350 		/* FALL THROUGH */
    351 
    352 	case Fdevfd:
    353 		if (ap->a_cnp->cn_namelen == 2 && bcmp(pname, "..", 2) == 0) {
    354 			error = fdesc_root(dvp->v_mount, vpp);
    355 			return (error);
    356 		}
    357 
    358 		fd = 0;
    359 		while (*pname >= '0' && *pname <= '9') {
    360 			fd = 10 * fd + *pname++ - '0';
    361 			if (fd >= nfiles)
    362 				break;
    363 		}
    364 
    365 		if (*pname != '\0') {
    366 			error = ENOENT;
    367 			goto bad;
    368 		}
    369 
    370 		if (fd >= nfiles || p->p_fd->fd_ofiles[fd] == NULL) {
    371 			error = EBADF;
    372 			goto bad;
    373 		}
    374 
    375 		error = fdesc_allocvp(Fdesc, FD_DESC+fd, dvp->v_mount, &fvp);
    376 		if (error)
    377 			goto bad;
    378 		VTOFDESC(fvp)->fd_fd = fd;
    379 		*vpp = fvp;
    380 		return (0);
    381 	}
    382 
    383 bad:;
    384 	*vpp = NULL;
    385 	return (error);
    386 }
    387 
    388 int
    389 fdesc_open(v)
    390 	void *v;
    391 {
    392 	struct vop_open_args /* {
    393 		struct vnode *a_vp;
    394 		int  a_mode;
    395 		struct ucred *a_cred;
    396 		struct proc *a_p;
    397 	} */ *ap = v;
    398 	struct vnode *vp = ap->a_vp;
    399 
    400 	switch (VTOFDESC(vp)->fd_type) {
    401 	case Fdesc:
    402 		/*
    403 		 * XXX Kludge: set p->p_dupfd to contain the value of the
    404 		 * the file descriptor being sought for duplication. The error
    405 		 * return ensures that the vnode for this device will be
    406 		 * released by vn_open. Open will detect this special error and
    407 		 * take the actions in dupfdopen.  Other callers of vn_open or
    408 		 * VOP_OPEN will simply report the error.
    409 		 */
    410 		ap->a_p->p_dupfd = VTOFDESC(vp)->fd_fd;	/* XXX */
    411 		return (ENODEV);
    412 
    413 	case Fctty:
    414 		return (cttyopen(devctty, ap->a_mode, 0, ap->a_p));
    415 	case Froot:
    416 	case Fdevfd:
    417 	case Flink:
    418 		break;
    419 	}
    420 
    421 	return (0);
    422 }
    423 
    424 static int
    425 fdesc_attr(fd, vap, cred, p)
    426 	int fd;
    427 	struct vattr *vap;
    428 	struct ucred *cred;
    429 	struct proc *p;
    430 {
    431 	struct filedesc *fdp = p->p_fd;
    432 	struct file *fp;
    433 	struct stat stb;
    434 	int error;
    435 
    436 	if (fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL)
    437 		return (EBADF);
    438 
    439 	switch (fp->f_type) {
    440 	case DTYPE_VNODE:
    441 		error = VOP_GETATTR((struct vnode *) fp->f_data, vap, cred, p);
    442 		if (error == 0 && vap->va_type == VDIR) {
    443 			/*
    444 			 * directories can cause loops in the namespace,
    445 			 * so turn off the 'x' bits to avoid trouble.
    446 			 */
    447 			vap->va_mode &= ~((VEXEC)|(VEXEC>>3)|(VEXEC>>6));
    448 		}
    449 		break;
    450 
    451 	case DTYPE_SOCKET:
    452 		error = soo_stat((struct socket *)fp->f_data, &stb);
    453 		if (error == 0) {
    454 			vattr_null(vap);
    455 			vap->va_type = VSOCK;
    456 			vap->va_mode = stb.st_mode;
    457 			vap->va_nlink = stb.st_nlink;
    458 			vap->va_uid = stb.st_uid;
    459 			vap->va_gid = stb.st_gid;
    460 			vap->va_fsid = stb.st_dev;
    461 			vap->va_fileid = stb.st_ino;
    462 			vap->va_size = stb.st_size;
    463 			vap->va_blocksize = stb.st_blksize;
    464 			vap->va_atime = stb.st_atimespec;
    465 			vap->va_mtime = stb.st_mtimespec;
    466 			vap->va_ctime = stb.st_ctimespec;
    467 			vap->va_gen = stb.st_gen;
    468 			vap->va_flags = stb.st_flags;
    469 			vap->va_rdev = stb.st_rdev;
    470 			vap->va_bytes = stb.st_blocks * stb.st_blksize;
    471 		}
    472 		break;
    473 
    474 	default:
    475 		panic("fdesc attr");
    476 		break;
    477 	}
    478 
    479 	return (error);
    480 }
    481 
    482 int
    483 fdesc_getattr(v)
    484 	void *v;
    485 {
    486 	struct vop_getattr_args /* {
    487 		struct vnode *a_vp;
    488 		struct vattr *a_vap;
    489 		struct ucred *a_cred;
    490 		struct proc *a_p;
    491 	} */ *ap = v;
    492 	struct vnode *vp = ap->a_vp;
    493 	struct vattr *vap = ap->a_vap;
    494 	unsigned fd;
    495 	int error = 0;
    496 
    497 	switch (VTOFDESC(vp)->fd_type) {
    498 	case Froot:
    499 	case Fdevfd:
    500 	case Flink:
    501 	case Fctty:
    502 		bzero((caddr_t) vap, sizeof(*vap));
    503 		vattr_null(vap);
    504 		vap->va_fileid = VTOFDESC(vp)->fd_ix;
    505 
    506 #define R_ALL (S_IRUSR|S_IRGRP|S_IROTH)
    507 #define W_ALL (S_IWUSR|S_IWGRP|S_IWOTH)
    508 #define X_ALL (S_IXUSR|S_IXGRP|S_IXOTH)
    509 
    510 		switch (VTOFDESC(vp)->fd_type) {
    511 		case Flink:
    512 			vap->va_mode = R_ALL|X_ALL;
    513 			vap->va_type = VLNK;
    514 			vap->va_nlink = 1;
    515 			vap->va_size = strlen(VTOFDESC(vp)->fd_link);
    516 			break;
    517 
    518 		case Fctty:
    519 			vap->va_mode = R_ALL|W_ALL;
    520 			vap->va_type = VFIFO;
    521 			vap->va_nlink = 1;
    522 			vap->va_size = 0;
    523 			break;
    524 
    525 		default:
    526 			vap->va_mode = R_ALL|X_ALL;
    527 			vap->va_type = VDIR;
    528 			vap->va_nlink = 2;
    529 			vap->va_size = DEV_BSIZE;
    530 			break;
    531 		}
    532 		vap->va_uid = 0;
    533 		vap->va_gid = 0;
    534 		vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
    535 		vap->va_blocksize = DEV_BSIZE;
    536 		vap->va_atime.tv_sec = boottime.tv_sec;
    537 		vap->va_atime.tv_nsec = 0;
    538 		vap->va_mtime = vap->va_atime;
    539 		vap->va_ctime = vap->va_mtime;
    540 		vap->va_gen = 0;
    541 		vap->va_flags = 0;
    542 		vap->va_rdev = 0;
    543 		vap->va_bytes = 0;
    544 		break;
    545 
    546 	case Fdesc:
    547 		fd = VTOFDESC(vp)->fd_fd;
    548 		error = fdesc_attr(fd, vap, ap->a_cred, ap->a_p);
    549 		break;
    550 
    551 	default:
    552 		panic("fdesc_getattr");
    553 		break;
    554 	}
    555 
    556 	if (error == 0)
    557 		vp->v_type = vap->va_type;
    558 
    559 	return (error);
    560 }
    561 
    562 int
    563 fdesc_setattr(v)
    564 	void *v;
    565 {
    566 	struct vop_setattr_args /* {
    567 		struct vnode *a_vp;
    568 		struct vattr *a_vap;
    569 		struct ucred *a_cred;
    570 		struct proc *a_p;
    571 	} */ *ap = v;
    572 	struct filedesc *fdp = ap->a_p->p_fd;
    573 	struct file *fp;
    574 	unsigned fd;
    575 	int error;
    576 
    577 	/*
    578 	 * Can't mess with the root vnode
    579 	 */
    580 	switch (VTOFDESC(ap->a_vp)->fd_type) {
    581 	case Fdesc:
    582 		break;
    583 
    584 	case Fctty:
    585 		return (0);
    586 
    587 	default:
    588 		return (EACCES);
    589 	}
    590 
    591 	fd = VTOFDESC(ap->a_vp)->fd_fd;
    592 	if (fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) {
    593 		return (EBADF);
    594 	}
    595 
    596 	/*
    597 	 * Can setattr the underlying vnode, but not sockets!
    598 	 */
    599 	switch (fp->f_type) {
    600 	case DTYPE_VNODE:
    601 		error = VOP_SETATTR((struct vnode *) fp->f_data, ap->a_vap, ap->a_cred, ap->a_p);
    602 		break;
    603 
    604 	case DTYPE_SOCKET:
    605 		error = 0;
    606 		break;
    607 
    608 	default:
    609 		panic("fdesc setattr");
    610 		break;
    611 	}
    612 
    613 	return (error);
    614 }
    615 
    616 #define UIO_MX 32
    617 
    618 struct fdesc_target {
    619 	ino_t ft_fileno;
    620 	u_char ft_type;
    621 	u_char ft_namlen;
    622 	char *ft_name;
    623 } fdesc_targets[] = {
    624 /* NOTE: The name must be less than UIO_MX-16 chars in length */
    625 #define N(s) sizeof(s)-1, s
    626 	{ FD_DEVFD,  DT_DIR,     N("fd")     },
    627 	{ FD_STDIN,  DT_LNK,     N("stdin")  },
    628 	{ FD_STDOUT, DT_LNK,     N("stdout") },
    629 	{ FD_STDERR, DT_LNK,     N("stderr") },
    630 	{ FD_CTTY,   DT_UNKNOWN, N("tty")    },
    631 #undef N
    632 };
    633 static int nfdesc_targets = sizeof(fdesc_targets) / sizeof(fdesc_targets[0]);
    634 
    635 int
    636 fdesc_readdir(v)
    637 	void *v;
    638 {
    639 	struct vop_readdir_args /* {
    640 		struct vnode *a_vp;
    641 		struct uio *a_uio;
    642 		struct ucred *a_cred;
    643 		int *a_eofflag;
    644 		u_long *a_cookies;
    645 		int a_ncookies;
    646 	} */ *ap = v;
    647 	struct uio *uio = ap->a_uio;
    648 	struct dirent d;
    649 	struct filedesc *fdp;
    650 	int i;
    651 	int error;
    652 	u_long *cookies = ap->a_cookies;
    653 	int ncookies = ap->a_ncookies;
    654 
    655 	switch (VTOFDESC(ap->a_vp)->fd_type) {
    656 	case Fctty:
    657 		return (0);
    658 
    659 	case Fdesc:
    660 		return (ENOTDIR);
    661 
    662 	default:
    663 		break;
    664 	}
    665 
    666 	fdp = uio->uio_procp->p_fd;
    667 
    668 	if (uio->uio_resid < UIO_MX)
    669 		return (EINVAL);
    670 	if (uio->uio_offset < 0)
    671 		return (EINVAL);
    672 
    673 	error = 0;
    674 	i = uio->uio_offset;
    675 	bzero((caddr_t)&d, UIO_MX);
    676 	d.d_reclen = UIO_MX;
    677 
    678 	if (VTOFDESC(ap->a_vp)->fd_type == Froot) {
    679 		struct fdesc_target *ft;
    680 
    681 		for (ft = &fdesc_targets[i];
    682 		     uio->uio_resid >= UIO_MX && i < nfdesc_targets; ft++, i++) {
    683 			switch (ft->ft_fileno) {
    684 			case FD_CTTY:
    685 				if (cttyvp(uio->uio_procp) == NULL)
    686 					continue;
    687 				break;
    688 
    689 			case FD_STDIN:
    690 			case FD_STDOUT:
    691 			case FD_STDERR:
    692 				if ((ft->ft_fileno - FD_STDIN) >= fdp->fd_nfiles)
    693 					continue;
    694 				if (fdp->fd_ofiles[ft->ft_fileno - FD_STDIN] == NULL)
    695 					continue;
    696 				break;
    697 			}
    698 
    699 			d.d_fileno = ft->ft_fileno;
    700 			d.d_namlen = ft->ft_namlen;
    701 			bcopy(ft->ft_name, d.d_name, ft->ft_namlen + 1);
    702 			d.d_type = ft->ft_type;
    703 
    704 			if ((error = uiomove((caddr_t)&d, UIO_MX, uio)) != 0)
    705 				break;
    706 			if (ncookies-- > 0)
    707 				*cookies++ = i + 1;
    708 		}
    709 	} else {
    710 		for (; i - 2 < fdp->fd_nfiles && uio->uio_resid >= UIO_MX;
    711 		     i++) {
    712 			switch (i) {
    713 			case 0:
    714 			case 1:
    715 				d.d_fileno = FD_ROOT;		/* XXX */
    716 				d.d_namlen = i + 1;
    717 				bcopy("..", d.d_name, d.d_namlen);
    718 				d.d_name[i + 1] = '\0';
    719 				d.d_type = DT_DIR;
    720 				break;
    721 
    722 			default:
    723 				if (fdp->fd_ofiles[i - 2] == NULL)
    724 					continue;
    725 				d.d_fileno = i - 2 + FD_STDIN;
    726 				d.d_namlen = sprintf(d.d_name, "%d", i - 2);
    727 				d.d_type = DT_UNKNOWN;
    728 				break;
    729 			}
    730 
    731 			if ((error = uiomove((caddr_t)&d, UIO_MX, uio)) != 0)
    732 				break;
    733 			if (ncookies-- > 0)
    734 				*cookies++ = i + 1;
    735 		}
    736 	}
    737 
    738 	uio->uio_offset = i;
    739 	return (error);
    740 }
    741 
    742 int
    743 fdesc_readlink(v)
    744 	void *v;
    745 {
    746 	struct vop_readlink_args /* {
    747 		struct vnode *a_vp;
    748 		struct uio *a_uio;
    749 		struct ucred *a_cred;
    750 	} */ *ap = v;
    751 	struct vnode *vp = ap->a_vp;
    752 	int error;
    753 
    754 	if (vp->v_type != VLNK)
    755 		return (EPERM);
    756 
    757 	if (VTOFDESC(vp)->fd_type == Flink) {
    758 		char *ln = VTOFDESC(vp)->fd_link;
    759 		error = uiomove(ln, strlen(ln), ap->a_uio);
    760 	} else {
    761 		error = EOPNOTSUPP;
    762 	}
    763 
    764 	return (error);
    765 }
    766 
    767 int
    768 fdesc_read(v)
    769 	void *v;
    770 {
    771 	struct vop_read_args /* {
    772 		struct vnode *a_vp;
    773 		struct uio *a_uio;
    774 		int  a_ioflag;
    775 		struct ucred *a_cred;
    776 	} */ *ap = v;
    777 	int error = EOPNOTSUPP;
    778 
    779 	switch (VTOFDESC(ap->a_vp)->fd_type) {
    780 	case Fctty:
    781 		error = cttyread(devctty, ap->a_uio, ap->a_ioflag);
    782 		break;
    783 
    784 	default:
    785 		error = EOPNOTSUPP;
    786 		break;
    787 	}
    788 
    789 	return (error);
    790 }
    791 
    792 int
    793 fdesc_write(v)
    794 	void *v;
    795 {
    796 	struct vop_write_args /* {
    797 		struct vnode *a_vp;
    798 		struct uio *a_uio;
    799 		int  a_ioflag;
    800 		struct ucred *a_cred;
    801 	} */ *ap = v;
    802 	int error = EOPNOTSUPP;
    803 
    804 	switch (VTOFDESC(ap->a_vp)->fd_type) {
    805 	case Fctty:
    806 		error = cttywrite(devctty, ap->a_uio, ap->a_ioflag);
    807 		break;
    808 
    809 	default:
    810 		error = EOPNOTSUPP;
    811 		break;
    812 	}
    813 
    814 	return (error);
    815 }
    816 
    817 int
    818 fdesc_ioctl(v)
    819 	void *v;
    820 {
    821 	struct vop_ioctl_args /* {
    822 		struct vnode *a_vp;
    823 		u_long a_command;
    824 		caddr_t  a_data;
    825 		int  a_fflag;
    826 		struct ucred *a_cred;
    827 		struct proc *a_p;
    828 	} */ *ap = v;
    829 	int error = EOPNOTSUPP;
    830 
    831 	switch (VTOFDESC(ap->a_vp)->fd_type) {
    832 	case Fctty:
    833 		error = cttyioctl(devctty, ap->a_command, ap->a_data,
    834 				  ap->a_fflag, ap->a_p);
    835 		break;
    836 
    837 	default:
    838 		error = EOPNOTSUPP;
    839 		break;
    840 	}
    841 
    842 	return (error);
    843 }
    844 
    845 int
    846 fdesc_select(v)
    847 	void *v;
    848 {
    849 	struct vop_select_args /* {
    850 		struct vnode *a_vp;
    851 		int  a_which;
    852 		int  a_fflags;
    853 		struct ucred *a_cred;
    854 		struct proc *a_p;
    855 	} */ *ap = v;
    856 	int error = EOPNOTSUPP;
    857 
    858 	switch (VTOFDESC(ap->a_vp)->fd_type) {
    859 	case Fctty:
    860 		error = cttyselect(devctty, ap->a_fflags, ap->a_p);
    861 		break;
    862 
    863 	default:
    864 		error = EOPNOTSUPP;
    865 		break;
    866 	}
    867 
    868 	return (error);
    869 }
    870 
    871 int
    872 fdesc_inactive(v)
    873 	void *v;
    874 {
    875 	struct vop_inactive_args /* {
    876 		struct vnode *a_vp;
    877 	} */ *ap = v;
    878 	struct vnode *vp = ap->a_vp;
    879 
    880 	/*
    881 	 * Clear out the v_type field to avoid
    882 	 * nasty things happening in vgone().
    883 	 */
    884 	vp->v_type = VNON;
    885 	return (0);
    886 }
    887 
    888 int
    889 fdesc_reclaim(v)
    890 	void *v;
    891 {
    892 	struct vop_reclaim_args /* {
    893 		struct vnode *a_vp;
    894 	} */ *ap = v;
    895 	struct vnode *vp = ap->a_vp;
    896 	struct fdescnode *fd = VTOFDESC(vp);
    897 
    898 	LIST_REMOVE(fd, fd_hash);
    899 	FREE(vp->v_data, M_TEMP);
    900 	vp->v_data = 0;
    901 
    902 	return (0);
    903 }
    904 
    905 /*
    906  * Return POSIX pathconf information applicable to special devices.
    907  */
    908 int
    909 fdesc_pathconf(v)
    910 	void *v;
    911 {
    912 	struct vop_pathconf_args /* {
    913 		struct vnode *a_vp;
    914 		int a_name;
    915 		register_t *a_retval;
    916 	} */ *ap = v;
    917 
    918 	switch (ap->a_name) {
    919 	case _PC_LINK_MAX:
    920 		*ap->a_retval = LINK_MAX;
    921 		return (0);
    922 	case _PC_MAX_CANON:
    923 		*ap->a_retval = MAX_CANON;
    924 		return (0);
    925 	case _PC_MAX_INPUT:
    926 		*ap->a_retval = MAX_INPUT;
    927 		return (0);
    928 	case _PC_PIPE_BUF:
    929 		*ap->a_retval = PIPE_BUF;
    930 		return (0);
    931 	case _PC_CHOWN_RESTRICTED:
    932 		*ap->a_retval = 1;
    933 		return (0);
    934 	case _PC_VDISABLE:
    935 		*ap->a_retval = _POSIX_VDISABLE;
    936 		return (0);
    937 	default:
    938 		return (EINVAL);
    939 	}
    940 	/* NOTREACHED */
    941 }
    942 
    943 /*
    944  * Print out the contents of a /dev/fd vnode.
    945  */
    946 /* ARGSUSED */
    947 int
    948 fdesc_print(v)
    949 	void *v;
    950 {
    951 	printf("tag VT_NON, fdesc vnode\n");
    952 	return (0);
    953 }
    954 
    955 int
    956 fdesc_vfree(v)
    957 	void *v;
    958 {
    959 	return (0);
    960 }
    961 
    962 int
    963 fdesc_link(v)
    964 	void *v;
    965 {
    966 	struct vop_link_args /* {
    967 		struct vnode *a_dvp;
    968 		struct vnode *a_vp;
    969 		struct componentname *a_cnp;
    970 	} */ *ap = v;
    971 
    972 	VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
    973 	vput(ap->a_dvp);
    974 	return (EROFS);
    975 }
    976 
    977 int
    978 fdesc_symlink(v)
    979 	void *v;
    980 {
    981 	struct vop_symlink_args /* {
    982 		struct vnode *a_dvp;
    983 		struct vnode **a_vpp;
    984 		struct componentname *a_cnp;
    985 		struct vattr *a_vap;
    986 		char *a_target;
    987 	} */ *ap = v;
    988 
    989 	VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
    990 	vput(ap->a_dvp);
    991 	return (EROFS);
    992 }
    993 
    994 int
    995 fdesc_abortop(v)
    996 	void *v;
    997 {
    998 	struct vop_abortop_args /* {
    999 		struct vnode *a_dvp;
   1000 		struct componentname *a_cnp;
   1001 	} */ *ap = v;
   1002 
   1003 	if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
   1004 		FREE(ap->a_cnp->cn_pnbuf, M_NAMEI);
   1005 	return (0);
   1006 }
   1007 
   1008 /*
   1009  * /dev/fd vnode unsupported operation
   1010  */
   1011 /*ARGSUSED*/
   1012 int
   1013 fdesc_enotsupp(v)
   1014 	void *v;
   1015 {
   1016 
   1017 	return (EOPNOTSUPP);
   1018 }
   1019 
   1020 /*
   1021  * /dev/fd "should never get here" operation
   1022  */
   1023 /*ARGSUSED*/
   1024 int
   1025 fdesc_badop(v)
   1026 	void *v;
   1027 {
   1028 
   1029 	panic("fdesc: bad op");
   1030 	/* NOTREACHED */
   1031 }
   1032