1 #define DEBUG 1 2 /* 3 * Copyright (c) 1992, 1993 4 * The Regents of the University of California. All rights reserved. 5 * Copyright (c) 2000 6 * Poul-Henning Kamp. All rights reserved. 7 * 8 * This code is derived from software donated to Berkeley by 9 * Jan-Simon Pendry. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * @(#)kernfs_vnops.c 8.15 (Berkeley) 5/21/95 33 * From: FreeBSD: src/sys/miscfs/kernfs/kernfs_vnops.c 1.43 34 * 35 * $FreeBSD$ 36 */ 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/conf.h> 41 #include <sys/dirent.h> 42 #include <sys/kernel.h> 43 #include <sys/lock.h> 44 #include <sys/malloc.h> 45 #include <sys/mount.h> 46 #include <sys/namei.h> 47 #include <sys/proc.h> 48 #include <sys/socket.h> 49 #include <sys/time.h> 50 #include <sys/vnode.h> 51 52 #include <fs/devfs/devfs.h> 53 54 static int devfs_access __P((struct vop_access_args *ap)); 55 static int devfs_badop __P((void)); 56 static int devfs_getattr __P((struct vop_getattr_args *ap)); 57 static int devfs_lookupx __P((struct vop_lookup_args *ap)); 58 static int devfs_print __P((struct vop_print_args *ap)); 59 static int devfs_read __P((struct vop_read_args *ap)); 60 static int devfs_readdir __P((struct vop_readdir_args *ap)); 61 static int devfs_readlink __P((struct vop_readlink_args *ap)); 62 static int devfs_reclaim __P((struct vop_reclaim_args *ap)); 63 static int devfs_remove __P((struct vop_remove_args *ap)); 64 static int devfs_revoke __P((struct vop_revoke_args *ap)); 65 static int devfs_setattr __P((struct vop_setattr_args *ap)); 66 static int devfs_symlink __P((struct vop_symlink_args *ap)); 67 68 int 69 devfs_allocv(struct devfs_dirent *de, struct mount *mp, struct vnode **vpp, struct proc *p) 70 { 71 int error; 72 struct vnode *vp; 73 dev_t dev; 74 75 if (p == NULL) 76 p = curproc; /* XXX */ 77 loop: 78 vp = de->de_vnode; 79 if (vp != NULL) { 80 if (vget(vp, LK_EXCLUSIVE, p ? p : curproc)) 81 goto loop; 82 *vpp = vp; 83 return (0); 84 } 85 if (de->de_dirent->d_type == DT_CHR) { 86 dev = *devfs_itod(de->de_inode); 87 if (dev == NULL) 88 return (ENOENT); 89 } else { 90 dev = NODEV; 91 } 92 error = getnewvnode(VT_DEVFS, mp, devfs_vnodeop_p, &vp); 93 if (error != 0) { 94 printf("devfs_allocv: failed to allocate new vnode\n"); 95 return (error); 96 } 97 98 if (de->de_dirent->d_type == DT_CHR) { 99 vp->v_type = VCHR; 100 vp = addaliasu(vp, dev->si_udev); 101 vp->v_op = devfs_specop_p; 102 } else if (de->de_dirent->d_type == DT_DIR) { 103 vp->v_type = VDIR; 104 } else if (de->de_dirent->d_type == DT_LNK) { 105 vp->v_type = VLNK; 106 } else { 107 vp->v_type = VBAD; 108 } 109 vp->v_data = de; 110 de->de_vnode = vp; 111 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); 112 *vpp = vp; 113 return (0); 114 } 115 116 static int 117 devfs_access(ap) 118 struct vop_access_args /* { 119 struct vnode *a_vp; 120 int a_mode; 121 struct ucred *a_cred; 122 struct proc *a_p; 123 } */ *ap; 124 { 125 struct vnode *vp = ap->a_vp; 126 struct devfs_dirent *de; 127 128 de = vp->v_data; 129 if (vp->v_type == VDIR) 130 de = de->de_dir; 131 132 return (vaccess(vp->v_type, de->de_mode, de->de_uid, de->de_gid, 133 ap->a_mode, ap->a_cred, NULL)); 134 } 135 136 static int 137 devfs_getattr(ap) 138 struct vop_getattr_args /* { 139 struct vnode *a_vp; 140 struct vattr *a_vap; 141 struct ucred *a_cred; 142 struct proc *a_p; 143 } */ *ap; 144 { 145 struct vnode *vp = ap->a_vp; 146 struct vattr *vap = ap->a_vap; 147 int error = 0; 148 struct devfs_dirent *de; 149 dev_t dev; 150 151 de = vp->v_data; 152 if (vp->v_type == VDIR) 153 de = de->de_dir; 154 bzero((caddr_t) vap, sizeof(*vap)); 155 vattr_null(vap); 156 vap->va_uid = de->de_uid; 157 vap->va_gid = de->de_gid; 158 vap->va_mode = de->de_mode; 159 if (vp->v_type == VLNK) 160 vap->va_size = de->de_dirent->d_namlen; 161 else 162 vap->va_size = 0; 163 vap->va_blocksize = DEV_BSIZE; 164 vap->va_type = vp->v_type; 165 166 #define fix(aa) \ 167 do { \ 168 if ((aa).tv_sec == 0) { \ 169 (aa).tv_sec = boottime.tv_sec; \ 170 (aa).tv_nsec = boottime.tv_usec * 1000; \ 171 } \ 172 } while (0) 173 174 if (vp->v_type != VCHR) { 175 fix(de->de_atime); 176 vap->va_atime = de->de_atime; 177 fix(de->de_mtime); 178 vap->va_mtime = de->de_mtime; 179 fix(de->de_ctime); 180 vap->va_ctime = de->de_ctime; 181 } else { 182 dev = vp->v_rdev; 183 fix(dev->si_atime); 184 vap->va_atime = dev->si_atime; 185 fix(dev->si_mtime); 186 vap->va_mtime = dev->si_mtime; 187 fix(dev->si_ctime); 188 vap->va_ctime = dev->si_ctime; 189 vap->va_rdev = dev->si_udev; 190 } 191 vap->va_gen = 0; 192 vap->va_flags = 0; 193 vap->va_bytes = 0; 194 vap->va_nlink = de->de_links; 195 vap->va_fileid = de->de_inode; 196 197 #ifdef DEBUG 198 if (error) 199 printf("devfs_getattr: return error %d\n", error); 200 #endif 201 return (error); 202 } 203 204 static int 205 devfs_lookupx(ap) 206 struct vop_lookup_args /* { 207 struct vnode * a_dvp; 208 struct vnode ** a_vpp; 209 struct componentname * a_cnp; 210 } */ *ap; 211 { 212 struct componentname *cnp; 213 struct vnode *dvp, **vpp; 214 struct proc *p; 215 struct devfs_dirent *de, *dd; 216 struct devfs_mount *dmp; 217 dev_t cdev; 218 int error, cloned, i, flags, nameiop; 219 char specname[SPECNAMELEN + 1], *pname; 220 221 cnp = ap->a_cnp; 222 vpp = ap->a_vpp; 223 dvp = ap->a_dvp; 224 pname = cnp->cn_nameptr; 225 p = cnp->cn_proc; 226 flags = cnp->cn_flags; 227 nameiop = cnp->cn_nameiop; 228 dmp = VFSTODEVFS(dvp->v_mount); 229 cloned = 0; 230 dd = dvp->v_data; 231 232 *vpp = NULLVP; 233 234 if (nameiop == RENAME) 235 return (EOPNOTSUPP); 236 237 if (dvp->v_type != VDIR) 238 return (ENOTDIR); 239 240 if ((flags & ISDOTDOT) && (dvp->v_flag & VROOT)) 241 return (EIO); 242 243 error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, cnp->cn_proc); 244 if (error) 245 return (error); 246 247 if (cnp->cn_namelen == 1 && *pname == '.') { 248 if (nameiop != LOOKUP) 249 return (EINVAL); 250 *vpp = dvp; 251 VREF(dvp); 252 return (0); 253 } 254 255 if (flags & ISDOTDOT) { 256 if (nameiop != LOOKUP) 257 return (EINVAL); 258 VOP_UNLOCK(dvp, 0, p); 259 de = TAILQ_FIRST(&dd->de_dlist); /* "." */ 260 de = TAILQ_NEXT(de, de_list); /* ".." */ 261 de = de->de_dir; 262 error = devfs_allocv(de, dvp->v_mount, vpp, p); 263 if (error) { 264 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p); 265 return (error); 266 } 267 if ((flags & LOCKPARENT) && (flags & ISLASTCN)) 268 error = vn_lock(dvp, LK_EXCLUSIVE, p); 269 if (error) 270 vput(*vpp); 271 return (error); 272 } 273 274 devfs_populate(dmp); 275 dd = dvp->v_data; 276 TAILQ_FOREACH(de, &dd->de_dlist, de_list) { 277 if (cnp->cn_namelen != de->de_dirent->d_namlen) 278 continue; 279 if (bcmp(cnp->cn_nameptr, de->de_dirent->d_name, 280 de->de_dirent->d_namlen) != 0) 281 continue; 282 goto found; 283 } 284 285 /* 286 * OK, we didn't have an entry for the name we were asked for 287 * so we try to see if anybody can create it on demand. 288 * We need to construct the full "devname" for this device 289 * relative to "basedir" or the clone functions would not 290 * be able to tell "/dev/foo" from "/dev/bar/foo" 291 */ 292 i = SPECNAMELEN; 293 specname[i] = '\0'; 294 i -= cnp->cn_namelen; 295 if (i < 0) 296 goto notfound; 297 bcopy(cnp->cn_nameptr, specname + i, cnp->cn_namelen); 298 de = dd; 299 while (de != dmp->dm_basedir) { 300 i--; 301 if (i < 0) 302 goto notfound; 303 specname[i] = '/'; 304 i -= de->de_dirent->d_namlen; 305 if (i < 0) 306 goto notfound; 307 bcopy(de->de_dirent->d_name, specname + i, 308 de->de_dirent->d_namlen); 309 de = TAILQ_FIRST(&de->de_dlist); /* "." */ 310 de = TAILQ_NEXT(de, de_list); /* ".." */ 311 de = de->de_dir; 312 } 313 314 #if 0 315 printf("Finished specname: %d \"%s\"\n", i, specname + i); 316 #endif 317 cdev = NODEV; 318 EVENTHANDLER_INVOKE(dev_clone, specname + i, 319 strlen(specname + i), &cdev); 320 #if 0 321 printf("cloned %s -> %p %s\n", specname + i, cdev, 322 cdev == NODEV ? "NODEV" : cdev->si_name); 323 #endif 324 if (cdev == NODEV) 325 goto notfound; 326 327 devfs_populate(dmp); 328 dd = dvp->v_data; 329 TAILQ_FOREACH(de, &dd->de_dlist, de_list) { 330 if (cnp->cn_namelen != de->de_dirent->d_namlen) 331 continue; 332 if (bcmp(cnp->cn_nameptr, de->de_dirent->d_name, 333 de->de_dirent->d_namlen) != 0) 334 continue; 335 goto found; 336 } 337 338 notfound: 339 340 if ((nameiop == CREATE || nameiop == RENAME) && 341 (flags & (LOCKPARENT | WANTPARENT)) && (flags & ISLASTCN)) { 342 cnp->cn_flags |= SAVENAME; 343 if (!(flags & LOCKPARENT)) 344 VOP_UNLOCK(dvp, 0, p); 345 return (EJUSTRETURN); 346 } 347 return (ENOENT); 348 349 350 found: 351 352 if ((cnp->cn_nameiop == DELETE) && (flags & ISLASTCN)) { 353 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, p); 354 if (error) 355 return (error); 356 if (*vpp == dvp) { 357 VREF(dvp); 358 *vpp = dvp; 359 return (0); 360 } 361 error = devfs_allocv(de, dvp->v_mount, vpp, p); 362 if (error) 363 return (error); 364 if (!(flags & LOCKPARENT)) 365 VOP_UNLOCK(dvp, 0, p); 366 return (0); 367 } 368 error = devfs_allocv(de, dvp->v_mount, vpp, p); 369 if (error) 370 return (error); 371 if (!(flags & LOCKPARENT) || !(flags & ISLASTCN)) 372 VOP_UNLOCK(dvp, 0, p); 373 return (0); 374 } 375 376 static int 377 devfs_lookup(struct vop_lookup_args *ap) 378 { 379 int j; 380 struct devfs_mount *dmp; 381 382 dmp = VFSTODEVFS(ap->a_dvp->v_mount); 383 lockmgr(&dmp->dm_lock, LK_SHARED, 0, curproc); 384 j = devfs_lookupx(ap); 385 lockmgr(&dmp->dm_lock, LK_RELEASE, 0, curproc); 386 return (j); 387 } 388 389 /* ARGSUSED */ 390 static int 391 devfs_print(ap) 392 struct vop_print_args /* { 393 struct vnode *a_vp; 394 } */ *ap; 395 { 396 397 printf("tag VT_DEVFS, devfs vnode\n"); 398 return (0); 399 } 400 401 static int 402 devfs_read(ap) 403 struct vop_read_args /* { 404 struct vnode *a_vp; 405 struct uio *a_uio; 406 int a_ioflag; 407 struct ucred *a_cred; 408 } */ *ap; 409 { 410 411 if (ap->a_vp->v_type != VDIR) 412 return (EINVAL); 413 return (VOP_READDIR(ap->a_vp, ap->a_uio, ap->a_cred, NULL, NULL, NULL)); 414 } 415 416 static int 417 devfs_readdir(ap) 418 struct vop_readdir_args /* { 419 struct vnode *a_vp; 420 struct uio *a_uio; 421 struct ucred *a_cred; 422 int *a_eofflag; 423 int *a_ncookies; 424 u_long **a_cookies; 425 } */ *ap; 426 { 427 int error; 428 struct uio *uio; 429 struct dirent *dp; 430 struct devfs_dirent *dd; 431 struct devfs_dirent *de; 432 struct devfs_mount *dmp; 433 off_t off; 434 435 if (ap->a_vp->v_type != VDIR) 436 return (ENOTDIR); 437 438 if (ap->a_ncookies) 439 return (EOPNOTSUPP); 440 441 uio = ap->a_uio; 442 if (uio->uio_offset < 0) 443 return (EINVAL); 444 445 dmp = VFSTODEVFS(ap->a_vp->v_mount); 446 lockmgr(&dmp->dm_lock, LK_SHARED, 0, curproc); 447 devfs_populate(dmp); 448 error = 0; 449 de = ap->a_vp->v_data; 450 dd = TAILQ_FIRST(&de->de_dlist); 451 off = 0; 452 while (dd != NULL) { 453 if (dd->de_dirent->d_type == DT_DIR) 454 de = dd->de_dir; 455 else 456 de = dd; 457 dp = dd->de_dirent; 458 if (dp->d_reclen > uio->uio_resid) 459 break; 460 dp->d_fileno = de->de_inode; 461 if (off >= uio->uio_offset) { 462 error = uiomove((caddr_t)dp, dp->d_reclen, uio); 463 if (error) 464 break; 465 } 466 off += dp->d_reclen; 467 dd = TAILQ_NEXT(dd, de_list); 468 } 469 lockmgr(&dmp->dm_lock, LK_RELEASE, 0, curproc); 470 uio->uio_offset = off; 471 return (error); 472 } 473 474 static int 475 devfs_readlink(ap) 476 struct vop_readlink_args /* { 477 struct vnode *a_vp; 478 struct uio *a_uio; 479 struct ucred *a_cead; 480 } */ *ap; 481 { 482 int error; 483 struct devfs_dirent *de; 484 485 de = ap->a_vp->v_data; 486 error = uiomove(de->de_symlink, strlen(de->de_symlink) + 1, ap->a_uio); 487 return (error); 488 } 489 490 static int 491 devfs_reclaim(ap) 492 struct vop_reclaim_args /* { 493 struct vnode *a_vp; 494 } */ *ap; 495 { 496 struct vnode *vp = ap->a_vp; 497 struct devfs_dirent *de; 498 int i; 499 500 de = vp->v_data; 501 if (de != NULL) 502 de->de_vnode = NULL; 503 if (de != NULL && de->de_flags & DE_ORPHAN) { 504 if (de->de_symlink) 505 FREE(de->de_symlink, M_DEVFS); 506 FREE(de, M_DEVFS); 507 } 508 vp->v_data = NULL; 509 if (vp->v_rdev != NODEV && vp->v_rdev != NULL) { 510 i = vcount(vp); 511 if ((vp->v_rdev->si_flags & SI_CHEAPCLONE) && i == 0) 512 destroy_dev(vp->v_rdev); 513 } 514 return (0); 515 } 516 517 static int 518 devfs_remove(ap) 519 struct vop_remove_args /* { 520 struct vnode *a_dvp; 521 struct vnode *a_vp; 522 struct componentname *a_cnp; 523 } */ *ap; 524 { 525 struct vnode *vp = ap->a_vp; 526 struct devfs_dirent *dd; 527 struct devfs_dirent *de, **dep; 528 struct devfs_mount *dmp = VFSTODEVFS(vp->v_mount); 529 530 lockmgr(&dmp->dm_lock, LK_EXCLUSIVE, 0, curproc); 531 dd = ap->a_dvp->v_data; 532 de = vp->v_data; 533 TAILQ_REMOVE(&dd->de_dlist, de, de_list); 534 dep = devfs_itode(dmp, de->de_inode); 535 if (dep != NULL) 536 *dep = DE_DELETED; 537 de->de_flags |= DE_ORPHAN; 538 lockmgr(&dmp->dm_lock, LK_RELEASE, 0, curproc); 539 return (0); 540 } 541 542 /* 543 * Revoke is called on a tty when a terminal session ends. The vnode 544 * is orphaned by setting v_op to deadfs so we need to let go of it 545 * as well so that we create a new one next time around. 546 */ 547 static int 548 devfs_revoke(ap) 549 struct vop_revoke_args /* { 550 struct vnode *a_vp; 551 int a_flags; 552 } */ *ap; 553 { 554 struct vnode *vp = ap->a_vp; 555 struct devfs_dirent *de; 556 557 de = vp->v_data; 558 de->de_vnode = NULL; 559 vop_revoke(ap); 560 return (0); 561 } 562 563 static int 564 devfs_setattr(ap) 565 struct vop_setattr_args /* { 566 struct vnode *a_vp; 567 struct vattr *a_vap; 568 struct ucred *a_cred; 569 struct proc *a_p; 570 } */ *ap; 571 { 572 struct devfs_dirent *de; 573 struct vattr *vap; 574 int c, error; 575 uid_t uid; 576 gid_t gid; 577 578 vap = ap->a_vap; 579 if ((vap->va_type != VNON) || 580 (vap->va_nlink != VNOVAL) || 581 (vap->va_fsid != VNOVAL) || 582 (vap->va_fileid != VNOVAL) || 583 (vap->va_blocksize != VNOVAL) || 584 (vap->va_flags != VNOVAL && vap->va_flags != 0) || 585 (vap->va_rdev != VNOVAL) || 586 ((int)vap->va_bytes != VNOVAL) || 587 (vap->va_gen != VNOVAL)) { 588 return (EINVAL); 589 } 590 591 de = ap->a_vp->v_data; 592 if (ap->a_vp->v_type == VDIR) 593 de = de->de_dir; 594 595 error = c = 0; 596 if (vap->va_uid == (uid_t)VNOVAL) 597 uid = de->de_uid; 598 else 599 uid = vap->va_uid; 600 if (vap->va_gid == (gid_t)VNOVAL) 601 gid = de->de_gid; 602 else 603 gid = vap->va_gid; 604 if (uid != de->de_uid || gid != de->de_gid) { 605 if (((ap->a_cred->cr_uid != de->de_uid) || uid != de->de_uid || 606 (gid != de->de_gid && !groupmember(gid, ap->a_cred))) && 607 (error = suser(ap->a_p)) != 0) 608 return (error); 609 de->de_uid = uid; 610 de->de_gid = gid; 611 c = 1; 612 } 613 if (vap->va_mode != (mode_t)VNOVAL) { 614 if ((ap->a_cred->cr_uid != de->de_uid) && 615 (error = suser(ap->a_p))) 616 return (error); 617 de->de_mode = vap->va_mode; 618 c = 1; 619 } 620 if (vap->va_atime.tv_sec != VNOVAL) { 621 if ((ap->a_cred->cr_uid != de->de_uid) && 622 (error = suser(ap->a_p))) 623 return (error); 624 de->de_atime = vap->va_atime; 625 c = 1; 626 } 627 if (vap->va_mtime.tv_sec != VNOVAL) { 628 if ((ap->a_cred->cr_uid != de->de_uid) && 629 (error = suser(ap->a_p))) 630 return (error); 631 de->de_mtime = vap->va_mtime; 632 c = 1; 633 } 634 635 if (c) 636 getnanotime(&de->de_ctime); 637 return (0); 638 } 639 640 static int 641 devfs_symlink(ap) 642 struct vop_symlink_args /* { 643 struct vnode *a_dvp; 644 struct vnode **a_vpp; 645 struct componentname *a_cnp; 646 struct vattr *a_vap; 647 char *a_target; 648 } */ *ap; 649 { 650 int i, error; 651 struct devfs_dirent *dd; 652 struct devfs_dirent *de; 653 struct devfs_mount *dmp; 654 655 error = suser(ap->a_cnp->cn_proc); 656 if (error) 657 return(error); 658 dmp = VFSTODEVFS(ap->a_dvp->v_mount); 659 dd = ap->a_dvp->v_data; 660 de = devfs_newdirent(ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen); 661 de->de_uid = 0; 662 de->de_gid = 0; 663 de->de_mode = 0755; 664 de->de_inode = dmp->dm_inode++; 665 de->de_dirent->d_type = DT_LNK; 666 i = strlen(ap->a_target) + 1; 667 MALLOC(de->de_symlink, char *, i, M_DEVFS, M_WAITOK); 668 bcopy(ap->a_target, de->de_symlink, i); 669 lockmgr(&dmp->dm_lock, LK_EXCLUSIVE, 0, curproc); 670 TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list); 671 devfs_allocv(de, ap->a_dvp->v_mount, ap->a_vpp, 0); 672 lockmgr(&dmp->dm_lock, LK_RELEASE, 0, curproc); 673 return (0); 674 } 675 676 /* 677 * DEVFS "should never get here" operation 678 */ 679 static int 680 devfs_badop() 681 { 682 return (EIO); 683 } 684 685 static vop_t **devfs_vnodeop_p; 686 static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = { 687 { &vop_default_desc, (vop_t *) vop_defaultop }, 688 { &vop_access_desc, (vop_t *) devfs_access }, 689 { &vop_getattr_desc, (vop_t *) devfs_getattr }, 690 { &vop_lookup_desc, (vop_t *) devfs_lookup }, 691 { &vop_pathconf_desc, (vop_t *) vop_stdpathconf }, 692 { &vop_print_desc, (vop_t *) devfs_print }, 693 { &vop_read_desc, (vop_t *) devfs_read }, 694 { &vop_readdir_desc, (vop_t *) devfs_readdir }, 695 { &vop_readlink_desc, (vop_t *) devfs_readlink }, 696 { &vop_reclaim_desc, (vop_t *) devfs_reclaim }, 697 { &vop_remove_desc, (vop_t *) devfs_remove }, 698 { &vop_revoke_desc, (vop_t *) devfs_revoke }, 699 { &vop_setattr_desc, (vop_t *) devfs_setattr }, 700 { &vop_symlink_desc, (vop_t *) devfs_symlink }, 701 { NULL, NULL } 702 }; 703 static struct vnodeopv_desc devfs_vnodeop_opv_desc = 704 { &devfs_vnodeop_p, devfs_vnodeop_entries }; 705 706 VNODEOP_SET(devfs_vnodeop_opv_desc); 707 708 static vop_t **devfs_specop_p; 709 static struct vnodeopv_entry_desc devfs_specop_entries[] = { 710 { &vop_default_desc, (vop_t *) spec_vnoperate }, 711 { &vop_access_desc, (vop_t *) devfs_access }, 712 { &vop_getattr_desc, (vop_t *) devfs_getattr }, 713 { &vop_print_desc, (vop_t *) devfs_print }, 714 { &vop_reclaim_desc, (vop_t *) devfs_reclaim }, 715 { &vop_remove_desc, (vop_t *) devfs_remove }, 716 { &vop_revoke_desc, (vop_t *) devfs_revoke }, 717 { &vop_setattr_desc, (vop_t *) devfs_setattr }, 718 { NULL, NULL } 719 }; 720 static struct vnodeopv_desc devfs_specop_opv_desc = 721 { &devfs_specop_p, devfs_specop_entries }; 722 723 VNODEOP_SET(devfs_specop_opv_desc); 724