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