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