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