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