1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * vnode ops for the /dev filesystem 28 * 29 * - VDIR, VCHR, CBLK, and VLNK are considered must supported files 30 * - VREG and VDOOR are used for some internal implementations in 31 * the global zone, e.g. devname and devfsadm communication 32 * - other file types are unusual in this namespace and 33 * not supported for now 34 */ 35 36 #include <sys/types.h> 37 #include <sys/param.h> 38 #include <sys/t_lock.h> 39 #include <sys/systm.h> 40 #include <sys/sysmacros.h> 41 #include <sys/user.h> 42 #include <sys/time.h> 43 #include <sys/vfs.h> 44 #include <sys/vnode.h> 45 #include <sys/vfs_opreg.h> 46 #include <sys/file.h> 47 #include <sys/fcntl.h> 48 #include <sys/flock.h> 49 #include <sys/kmem.h> 50 #include <sys/uio.h> 51 #include <sys/errno.h> 52 #include <sys/stat.h> 53 #include <sys/cred.h> 54 #include <sys/cred_impl.h> 55 #include <sys/dirent.h> 56 #include <sys/pathname.h> 57 #include <sys/cmn_err.h> 58 #include <sys/debug.h> 59 #include <sys/policy.h> 60 #include <vm/hat.h> 61 #include <vm/seg_vn.h> 62 #include <vm/seg_map.h> 63 #include <vm/seg.h> 64 #include <vm/as.h> 65 #include <vm/page.h> 66 #include <sys/proc.h> 67 #include <sys/mode.h> 68 #include <sys/sunndi.h> 69 #include <sys/ptms.h> 70 #include <fs/fs_subr.h> 71 #include <sys/fs/dv_node.h> 72 #include <sys/fs/sdev_impl.h> 73 74 /*ARGSUSED*/ 75 static int 76 sdev_open(struct vnode **vpp, int flag, struct cred *cred, caller_context_t *ct) 77 { 78 struct sdev_node *dv = VTOSDEV(*vpp); 79 struct sdev_node *ddv = dv->sdev_dotdot; 80 int error = 0; 81 82 if ((*vpp)->v_type == VDIR) 83 return (0); 84 85 if (!SDEV_IS_GLOBAL(dv)) 86 return (ENOTSUP); 87 88 ASSERT((*vpp)->v_type == VREG); 89 if ((*vpp)->v_type != VREG) 90 return (ENOTSUP); 91 92 ASSERT(ddv); 93 rw_enter(&ddv->sdev_contents, RW_READER); 94 if (dv->sdev_attrvp == NULL) { 95 rw_exit(&ddv->sdev_contents); 96 return (ENOENT); 97 } 98 error = VOP_OPEN(&(dv->sdev_attrvp), flag, cred, ct); 99 rw_exit(&ddv->sdev_contents); 100 return (error); 101 } 102 103 /*ARGSUSED1*/ 104 static int 105 sdev_close(struct vnode *vp, int flag, int count, 106 offset_t offset, struct cred *cred, caller_context_t *ct) 107 { 108 struct sdev_node *dv = VTOSDEV(vp); 109 110 if (vp->v_type == VDIR) { 111 cleanlocks(vp, ttoproc(curthread)->p_pid, 0); 112 cleanshares(vp, ttoproc(curthread)->p_pid); 113 return (0); 114 } 115 116 if (!SDEV_IS_GLOBAL(dv)) 117 return (ENOTSUP); 118 119 ASSERT(vp->v_type == VREG); 120 if (vp->v_type != VREG) 121 return (ENOTSUP); 122 123 ASSERT(dv->sdev_attrvp); 124 return (VOP_CLOSE(dv->sdev_attrvp, flag, count, offset, cred, ct)); 125 } 126 127 /*ARGSUSED*/ 128 static int 129 sdev_read(struct vnode *vp, struct uio *uio, int ioflag, struct cred *cred, 130 struct caller_context *ct) 131 { 132 struct sdev_node *dv = (struct sdev_node *)VTOSDEV(vp); 133 int error; 134 135 if (!SDEV_IS_GLOBAL(dv)) 136 return (EINVAL); 137 138 if (vp->v_type == VDIR) 139 return (EISDIR); 140 141 /* only supporting regular files in /dev */ 142 ASSERT(vp->v_type == VREG); 143 if (vp->v_type != VREG) 144 return (EINVAL); 145 146 ASSERT(RW_READ_HELD(&VTOSDEV(vp)->sdev_contents)); 147 ASSERT(dv->sdev_attrvp); 148 (void) VOP_RWLOCK(dv->sdev_attrvp, 0, ct); 149 error = VOP_READ(dv->sdev_attrvp, uio, ioflag, cred, ct); 150 VOP_RWUNLOCK(dv->sdev_attrvp, 0, ct); 151 return (error); 152 } 153 154 /*ARGSUSED*/ 155 static int 156 sdev_write(struct vnode *vp, struct uio *uio, int ioflag, struct cred *cred, 157 struct caller_context *ct) 158 { 159 struct sdev_node *dv = VTOSDEV(vp); 160 int error = 0; 161 162 if (!SDEV_IS_GLOBAL(dv)) 163 return (EINVAL); 164 165 if (vp->v_type == VDIR) 166 return (EISDIR); 167 168 /* only supporting regular files in /dev */ 169 ASSERT(vp->v_type == VREG); 170 if (vp->v_type != VREG) 171 return (EINVAL); 172 173 ASSERT(dv->sdev_attrvp); 174 175 (void) VOP_RWLOCK(dv->sdev_attrvp, 1, ct); 176 error = VOP_WRITE(dv->sdev_attrvp, uio, ioflag, cred, ct); 177 VOP_RWUNLOCK(dv->sdev_attrvp, 1, ct); 178 if (error == 0) { 179 sdev_update_timestamps(dv->sdev_attrvp, kcred, 180 AT_MTIME); 181 } 182 return (error); 183 } 184 185 /*ARGSUSED*/ 186 static int 187 sdev_ioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, 188 struct cred *cred, int *rvalp, caller_context_t *ct) 189 { 190 struct sdev_node *dv = VTOSDEV(vp); 191 192 if (!SDEV_IS_GLOBAL(dv) || (vp->v_type == VDIR)) 193 return (ENOTTY); 194 195 ASSERT(vp->v_type == VREG); 196 if (vp->v_type != VREG) 197 return (EINVAL); 198 199 ASSERT(dv->sdev_attrvp); 200 return (VOP_IOCTL(dv->sdev_attrvp, cmd, arg, flag, cred, rvalp, ct)); 201 } 202 203 static int 204 sdev_getattr(struct vnode *vp, struct vattr *vap, int flags, 205 struct cred *cr, caller_context_t *ct) 206 { 207 int error = 0; 208 struct sdev_node *dv = VTOSDEV(vp); 209 struct sdev_node *parent = dv->sdev_dotdot; 210 211 ASSERT(parent); 212 213 rw_enter(&parent->sdev_contents, RW_READER); 214 ASSERT(dv->sdev_attr || dv->sdev_attrvp); 215 216 /* 217 * search order: 218 * - for persistent nodes (SDEV_PERSIST): backstore 219 * - for non-persistent nodes: module ops if global, then memory 220 */ 221 if (dv->sdev_attrvp) { 222 rw_exit(&parent->sdev_contents); 223 error = VOP_GETATTR(dv->sdev_attrvp, vap, flags, cr, ct); 224 sdev_vattr_merge(dv, vap); 225 } else { 226 ASSERT(dv->sdev_attr); 227 *vap = *dv->sdev_attr; 228 sdev_vattr_merge(dv, vap); 229 rw_exit(&parent->sdev_contents); 230 } 231 232 return (error); 233 } 234 235 /*ARGSUSED4*/ 236 static int 237 sdev_setattr(struct vnode *vp, struct vattr *vap, int flags, 238 struct cred *cred, caller_context_t *ctp) 239 { 240 return (devname_setattr_func(vp, vap, flags, cred, NULL, 0)); 241 } 242 243 static int 244 sdev_getsecattr(struct vnode *vp, struct vsecattr *vsap, int flags, 245 struct cred *cr, caller_context_t *ct) 246 { 247 int error; 248 struct sdev_node *dv = VTOSDEV(vp); 249 struct vnode *avp = dv->sdev_attrvp; 250 251 if (avp == NULL) { 252 /* return fs_fab_acl() if flavor matches, else do nothing */ 253 if ((SDEV_ACL_FLAVOR(vp) == _ACL_ACLENT_ENABLED && 254 (vsap->vsa_mask & (VSA_ACLCNT | VSA_DFACLCNT))) || 255 (SDEV_ACL_FLAVOR(vp) == _ACL_ACE_ENABLED && 256 (vsap->vsa_mask & (VSA_ACECNT | VSA_ACE)))) 257 return (fs_fab_acl(vp, vsap, flags, cr, ct)); 258 259 return (ENOSYS); 260 } 261 262 (void) VOP_RWLOCK(avp, 1, ct); 263 error = VOP_GETSECATTR(avp, vsap, flags, cr, ct); 264 VOP_RWUNLOCK(avp, 1, ct); 265 return (error); 266 } 267 268 static int 269 sdev_setsecattr(struct vnode *vp, struct vsecattr *vsap, int flags, 270 struct cred *cr, caller_context_t *ct) 271 { 272 int error; 273 struct sdev_node *dv = VTOSDEV(vp); 274 struct vnode *avp = dv->sdev_attrvp; 275 276 if (dv->sdev_state == SDEV_ZOMBIE) 277 return (0); 278 279 if (avp == NULL) { 280 if (SDEV_IS_GLOBAL(dv) && !SDEV_IS_PERSIST(dv)) 281 return (fs_nosys()); 282 ASSERT(dv->sdev_attr); 283 /* 284 * if coming in directly, the acl system call will 285 * have held the read-write lock via VOP_RWLOCK() 286 * If coming in via specfs, specfs will have 287 * held the rw lock on the realvp i.e. us. 288 */ 289 ASSERT(RW_WRITE_HELD(&dv->sdev_contents)); 290 sdev_vattr_merge(dv, dv->sdev_attr); 291 error = sdev_shadow_node(dv, cr); 292 if (error) { 293 return (fs_nosys()); 294 } 295 296 ASSERT(dv->sdev_attrvp); 297 /* clean out the memory copy if any */ 298 if (dv->sdev_attr) { 299 kmem_free(dv->sdev_attr, sizeof (struct vattr)); 300 dv->sdev_attr = NULL; 301 } 302 avp = dv->sdev_attrvp; 303 } 304 ASSERT(avp); 305 306 (void) VOP_RWLOCK(avp, V_WRITELOCK_TRUE, ct); 307 error = VOP_SETSECATTR(avp, vsap, flags, cr, ct); 308 VOP_RWUNLOCK(avp, V_WRITELOCK_TRUE, ct); 309 return (error); 310 } 311 312 int 313 sdev_unlocked_access(void *vdv, int mode, struct cred *cr) 314 { 315 struct sdev_node *dv = vdv; 316 int shift = 0; 317 uid_t owner = dv->sdev_attr->va_uid; 318 319 if (crgetuid(cr) != owner) { 320 shift += 3; 321 if (groupmember(dv->sdev_attr->va_gid, cr) == 0) 322 shift += 3; 323 } 324 325 mode &= ~(dv->sdev_attr->va_mode << shift); 326 if (mode == 0) 327 return (0); 328 329 return (secpolicy_vnode_access(cr, SDEVTOV(dv), owner, mode)); 330 } 331 332 static int 333 sdev_access(struct vnode *vp, int mode, int flags, struct cred *cr, 334 caller_context_t *ct) 335 { 336 struct sdev_node *dv = VTOSDEV(vp); 337 int ret = 0; 338 339 ASSERT(dv->sdev_attr || dv->sdev_attrvp); 340 341 if (dv->sdev_attrvp) { 342 ret = VOP_ACCESS(dv->sdev_attrvp, mode, flags, cr, ct); 343 } else if (dv->sdev_attr) { 344 rw_enter(&dv->sdev_contents, RW_READER); 345 ret = sdev_unlocked_access(dv, mode, cr); 346 if (ret) 347 ret = EACCES; 348 rw_exit(&dv->sdev_contents); 349 } 350 351 return (ret); 352 } 353 354 /* 355 * Lookup 356 */ 357 /*ARGSUSED3*/ 358 static int 359 sdev_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, 360 struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred, 361 caller_context_t *ct, int *direntflags, pathname_t *realpnp) 362 { 363 struct sdev_node *parent; 364 int error; 365 366 parent = VTOSDEV(dvp); 367 ASSERT(parent); 368 369 /* execute access is required to search the directory */ 370 if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) 371 return (error); 372 373 if (!SDEV_IS_GLOBAL(parent)) 374 return (prof_lookup(dvp, nm, vpp, cred)); 375 return (devname_lookup_func(parent, nm, vpp, cred, NULL, 0)); 376 } 377 378 /*ARGSUSED2*/ 379 static int 380 sdev_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl, 381 int mode, struct vnode **vpp, struct cred *cred, int flag, 382 caller_context_t *ct, vsecattr_t *vsecp) 383 { 384 struct vnode *vp = NULL; 385 struct vnode *avp; 386 struct sdev_node *parent; 387 struct sdev_node *self = NULL; 388 int error = 0; 389 vtype_t type = vap->va_type; 390 391 ASSERT(type != VNON && type != VBAD); 392 393 if ((type == VFIFO) || (type == VSOCK) || 394 (type == VPROC) || (type == VPORT)) 395 return (ENOTSUP); 396 397 parent = VTOSDEV(dvp); 398 ASSERT(parent); 399 400 rw_enter(&parent->sdev_dotdot->sdev_contents, RW_READER); 401 if (parent->sdev_state == SDEV_ZOMBIE) { 402 rw_exit(&parent->sdev_dotdot->sdev_contents); 403 return (ENOENT); 404 } 405 406 /* non-global do not allow pure node creation */ 407 if (!SDEV_IS_GLOBAL(parent)) { 408 rw_exit(&parent->sdev_dotdot->sdev_contents); 409 return (prof_lookup(dvp, nm, vpp, cred)); 410 } 411 rw_exit(&parent->sdev_dotdot->sdev_contents); 412 413 /* execute access is required to search the directory */ 414 if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) 415 return (error); 416 417 /* check existing name */ 418 /* XXXci - We may need to translate the C-I flags on VOP_LOOKUP */ 419 error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL); 420 421 /* name found */ 422 if (error == 0) { 423 ASSERT(vp); 424 if (excl == EXCL) { 425 error = EEXIST; 426 } else if ((vp->v_type == VDIR) && (mode & VWRITE)) { 427 /* allowing create/read-only an existing directory */ 428 error = EISDIR; 429 } else { 430 error = VOP_ACCESS(vp, mode, 0, cred, ct); 431 } 432 433 if (error) { 434 VN_RELE(vp); 435 return (error); 436 } 437 438 /* truncation first */ 439 if ((vp->v_type == VREG) && (vap->va_mask & AT_SIZE) && 440 (vap->va_size == 0)) { 441 ASSERT(parent->sdev_attrvp); 442 error = VOP_CREATE(parent->sdev_attrvp, 443 nm, vap, excl, mode, &avp, cred, flag, ct, vsecp); 444 445 if (error) { 446 VN_RELE(vp); 447 return (error); 448 } 449 } 450 451 sdev_update_timestamps(vp, kcred, 452 AT_CTIME|AT_MTIME|AT_ATIME); 453 *vpp = vp; 454 return (0); 455 } 456 457 /* bail out early */ 458 if (error != ENOENT) 459 return (error); 460 461 /* verify write access - compliance specifies ENXIO */ 462 if ((error = VOP_ACCESS(dvp, VEXEC|VWRITE, 0, cred, ct)) != 0) { 463 if (error == EACCES) 464 error = ENXIO; 465 return (error); 466 } 467 468 /* 469 * For memory-based (ROFS) directory: 470 * - either disallow node creation; 471 * - or implement VOP_CREATE of its own 472 */ 473 rw_enter(&parent->sdev_contents, RW_WRITER); 474 if (!SDEV_IS_PERSIST(parent)) { 475 rw_exit(&parent->sdev_contents); 476 return (ENOTSUP); 477 } 478 ASSERT(parent->sdev_attrvp); 479 error = sdev_mknode(parent, nm, &self, vap, NULL, NULL, 480 cred, SDEV_READY); 481 if (error) { 482 rw_exit(&parent->sdev_contents); 483 if (self) 484 SDEV_RELE(self); 485 return (error); 486 } 487 rw_exit(&parent->sdev_contents); 488 489 ASSERT(self); 490 /* take care the timestamps for the node and its parent */ 491 sdev_update_timestamps(SDEVTOV(self), kcred, 492 AT_CTIME|AT_MTIME|AT_ATIME); 493 sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME); 494 if (SDEV_IS_GLOBAL(parent)) 495 atomic_inc_ulong(&parent->sdev_gdir_gen); 496 497 /* wake up other threads blocked on looking up this node */ 498 mutex_enter(&self->sdev_lookup_lock); 499 SDEV_UNBLOCK_OTHERS(self, SDEV_LOOKUP); 500 mutex_exit(&self->sdev_lookup_lock); 501 error = sdev_to_vp(self, vpp); 502 return (error); 503 } 504 505 static int 506 sdev_remove(struct vnode *dvp, char *nm, struct cred *cred, 507 caller_context_t *ct, int flags) 508 { 509 int error; 510 struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp); 511 struct vnode *vp = NULL; 512 struct sdev_node *dv = NULL; 513 int len; 514 int bkstore = 0; 515 516 /* bail out early */ 517 len = strlen(nm); 518 if (nm[0] == '.') { 519 if (len == 1) { 520 return (EINVAL); 521 } else if (len == 2 && nm[1] == '.') { 522 return (EEXIST); 523 } 524 } 525 526 ASSERT(parent); 527 rw_enter(&parent->sdev_contents, RW_READER); 528 if (!SDEV_IS_GLOBAL(parent)) { 529 rw_exit(&parent->sdev_contents); 530 return (ENOTSUP); 531 } 532 533 /* execute access is required to search the directory */ 534 if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) { 535 rw_exit(&parent->sdev_contents); 536 return (error); 537 } 538 539 /* check existence first */ 540 dv = sdev_cache_lookup(parent, nm); 541 if (dv == NULL) { 542 rw_exit(&parent->sdev_contents); 543 return (ENOENT); 544 } 545 546 vp = SDEVTOV(dv); 547 if ((dv->sdev_state == SDEV_INIT) || 548 (dv->sdev_state == SDEV_ZOMBIE)) { 549 rw_exit(&parent->sdev_contents); 550 VN_RELE(vp); 551 return (ENOENT); 552 } 553 554 /* write access is required to remove an entry */ 555 if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred, ct)) != 0) { 556 rw_exit(&parent->sdev_contents); 557 VN_RELE(vp); 558 return (error); 559 } 560 561 /* 562 * sdev_dirdelete does the real job of: 563 * - make sure no open ref count 564 * - destroying the sdev_node 565 * - releasing the hold on attrvp 566 */ 567 bkstore = SDEV_IS_PERSIST(dv) ? 1 : 0; 568 if (!rw_tryupgrade(&parent->sdev_contents)) { 569 rw_exit(&parent->sdev_contents); 570 rw_enter(&parent->sdev_contents, RW_WRITER); 571 } 572 error = sdev_cache_update(parent, &dv, nm, SDEV_CACHE_DELETE); 573 rw_exit(&parent->sdev_contents); 574 575 sdcmn_err2(("sdev_remove: cache_update error %d\n", error)); 576 if (error && (error != EBUSY)) { 577 /* report errors other than EBUSY */ 578 VN_RELE(vp); 579 } else { 580 sdcmn_err2(("sdev_remove: cleaning node %s from cache " 581 " with error %d\n", nm, error)); 582 583 /* 584 * best efforts clean up the backing store 585 */ 586 if (bkstore) { 587 ASSERT(parent->sdev_attrvp); 588 error = VOP_REMOVE(parent->sdev_attrvp, nm, cred, 589 ct, flags); 590 /* 591 * do not report BUSY error 592 * because the backing store ref count is released 593 * when the last ref count on the sdev_node is 594 * released. 595 */ 596 if (error == EBUSY) { 597 sdcmn_err2(("sdev_remove: device %s is still on" 598 "disk %s\n", nm, parent->sdev_path)); 599 error = 0; 600 } 601 } 602 603 if (error == EBUSY) 604 error = 0; 605 } 606 607 return (error); 608 } 609 610 /* 611 * Some restrictions for this file system: 612 * - both oldnm and newnm are in the scope of /dev file system, 613 * to simply the namespace management model. 614 */ 615 /*ARGSUSED6*/ 616 static int 617 sdev_rename(struct vnode *odvp, char *onm, struct vnode *ndvp, char *nnm, 618 struct cred *cred, caller_context_t *ct, int flags) 619 { 620 struct sdev_node *fromparent = NULL; 621 struct vattr vattr; 622 struct sdev_node *toparent; 623 struct sdev_node *fromdv = NULL; /* source node */ 624 struct vnode *ovp = NULL; /* source vnode */ 625 struct sdev_node *todv = NULL; /* destination node */ 626 struct vnode *nvp = NULL; /* destination vnode */ 627 int samedir = 0; /* set if odvp == ndvp */ 628 struct vnode *realvp; 629 int error = 0; 630 dev_t fsid; 631 int bkstore = 0; 632 vtype_t type; 633 634 /* prevent modifying "." and ".." */ 635 if ((onm[0] == '.' && 636 (onm[1] == '\0' || (onm[1] == '.' && onm[2] == '\0'))) || 637 (nnm[0] == '.' && 638 (nnm[1] == '\0' || (nnm[1] == '.' && nnm[2] == '\0')))) { 639 return (EINVAL); 640 } 641 642 fromparent = VTOSDEV(odvp); 643 toparent = VTOSDEV(ndvp); 644 645 /* ZOMBIE parent doesn't allow new node creation */ 646 rw_enter(&fromparent->sdev_dotdot->sdev_contents, RW_READER); 647 if (fromparent->sdev_state == SDEV_ZOMBIE) { 648 rw_exit(&fromparent->sdev_dotdot->sdev_contents); 649 return (ENOENT); 650 } 651 652 /* renaming only supported for global device nodes */ 653 if (!SDEV_IS_GLOBAL(fromparent)) { 654 rw_exit(&fromparent->sdev_dotdot->sdev_contents); 655 return (ENOTSUP); 656 } 657 rw_exit(&fromparent->sdev_dotdot->sdev_contents); 658 659 rw_enter(&toparent->sdev_dotdot->sdev_contents, RW_READER); 660 if (toparent->sdev_state == SDEV_ZOMBIE) { 661 rw_exit(&toparent->sdev_dotdot->sdev_contents); 662 return (ENOENT); 663 } 664 rw_exit(&toparent->sdev_dotdot->sdev_contents); 665 666 /* 667 * acquire the global lock to prevent 668 * mount/unmount/other rename activities. 669 */ 670 mutex_enter(&sdev_lock); 671 672 /* check existence of the source node */ 673 /* XXXci - We may need to translate the C-I flags on VOP_LOOKUP */ 674 error = VOP_LOOKUP(odvp, onm, &ovp, NULL, 0, NULL, cred, ct, 675 NULL, NULL); 676 if (error) { 677 sdcmn_err2(("sdev_rename: the source node %s exists\n", 678 onm)); 679 mutex_exit(&sdev_lock); 680 return (error); 681 } 682 683 if (VOP_REALVP(ovp, &realvp, ct) == 0) { 684 VN_HOLD(realvp); 685 VN_RELE(ovp); 686 ovp = realvp; 687 } 688 689 /* check existence of destination */ 690 /* XXXci - We may need to translate the C-I flags on VOP_LOOKUP */ 691 error = VOP_LOOKUP(ndvp, nnm, &nvp, NULL, 0, NULL, cred, ct, 692 NULL, NULL); 693 if (error && (error != ENOENT)) { 694 mutex_exit(&sdev_lock); 695 VN_RELE(ovp); 696 return (error); 697 } 698 699 if (nvp && (VOP_REALVP(nvp, &realvp, ct) == 0)) { 700 VN_HOLD(realvp); 701 VN_RELE(nvp); 702 nvp = realvp; 703 } 704 705 /* 706 * make sure the source and the destination are 707 * in the same dev filesystem 708 */ 709 if (odvp != ndvp) { 710 vattr.va_mask = AT_FSID; 711 if (error = VOP_GETATTR(odvp, &vattr, 0, cred, ct)) { 712 mutex_exit(&sdev_lock); 713 VN_RELE(ovp); 714 return (error); 715 } 716 fsid = vattr.va_fsid; 717 vattr.va_mask = AT_FSID; 718 if (error = VOP_GETATTR(ndvp, &vattr, 0, cred, ct)) { 719 mutex_exit(&sdev_lock); 720 VN_RELE(ovp); 721 return (error); 722 } 723 if (fsid != vattr.va_fsid) { 724 mutex_exit(&sdev_lock); 725 VN_RELE(ovp); 726 return (EXDEV); 727 } 728 } 729 730 /* make sure the old entry can be deleted */ 731 error = VOP_ACCESS(odvp, VWRITE, 0, cred, ct); 732 if (error) { 733 mutex_exit(&sdev_lock); 734 VN_RELE(ovp); 735 return (error); 736 } 737 738 /* make sure the destination allows creation */ 739 samedir = (fromparent == toparent); 740 if (!samedir) { 741 error = VOP_ACCESS(ndvp, VEXEC|VWRITE, 0, cred, ct); 742 if (error) { 743 mutex_exit(&sdev_lock); 744 VN_RELE(ovp); 745 return (error); 746 } 747 } 748 749 fromdv = VTOSDEV(ovp); 750 ASSERT(fromdv); 751 752 /* destination file exists */ 753 if (nvp) { 754 todv = VTOSDEV(nvp); 755 ASSERT(todv); 756 } 757 758 /* 759 * link source to new target in the memory 760 */ 761 error = sdev_rnmnode(fromparent, fromdv, toparent, &todv, nnm, cred); 762 if (error) { 763 sdcmn_err2(("sdev_rename: renaming %s to %s failed " 764 " with error %d\n", onm, nnm, error)); 765 mutex_exit(&sdev_lock); 766 if (nvp) 767 VN_RELE(nvp); 768 VN_RELE(ovp); 769 return (error); 770 } 771 772 /* 773 * unlink from source 774 */ 775 rw_enter(&fromparent->sdev_contents, RW_READER); 776 fromdv = sdev_cache_lookup(fromparent, onm); 777 if (fromdv == NULL) { 778 rw_exit(&fromparent->sdev_contents); 779 mutex_exit(&sdev_lock); 780 sdcmn_err2(("sdev_rename: the source is deleted already\n")); 781 return (0); 782 } 783 784 if (fromdv->sdev_state == SDEV_ZOMBIE) { 785 rw_exit(&fromparent->sdev_contents); 786 mutex_exit(&sdev_lock); 787 VN_RELE(SDEVTOV(fromdv)); 788 sdcmn_err2(("sdev_rename: the source is being deleted\n")); 789 return (0); 790 } 791 rw_exit(&fromparent->sdev_contents); 792 ASSERT(SDEVTOV(fromdv) == ovp); 793 VN_RELE(ovp); 794 795 /* clean out the directory contents before it can be removed */ 796 type = SDEVTOV(fromdv)->v_type; 797 if (type == VDIR) { 798 error = sdev_cleandir(fromdv, NULL, 0); 799 sdcmn_err2(("sdev_rename: cleandir finished with %d\n", 800 error)); 801 if (error == EBUSY) 802 error = 0; 803 } 804 805 rw_enter(&fromparent->sdev_contents, RW_WRITER); 806 bkstore = SDEV_IS_PERSIST(fromdv) ? 1 : 0; 807 error = sdev_cache_update(fromparent, &fromdv, onm, 808 SDEV_CACHE_DELETE); 809 810 /* best effforts clean up the backing store */ 811 if (bkstore) { 812 ASSERT(fromparent->sdev_attrvp); 813 if (type != VDIR) { 814 /* XXXci - We may need to translate the C-I flags on VOP_REMOVE */ 815 error = VOP_REMOVE(fromparent->sdev_attrvp, 816 onm, kcred, ct, 0); 817 } else { 818 /* XXXci - We may need to translate the C-I flags on VOP_RMDIR */ 819 error = VOP_RMDIR(fromparent->sdev_attrvp, 820 onm, fromparent->sdev_attrvp, kcred, ct, 0); 821 } 822 823 if (error) { 824 sdcmn_err2(("sdev_rename: device %s is " 825 "still on disk %s\n", onm, 826 fromparent->sdev_path)); 827 error = 0; 828 } 829 } 830 rw_exit(&fromparent->sdev_contents); 831 mutex_exit(&sdev_lock); 832 833 /* once reached to this point, the rename is regarded successful */ 834 return (0); 835 } 836 837 /* 838 * dev-fs version of "ln -s path dev-name" 839 * tnm - path, e.g. /devices/... or /dev/... 840 * lnm - dev_name 841 */ 842 /*ARGSUSED6*/ 843 static int 844 sdev_symlink(struct vnode *dvp, char *lnm, struct vattr *tva, 845 char *tnm, struct cred *cred, caller_context_t *ct, int flags) 846 { 847 int error; 848 struct vnode *vp = NULL; 849 struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp); 850 struct sdev_node *self = (struct sdev_node *)NULL; 851 852 ASSERT(parent); 853 rw_enter(&parent->sdev_dotdot->sdev_contents, RW_READER); 854 if (parent->sdev_state == SDEV_ZOMBIE) { 855 rw_exit(&parent->sdev_dotdot->sdev_contents); 856 sdcmn_err2(("sdev_symlink: parent %s is ZOMBIED \n", 857 parent->sdev_name)); 858 return (ENOENT); 859 } 860 861 if (!SDEV_IS_GLOBAL(parent)) { 862 rw_exit(&parent->sdev_dotdot->sdev_contents); 863 return (ENOTSUP); 864 } 865 rw_exit(&parent->sdev_dotdot->sdev_contents); 866 867 /* execute access is required to search a directory */ 868 if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) 869 return (error); 870 871 /* find existing name */ 872 /* XXXci - We may need to translate the C-I flags here */ 873 error = VOP_LOOKUP(dvp, lnm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL); 874 if (error == 0) { 875 ASSERT(vp); 876 VN_RELE(vp); 877 sdcmn_err2(("sdev_symlink: node %s already exists\n", lnm)); 878 return (EEXIST); 879 } 880 if (error != ENOENT) 881 return (error); 882 883 /* write access is required to create a symlink */ 884 if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred, ct)) != 0) 885 return (error); 886 887 /* put it into memory cache */ 888 rw_enter(&parent->sdev_contents, RW_WRITER); 889 error = sdev_mknode(parent, lnm, &self, tva, NULL, (void *)tnm, 890 cred, SDEV_READY); 891 if (error) { 892 rw_exit(&parent->sdev_contents); 893 sdcmn_err2(("sdev_symlink: node %s creation failed\n", lnm)); 894 if (self) 895 SDEV_RELE(self); 896 897 return (error); 898 } 899 ASSERT(self && (self->sdev_state == SDEV_READY)); 900 rw_exit(&parent->sdev_contents); 901 902 /* take care the timestamps for the node and its parent */ 903 sdev_update_timestamps(SDEVTOV(self), kcred, 904 AT_CTIME|AT_MTIME|AT_ATIME); 905 sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME); 906 if (SDEV_IS_GLOBAL(parent)) 907 atomic_inc_ulong(&parent->sdev_gdir_gen); 908 909 /* wake up other threads blocked on looking up this node */ 910 mutex_enter(&self->sdev_lookup_lock); 911 SDEV_UNBLOCK_OTHERS(self, SDEV_LOOKUP); 912 mutex_exit(&self->sdev_lookup_lock); 913 SDEV_RELE(self); /* don't return with vnode held */ 914 return (0); 915 } 916 917 /*ARGSUSED6*/ 918 static int 919 sdev_mkdir(struct vnode *dvp, char *nm, struct vattr *va, struct vnode **vpp, 920 struct cred *cred, caller_context_t *ct, int flags, vsecattr_t *vsecp) 921 { 922 int error; 923 struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp); 924 struct sdev_node *self = NULL; 925 struct vnode *vp = NULL; 926 927 ASSERT(parent && parent->sdev_dotdot); 928 rw_enter(&parent->sdev_dotdot->sdev_contents, RW_READER); 929 if (parent->sdev_state == SDEV_ZOMBIE) { 930 rw_exit(&parent->sdev_dotdot->sdev_contents); 931 return (ENOENT); 932 } 933 934 /* non-global do not allow pure directory creation */ 935 if (!SDEV_IS_GLOBAL(parent)) { 936 rw_exit(&parent->sdev_dotdot->sdev_contents); 937 return (prof_lookup(dvp, nm, vpp, cred)); 938 } 939 rw_exit(&parent->sdev_dotdot->sdev_contents); 940 941 /* execute access is required to search the directory */ 942 if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) { 943 return (error); 944 } 945 946 /* find existing name */ 947 /* XXXci - We may need to translate the C-I flags on VOP_LOOKUP */ 948 error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL); 949 if (error == 0) { 950 VN_RELE(vp); 951 return (EEXIST); 952 } 953 if (error != ENOENT) 954 return (error); 955 956 /* require write access to create a directory */ 957 if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred, ct)) != 0) { 958 return (error); 959 } 960 961 /* put it into memory */ 962 rw_enter(&parent->sdev_contents, RW_WRITER); 963 error = sdev_mknode(parent, nm, &self, 964 va, NULL, NULL, cred, SDEV_READY); 965 if (error) { 966 rw_exit(&parent->sdev_contents); 967 if (self) 968 SDEV_RELE(self); 969 return (error); 970 } 971 ASSERT(self && (self->sdev_state == SDEV_READY)); 972 rw_exit(&parent->sdev_contents); 973 974 /* take care the timestamps for the node and its parent */ 975 sdev_update_timestamps(SDEVTOV(self), kcred, 976 AT_CTIME|AT_MTIME|AT_ATIME); 977 sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME); 978 if (SDEV_IS_GLOBAL(parent)) 979 atomic_inc_ulong(&parent->sdev_gdir_gen); 980 981 /* wake up other threads blocked on looking up this node */ 982 mutex_enter(&self->sdev_lookup_lock); 983 SDEV_UNBLOCK_OTHERS(self, SDEV_LOOKUP); 984 mutex_exit(&self->sdev_lookup_lock); 985 *vpp = SDEVTOV(self); 986 return (0); 987 } 988 989 /* 990 * allowing removing an empty directory under /dev 991 */ 992 /*ARGSUSED*/ 993 static int 994 sdev_rmdir(struct vnode *dvp, char *nm, struct vnode *cdir, struct cred *cred, 995 caller_context_t *ct, int flags) 996 { 997 int error = 0; 998 struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp); 999 struct sdev_node *self = NULL; 1000 struct vnode *vp = NULL; 1001 1002 /* bail out early */ 1003 if (strcmp(nm, ".") == 0) 1004 return (EINVAL); 1005 if (strcmp(nm, "..") == 0) 1006 return (EEXIST); /* should be ENOTEMPTY */ 1007 1008 /* no destruction of non-global node */ 1009 ASSERT(parent && parent->sdev_dotdot); 1010 rw_enter(&parent->sdev_dotdot->sdev_contents, RW_READER); 1011 if (!SDEV_IS_GLOBAL(parent)) { 1012 rw_exit(&parent->sdev_dotdot->sdev_contents); 1013 return (ENOTSUP); 1014 } 1015 rw_exit(&parent->sdev_dotdot->sdev_contents); 1016 1017 /* execute access is required to search the directory */ 1018 if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) 1019 return (error); 1020 1021 /* check existing name */ 1022 rw_enter(&parent->sdev_contents, RW_WRITER); 1023 self = sdev_cache_lookup(parent, nm); 1024 if (self == NULL) { 1025 rw_exit(&parent->sdev_contents); 1026 return (ENOENT); 1027 } 1028 1029 vp = SDEVTOV(self); 1030 if ((self->sdev_state == SDEV_INIT) || 1031 (self->sdev_state == SDEV_ZOMBIE)) { 1032 rw_exit(&parent->sdev_contents); 1033 VN_RELE(vp); 1034 return (ENOENT); 1035 } 1036 1037 /* write access is required to remove a directory */ 1038 if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred, ct)) != 0) { 1039 rw_exit(&parent->sdev_contents); 1040 VN_RELE(vp); 1041 return (error); 1042 } 1043 1044 /* some sanity checks */ 1045 if (vp == dvp || vp == cdir) { 1046 rw_exit(&parent->sdev_contents); 1047 VN_RELE(vp); 1048 return (EINVAL); 1049 } 1050 1051 if (vp->v_type != VDIR) { 1052 rw_exit(&parent->sdev_contents); 1053 VN_RELE(vp); 1054 return (ENOTDIR); 1055 } 1056 1057 if (vn_vfswlock(vp)) { 1058 rw_exit(&parent->sdev_contents); 1059 VN_RELE(vp); 1060 return (EBUSY); 1061 } 1062 1063 if (vn_mountedvfs(vp) != NULL) { 1064 rw_exit(&parent->sdev_contents); 1065 vn_vfsunlock(vp); 1066 VN_RELE(vp); 1067 return (EBUSY); 1068 } 1069 1070 self = VTOSDEV(vp); 1071 /* bail out on a non-empty directory */ 1072 rw_enter(&self->sdev_contents, RW_READER); 1073 if (self->sdev_nlink > 2) { 1074 rw_exit(&self->sdev_contents); 1075 rw_exit(&parent->sdev_contents); 1076 vn_vfsunlock(vp); 1077 VN_RELE(vp); 1078 return (ENOTEMPTY); 1079 } 1080 rw_exit(&self->sdev_contents); 1081 1082 /* unlink it from the directory cache */ 1083 error = sdev_cache_update(parent, &self, nm, SDEV_CACHE_DELETE); 1084 rw_exit(&parent->sdev_contents); 1085 vn_vfsunlock(vp); 1086 1087 if (error && (error != EBUSY)) { 1088 VN_RELE(vp); 1089 } else { 1090 sdcmn_err2(("sdev_rmdir: cleaning node %s from directory " 1091 " cache with error %d\n", nm, error)); 1092 1093 /* best effort to clean up the backing store */ 1094 if (SDEV_IS_PERSIST(parent)) { 1095 ASSERT(parent->sdev_attrvp); 1096 error = VOP_RMDIR(parent->sdev_attrvp, nm, 1097 parent->sdev_attrvp, kcred, ct, flags); 1098 sdcmn_err2(("sdev_rmdir: cleaning device %s is on" 1099 " disk error %d\n", parent->sdev_path, error)); 1100 } 1101 1102 if (error == EBUSY) 1103 error = 0; 1104 } 1105 1106 return (error); 1107 } 1108 1109 /* 1110 * read the contents of a symbolic link 1111 */ 1112 static int 1113 sdev_readlink(struct vnode *vp, struct uio *uiop, struct cred *cred, 1114 caller_context_t *ct) 1115 { 1116 struct sdev_node *dv; 1117 int error = 0; 1118 1119 ASSERT(vp->v_type == VLNK); 1120 1121 dv = VTOSDEV(vp); 1122 1123 if (dv->sdev_attrvp) { 1124 /* non-NULL attrvp implys a persisted node at READY state */ 1125 return (VOP_READLINK(dv->sdev_attrvp, uiop, cred, ct)); 1126 } else if (dv->sdev_symlink != NULL) { 1127 /* memory nodes, e.g. local nodes */ 1128 rw_enter(&dv->sdev_contents, RW_READER); 1129 sdcmn_err2(("sdev_readlink link is %s\n", dv->sdev_symlink)); 1130 error = uiomove(dv->sdev_symlink, strlen(dv->sdev_symlink), 1131 UIO_READ, uiop); 1132 rw_exit(&dv->sdev_contents); 1133 return (error); 1134 } 1135 1136 return (ENOENT); 1137 } 1138 1139 /*ARGSUSED4*/ 1140 static int 1141 sdev_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, int *eofp, 1142 caller_context_t *ct, int flags) 1143 { 1144 struct sdev_node *parent = VTOSDEV(dvp); 1145 int error; 1146 1147 /* execute access is required to search the directory */ 1148 if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) 1149 return (error); 1150 1151 ASSERT(parent); 1152 if (!SDEV_IS_GLOBAL(parent)) 1153 prof_filldir(parent); 1154 return (devname_readdir_func(dvp, uiop, cred, eofp, SDEV_BROWSE)); 1155 } 1156 1157 /*ARGSUSED1*/ 1158 static void 1159 sdev_inactive(struct vnode *vp, struct cred *cred, caller_context_t *ct) 1160 { 1161 devname_inactive_func(vp, cred, NULL); 1162 } 1163 1164 /*ARGSUSED2*/ 1165 static int 1166 sdev_fid(struct vnode *vp, struct fid *fidp, caller_context_t *ct) 1167 { 1168 struct sdev_node *dv = VTOSDEV(vp); 1169 struct sdev_fid *sdev_fid; 1170 1171 if (fidp->fid_len < (sizeof (struct sdev_fid) - sizeof (ushort_t))) { 1172 fidp->fid_len = sizeof (struct sdev_fid) - sizeof (ushort_t); 1173 return (ENOSPC); 1174 } 1175 1176 sdev_fid = (struct sdev_fid *)fidp; 1177 bzero(sdev_fid, sizeof (struct sdev_fid)); 1178 sdev_fid->sdevfid_len = 1179 (int)sizeof (struct sdev_fid) - sizeof (ushort_t); 1180 sdev_fid->sdevfid_ino = dv->sdev_ino; 1181 1182 return (0); 1183 } 1184 1185 /* 1186 * This pair of routines bracket all VOP_READ, VOP_WRITE 1187 * and VOP_READDIR requests. The contents lock stops things 1188 * moving around while we're looking at them. 1189 */ 1190 /*ARGSUSED2*/ 1191 static int 1192 sdev_rwlock(struct vnode *vp, int write_flag, caller_context_t *ctp) 1193 { 1194 rw_enter(&VTOSDEV(vp)->sdev_contents, 1195 write_flag ? RW_WRITER : RW_READER); 1196 return (write_flag ? V_WRITELOCK_TRUE : V_WRITELOCK_FALSE); 1197 } 1198 1199 /*ARGSUSED1*/ 1200 static void 1201 sdev_rwunlock(struct vnode *vp, int write_flag, caller_context_t *ctp) 1202 { 1203 rw_exit(&VTOSDEV(vp)->sdev_contents); 1204 } 1205 1206 /*ARGSUSED1*/ 1207 static int 1208 sdev_seek(struct vnode *vp, offset_t ooff, offset_t *noffp, 1209 caller_context_t *ct) 1210 { 1211 struct vnode *attrvp = VTOSDEV(vp)->sdev_attrvp; 1212 1213 ASSERT(vp->v_type != VCHR && 1214 vp->v_type != VBLK && vp->v_type != VLNK); 1215 1216 if (vp->v_type == VDIR) 1217 return (fs_seek(vp, ooff, noffp, ct)); 1218 1219 ASSERT(attrvp); 1220 return (VOP_SEEK(attrvp, ooff, noffp, ct)); 1221 } 1222 1223 /*ARGSUSED1*/ 1224 static int 1225 sdev_frlock(struct vnode *vp, int cmd, struct flock64 *bfp, int flag, 1226 offset_t offset, struct flk_callback *flk_cbp, struct cred *cr, 1227 caller_context_t *ct) 1228 { 1229 int error; 1230 struct sdev_node *dv = VTOSDEV(vp); 1231 1232 ASSERT(dv); 1233 ASSERT(dv->sdev_attrvp); 1234 error = VOP_FRLOCK(dv->sdev_attrvp, cmd, bfp, flag, offset, 1235 flk_cbp, cr, ct); 1236 1237 return (error); 1238 } 1239 1240 static int 1241 sdev_setfl(struct vnode *vp, int oflags, int nflags, cred_t *cr, 1242 caller_context_t *ct) 1243 { 1244 struct sdev_node *dv = VTOSDEV(vp); 1245 ASSERT(dv); 1246 ASSERT(dv->sdev_attrvp); 1247 1248 return (VOP_SETFL(dv->sdev_attrvp, oflags, nflags, cr, ct)); 1249 } 1250 1251 static int 1252 sdev_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr, 1253 caller_context_t *ct) 1254 { 1255 switch (cmd) { 1256 case _PC_ACL_ENABLED: 1257 *valp = SDEV_ACL_FLAVOR(vp); 1258 return (0); 1259 } 1260 1261 return (fs_pathconf(vp, cmd, valp, cr, ct)); 1262 } 1263 1264 vnodeops_t *sdev_vnodeops; 1265 1266 const fs_operation_def_t sdev_vnodeops_tbl[] = { 1267 VOPNAME_OPEN, { .vop_open = sdev_open }, 1268 VOPNAME_CLOSE, { .vop_close = sdev_close }, 1269 VOPNAME_READ, { .vop_read = sdev_read }, 1270 VOPNAME_WRITE, { .vop_write = sdev_write }, 1271 VOPNAME_IOCTL, { .vop_ioctl = sdev_ioctl }, 1272 VOPNAME_GETATTR, { .vop_getattr = sdev_getattr }, 1273 VOPNAME_SETATTR, { .vop_setattr = sdev_setattr }, 1274 VOPNAME_ACCESS, { .vop_access = sdev_access }, 1275 VOPNAME_LOOKUP, { .vop_lookup = sdev_lookup }, 1276 VOPNAME_CREATE, { .vop_create = sdev_create }, 1277 VOPNAME_RENAME, { .vop_rename = sdev_rename }, 1278 VOPNAME_REMOVE, { .vop_remove = sdev_remove }, 1279 VOPNAME_MKDIR, { .vop_mkdir = sdev_mkdir }, 1280 VOPNAME_RMDIR, { .vop_rmdir = sdev_rmdir }, 1281 VOPNAME_READDIR, { .vop_readdir = sdev_readdir }, 1282 VOPNAME_SYMLINK, { .vop_symlink = sdev_symlink }, 1283 VOPNAME_READLINK, { .vop_readlink = sdev_readlink }, 1284 VOPNAME_INACTIVE, { .vop_inactive = sdev_inactive }, 1285 VOPNAME_FID, { .vop_fid = sdev_fid }, 1286 VOPNAME_RWLOCK, { .vop_rwlock = sdev_rwlock }, 1287 VOPNAME_RWUNLOCK, { .vop_rwunlock = sdev_rwunlock }, 1288 VOPNAME_SEEK, { .vop_seek = sdev_seek }, 1289 VOPNAME_FRLOCK, { .vop_frlock = sdev_frlock }, 1290 VOPNAME_PATHCONF, { .vop_pathconf = sdev_pathconf }, 1291 VOPNAME_SETFL, { .vop_setfl = sdev_setfl }, 1292 VOPNAME_SETSECATTR, { .vop_setsecattr = sdev_setsecattr }, 1293 VOPNAME_GETSECATTR, { .vop_getsecattr = sdev_getsecattr }, 1294 NULL, NULL 1295 }; 1296 1297 int sdev_vnodeops_tbl_size = sizeof (sdev_vnodeops_tbl); 1298