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