xref: /freebsd/sys/fs/fdescfs/fdesc_vnops.c (revision 4cf49a43559ed9fdad601bdcccd2c55963008675)
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  * $FreeBSD$
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/filedesc.h>
50 #include <sys/vnode.h>
51 #include <sys/malloc.h>
52 #include <sys/file.h>
53 #include <sys/stat.h>
54 #include <sys/mount.h>
55 #include <sys/namei.h>
56 #include <sys/dirent.h>
57 #include <sys/socketvar.h>
58 #include <sys/conf.h>
59 #include <miscfs/fdesc/fdesc.h>
60 
61 extern	struct cdevsw ctty_cdevsw;
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 static vop_t **fdesc_vnodeop_p;
70 
71 dev_t devctty;
72 
73 #if (FD_STDIN != FD_STDOUT-1) || (FD_STDOUT != FD_STDERR-1)
74 FD_STDIN, FD_STDOUT, FD_STDERR must be a sequence n, n+1, n+2
75 #endif
76 
77 #define	NFDCACHE 4
78 #define FD_NHASH(ix) \
79 	(&fdhashtbl[(ix) & fdhash])
80 static LIST_HEAD(fdhashhead, fdescnode) *fdhashtbl;
81 static u_long fdhash;
82 
83 static int	fdesc_attr __P((int fd, struct vattr *vap, struct ucred *cred,
84 				struct proc *p));
85 static int	fdesc_badop __P((void));
86 static int	fdesc_getattr __P((struct vop_getattr_args *ap));
87 static int	fdesc_inactive __P((struct vop_inactive_args *ap));
88 static int	fdesc_ioctl __P((struct vop_ioctl_args *ap));
89 static int	fdesc_lookup __P((struct vop_lookup_args *ap));
90 static int	fdesc_open __P((struct vop_open_args *ap));
91 static int	fdesc_print __P((struct vop_print_args *ap));
92 static int	fdesc_read __P((struct vop_read_args *ap));
93 static int	fdesc_readdir __P((struct vop_readdir_args *ap));
94 static int	fdesc_readlink __P((struct vop_readlink_args *ap));
95 static int	fdesc_reclaim __P((struct vop_reclaim_args *ap));
96 static int	fdesc_poll __P((struct vop_poll_args *ap));
97 static int	fdesc_setattr __P((struct vop_setattr_args *ap));
98 static int	fdesc_write __P((struct vop_write_args *ap));
99 
100 /*
101  * Initialise cache headers
102  */
103 int
104 fdesc_init(vfsp)
105 	struct vfsconf *vfsp;
106 {
107 
108 	devctty = NODEV;
109 	fdhashtbl = hashinit(NFDCACHE, M_CACHE, &fdhash);
110 	return (0);
111 }
112 
113 int
114 fdesc_allocvp(ftype, ix, mp, vpp)
115 	fdntype ftype;
116 	int ix;
117 	struct mount *mp;
118 	struct vnode **vpp;
119 {
120 	struct proc *p = curproc;	/* XXX */
121 	struct fdhashhead *fc;
122 	struct fdescnode *fd;
123 	int error = 0;
124 
125 	fc = FD_NHASH(ix);
126 loop:
127 	for (fd = fc->lh_first; fd != 0; fd = fd->fd_hash.le_next) {
128 		if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) {
129 			if (vget(fd->fd_vnode, 0, p))
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 		(void) tsleep((caddr_t) &fdcache_lock, PINOD, "fdalvp", 0);
143 		goto loop;
144 	}
145 	fdcache_lock |= FDL_LOCKED;
146 
147 	/*
148 	 * Do the MALLOC before the getnewvnode since doing so afterward
149 	 * might cause a bogus v_data pointer to get dereferenced
150 	 * elsewhere if MALLOC should block.
151 	 */
152 	MALLOC(fd, struct fdescnode *, sizeof(struct fdescnode), M_TEMP, M_WAITOK);
153 
154 	error = getnewvnode(VT_FDESC, mp, fdesc_vnodeop_p, vpp);
155 	if (error) {
156 		FREE(fd, M_TEMP);
157 		goto out;
158 	}
159 	(*vpp)->v_data = fd;
160 	fd->fd_vnode = *vpp;
161 	fd->fd_type = ftype;
162 	fd->fd_fd = -1;
163 	fd->fd_link = 0;
164 	fd->fd_ix = ix;
165 	LIST_INSERT_HEAD(fc, fd, fd_hash);
166 
167 out:;
168 	fdcache_lock &= ~FDL_LOCKED;
169 
170 	if (fdcache_lock & FDL_WANT) {
171 		fdcache_lock &= ~FDL_WANT;
172 		wakeup((caddr_t) &fdcache_lock);
173 	}
174 
175 	return (error);
176 }
177 
178 /*
179  * vp is the current namei directory
180  * ndp is the name to locate in that directory...
181  */
182 static int
183 fdesc_lookup(ap)
184 	struct vop_lookup_args /* {
185 		struct vnode * a_dvp;
186 		struct vnode ** a_vpp;
187 		struct componentname * a_cnp;
188 	} */ *ap;
189 {
190 	struct vnode **vpp = ap->a_vpp;
191 	struct vnode *dvp = ap->a_dvp;
192 	struct componentname *cnp = ap->a_cnp;
193 	char *pname = cnp->cn_nameptr;
194 	struct proc *p = cnp->cn_proc;
195 	int nfiles = p->p_fd->fd_nfiles;
196 	unsigned fd = -1;
197 	int error;
198 	struct vnode *fvp;
199 	char *ln;
200 
201 	if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) {
202 		error = EROFS;
203 		goto bad;
204 	}
205 
206 	VOP_UNLOCK(dvp, 0, p);
207 	if (cnp->cn_namelen == 1 && *pname == '.') {
208 		*vpp = dvp;
209 		VREF(dvp);
210 		vn_lock(dvp, LK_SHARED | LK_RETRY, p);
211 		return (0);
212 	}
213 
214 	switch (VTOFDESC(dvp)->fd_type) {
215 	default:
216 	case Flink:
217 	case Fdesc:
218 	case Fctty:
219 		error = ENOTDIR;
220 		goto bad;
221 
222 	case Froot:
223 		if (cnp->cn_namelen == 2 && bcmp(pname, "fd", 2) == 0) {
224 			error = fdesc_allocvp(Fdevfd, FD_DEVFD, dvp->v_mount, &fvp);
225 			if (error)
226 				goto bad;
227 			*vpp = fvp;
228 			fvp->v_type = VDIR;
229 			vn_lock(fvp, LK_SHARED | LK_RETRY, p);
230 			return (0);
231 		}
232 
233 		if (cnp->cn_namelen == 3 && bcmp(pname, "tty", 3) == 0) {
234 			struct vnode *ttyvp = cttyvp(p);
235 			if (ttyvp == NULL) {
236 				error = ENXIO;
237 				goto bad;
238 			}
239 			error = fdesc_allocvp(Fctty, FD_CTTY, dvp->v_mount, &fvp);
240 			if (error)
241 				goto bad;
242 			*vpp = fvp;
243 			fvp->v_type = VFIFO;
244 			vn_lock(fvp, LK_SHARED | LK_RETRY, p);
245 			return (0);
246 		}
247 
248 		ln = 0;
249 		switch (cnp->cn_namelen) {
250 		case 5:
251 			if (bcmp(pname, "stdin", 5) == 0) {
252 				ln = "fd/0";
253 				fd = FD_STDIN;
254 			}
255 			break;
256 		case 6:
257 			if (bcmp(pname, "stdout", 6) == 0) {
258 				ln = "fd/1";
259 				fd = FD_STDOUT;
260 			} else
261 			if (bcmp(pname, "stderr", 6) == 0) {
262 				ln = "fd/2";
263 				fd = FD_STDERR;
264 			}
265 			break;
266 		}
267 
268 		if (ln) {
269 			error = fdesc_allocvp(Flink, fd, dvp->v_mount, &fvp);
270 			if (error)
271 				goto bad;
272 			VTOFDESC(fvp)->fd_link = ln;
273 			*vpp = fvp;
274 			fvp->v_type = VLNK;
275 			vn_lock(fvp, LK_SHARED | LK_RETRY, p);
276 			return (0);
277 		} else {
278 			error = ENOENT;
279 			goto bad;
280 		}
281 
282 		/* FALL THROUGH */
283 
284 	case Fdevfd:
285 		if (cnp->cn_namelen == 2 && bcmp(pname, "..", 2) == 0) {
286 			if ((error = fdesc_root(dvp->v_mount, vpp)) != 0)
287 				goto bad;
288 			return (0);
289 		}
290 
291 		fd = 0;
292 		while (*pname >= '0' && *pname <= '9') {
293 			fd = 10 * fd + *pname++ - '0';
294 			if (fd >= nfiles)
295 				break;
296 		}
297 
298 		if (*pname != '\0') {
299 			error = ENOENT;
300 			goto bad;
301 		}
302 
303 		if (fd >= nfiles || p->p_fd->fd_ofiles[fd] == NULL) {
304 			error = EBADF;
305 			goto bad;
306 		}
307 
308 		error = fdesc_allocvp(Fdesc, FD_DESC+fd, dvp->v_mount, &fvp);
309 		if (error)
310 			goto bad;
311 		VTOFDESC(fvp)->fd_fd = fd;
312 		vn_lock(fvp, LK_SHARED | LK_RETRY, p);
313 		*vpp = fvp;
314 		return (0);
315 	}
316 
317 bad:;
318 	vn_lock(dvp, LK_SHARED | LK_RETRY, p);
319 	*vpp = NULL;
320 	return (error);
321 }
322 
323 static int
324 fdesc_open(ap)
325 	struct vop_open_args /* {
326 		struct vnode *a_vp;
327 		int  a_mode;
328 		struct ucred *a_cred;
329 		struct proc *a_p;
330 	} */ *ap;
331 {
332 	struct vnode *vp = ap->a_vp;
333 	int error = 0;
334 
335 	switch (VTOFDESC(vp)->fd_type) {
336 	case Fdesc:
337 		/*
338 		 * XXX Kludge: set p->p_dupfd to contain the value of the
339 		 * the file descriptor being sought for duplication. The error
340 		 * return ensures that the vnode for this device will be
341 		 * released by vn_open. Open will detect this special error and
342 		 * take the actions in dupfdopen.  Other callers of vn_open or
343 		 * VOP_OPEN will simply report the error.
344 		 */
345 		ap->a_p->p_dupfd = VTOFDESC(vp)->fd_fd;	/* XXX */
346 		error = ENODEV;
347 		break;
348 
349 	case Fctty:
350 		error = (*ctty_cdevsw.d_open)(devctty, ap->a_mode, 0, ap->a_p);
351 		break;
352 	}
353 
354 	return (error);
355 }
356 
357 static int
358 fdesc_attr(fd, vap, cred, p)
359 	int fd;
360 	struct vattr *vap;
361 	struct ucred *cred;
362 	struct proc *p;
363 {
364 	struct filedesc *fdp = p->p_fd;
365 	struct file *fp;
366 	struct stat stb;
367 	int error;
368 
369 	if (fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL)
370 		return (EBADF);
371 
372 	switch (fp->f_type) {
373 	case DTYPE_FIFO:
374 	case DTYPE_VNODE:
375 		error = VOP_GETATTR((struct vnode *) fp->f_data, vap, cred, p);
376 		if (error == 0 && vap->va_type == VDIR) {
377 			/*
378 			 * directories can cause loops in the namespace,
379 			 * so turn off the 'x' bits to avoid trouble.
380 			 */
381 			vap->va_mode &= ~((VEXEC)|(VEXEC>>3)|(VEXEC>>6));
382 		}
383 		break;
384 
385 	case DTYPE_SOCKET:
386 		error = soo_stat((struct socket *)fp->f_data, &stb);
387 		if (error == 0) {
388 			vattr_null(vap);
389 			vap->va_type = VSOCK;
390 			vap->va_mode = stb.st_mode;
391 			vap->va_nlink = stb.st_nlink;
392 			vap->va_uid = stb.st_uid;
393 			vap->va_gid = stb.st_gid;
394 			vap->va_fsid = stb.st_dev;
395 			vap->va_fileid = stb.st_ino;
396 			vap->va_size = stb.st_size;
397 			vap->va_blocksize = stb.st_blksize;
398 			vap->va_atime = stb.st_atimespec;
399 			vap->va_mtime = stb.st_mtimespec;
400 			vap->va_ctime = stb.st_ctimespec;
401 			vap->va_gen = stb.st_gen;
402 			vap->va_flags = stb.st_flags;
403 			vap->va_rdev = stb.st_rdev;
404 			vap->va_bytes = stb.st_blocks * stb.st_blksize;
405 		}
406 		break;
407 
408 	default:
409 		panic("fdesc attr");
410 		break;
411 	}
412 
413 	return (error);
414 }
415 
416 static int
417 fdesc_getattr(ap)
418 	struct vop_getattr_args /* {
419 		struct vnode *a_vp;
420 		struct vattr *a_vap;
421 		struct ucred *a_cred;
422 		struct proc *a_p;
423 	} */ *ap;
424 {
425 	struct vnode *vp = ap->a_vp;
426 	struct vattr *vap = ap->a_vap;
427 	unsigned fd;
428 	int error = 0;
429 
430 	switch (VTOFDESC(vp)->fd_type) {
431 	case Froot:
432 	case Fdevfd:
433 	case Flink:
434 	case Fctty:
435 		bzero((caddr_t) vap, sizeof(*vap));
436 		vattr_null(vap);
437 		vap->va_fileid = VTOFDESC(vp)->fd_ix;
438 
439 		switch (VTOFDESC(vp)->fd_type) {
440 		case Flink:
441 			vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
442 			vap->va_type = VLNK;
443 			vap->va_nlink = 1;
444 			vap->va_size = strlen(VTOFDESC(vp)->fd_link);
445 			break;
446 
447 		case Fctty:
448 			vap->va_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
449 			vap->va_type = VFIFO;
450 			vap->va_nlink = 1;
451 			vap->va_size = 0;
452 			break;
453 
454 		default:
455 			vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
456 			vap->va_type = VDIR;
457 			vap->va_nlink = 2;
458 			vap->va_size = DEV_BSIZE;
459 			break;
460 		}
461 		vap->va_uid = 0;
462 		vap->va_gid = 0;
463 		vap->va_blocksize = DEV_BSIZE;
464 		vap->va_atime.tv_sec = boottime.tv_sec;
465 		vap->va_atime.tv_nsec = 0;
466 		vap->va_mtime = vap->va_atime;
467 		vap->va_ctime = vap->va_mtime;
468 		vap->va_gen = 0;
469 		vap->va_flags = 0;
470 		vap->va_rdev = 0;
471 		vap->va_bytes = 0;
472 		break;
473 
474 	case Fdesc:
475 		fd = VTOFDESC(vp)->fd_fd;
476 		error = fdesc_attr(fd, vap, ap->a_cred, ap->a_p);
477 		break;
478 
479 	default:
480 		panic("fdesc_getattr");
481 		break;
482 	}
483 
484 	if (error == 0)
485 		vp->v_type = vap->va_type;
486 
487 	return (error);
488 }
489 
490 static int
491 fdesc_setattr(ap)
492 	struct vop_setattr_args /* {
493 		struct vnode *a_vp;
494 		struct vattr *a_vap;
495 		struct ucred *a_cred;
496 		struct proc *a_p;
497 	} */ *ap;
498 {
499 	struct filedesc *fdp = ap->a_p->p_fd;
500 	struct vattr *vap = ap->a_vap;
501 	struct file *fp;
502 	unsigned fd;
503 	int error;
504 
505 	/*
506 	 * Can't mess with the root vnode
507 	 */
508 	switch (VTOFDESC(ap->a_vp)->fd_type) {
509 	case Fdesc:
510 		break;
511 
512 	case Fctty:
513 		if (vap->va_flags != VNOVAL)
514 			return (EOPNOTSUPP);
515 		return (0);
516 
517 	default:
518 		return (EACCES);
519 	}
520 
521 	fd = VTOFDESC(ap->a_vp)->fd_fd;
522 	if (fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) {
523 		return (EBADF);
524 	}
525 
526 	/*
527 	 * Can setattr the underlying vnode, but not sockets!
528 	 */
529 	switch (fp->f_type) {
530 	case DTYPE_FIFO:
531 	case DTYPE_PIPE:
532 	case DTYPE_VNODE:
533 		error = VOP_SETATTR((struct vnode *) fp->f_data, ap->a_vap, ap->a_cred, ap->a_p);
534 		break;
535 
536 	case DTYPE_SOCKET:
537 		if (vap->va_flags != VNOVAL)
538 			error = EOPNOTSUPP;
539 		else
540 			error = 0;
541 		break;
542 
543 	default:
544 		error = EBADF;
545 		break;
546 	}
547 
548 	return (error);
549 }
550 
551 #define UIO_MX 16
552 
553 static struct dirtmp {
554 	u_long d_fileno;
555 	u_short d_reclen;
556 	u_short d_namlen;
557 	char d_name[8];
558 } rootent[] = {
559 	{ FD_DEVFD, UIO_MX, 2, "fd" },
560 	{ FD_STDIN, UIO_MX, 5, "stdin" },
561 	{ FD_STDOUT, UIO_MX, 6, "stdout" },
562 	{ FD_STDERR, UIO_MX, 6, "stderr" },
563 	{ FD_CTTY, UIO_MX, 3, "tty" },
564 };
565 
566 static int
567 fdesc_readdir(ap)
568 	struct vop_readdir_args /* {
569 		struct vnode *a_vp;
570 		struct uio *a_uio;
571 		struct ucred *a_cred;
572 		int *a_eofflag;
573 		u_long *a_cookies;
574 		int a_ncookies;
575 	} */ *ap;
576 {
577 	struct uio *uio = ap->a_uio;
578 	struct filedesc *fdp;
579 	int error, i, off;
580 
581 	/*
582 	 * We don't allow exporting fdesc mounts, and currently local
583 	 * requests do not need cookies.
584 	 */
585 	if (ap->a_ncookies)
586 		panic("fdesc_readdir: not hungry");
587 
588 	if (VTOFDESC(ap->a_vp)->fd_type != Froot &&
589 	    VTOFDESC(ap->a_vp)->fd_type != Fdevfd)
590 		panic("fdesc_readdir: not dir");
591 
592 	off = (int)uio->uio_offset;
593 	if (off != uio->uio_offset || off < 0 || (u_int)off % UIO_MX != 0 ||
594 	    uio->uio_resid < UIO_MX)
595 		return (EINVAL);
596 	i = (u_int)off / UIO_MX;
597 	fdp = uio->uio_procp->p_fd;
598 
599 	if (VTOFDESC(ap->a_vp)->fd_type == Froot) {
600 		struct dirent d;
601 		struct dirent *dp = &d;
602 		struct dirtmp *dt;
603 
604 		error = 0;
605 
606 		while (i < sizeof(rootent) / sizeof(rootent[0]) &&
607 		    uio->uio_resid >= UIO_MX) {
608 			dt = &rootent[i];
609 			switch (dt->d_fileno) {
610 			case FD_CTTY:
611 				if (cttyvp(uio->uio_procp) == NULL)
612 					continue;
613 				break;
614 
615 			case FD_STDIN:
616 			case FD_STDOUT:
617 			case FD_STDERR:
618 				if ((dt->d_fileno-FD_STDIN) >= fdp->fd_nfiles)
619 					continue;
620 				if (fdp->fd_ofiles[dt->d_fileno-FD_STDIN] == NULL)
621 					continue;
622 				break;
623 			}
624 			bzero((caddr_t) dp, UIO_MX);
625 			dp->d_fileno = dt->d_fileno;
626 			dp->d_namlen = dt->d_namlen;
627 			dp->d_type = DT_UNKNOWN;
628 			dp->d_reclen = dt->d_reclen;
629 			bcopy(dt->d_name, dp->d_name, dp->d_namlen+1);
630 			error = uiomove((caddr_t) dp, UIO_MX, uio);
631 			if (error)
632 				break;
633 			i++;
634 		}
635 		uio->uio_offset = i * UIO_MX;
636 		return (error);
637 	}
638 
639 	error = 0;
640 	while (i < fdp->fd_nfiles && uio->uio_resid >= UIO_MX) {
641 		if (fdp->fd_ofiles[i] != NULL) {
642 			struct dirent d;
643 			struct dirent *dp = &d;
644 
645 			bzero((caddr_t) dp, UIO_MX);
646 
647 			dp->d_namlen = sprintf(dp->d_name, "%d", i);
648 			dp->d_reclen = UIO_MX;
649 			dp->d_type = DT_UNKNOWN;
650 			dp->d_fileno = i + FD_STDIN;
651 			/*
652 			 * And ship to userland
653 			 */
654 			error = uiomove((caddr_t) dp, UIO_MX, uio);
655 			if (error)
656 				break;
657 		}
658 		i++;
659 	}
660 
661 	uio->uio_offset = i * UIO_MX;
662 	return (error);
663 }
664 
665 static int
666 fdesc_readlink(ap)
667 	struct vop_readlink_args /* {
668 		struct vnode *a_vp;
669 		struct uio *a_uio;
670 		struct ucred *a_cred;
671 	} */ *ap;
672 {
673 	struct vnode *vp = ap->a_vp;
674 	int error;
675 
676 	if (vp->v_type != VLNK)
677 		return (EPERM);
678 
679 	if (VTOFDESC(vp)->fd_type == Flink) {
680 		char *ln = VTOFDESC(vp)->fd_link;
681 		error = uiomove(ln, strlen(ln), ap->a_uio);
682 	} else {
683 		error = EOPNOTSUPP;
684 	}
685 
686 	return (error);
687 }
688 
689 static int
690 fdesc_read(ap)
691 	struct vop_read_args /* {
692 		struct vnode *a_vp;
693 		struct uio *a_uio;
694 		int  a_ioflag;
695 		struct ucred *a_cred;
696 	} */ *ap;
697 {
698 	int error = EOPNOTSUPP;
699 
700 	switch (VTOFDESC(ap->a_vp)->fd_type) {
701 	case Fctty:
702 		error = (*ctty_cdevsw.d_read)(devctty, ap->a_uio, ap->a_ioflag);
703 		break;
704 
705 	default:
706 		error = EOPNOTSUPP;
707 		break;
708 	}
709 
710 	return (error);
711 }
712 
713 static int
714 fdesc_write(ap)
715 	struct vop_write_args /* {
716 		struct vnode *a_vp;
717 		struct uio *a_uio;
718 		int  a_ioflag;
719 		struct ucred *a_cred;
720 	} */ *ap;
721 {
722 	int error = EOPNOTSUPP;
723 
724 	switch (VTOFDESC(ap->a_vp)->fd_type) {
725 	case Fctty:
726 		error = (*ctty_cdevsw.d_write)(devctty, ap->a_uio, ap->a_ioflag);
727 		break;
728 
729 	default:
730 		error = EOPNOTSUPP;
731 		break;
732 	}
733 
734 	return (error);
735 }
736 
737 static int
738 fdesc_ioctl(ap)
739 	struct vop_ioctl_args /* {
740 		struct vnode *a_vp;
741 		int  a_command;
742 		caddr_t  a_data;
743 		int  a_fflag;
744 		struct ucred *a_cred;
745 		struct proc *a_p;
746 	} */ *ap;
747 {
748 	int error = EOPNOTSUPP;
749 
750 	switch (VTOFDESC(ap->a_vp)->fd_type) {
751 	case Fctty:
752 		error = (*ctty_cdevsw.d_ioctl)(devctty, ap->a_command,
753 					ap->a_data, ap->a_fflag, ap->a_p);
754 		break;
755 
756 	default:
757 		error = EOPNOTSUPP;
758 		break;
759 	}
760 
761 	return (error);
762 }
763 
764 static int
765 fdesc_poll(ap)
766 	struct vop_poll_args /* {
767 		struct vnode *a_vp;
768 		int  a_events;
769 		struct ucred *a_cred;
770 		struct proc *a_p;
771 	} */ *ap;
772 {
773 	int revents;
774 
775 	switch (VTOFDESC(ap->a_vp)->fd_type) {
776 	case Fctty:
777 		revents = (*ctty_cdevsw.d_poll)(devctty, ap->a_events, ap->a_p);
778 		break;
779 
780 	default:
781 		revents = seltrue(0, ap->a_events, ap->a_p);
782 		break;
783 	}
784 
785 	return (revents);
786 }
787 
788 static int
789 fdesc_inactive(ap)
790 	struct vop_inactive_args /* {
791 		struct vnode *a_vp;
792 		struct proc *a_p;
793 	} */ *ap;
794 {
795 	struct vnode *vp = ap->a_vp;
796 
797 	/*
798 	 * Clear out the v_type field to avoid
799 	 * nasty things happening in vgone().
800 	 */
801 	VOP_UNLOCK(vp, 0, ap->a_p);
802 	vp->v_type = VNON;
803 	return (0);
804 }
805 
806 static int
807 fdesc_reclaim(ap)
808 	struct vop_reclaim_args /* {
809 		struct vnode *a_vp;
810 	} */ *ap;
811 {
812 	struct vnode *vp = ap->a_vp;
813 	struct fdescnode *fd = VTOFDESC(vp);
814 
815 	LIST_REMOVE(fd, fd_hash);
816 	FREE(vp->v_data, M_TEMP);
817 	vp->v_data = 0;
818 
819 	return (0);
820 }
821 
822 /*
823  * Print out the contents of a /dev/fd vnode.
824  */
825 /* ARGSUSED */
826 static int
827 fdesc_print(ap)
828 	struct vop_print_args /* {
829 		struct vnode *a_vp;
830 	} */ *ap;
831 {
832 
833 	printf("tag VT_NON, fdesc vnode\n");
834 	return (0);
835 }
836 
837 /*
838  * /dev/fd "should never get here" operation
839  */
840 static int
841 fdesc_badop()
842 {
843 
844 	panic("fdesc: bad op");
845 	/* NOTREACHED */
846 }
847 
848 static struct vnodeopv_entry_desc fdesc_vnodeop_entries[] = {
849 	{ &vop_default_desc,		(vop_t *) vop_defaultop },
850 	{ &vop_access_desc,		(vop_t *) vop_null },
851 	{ &vop_bmap_desc,		(vop_t *) fdesc_badop },
852 	{ &vop_getattr_desc,		(vop_t *) fdesc_getattr },
853 	{ &vop_inactive_desc,		(vop_t *) fdesc_inactive },
854 	{ &vop_ioctl_desc,		(vop_t *) fdesc_ioctl },
855 	{ &vop_lookup_desc,		(vop_t *) fdesc_lookup },
856 	{ &vop_open_desc,		(vop_t *) fdesc_open },
857 	{ &vop_pathconf_desc,		(vop_t *) vop_stdpathconf },
858 	{ &vop_poll_desc,		(vop_t *) fdesc_poll },
859 	{ &vop_print_desc,		(vop_t *) fdesc_print },
860 	{ &vop_read_desc,		(vop_t *) fdesc_read },
861 	{ &vop_readdir_desc,		(vop_t *) fdesc_readdir },
862 	{ &vop_readlink_desc,		(vop_t *) fdesc_readlink },
863 	{ &vop_reclaim_desc,		(vop_t *) fdesc_reclaim },
864 	{ &vop_setattr_desc,		(vop_t *) fdesc_setattr },
865 	{ &vop_write_desc,		(vop_t *) fdesc_write },
866 	{ NULL, NULL }
867 };
868 static struct vnodeopv_desc fdesc_vnodeop_opv_desc =
869 	{ &fdesc_vnodeop_p, fdesc_vnodeop_entries };
870 
871 VNODEOP_SET(fdesc_vnodeop_opv_desc);
872