fdesc_vnops.c revision 1.101.12.1 1 /* $NetBSD: fdesc_vnops.c,v 1.101.12.1 2008/04/03 12:43:06 mjf Exp $ */
2
3 /*
4 * Copyright (c) 1992, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software donated to Berkeley by
8 * Jan-Simon Pendry.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * @(#)fdesc_vnops.c 8.17 (Berkeley) 5/22/95
35 *
36 * #Id: fdesc_vnops.c,v 1.12 1993/04/06 16:17:17 jsp Exp #
37 */
38
39 /*
40 * /dev/fd Filesystem
41 */
42
43 #include <sys/cdefs.h>
44 __KERNEL_RCSID(0, "$NetBSD: fdesc_vnops.c,v 1.101.12.1 2008/04/03 12:43:06 mjf Exp $");
45
46 #include <sys/param.h>
47 #include <sys/systm.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/socketvar.h>
53 #include <sys/filedesc.h>
54 #include <sys/vnode.h>
55 #include <sys/malloc.h>
56 #include <sys/conf.h>
57 #include <sys/file.h>
58 #include <sys/stat.h>
59 #include <sys/mount.h>
60 #include <sys/namei.h>
61 #include <sys/buf.h>
62 #include <sys/dirent.h>
63 #include <sys/tty.h>
64 #include <sys/kauth.h>
65 #include <sys/atomic.h>
66
67 #include <miscfs/fdesc/fdesc.h>
68 #include <miscfs/genfs/genfs.h>
69
70 #define cttyvp(p) ((p)->p_lflag & PL_CONTROLT ? (p)->p_session->s_ttyvp : NULL)
71
72 #define FDL_WANT 0x01
73 #define FDL_LOCKED 0x02
74 static int fdcache_lock;
75
76 dev_t devctty;
77
78 #if (FD_STDIN != FD_STDOUT-1) || (FD_STDOUT != FD_STDERR-1)
79 FD_STDIN, FD_STDOUT, FD_STDERR must be a sequence n, n+1, n+2
80 #endif
81
82 #define NFDCACHE 4
83
84 #define FD_NHASH(ix) \
85 (&fdhashtbl[(ix) & fdhash])
86 LIST_HEAD(fdhashhead, fdescnode) *fdhashtbl;
87 u_long fdhash;
88
89 int fdesc_lookup(void *);
90 #define fdesc_create genfs_eopnotsupp
91 #define fdesc_mknod genfs_eopnotsupp
92 int fdesc_open(void *);
93 #define fdesc_close genfs_nullop
94 #define fdesc_access genfs_nullop
95 int fdesc_getattr(void *);
96 int fdesc_setattr(void *);
97 int fdesc_read(void *);
98 int fdesc_write(void *);
99 int fdesc_ioctl(void *);
100 int fdesc_poll(void *);
101 int fdesc_kqfilter(void *);
102 #define fdesc_mmap genfs_eopnotsupp
103 #define fdesc_fcntl genfs_fcntl
104 #define fdesc_fsync genfs_nullop
105 #define fdesc_seek genfs_seek
106 #define fdesc_remove genfs_eopnotsupp
107 int fdesc_link(void *);
108 #define fdesc_rename genfs_eopnotsupp
109 #define fdesc_mkdir genfs_eopnotsupp
110 #define fdesc_rmdir genfs_eopnotsupp
111 int fdesc_symlink(void *);
112 int fdesc_readdir(void *);
113 int fdesc_readlink(void *);
114 #define fdesc_abortop genfs_abortop
115 int fdesc_inactive(void *);
116 int fdesc_reclaim(void *);
117 #define fdesc_lock genfs_lock
118 #define fdesc_unlock genfs_unlock
119 #define fdesc_bmap genfs_badop
120 #define fdesc_strategy genfs_badop
121 int fdesc_print(void *);
122 int fdesc_pathconf(void *);
123 #define fdesc_islocked genfs_islocked
124 #define fdesc_advlock genfs_einval
125 #define fdesc_bwrite genfs_eopnotsupp
126 #define fdesc_revoke genfs_revoke
127 #define fdesc_putpages genfs_null_putpages
128
129 static int fdesc_attr(int, struct vattr *, kauth_cred_t);
130
131 int (**fdesc_vnodeop_p)(void *);
132 const struct vnodeopv_entry_desc fdesc_vnodeop_entries[] = {
133 { &vop_default_desc, vn_default_error },
134 { &vop_lookup_desc, fdesc_lookup }, /* lookup */
135 { &vop_create_desc, fdesc_create }, /* create */
136 { &vop_mknod_desc, fdesc_mknod }, /* mknod */
137 { &vop_open_desc, fdesc_open }, /* open */
138 { &vop_close_desc, fdesc_close }, /* close */
139 { &vop_access_desc, fdesc_access }, /* access */
140 { &vop_getattr_desc, fdesc_getattr }, /* getattr */
141 { &vop_setattr_desc, fdesc_setattr }, /* setattr */
142 { &vop_read_desc, fdesc_read }, /* read */
143 { &vop_write_desc, fdesc_write }, /* write */
144 { &vop_ioctl_desc, fdesc_ioctl }, /* ioctl */
145 { &vop_fcntl_desc, fdesc_fcntl }, /* fcntl */
146 { &vop_poll_desc, fdesc_poll }, /* poll */
147 { &vop_kqfilter_desc, fdesc_kqfilter }, /* kqfilter */
148 { &vop_revoke_desc, fdesc_revoke }, /* revoke */
149 { &vop_mmap_desc, fdesc_mmap }, /* mmap */
150 { &vop_fsync_desc, fdesc_fsync }, /* fsync */
151 { &vop_seek_desc, fdesc_seek }, /* seek */
152 { &vop_remove_desc, fdesc_remove }, /* remove */
153 { &vop_link_desc, fdesc_link }, /* link */
154 { &vop_rename_desc, fdesc_rename }, /* rename */
155 { &vop_mkdir_desc, fdesc_mkdir }, /* mkdir */
156 { &vop_rmdir_desc, fdesc_rmdir }, /* rmdir */
157 { &vop_symlink_desc, fdesc_symlink }, /* symlink */
158 { &vop_readdir_desc, fdesc_readdir }, /* readdir */
159 { &vop_readlink_desc, fdesc_readlink }, /* readlink */
160 { &vop_abortop_desc, fdesc_abortop }, /* abortop */
161 { &vop_inactive_desc, fdesc_inactive }, /* inactive */
162 { &vop_reclaim_desc, fdesc_reclaim }, /* reclaim */
163 { &vop_lock_desc, fdesc_lock }, /* lock */
164 { &vop_unlock_desc, fdesc_unlock }, /* unlock */
165 { &vop_bmap_desc, fdesc_bmap }, /* bmap */
166 { &vop_strategy_desc, fdesc_strategy }, /* strategy */
167 { &vop_print_desc, fdesc_print }, /* print */
168 { &vop_islocked_desc, fdesc_islocked }, /* islocked */
169 { &vop_pathconf_desc, fdesc_pathconf }, /* pathconf */
170 { &vop_advlock_desc, fdesc_advlock }, /* advlock */
171 { &vop_bwrite_desc, fdesc_bwrite }, /* bwrite */
172 { &vop_putpages_desc, fdesc_putpages }, /* putpages */
173 { NULL, NULL }
174 };
175
176 const struct vnodeopv_desc fdesc_vnodeop_opv_desc =
177 { &fdesc_vnodeop_p, fdesc_vnodeop_entries };
178
179 extern const struct cdevsw ctty_cdevsw;
180
181 /*
182 * Initialise cache headers
183 */
184 void
185 fdesc_init()
186 {
187 int cttymajor;
188
189 /* locate the major number */
190 cttymajor = cdevsw_lookup_major(&ctty_cdevsw);
191 devctty = makedev(cttymajor, 0);
192 fdhashtbl = hashinit(NFDCACHE, HASH_LIST, M_CACHE, M_NOWAIT, &fdhash);
193 }
194
195 /*
196 * Free hash table.
197 */
198 void
199 fdesc_done()
200 {
201 hashdone(fdhashtbl, M_CACHE);
202 }
203
204 /*
205 * Return a locked vnode of the correct type.
206 */
207 int
208 fdesc_allocvp(ftype, ix, mp, vpp)
209 fdntype ftype;
210 int ix;
211 struct mount *mp;
212 struct vnode **vpp;
213 {
214 struct fdhashhead *fc;
215 struct fdescnode *fd;
216 int error = 0;
217
218 fc = FD_NHASH(ix);
219 loop:
220 for (fd = fc->lh_first; fd != 0; fd = fd->fd_hash.le_next) {
221 if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) {
222 if (vget(fd->fd_vnode, LK_EXCLUSIVE))
223 goto loop;
224 *vpp = fd->fd_vnode;
225 return (error);
226 }
227 }
228
229 /*
230 * otherwise lock the array while we call getnewvnode
231 * since that can block.
232 */
233 if (fdcache_lock & FDL_LOCKED) {
234 fdcache_lock |= FDL_WANT;
235 (void) tsleep(&fdcache_lock, PINOD, "fdcache", 0);
236 goto loop;
237 }
238 fdcache_lock |= FDL_LOCKED;
239
240 error = getnewvnode(VT_FDESC, mp, fdesc_vnodeop_p, vpp);
241 if (error)
242 goto out;
243 MALLOC(fd, void *, sizeof(struct fdescnode), M_TEMP, M_WAITOK);
244 (*vpp)->v_data = fd;
245 fd->fd_vnode = *vpp;
246 fd->fd_type = ftype;
247 fd->fd_fd = -1;
248 fd->fd_link = 0;
249 fd->fd_ix = ix;
250 uvm_vnp_setsize(*vpp, 0);
251 VOP_LOCK(*vpp, LK_EXCLUSIVE);
252 LIST_INSERT_HEAD(fc, fd, fd_hash);
253
254 out:;
255 fdcache_lock &= ~FDL_LOCKED;
256
257 if (fdcache_lock & FDL_WANT) {
258 fdcache_lock &= ~FDL_WANT;
259 wakeup(&fdcache_lock);
260 }
261
262 return (error);
263 }
264
265 /*
266 * vp is the current namei directory
267 * ndp is the name to locate in that directory...
268 */
269 int
270 fdesc_lookup(v)
271 void *v;
272 {
273 struct vop_lookup_args /* {
274 struct vnode * a_dvp;
275 struct vnode ** a_vpp;
276 struct componentname * a_cnp;
277 } */ *ap = v;
278 struct vnode **vpp = ap->a_vpp;
279 struct vnode *dvp = ap->a_dvp;
280 struct componentname *cnp = ap->a_cnp;
281 struct lwp *l = curlwp;
282 const char *pname = cnp->cn_nameptr;
283 struct proc *p = l->l_proc;
284 filedesc_t *fdp = p->p_fd;
285 int numfiles = fdp->fd_nfiles;
286 unsigned fd = 0;
287 int error;
288 struct vnode *fvp;
289 const char *ln;
290
291 if (cnp->cn_namelen == 1 && *pname == '.') {
292 *vpp = dvp;
293 VREF(dvp);
294 return (0);
295 }
296
297 switch (VTOFDESC(dvp)->fd_type) {
298 default:
299 case Flink:
300 case Fdesc:
301 case Fctty:
302 error = ENOTDIR;
303 goto bad;
304
305 case Froot:
306 if (cnp->cn_namelen == 2 && memcmp(pname, "fd", 2) == 0) {
307 error = fdesc_allocvp(Fdevfd, FD_DEVFD, dvp->v_mount, &fvp);
308 if (error)
309 goto bad;
310 *vpp = fvp;
311 fvp->v_type = VDIR;
312 goto good;
313 }
314
315 if (cnp->cn_namelen == 3 && memcmp(pname, "tty", 3) == 0) {
316 struct vnode *ttyvp = cttyvp(p);
317 if (ttyvp == NULL) {
318 error = ENXIO;
319 goto bad;
320 }
321 error = fdesc_allocvp(Fctty, FD_CTTY, dvp->v_mount, &fvp);
322 if (error)
323 goto bad;
324 *vpp = fvp;
325 fvp->v_type = VCHR;
326 goto good;
327 }
328
329 ln = 0;
330 switch (cnp->cn_namelen) {
331 case 5:
332 if (memcmp(pname, "stdin", 5) == 0) {
333 ln = "fd/0";
334 fd = FD_STDIN;
335 }
336 break;
337 case 6:
338 if (memcmp(pname, "stdout", 6) == 0) {
339 ln = "fd/1";
340 fd = FD_STDOUT;
341 } else
342 if (memcmp(pname, "stderr", 6) == 0) {
343 ln = "fd/2";
344 fd = FD_STDERR;
345 }
346 break;
347 }
348
349 if (ln) {
350 error = fdesc_allocvp(Flink, fd, dvp->v_mount, &fvp);
351 if (error)
352 goto bad;
353 /* XXXUNCONST */
354 VTOFDESC(fvp)->fd_link = __UNCONST(ln);
355 *vpp = fvp;
356 fvp->v_type = VLNK;
357 goto good;
358 } else {
359 error = ENOENT;
360 goto bad;
361 }
362
363 /* FALL THROUGH */
364
365 case Fdevfd:
366 if (cnp->cn_namelen == 2 && memcmp(pname, "..", 2) == 0) {
367 VOP_UNLOCK(dvp, 0);
368 error = fdesc_root(dvp->v_mount, vpp);
369 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
370 if (error)
371 goto bad;
372 return (error);
373 }
374
375 fd = 0;
376 while (*pname >= '0' && *pname <= '9') {
377 fd = 10 * fd + *pname++ - '0';
378 if (fd >= numfiles)
379 break;
380 }
381
382 if (*pname != '\0') {
383 error = ENOENT;
384 goto bad;
385 }
386
387 mutex_enter(&fdp->fd_lock);
388 if (fd >= numfiles ||fdp->fd_ofiles[fd] == NULL ||
389 fdp->fd_ofiles[fd]->ff_file == NULL) {
390 mutex_exit(&fdp->fd_lock);
391 error = EBADF;
392 goto bad;
393 }
394 mutex_exit(&fdp->fd_lock);
395
396 error = fdesc_allocvp(Fdesc, FD_DESC+fd, dvp->v_mount, &fvp);
397 if (error)
398 goto bad;
399 VTOFDESC(fvp)->fd_fd = fd;
400 *vpp = fvp;
401 goto good;
402 }
403
404 bad:
405 *vpp = NULL;
406 return (error);
407
408 good:
409 return (0);
410 }
411
412 int
413 fdesc_open(v)
414 void *v;
415 {
416 struct vop_open_args /* {
417 struct vnode *a_vp;
418 int a_mode;
419 kauth_cred_t a_cred;
420 } */ *ap = v;
421 struct vnode *vp = ap->a_vp;
422
423 switch (VTOFDESC(vp)->fd_type) {
424 case Fdesc:
425 /*
426 * XXX Kludge: set dupfd to contain the value of the
427 * the file descriptor being sought for duplication. The error
428 * return ensures that the vnode for this device will be
429 * released by vn_open. Open will detect this special error and
430 * take the actions in dupfdopen. Other callers of vn_open or
431 * VOP_OPEN will simply report the error.
432 */
433 curlwp->l_dupfd = VTOFDESC(vp)->fd_fd; /* XXX */
434 return EDUPFD;
435
436 case Fctty:
437 return cdev_open(devctty, ap->a_mode, 0, curlwp);
438 case Froot:
439 case Fdevfd:
440 case Flink:
441 break;
442 }
443
444 return (0);
445 }
446
447 static int
448 fdesc_attr(fd, vap, cred)
449 int fd;
450 struct vattr *vap;
451 kauth_cred_t cred;
452 {
453 file_t *fp;
454 struct stat stb;
455 int error;
456
457 if ((fp = fd_getfile(fd)) == NULL)
458 return (EBADF);
459
460 switch (fp->f_type) {
461 case DTYPE_VNODE:
462 error = VOP_GETATTR((struct vnode *) fp->f_data, vap, cred);
463 if (error == 0 && vap->va_type == VDIR) {
464 /*
465 * directories can cause loops in the namespace,
466 * so turn off the 'x' bits to avoid trouble.
467 */
468 vap->va_mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
469 }
470 break;
471
472 default:
473 memset(&stb, 0, sizeof(stb));
474 error = (*fp->f_ops->fo_stat)(fp, &stb);
475 if (error)
476 break;
477
478 vattr_null(vap);
479 switch(fp->f_type) {
480 case DTYPE_SOCKET:
481 vap->va_type = VSOCK;
482 break;
483 case DTYPE_PIPE:
484 vap->va_type = VFIFO;
485 break;
486 default:
487 /* use VNON perhaps? */
488 vap->va_type = VBAD;
489 break;
490 }
491 vap->va_mode = stb.st_mode;
492 vap->va_nlink = stb.st_nlink;
493 vap->va_uid = stb.st_uid;
494 vap->va_gid = stb.st_gid;
495 vap->va_fsid = stb.st_dev;
496 vap->va_fileid = stb.st_ino;
497 vap->va_size = stb.st_size;
498 vap->va_blocksize = stb.st_blksize;
499 vap->va_atime = stb.st_atimespec;
500 vap->va_mtime = stb.st_mtimespec;
501 vap->va_ctime = stb.st_ctimespec;
502 vap->va_gen = stb.st_gen;
503 vap->va_flags = stb.st_flags;
504 vap->va_rdev = stb.st_rdev;
505 vap->va_bytes = stb.st_blocks * stb.st_blksize;
506 break;
507 }
508
509 fd_putfile(fd);
510 return (error);
511 }
512
513 int
514 fdesc_getattr(v)
515 void *v;
516 {
517 struct vop_getattr_args /* {
518 struct vnode *a_vp;
519 struct vattr *a_vap;
520 kauth_cred_t a_cred;
521 struct lwp *a_l;
522 } */ *ap = v;
523 struct vnode *vp = ap->a_vp;
524 struct vattr *vap = ap->a_vap;
525 unsigned fd;
526 int error = 0;
527
528 switch (VTOFDESC(vp)->fd_type) {
529 case Froot:
530 case Fdevfd:
531 case Flink:
532 case Fctty:
533 VATTR_NULL(vap);
534 vap->va_fileid = VTOFDESC(vp)->fd_ix;
535
536 #define R_ALL (S_IRUSR|S_IRGRP|S_IROTH)
537 #define W_ALL (S_IWUSR|S_IWGRP|S_IWOTH)
538 #define X_ALL (S_IXUSR|S_IXGRP|S_IXOTH)
539
540 switch (VTOFDESC(vp)->fd_type) {
541 case Flink:
542 vap->va_mode = R_ALL|X_ALL;
543 vap->va_type = VLNK;
544 vap->va_rdev = 0;
545 vap->va_nlink = 1;
546 vap->va_size = strlen(VTOFDESC(vp)->fd_link);
547 break;
548
549 case Fctty:
550 vap->va_mode = R_ALL|W_ALL;
551 vap->va_type = VCHR;
552 vap->va_rdev = devctty;
553 vap->va_nlink = 1;
554 vap->va_size = 0;
555 break;
556
557 default:
558 vap->va_mode = R_ALL|X_ALL;
559 vap->va_type = VDIR;
560 vap->va_rdev = 0;
561 vap->va_nlink = 2;
562 vap->va_size = DEV_BSIZE;
563 break;
564 }
565 vap->va_uid = 0;
566 vap->va_gid = 0;
567 vap->va_fsid = vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0];
568 vap->va_blocksize = DEV_BSIZE;
569 vap->va_atime.tv_sec = boottime.tv_sec;
570 vap->va_atime.tv_nsec = 0;
571 vap->va_mtime = vap->va_atime;
572 vap->va_ctime = vap->va_mtime;
573 vap->va_gen = 0;
574 vap->va_flags = 0;
575 vap->va_bytes = 0;
576 break;
577
578 case Fdesc:
579 fd = VTOFDESC(vp)->fd_fd;
580 error = fdesc_attr(fd, vap, ap->a_cred);
581 break;
582
583 default:
584 panic("fdesc_getattr");
585 break;
586 }
587
588 if (error == 0)
589 vp->v_type = vap->va_type;
590
591 return (error);
592 }
593
594 int
595 fdesc_setattr(v)
596 void *v;
597 {
598 struct vop_setattr_args /* {
599 struct vnode *a_vp;
600 struct vattr *a_vap;
601 kauth_cred_t a_cred;
602 } */ *ap = v;
603 file_t *fp;
604 unsigned fd;
605
606 /*
607 * Can't mess with the root vnode
608 */
609 switch (VTOFDESC(ap->a_vp)->fd_type) {
610 case Fdesc:
611 break;
612
613 case Fctty:
614 return (0);
615
616 default:
617 return (EACCES);
618 }
619
620 fd = VTOFDESC(ap->a_vp)->fd_fd;
621 if ((fp = fd_getfile(fd)) == NULL)
622 return (EBADF);
623
624 /*
625 * XXX: Can't reasonably set the attr's on any types currently.
626 * On vnode's this will cause truncation and socket/pipes make
627 * no sense.
628 */
629 fd_putfile(fd);
630 return (0);
631 }
632
633
634 struct fdesc_target {
635 ino_t ft_fileno;
636 u_char ft_type;
637 u_char ft_namlen;
638 const char *ft_name;
639 } fdesc_targets[] = {
640 #define N(s) sizeof(s)-1, s
641 { FD_DEVFD, DT_DIR, N("fd") },
642 { FD_STDIN, DT_LNK, N("stdin") },
643 { FD_STDOUT, DT_LNK, N("stdout") },
644 { FD_STDERR, DT_LNK, N("stderr") },
645 { FD_CTTY, DT_UNKNOWN, N("tty") },
646 #undef N
647 #define UIO_MX _DIRENT_RECLEN((struct dirent *)NULL, sizeof("stderr") - 1)
648 };
649 static int nfdesc_targets = sizeof(fdesc_targets) / sizeof(fdesc_targets[0]);
650
651 int
652 fdesc_readdir(v)
653 void *v;
654 {
655 struct vop_readdir_args /* {
656 struct vnode *a_vp;
657 struct uio *a_uio;
658 kauth_cred_t a_cred;
659 int *a_eofflag;
660 off_t **a_cookies;
661 int *a_ncookies;
662 } */ *ap = v;
663 struct uio *uio = ap->a_uio;
664 struct dirent d;
665 filedesc_t *fdp;
666 off_t i;
667 int j;
668 int error;
669 off_t *cookies = NULL;
670 int ncookies;
671
672 switch (VTOFDESC(ap->a_vp)->fd_type) {
673 case Fctty:
674 return 0;
675
676 case Fdesc:
677 return ENOTDIR;
678
679 default:
680 break;
681 }
682
683 fdp = curproc->p_fd;
684
685 if (uio->uio_resid < UIO_MX)
686 return EINVAL;
687 if (uio->uio_offset < 0)
688 return EINVAL;
689
690 error = 0;
691 i = uio->uio_offset;
692 (void)memset(&d, 0, UIO_MX);
693 d.d_reclen = UIO_MX;
694 if (ap->a_ncookies)
695 ncookies = uio->uio_resid / UIO_MX;
696 else
697 ncookies = 0;
698
699 if (VTOFDESC(ap->a_vp)->fd_type == Froot) {
700 struct fdesc_target *ft;
701
702 if (i >= nfdesc_targets)
703 return 0;
704
705 if (ap->a_ncookies) {
706 ncookies = min(ncookies, (nfdesc_targets - i));
707 cookies = malloc(ncookies * sizeof(off_t),
708 M_TEMP, M_WAITOK);
709 *ap->a_cookies = cookies;
710 *ap->a_ncookies = ncookies;
711 }
712
713 for (ft = &fdesc_targets[i]; uio->uio_resid >= UIO_MX &&
714 i < nfdesc_targets; ft++, i++) {
715 switch (ft->ft_fileno) {
716 case FD_CTTY:
717 if (cttyvp(curproc) == NULL)
718 continue;
719 break;
720
721 case FD_STDIN:
722 case FD_STDOUT:
723 case FD_STDERR:
724 if (fdp == NULL)
725 continue;
726 if ((ft->ft_fileno - FD_STDIN) >=
727 fdp->fd_nfiles)
728 continue;
729 membar_consumer();
730 if (fdp->fd_ofiles[ft->ft_fileno - FD_STDIN]
731 == NULL || fdp->fd_ofiles[ft->ft_fileno -
732 FD_STDIN]->ff_file == NULL)
733 continue;
734 break;
735 }
736
737 d.d_fileno = ft->ft_fileno;
738 d.d_namlen = ft->ft_namlen;
739 (void)memcpy(d.d_name, ft->ft_name, ft->ft_namlen + 1);
740 d.d_type = ft->ft_type;
741
742 if ((error = uiomove(&d, UIO_MX, uio)) != 0)
743 break;
744 if (cookies)
745 *cookies++ = i + 1;
746 }
747 } else {
748 int nfdp = fdp ? fdp->fd_nfiles : 0;
749 membar_consumer();
750 if (ap->a_ncookies) {
751 ncookies = min(ncookies, nfdp + 2);
752 cookies = malloc(ncookies * sizeof(off_t),
753 M_TEMP, M_WAITOK);
754 *ap->a_cookies = cookies;
755 *ap->a_ncookies = ncookies;
756 }
757 for (; i - 2 < nfdp && uio->uio_resid >= UIO_MX; i++) {
758 switch (i) {
759 case 0:
760 case 1:
761 d.d_fileno = FD_ROOT; /* XXX */
762 d.d_namlen = i + 1;
763 (void)memcpy(d.d_name, "..", d.d_namlen);
764 d.d_name[i + 1] = '\0';
765 d.d_type = DT_DIR;
766 break;
767
768 default:
769 KASSERT(fdp != NULL);
770 j = (int)i - 2;
771 if (fdp == NULL || fdp->fd_ofiles[j] == NULL ||
772 fdp->fd_ofiles[j]->ff_file == NULL)
773 continue;
774 d.d_fileno = j + FD_STDIN;
775 d.d_namlen = sprintf(d.d_name, "%d", j);
776 d.d_type = DT_UNKNOWN;
777 break;
778 }
779
780 if ((error = uiomove(&d, UIO_MX, uio)) != 0)
781 break;
782 if (cookies)
783 *cookies++ = i + 1;
784 }
785 }
786
787 if (ap->a_ncookies && error) {
788 free(*ap->a_cookies, M_TEMP);
789 *ap->a_ncookies = 0;
790 *ap->a_cookies = NULL;
791 }
792
793 uio->uio_offset = i;
794 return error;
795 }
796
797 int
798 fdesc_readlink(v)
799 void *v;
800 {
801 struct vop_readlink_args /* {
802 struct vnode *a_vp;
803 struct uio *a_uio;
804 kauth_cred_t a_cred;
805 } */ *ap = v;
806 struct vnode *vp = ap->a_vp;
807 int error;
808
809 if (vp->v_type != VLNK)
810 return (EPERM);
811
812 if (VTOFDESC(vp)->fd_type == Flink) {
813 char *ln = VTOFDESC(vp)->fd_link;
814 error = uiomove(ln, strlen(ln), ap->a_uio);
815 } else {
816 error = EOPNOTSUPP;
817 }
818
819 return (error);
820 }
821
822 int
823 fdesc_read(v)
824 void *v;
825 {
826 struct vop_read_args /* {
827 struct vnode *a_vp;
828 struct uio *a_uio;
829 int a_ioflag;
830 kauth_cred_t a_cred;
831 } */ *ap = v;
832 int error = EOPNOTSUPP;
833 struct vnode *vp = ap->a_vp;
834
835 switch (VTOFDESC(vp)->fd_type) {
836 case Fctty:
837 VOP_UNLOCK(vp, 0);
838 error = cdev_read(devctty, ap->a_uio, ap->a_ioflag);
839 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
840 break;
841
842 default:
843 error = EOPNOTSUPP;
844 break;
845 }
846
847 return (error);
848 }
849
850 int
851 fdesc_write(v)
852 void *v;
853 {
854 struct vop_write_args /* {
855 struct vnode *a_vp;
856 struct uio *a_uio;
857 int a_ioflag;
858 kauth_cred_t a_cred;
859 } */ *ap = v;
860 int error = EOPNOTSUPP;
861 struct vnode *vp = ap->a_vp;
862
863 switch (VTOFDESC(vp)->fd_type) {
864 case Fctty:
865 VOP_UNLOCK(vp, 0);
866 error = cdev_write(devctty, ap->a_uio, ap->a_ioflag);
867 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
868 break;
869
870 default:
871 error = EOPNOTSUPP;
872 break;
873 }
874
875 return (error);
876 }
877
878 int
879 fdesc_ioctl(v)
880 void *v;
881 {
882 struct vop_ioctl_args /* {
883 struct vnode *a_vp;
884 u_long a_command;
885 void *a_data;
886 int a_fflag;
887 kauth_cred_t a_cred;
888 } */ *ap = v;
889 int error = EOPNOTSUPP;
890
891 switch (VTOFDESC(ap->a_vp)->fd_type) {
892 case Fctty:
893 error = cdev_ioctl(devctty, ap->a_command, ap->a_data,
894 ap->a_fflag, curlwp);
895 break;
896
897 default:
898 error = EOPNOTSUPP;
899 break;
900 }
901
902 return (error);
903 }
904
905 int
906 fdesc_poll(v)
907 void *v;
908 {
909 struct vop_poll_args /* {
910 struct vnode *a_vp;
911 int a_events;
912 } */ *ap = v;
913 int revents;
914
915 switch (VTOFDESC(ap->a_vp)->fd_type) {
916 case Fctty:
917 revents = cdev_poll(devctty, ap->a_events, curlwp);
918 break;
919
920 default:
921 revents = genfs_poll(v);
922 break;
923 }
924
925 return (revents);
926 }
927
928 int
929 fdesc_kqfilter(v)
930 void *v;
931 {
932 struct vop_kqfilter_args /* {
933 struct vnode *a_vp;
934 struct knote *a_kn;
935 } */ *ap = v;
936 int error, fd;
937 file_t *fp;
938
939 switch (VTOFDESC(ap->a_vp)->fd_type) {
940 case Fctty:
941 error = cdev_kqfilter(devctty, ap->a_kn);
942 break;
943
944 case Fdesc:
945 /* just invoke kqfilter for the underlying descriptor */
946 fd = VTOFDESC(ap->a_vp)->fd_fd;
947 if ((fp = fd_getfile(fd)) == NULL)
948 return (1);
949 error = (*fp->f_ops->fo_kqfilter)(fp, ap->a_kn);
950 fd_putfile(fd);
951 break;
952
953 default:
954 return (genfs_kqfilter(v));
955 }
956
957 return (error);
958 }
959
960 int
961 fdesc_inactive(v)
962 void *v;
963 {
964 struct vop_inactive_args /* {
965 struct vnode *a_vp;
966 } */ *ap = v;
967 struct vnode *vp = ap->a_vp;
968
969 /*
970 * Clear out the v_type field to avoid
971 * nasty things happening in vgone().
972 */
973 VOP_UNLOCK(vp, 0);
974 vp->v_type = VNON;
975 return (0);
976 }
977
978 int
979 fdesc_reclaim(v)
980 void *v;
981 {
982 struct vop_reclaim_args /* {
983 struct vnode *a_vp;
984 } */ *ap = v;
985 struct vnode *vp = ap->a_vp;
986 struct fdescnode *fd = VTOFDESC(vp);
987
988 LIST_REMOVE(fd, fd_hash);
989 FREE(vp->v_data, M_TEMP);
990 vp->v_data = 0;
991
992 return (0);
993 }
994
995 /*
996 * Return POSIX pathconf information applicable to special devices.
997 */
998 int
999 fdesc_pathconf(v)
1000 void *v;
1001 {
1002 struct vop_pathconf_args /* {
1003 struct vnode *a_vp;
1004 int a_name;
1005 register_t *a_retval;
1006 } */ *ap = v;
1007
1008 switch (ap->a_name) {
1009 case _PC_LINK_MAX:
1010 *ap->a_retval = LINK_MAX;
1011 return (0);
1012 case _PC_MAX_CANON:
1013 *ap->a_retval = MAX_CANON;
1014 return (0);
1015 case _PC_MAX_INPUT:
1016 *ap->a_retval = MAX_INPUT;
1017 return (0);
1018 case _PC_PIPE_BUF:
1019 *ap->a_retval = PIPE_BUF;
1020 return (0);
1021 case _PC_CHOWN_RESTRICTED:
1022 *ap->a_retval = 1;
1023 return (0);
1024 case _PC_VDISABLE:
1025 *ap->a_retval = _POSIX_VDISABLE;
1026 return (0);
1027 case _PC_SYNC_IO:
1028 *ap->a_retval = 1;
1029 return (0);
1030 default:
1031 return (EINVAL);
1032 }
1033 /* NOTREACHED */
1034 }
1035
1036 /*
1037 * Print out the contents of a /dev/fd vnode.
1038 */
1039 /* ARGSUSED */
1040 int
1041 fdesc_print(void *v)
1042 {
1043 printf("tag VT_NON, fdesc vnode\n");
1044 return (0);
1045 }
1046
1047 int
1048 fdesc_link(v)
1049 void *v;
1050 {
1051 struct vop_link_args /* {
1052 struct vnode *a_dvp;
1053 struct vnode *a_vp;
1054 struct componentname *a_cnp;
1055 } */ *ap = v;
1056
1057 VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
1058 vput(ap->a_dvp);
1059 return (EROFS);
1060 }
1061
1062 int
1063 fdesc_symlink(v)
1064 void *v;
1065 {
1066 struct vop_symlink_args /* {
1067 struct vnode *a_dvp;
1068 struct vnode **a_vpp;
1069 struct componentname *a_cnp;
1070 struct vattr *a_vap;
1071 char *a_target;
1072 } */ *ap = v;
1073
1074 VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
1075 vput(ap->a_dvp);
1076 return (EROFS);
1077 }
1078