1 /* $NetBSD: fdesc_vnops.c,v 1.140 2022/03/27 17:10:55 christos 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.140 2022/03/27 17:10:55 christos 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/resourcevar.h> 51 #include <sys/socketvar.h> 52 #include <sys/filedesc.h> 53 #include <sys/vnode.h> 54 #include <sys/malloc.h> 55 #include <sys/conf.h> 56 #include <sys/file.h> 57 #include <sys/stat.h> 58 #include <sys/mount.h> 59 #include <sys/namei.h> 60 #include <sys/buf.h> 61 #include <sys/dirent.h> 62 #include <sys/tty.h> 63 #include <sys/kauth.h> 64 #include <sys/atomic.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 dev_t devctty; 72 73 #if (FD_STDIN != FD_STDOUT-1) || (FD_STDOUT != FD_STDERR-1) 74 FD_STDIN, FD_STDOUT, FD_STDERR must be a sequence n, n+1, n+2 75 #endif 76 77 int fdesc_lookup(void *); 78 int fdesc_open(void *); 79 int fdesc_getattr(void *); 80 int fdesc_setattr(void *); 81 int fdesc_read(void *); 82 int fdesc_write(void *); 83 int fdesc_ioctl(void *); 84 int fdesc_poll(void *); 85 int fdesc_kqfilter(void *); 86 int fdesc_readdir(void *); 87 int fdesc_readlink(void *); 88 int fdesc_inactive(void *); 89 int fdesc_reclaim(void *); 90 int fdesc_print(void *); 91 int fdesc_pathconf(void *); 92 93 static int fdesc_attr(int, struct vattr *, kauth_cred_t); 94 95 int (**fdesc_vnodeop_p)(void *); 96 const struct vnodeopv_entry_desc fdesc_vnodeop_entries[] = { 97 { &vop_default_desc, vn_default_error }, 98 { &vop_parsepath_desc, genfs_parsepath }, /* parsepath */ 99 { &vop_lookup_desc, fdesc_lookup }, /* lookup */ 100 { &vop_create_desc, genfs_eopnotsupp }, /* create */ 101 { &vop_mknod_desc, genfs_eopnotsupp }, /* mknod */ 102 { &vop_open_desc, fdesc_open }, /* open */ 103 { &vop_close_desc, genfs_nullop }, /* close */ 104 { &vop_access_desc, genfs_nullop }, /* access */ 105 { &vop_accessx_desc, genfs_accessx }, /* accessx */ 106 { &vop_getattr_desc, fdesc_getattr }, /* getattr */ 107 { &vop_setattr_desc, fdesc_setattr }, /* setattr */ 108 { &vop_read_desc, fdesc_read }, /* read */ 109 { &vop_write_desc, fdesc_write }, /* write */ 110 { &vop_fallocate_desc, genfs_eopnotsupp }, /* fallocate */ 111 { &vop_fdiscard_desc, genfs_eopnotsupp }, /* fdiscard */ 112 { &vop_ioctl_desc, fdesc_ioctl }, /* ioctl */ 113 { &vop_fcntl_desc, genfs_fcntl }, /* fcntl */ 114 { &vop_poll_desc, fdesc_poll }, /* poll */ 115 { &vop_kqfilter_desc, fdesc_kqfilter }, /* kqfilter */ 116 { &vop_revoke_desc, genfs_revoke }, /* revoke */ 117 { &vop_mmap_desc, genfs_eopnotsupp }, /* mmap */ 118 { &vop_fsync_desc, genfs_nullop }, /* fsync */ 119 { &vop_seek_desc, genfs_seek }, /* seek */ 120 { &vop_remove_desc, genfs_eopnotsupp }, /* remove */ 121 { &vop_link_desc, genfs_erofs_link }, /* link */ 122 { &vop_rename_desc, genfs_eopnotsupp }, /* rename */ 123 { &vop_mkdir_desc, genfs_eopnotsupp }, /* mkdir */ 124 { &vop_rmdir_desc, genfs_eopnotsupp }, /* rmdir */ 125 { &vop_symlink_desc, genfs_erofs_symlink }, /* symlink */ 126 { &vop_readdir_desc, fdesc_readdir }, /* readdir */ 127 { &vop_readlink_desc, fdesc_readlink }, /* readlink */ 128 { &vop_abortop_desc, genfs_abortop }, /* abortop */ 129 { &vop_inactive_desc, fdesc_inactive }, /* inactive */ 130 { &vop_reclaim_desc, fdesc_reclaim }, /* reclaim */ 131 { &vop_lock_desc, genfs_lock }, /* lock */ 132 { &vop_unlock_desc, genfs_unlock }, /* unlock */ 133 { &vop_bmap_desc, genfs_eopnotsupp }, /* bmap */ 134 { &vop_strategy_desc, genfs_badop }, /* strategy */ 135 { &vop_print_desc, fdesc_print }, /* print */ 136 { &vop_islocked_desc, genfs_islocked }, /* islocked */ 137 { &vop_pathconf_desc, fdesc_pathconf }, /* pathconf */ 138 { &vop_advlock_desc, genfs_einval }, /* advlock */ 139 { &vop_bwrite_desc, genfs_eopnotsupp }, /* bwrite */ 140 { &vop_putpages_desc, genfs_null_putpages }, /* putpages */ 141 { NULL, NULL } 142 }; 143 144 const struct vnodeopv_desc fdesc_vnodeop_opv_desc = 145 { &fdesc_vnodeop_p, fdesc_vnodeop_entries }; 146 147 /* 148 * Initialise cache headers 149 */ 150 void 151 fdesc_init(void) 152 { 153 int cttymajor; 154 155 /* locate the major number */ 156 cttymajor = devsw_name2chr("ctty", NULL, 0); 157 devctty = makedev(cttymajor, 0); 158 } 159 160 void 161 fdesc_done(void) 162 { 163 } 164 165 /* 166 * vp is the current namei directory 167 * ndp is the name to locate in that directory... 168 */ 169 int 170 fdesc_lookup(void *v) 171 { 172 struct vop_lookup_v2_args /* { 173 struct vnode * a_dvp; 174 struct vnode ** a_vpp; 175 struct componentname * a_cnp; 176 } */ *ap = v; 177 struct vnode **vpp = ap->a_vpp; 178 struct vnode *dvp = ap->a_dvp; 179 struct componentname *cnp = ap->a_cnp; 180 struct lwp *l = curlwp; 181 const char *pname = cnp->cn_nameptr; 182 struct proc *p = l->l_proc; 183 unsigned fd = 0; 184 int error, ix = -1; 185 fdtab_t *dt; 186 187 dt = atomic_load_consume(&curlwp->l_fd->fd_dt); 188 189 if (cnp->cn_namelen == 1 && *pname == '.') { 190 *vpp = dvp; 191 vref(dvp); 192 return (0); 193 } 194 195 switch (VTOFDESC(dvp)->fd_type) { 196 default: 197 case Flink: 198 case Fdesc: 199 case Fctty: 200 error = ENOTDIR; 201 goto bad; 202 203 case Froot: 204 if (cnp->cn_namelen == 2 && memcmp(pname, "fd", 2) == 0) { 205 ix = FD_DEVFD; 206 goto good; 207 } 208 209 if (cnp->cn_namelen == 3 && memcmp(pname, "tty", 3) == 0) { 210 struct vnode *ttyvp = cttyvp(p); 211 if (ttyvp == NULL) { 212 error = ENXIO; 213 goto bad; 214 } 215 ix = FD_CTTY; 216 goto good; 217 } 218 219 switch (cnp->cn_namelen) { 220 case 5: 221 if (memcmp(pname, "stdin", 5) == 0) { 222 ix = FD_STDIN; 223 goto good; 224 } 225 break; 226 case 6: 227 if (memcmp(pname, "stdout", 6) == 0) { 228 ix = FD_STDOUT; 229 goto good; 230 } else if (memcmp(pname, "stderr", 6) == 0) { 231 ix = FD_STDERR; 232 goto good; 233 } 234 break; 235 } 236 237 error = ENOENT; 238 goto bad; 239 240 case Fdevfd: 241 if (cnp->cn_namelen == 2 && memcmp(pname, "..", 2) == 0) { 242 ix = FD_ROOT; 243 goto good; 244 } 245 246 fd = 0; 247 while (*pname >= '0' && *pname <= '9') { 248 fd = 10 * fd + *pname++ - '0'; 249 if (fd >= dt->dt_nfiles) 250 break; 251 } 252 253 if (*pname != '\0') { 254 error = ENOENT; 255 goto bad; 256 } 257 258 if (fd >= dt->dt_nfiles || dt->dt_ff[fd] == NULL || 259 dt->dt_ff[fd]->ff_file == NULL) { 260 error = EBADF; 261 goto bad; 262 } 263 264 ix = FD_DESC + fd; 265 goto good; 266 } 267 268 bad: 269 *vpp = NULL; 270 return error; 271 272 good: 273 KASSERT(ix != -1); 274 error = vcache_get(dvp->v_mount, &ix, sizeof(ix), vpp); 275 if (error) 276 return error; 277 278 /* 279 * Prevent returning VNON nodes. 280 * Operation fdesc_inactive() will reset the type to VNON. 281 */ 282 if (ix == FD_CTTY) 283 (*vpp)->v_type = VCHR; 284 else if (ix >= FD_DESC) 285 (*vpp)->v_type = VREG; 286 KASSERT((*vpp)->v_type != VNON); 287 288 return 0; 289 } 290 291 int 292 fdesc_open(void *v) 293 { 294 struct vop_open_args /* { 295 struct vnode *a_vp; 296 int a_mode; 297 kauth_cred_t a_cred; 298 } */ *ap = v; 299 struct vnode *vp = ap->a_vp; 300 301 switch (VTOFDESC(vp)->fd_type) { 302 case Fdesc: 303 /* 304 * XXX Kludge: set dupfd to contain the value of the 305 * the file descriptor being sought for duplication. 306 * The error return ensures that the vnode for this 307 * device will be released by vn_open. vn_open will 308 * then detect this special error and take the actions 309 * in fd_dupopen. Other callers of vn_open or VOP_OPEN 310 * not prepared to deal with this situation will 311 * report a real error. 312 */ 313 curlwp->l_dupfd = VTOFDESC(vp)->fd_fd; /* XXX */ 314 return EDUPFD; 315 316 case Fctty: 317 return cdev_open(devctty, ap->a_mode, 0, curlwp); 318 case Froot: 319 case Fdevfd: 320 case Flink: 321 break; 322 } 323 324 return (0); 325 } 326 327 static int 328 fdesc_attr(int fd, struct vattr *vap, kauth_cred_t cred) 329 { 330 file_t *fp; 331 struct stat stb; 332 int error; 333 334 if ((fp = fd_getfile(fd)) == NULL) 335 return (EBADF); 336 337 switch (fp->f_type) { 338 case DTYPE_VNODE: 339 vn_lock(fp->f_vnode, LK_SHARED | LK_RETRY); 340 error = VOP_GETATTR(fp->f_vnode, vap, cred); 341 VOP_UNLOCK(fp->f_vnode); 342 if (error == 0 && vap->va_type == VDIR) { 343 /* 344 * directories can cause loops in the namespace, 345 * so turn off the 'x' bits to avoid trouble. 346 */ 347 vap->va_mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH); 348 } 349 break; 350 351 default: 352 memset(&stb, 0, sizeof(stb)); 353 error = (*fp->f_ops->fo_stat)(fp, &stb); 354 if (error) 355 break; 356 357 vattr_null(vap); 358 switch(fp->f_type) { 359 case DTYPE_SOCKET: 360 vap->va_type = VSOCK; 361 break; 362 case DTYPE_PIPE: 363 vap->va_type = VFIFO; 364 break; 365 default: 366 /* use VNON perhaps? */ 367 vap->va_type = VBAD; 368 break; 369 } 370 vap->va_mode = stb.st_mode; 371 vap->va_nlink = stb.st_nlink; 372 vap->va_uid = stb.st_uid; 373 vap->va_gid = stb.st_gid; 374 vap->va_fsid = stb.st_dev; 375 vap->va_fileid = stb.st_ino; 376 vap->va_size = stb.st_size; 377 vap->va_blocksize = stb.st_blksize; 378 vap->va_atime = stb.st_atimespec; 379 vap->va_mtime = stb.st_mtimespec; 380 vap->va_ctime = stb.st_ctimespec; 381 vap->va_gen = stb.st_gen; 382 vap->va_flags = stb.st_flags; 383 vap->va_rdev = stb.st_rdev; 384 vap->va_bytes = stb.st_blocks * stb.st_blksize; 385 break; 386 } 387 388 fd_putfile(fd); 389 return (error); 390 } 391 392 int 393 fdesc_getattr(void *v) 394 { 395 struct vop_getattr_args /* { 396 struct vnode *a_vp; 397 struct vattr *a_vap; 398 kauth_cred_t a_cred; 399 struct lwp *a_l; 400 } */ *ap = v; 401 struct vnode *vp = ap->a_vp; 402 struct vattr *vap = ap->a_vap; 403 unsigned fd; 404 int error = 0; 405 struct timeval tv; 406 407 switch (VTOFDESC(vp)->fd_type) { 408 case Froot: 409 case Fdevfd: 410 case Flink: 411 case Fctty: 412 vattr_null(vap); 413 vap->va_fileid = VTOFDESC(vp)->fd_ix; 414 415 #define R_ALL (S_IRUSR|S_IRGRP|S_IROTH) 416 #define W_ALL (S_IWUSR|S_IWGRP|S_IWOTH) 417 #define X_ALL (S_IXUSR|S_IXGRP|S_IXOTH) 418 419 switch (VTOFDESC(vp)->fd_type) { 420 case Flink: 421 vap->va_mode = R_ALL|X_ALL; 422 vap->va_type = VLNK; 423 vap->va_rdev = 0; 424 vap->va_nlink = 1; 425 vap->va_size = strlen(VTOFDESC(vp)->fd_link); 426 break; 427 428 case Fctty: 429 vap->va_mode = R_ALL|W_ALL; 430 vap->va_type = VCHR; 431 vap->va_rdev = devctty; 432 vap->va_nlink = 1; 433 vap->va_size = 0; 434 break; 435 436 default: 437 vap->va_mode = R_ALL|X_ALL; 438 vap->va_type = VDIR; 439 vap->va_rdev = 0; 440 vap->va_nlink = 2; 441 vap->va_size = DEV_BSIZE; 442 break; 443 } 444 vap->va_uid = 0; 445 vap->va_gid = 0; 446 vap->va_fsid = vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0]; 447 vap->va_blocksize = DEV_BSIZE; 448 getmicroboottime(&tv); 449 vap->va_atime.tv_sec = tv.tv_sec; 450 vap->va_atime.tv_nsec = 0; 451 vap->va_mtime = vap->va_atime; 452 vap->va_ctime = vap->va_mtime; 453 vap->va_gen = 0; 454 vap->va_flags = 0; 455 vap->va_bytes = 0; 456 break; 457 458 case Fdesc: 459 fd = VTOFDESC(vp)->fd_fd; 460 error = fdesc_attr(fd, vap, ap->a_cred); 461 break; 462 463 default: 464 panic("fdesc_getattr"); 465 break; 466 } 467 468 if (error == 0) 469 vp->v_type = vap->va_type; 470 471 return (error); 472 } 473 474 int 475 fdesc_setattr(void *v) 476 { 477 struct vop_setattr_args /* { 478 struct vnode *a_vp; 479 struct vattr *a_vap; 480 kauth_cred_t a_cred; 481 } */ *ap = v; 482 file_t *fp; 483 unsigned fd; 484 485 /* 486 * Can't mess with the root vnode 487 */ 488 switch (VTOFDESC(ap->a_vp)->fd_type) { 489 case Fdesc: 490 break; 491 492 case Fctty: 493 return (0); 494 495 default: 496 return (EACCES); 497 } 498 499 fd = VTOFDESC(ap->a_vp)->fd_fd; 500 if ((fp = fd_getfile(fd)) == NULL) 501 return (EBADF); 502 503 /* 504 * XXX: Can't reasonably set the attr's on any types currently. 505 * On vnode's this will cause truncation and socket/pipes make 506 * no sense. 507 */ 508 fd_putfile(fd); 509 return (0); 510 } 511 512 513 struct fdesc_target { 514 ino_t ft_fileno; 515 u_char ft_type; 516 u_char ft_namlen; 517 const char *ft_name; 518 } fdesc_targets[] = { 519 #define N(s) sizeof(s)-1, s 520 { FD_DEVFD, DT_DIR, N("fd") }, 521 { FD_STDIN, DT_LNK, N("stdin") }, 522 { FD_STDOUT, DT_LNK, N("stdout") }, 523 { FD_STDERR, DT_LNK, N("stderr") }, 524 { FD_CTTY, DT_UNKNOWN, N("tty") }, 525 #undef N 526 #define UIO_MX _DIRENT_RECLEN((struct dirent *)NULL, sizeof("stderr") - 1) 527 }; 528 static int nfdesc_targets = sizeof(fdesc_targets) / sizeof(fdesc_targets[0]); 529 530 int 531 fdesc_readdir(void *v) 532 { 533 struct vop_readdir_args /* { 534 struct vnode *a_vp; 535 struct uio *a_uio; 536 kauth_cred_t a_cred; 537 int *a_eofflag; 538 off_t **a_cookies; 539 int *a_ncookies; 540 } */ *ap = v; 541 struct uio *uio = ap->a_uio; 542 struct dirent d; 543 off_t i; 544 int j; 545 int error; 546 off_t *cookies = NULL; 547 int ncookies; 548 fdtab_t *dt; 549 550 switch (VTOFDESC(ap->a_vp)->fd_type) { 551 case Fctty: 552 return 0; 553 554 case Fdesc: 555 return ENOTDIR; 556 557 default: 558 break; 559 } 560 561 dt = atomic_load_consume(&curlwp->l_fd->fd_dt); 562 563 if (uio->uio_resid < UIO_MX) 564 return EINVAL; 565 if (uio->uio_offset < 0) 566 return EINVAL; 567 568 error = 0; 569 i = uio->uio_offset; 570 (void)memset(&d, 0, UIO_MX); 571 d.d_reclen = UIO_MX; 572 if (ap->a_ncookies) 573 ncookies = uio->uio_resid / UIO_MX; 574 else 575 ncookies = 0; 576 577 if (VTOFDESC(ap->a_vp)->fd_type == Froot) { 578 struct fdesc_target *ft; 579 580 if (i >= nfdesc_targets) 581 return 0; 582 583 if (ap->a_ncookies) { 584 ncookies = uimin(ncookies, (nfdesc_targets - i)); 585 cookies = malloc(ncookies * sizeof(off_t), 586 M_TEMP, M_WAITOK); 587 *ap->a_cookies = cookies; 588 *ap->a_ncookies = ncookies; 589 } 590 591 for (ft = &fdesc_targets[i]; uio->uio_resid >= UIO_MX && 592 i < nfdesc_targets; ft++, i++) { 593 switch (ft->ft_fileno) { 594 case FD_CTTY: 595 if (cttyvp(curproc) == NULL) 596 continue; 597 break; 598 599 case FD_STDIN: 600 case FD_STDOUT: 601 case FD_STDERR: 602 if ((ft->ft_fileno - FD_STDIN) >= 603 dt->dt_nfiles) 604 continue; 605 if (dt->dt_ff[ft->ft_fileno - FD_STDIN] 606 == NULL || dt->dt_ff[ft->ft_fileno - 607 FD_STDIN]->ff_file == NULL) 608 continue; 609 break; 610 } 611 612 d.d_fileno = ft->ft_fileno; 613 d.d_namlen = ft->ft_namlen; 614 (void)memcpy(d.d_name, ft->ft_name, ft->ft_namlen + 1); 615 d.d_type = ft->ft_type; 616 617 if ((error = uiomove(&d, UIO_MX, uio)) != 0) 618 break; 619 if (cookies) 620 *cookies++ = i + 1; 621 } 622 } else { 623 if (ap->a_ncookies) { 624 ncookies = uimin(ncookies, dt->dt_nfiles + 2); 625 cookies = malloc(ncookies * sizeof(off_t), 626 M_TEMP, M_WAITOK); 627 *ap->a_cookies = cookies; 628 *ap->a_ncookies = ncookies; 629 } 630 for (; i - 2 < dt->dt_nfiles && uio->uio_resid >= UIO_MX; i++) { 631 switch (i) { 632 case 0: 633 case 1: 634 d.d_fileno = FD_ROOT; /* XXX */ 635 d.d_namlen = i + 1; 636 (void)memcpy(d.d_name, "..", d.d_namlen); 637 d.d_name[i + 1] = '\0'; 638 d.d_type = DT_DIR; 639 break; 640 641 default: 642 j = (int)i - 2; 643 if (dt->dt_ff[j] == NULL || 644 dt->dt_ff[j]->ff_file == NULL) 645 continue; 646 d.d_fileno = j + FD_STDIN; 647 d.d_namlen = snprintf(d.d_name, 648 sizeof(d.d_name), "%d", j); 649 d.d_type = DT_UNKNOWN; 650 break; 651 } 652 653 if ((error = uiomove(&d, UIO_MX, uio)) != 0) 654 break; 655 if (cookies) 656 *cookies++ = i + 1; 657 } 658 } 659 660 if (ap->a_ncookies && error) { 661 free(*ap->a_cookies, M_TEMP); 662 *ap->a_ncookies = 0; 663 *ap->a_cookies = NULL; 664 } 665 666 uio->uio_offset = i; 667 return error; 668 } 669 670 int 671 fdesc_readlink(void *v) 672 { 673 struct vop_readlink_args /* { 674 struct vnode *a_vp; 675 struct uio *a_uio; 676 kauth_cred_t a_cred; 677 } */ *ap = v; 678 struct vnode *vp = ap->a_vp; 679 int error; 680 681 if (vp->v_type != VLNK) 682 return (EPERM); 683 684 if (VTOFDESC(vp)->fd_type == Flink) { 685 const char *ln = VTOFDESC(vp)->fd_link; 686 error = uiomove(__UNCONST(ln), strlen(ln), ap->a_uio); 687 } else { 688 error = EOPNOTSUPP; 689 } 690 691 return (error); 692 } 693 694 int 695 fdesc_read(void *v) 696 { 697 struct vop_read_args /* { 698 struct vnode *a_vp; 699 struct uio *a_uio; 700 int a_ioflag; 701 kauth_cred_t a_cred; 702 } */ *ap = v; 703 int error = EOPNOTSUPP; 704 struct vnode *vp = ap->a_vp; 705 706 switch (VTOFDESC(vp)->fd_type) { 707 case Fctty: 708 VOP_UNLOCK(vp); 709 error = cdev_read(devctty, ap->a_uio, ap->a_ioflag); 710 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 711 break; 712 713 default: 714 error = EOPNOTSUPP; 715 break; 716 } 717 718 return (error); 719 } 720 721 int 722 fdesc_write(void *v) 723 { 724 struct vop_write_args /* { 725 struct vnode *a_vp; 726 struct uio *a_uio; 727 int a_ioflag; 728 kauth_cred_t a_cred; 729 } */ *ap = v; 730 int error = EOPNOTSUPP; 731 struct vnode *vp = ap->a_vp; 732 733 switch (VTOFDESC(vp)->fd_type) { 734 case Fctty: 735 VOP_UNLOCK(vp); 736 error = cdev_write(devctty, ap->a_uio, ap->a_ioflag); 737 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 738 break; 739 740 default: 741 error = EOPNOTSUPP; 742 break; 743 } 744 745 return (error); 746 } 747 748 int 749 fdesc_ioctl(void *v) 750 { 751 struct vop_ioctl_args /* { 752 struct vnode *a_vp; 753 u_long a_command; 754 void *a_data; 755 int a_fflag; 756 kauth_cred_t a_cred; 757 } */ *ap = v; 758 int error = EOPNOTSUPP; 759 760 switch (VTOFDESC(ap->a_vp)->fd_type) { 761 case Fctty: 762 error = cdev_ioctl(devctty, ap->a_command, ap->a_data, 763 ap->a_fflag, curlwp); 764 break; 765 766 default: 767 error = EOPNOTSUPP; 768 break; 769 } 770 771 return (error); 772 } 773 774 int 775 fdesc_poll(void *v) 776 { 777 struct vop_poll_args /* { 778 struct vnode *a_vp; 779 int a_events; 780 } */ *ap = v; 781 int revents; 782 783 switch (VTOFDESC(ap->a_vp)->fd_type) { 784 case Fctty: 785 revents = cdev_poll(devctty, ap->a_events, curlwp); 786 break; 787 788 default: 789 revents = genfs_poll(v); 790 break; 791 } 792 793 return (revents); 794 } 795 796 int 797 fdesc_kqfilter(void *v) 798 { 799 struct vop_kqfilter_args /* { 800 struct vnode *a_vp; 801 struct knote *a_kn; 802 } */ *ap = v; 803 int error, fd; 804 file_t *fp; 805 806 switch (VTOFDESC(ap->a_vp)->fd_type) { 807 case Fctty: 808 error = cdev_kqfilter(devctty, ap->a_kn); 809 break; 810 811 case Fdesc: 812 /* just invoke kqfilter for the underlying descriptor */ 813 fd = VTOFDESC(ap->a_vp)->fd_fd; 814 if ((fp = fd_getfile(fd)) == NULL) 815 return (1); 816 error = (*fp->f_ops->fo_kqfilter)(fp, ap->a_kn); 817 fd_putfile(fd); 818 break; 819 820 default: 821 return (genfs_kqfilter(v)); 822 } 823 824 return (error); 825 } 826 827 int 828 fdesc_inactive(void *v) 829 { 830 struct vop_inactive_v2_args /* { 831 struct vnode *a_vp; 832 } */ *ap = v; 833 struct vnode *vp = ap->a_vp; 834 struct fdescnode *fd = VTOFDESC(vp); 835 836 /* 837 * Clear out the v_type field to avoid 838 * nasty things happening on reclaim. 839 */ 840 if (fd->fd_type == Fctty || fd->fd_type == Fdesc) 841 vp->v_type = VNON; 842 843 return (0); 844 } 845 846 int 847 fdesc_reclaim(void *v) 848 { 849 struct vop_reclaim_v2_args /* { 850 struct vnode *a_vp; 851 } */ *ap = v; 852 struct vnode *vp = ap->a_vp; 853 struct fdescnode *fd = VTOFDESC(vp); 854 855 VOP_UNLOCK(vp); 856 857 vp->v_data = NULL; 858 kmem_free(fd, sizeof(struct fdescnode)); 859 860 return (0); 861 } 862 863 /* 864 * Return POSIX pathconf information applicable to special devices. 865 */ 866 int 867 fdesc_pathconf(void *v) 868 { 869 struct vop_pathconf_args /* { 870 struct vnode *a_vp; 871 int a_name; 872 register_t *a_retval; 873 } */ *ap = v; 874 875 switch (ap->a_name) { 876 case _PC_LINK_MAX: 877 *ap->a_retval = LINK_MAX; 878 return (0); 879 case _PC_MAX_CANON: 880 *ap->a_retval = MAX_CANON; 881 return (0); 882 case _PC_MAX_INPUT: 883 *ap->a_retval = MAX_INPUT; 884 return (0); 885 case _PC_PIPE_BUF: 886 *ap->a_retval = PIPE_BUF; 887 return (0); 888 case _PC_CHOWN_RESTRICTED: 889 *ap->a_retval = 1; 890 return (0); 891 case _PC_VDISABLE: 892 *ap->a_retval = _POSIX_VDISABLE; 893 return (0); 894 case _PC_SYNC_IO: 895 *ap->a_retval = 1; 896 return (0); 897 default: 898 return genfs_pathconf(ap); 899 } 900 /* NOTREACHED */ 901 } 902 903 /* 904 * Print out the contents of a /dev/fd vnode. 905 */ 906 /* ARGSUSED */ 907 int 908 fdesc_print(void *v) 909 { 910 printf("tag VT_NON, fdesc vnode\n"); 911 return (0); 912 } 913