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