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