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