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