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