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