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