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