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