12621Sllai1 /* 22621Sllai1 * CDDL HEADER START 32621Sllai1 * 42621Sllai1 * The contents of this file are subject to the terms of the 52621Sllai1 * Common Development and Distribution License (the "License"). 62621Sllai1 * You may not use this file except in compliance with the License. 72621Sllai1 * 82621Sllai1 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 92621Sllai1 * or http://www.opensolaris.org/os/licensing. 102621Sllai1 * See the License for the specific language governing permissions 112621Sllai1 * and limitations under the License. 122621Sllai1 * 132621Sllai1 * When distributing Covered Code, include this CDDL HEADER in each 142621Sllai1 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 152621Sllai1 * If applicable, add the following below this CDDL HEADER, with the 162621Sllai1 * fields enclosed by brackets "[]" replaced with your own identifying 172621Sllai1 * information: Portions Copyright [yyyy] [name of copyright owner] 182621Sllai1 * 192621Sllai1 * CDDL HEADER END 202621Sllai1 */ 212621Sllai1 /* 223748Sjg * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 232621Sllai1 * Use is subject to license terms. 242621Sllai1 */ 252621Sllai1 262621Sllai1 #pragma ident "%Z%%M% %I% %E% SMI" 272621Sllai1 282621Sllai1 /* 292621Sllai1 * vnode ops for the /dev filesystem 302621Sllai1 * 312621Sllai1 * - VDIR, VCHR, CBLK, and VLNK are considered must supported files 322621Sllai1 * - VREG and VDOOR are used for some internal implementations in 332621Sllai1 * the global zone, e.g. devname and devfsadm communication 342621Sllai1 * - other file types are unusual in this namespace and 352621Sllai1 * not supported for now 362621Sllai1 */ 372621Sllai1 382621Sllai1 #include <sys/types.h> 392621Sllai1 #include <sys/param.h> 402621Sllai1 #include <sys/t_lock.h> 412621Sllai1 #include <sys/systm.h> 422621Sllai1 #include <sys/sysmacros.h> 432621Sllai1 #include <sys/user.h> 442621Sllai1 #include <sys/time.h> 452621Sllai1 #include <sys/vfs.h> 462621Sllai1 #include <sys/vnode.h> 473898Srsb #include <sys/vfs_opreg.h> 482621Sllai1 #include <sys/file.h> 492621Sllai1 #include <sys/fcntl.h> 502621Sllai1 #include <sys/flock.h> 512621Sllai1 #include <sys/kmem.h> 522621Sllai1 #include <sys/uio.h> 532621Sllai1 #include <sys/errno.h> 542621Sllai1 #include <sys/stat.h> 552621Sllai1 #include <sys/cred.h> 562621Sllai1 #include <sys/cred_impl.h> 572621Sllai1 #include <sys/dirent.h> 582621Sllai1 #include <sys/pathname.h> 592621Sllai1 #include <sys/cmn_err.h> 602621Sllai1 #include <sys/debug.h> 612621Sllai1 #include <sys/policy.h> 622621Sllai1 #include <vm/hat.h> 632621Sllai1 #include <vm/seg_vn.h> 642621Sllai1 #include <vm/seg_map.h> 652621Sllai1 #include <vm/seg.h> 662621Sllai1 #include <vm/as.h> 672621Sllai1 #include <vm/page.h> 682621Sllai1 #include <sys/proc.h> 692621Sllai1 #include <sys/mode.h> 702621Sllai1 #include <sys/sunndi.h> 712621Sllai1 #include <sys/ptms.h> 722621Sllai1 #include <fs/fs_subr.h> 732621Sllai1 #include <sys/fs/dv_node.h> 742621Sllai1 #include <sys/fs/sdev_impl.h> 752621Sllai1 #include <sys/fs/sdev_node.h> 762621Sllai1 772621Sllai1 /*ARGSUSED*/ 782621Sllai1 static int 79*5331Samw sdev_open(struct vnode **vpp, int flag, struct cred *cred, caller_context_t *ct) 802621Sllai1 { 812621Sllai1 struct sdev_node *dv = VTOSDEV(*vpp); 822621Sllai1 struct sdev_node *ddv = dv->sdev_dotdot; 832621Sllai1 int error = 0; 842621Sllai1 852621Sllai1 if ((*vpp)->v_type == VDIR) 862621Sllai1 return (0); 872621Sllai1 882621Sllai1 if (!SDEV_IS_GLOBAL(dv)) 892621Sllai1 return (ENOTSUP); 902621Sllai1 912621Sllai1 ASSERT((*vpp)->v_type == VREG); 922621Sllai1 if ((*vpp)->v_type != VREG) 932621Sllai1 return (ENOTSUP); 942621Sllai1 952621Sllai1 ASSERT(ddv); 962621Sllai1 rw_enter(&ddv->sdev_contents, RW_READER); 972621Sllai1 if (dv->sdev_attrvp == NULL) { 982621Sllai1 rw_exit(&ddv->sdev_contents); 992621Sllai1 return (ENOENT); 1002621Sllai1 } 101*5331Samw error = VOP_OPEN(&(dv->sdev_attrvp), flag, cred, ct); 1022621Sllai1 rw_exit(&ddv->sdev_contents); 1032621Sllai1 return (error); 1042621Sllai1 } 1052621Sllai1 1062621Sllai1 /*ARGSUSED1*/ 1072621Sllai1 static int 1082621Sllai1 sdev_close(struct vnode *vp, int flag, int count, 109*5331Samw offset_t offset, struct cred *cred, caller_context_t *ct) 1102621Sllai1 { 1112621Sllai1 struct sdev_node *dv = VTOSDEV(vp); 1122621Sllai1 1132621Sllai1 if (vp->v_type == VDIR) { 1142621Sllai1 cleanlocks(vp, ttoproc(curthread)->p_pid, 0); 1152621Sllai1 cleanshares(vp, ttoproc(curthread)->p_pid); 1162621Sllai1 return (0); 1172621Sllai1 } 1182621Sllai1 1192621Sllai1 if (!SDEV_IS_GLOBAL(dv)) 1202621Sllai1 return (ENOTSUP); 1212621Sllai1 1222621Sllai1 ASSERT(vp->v_type == VREG); 1232621Sllai1 if (vp->v_type != VREG) 1242621Sllai1 return (ENOTSUP); 1252621Sllai1 1262621Sllai1 ASSERT(dv->sdev_attrvp); 127*5331Samw return (VOP_CLOSE(dv->sdev_attrvp, flag, count, offset, cred, ct)); 1282621Sllai1 } 1292621Sllai1 1302621Sllai1 /*ARGSUSED*/ 1312621Sllai1 static int 1322621Sllai1 sdev_read(struct vnode *vp, struct uio *uio, int ioflag, struct cred *cred, 1332621Sllai1 struct caller_context *ct) 1342621Sllai1 { 1352621Sllai1 struct sdev_node *dv = (struct sdev_node *)VTOSDEV(vp); 1362621Sllai1 int error; 1372621Sllai1 1382621Sllai1 if (!SDEV_IS_GLOBAL(dv)) 1392621Sllai1 return (EINVAL); 1402621Sllai1 1412621Sllai1 if (vp->v_type == VDIR) 1422621Sllai1 return (EISDIR); 1432621Sllai1 1442621Sllai1 /* only supporting regular files in /dev */ 1452621Sllai1 ASSERT(vp->v_type == VREG); 1462621Sllai1 if (vp->v_type != VREG) 1472621Sllai1 return (EINVAL); 1482621Sllai1 1492621Sllai1 ASSERT(RW_READ_HELD(&VTOSDEV(vp)->sdev_contents)); 1502621Sllai1 ASSERT(dv->sdev_attrvp); 151*5331Samw (void) VOP_RWLOCK(dv->sdev_attrvp, 0, ct); 1522621Sllai1 error = VOP_READ(dv->sdev_attrvp, uio, ioflag, cred, ct); 153*5331Samw VOP_RWUNLOCK(dv->sdev_attrvp, 0, ct); 1542621Sllai1 return (error); 1552621Sllai1 } 1562621Sllai1 1572621Sllai1 /*ARGSUSED*/ 1582621Sllai1 static int 1592621Sllai1 sdev_write(struct vnode *vp, struct uio *uio, int ioflag, struct cred *cred, 1602621Sllai1 struct caller_context *ct) 1612621Sllai1 { 1622621Sllai1 struct sdev_node *dv = VTOSDEV(vp); 1632621Sllai1 int error = 0; 1642621Sllai1 1652621Sllai1 if (!SDEV_IS_GLOBAL(dv)) 1662621Sllai1 return (EINVAL); 1672621Sllai1 1682621Sllai1 if (vp->v_type == VDIR) 1692621Sllai1 return (EISDIR); 1702621Sllai1 1712621Sllai1 /* only supporting regular files in /dev */ 1722621Sllai1 ASSERT(vp->v_type == VREG); 1732621Sllai1 if (vp->v_type != VREG) 1742621Sllai1 return (EINVAL); 1752621Sllai1 1762621Sllai1 ASSERT(dv->sdev_attrvp); 1772621Sllai1 178*5331Samw (void) VOP_RWLOCK(dv->sdev_attrvp, 1, ct); 1792621Sllai1 error = VOP_WRITE(dv->sdev_attrvp, uio, ioflag, cred, ct); 180*5331Samw VOP_RWUNLOCK(dv->sdev_attrvp, 1, ct); 1812621Sllai1 if (error == 0) { 1822621Sllai1 sdev_update_timestamps(dv->sdev_attrvp, kcred, 1832621Sllai1 AT_MTIME); 1842621Sllai1 } 1852621Sllai1 return (error); 1862621Sllai1 } 1872621Sllai1 1882621Sllai1 /*ARGSUSED*/ 1892621Sllai1 static int 1902621Sllai1 sdev_ioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, 191*5331Samw struct cred *cred, int *rvalp, caller_context_t *ct) 1922621Sllai1 { 1932621Sllai1 struct sdev_node *dv = VTOSDEV(vp); 1942621Sllai1 1952621Sllai1 if (!SDEV_IS_GLOBAL(dv) || (vp->v_type == VDIR)) 1962621Sllai1 return (ENOTTY); 1972621Sllai1 1982621Sllai1 ASSERT(vp->v_type == VREG); 1992621Sllai1 if (vp->v_type != VREG) 2002621Sllai1 return (EINVAL); 2012621Sllai1 2022621Sllai1 ASSERT(dv->sdev_attrvp); 203*5331Samw return (VOP_IOCTL(dv->sdev_attrvp, cmd, arg, flag, cred, rvalp, ct)); 2042621Sllai1 } 2052621Sllai1 2062621Sllai1 static int 207*5331Samw sdev_getattr(struct vnode *vp, struct vattr *vap, int flags, 208*5331Samw struct cred *cr, caller_context_t *ct) 2092621Sllai1 { 2102621Sllai1 int error = 0; 2112621Sllai1 struct sdev_node *dv = VTOSDEV(vp); 2122621Sllai1 struct sdev_node *parent = dv->sdev_dotdot; 2132621Sllai1 struct devname_nsmap *map = NULL; 2142621Sllai1 struct devname_ops *dirops = NULL; 2152621Sllai1 int (*fn)(devname_handle_t *, struct vattr *, struct cred *); 2162621Sllai1 2172621Sllai1 ASSERT(parent); 2182621Sllai1 2192621Sllai1 rw_enter(&parent->sdev_contents, RW_READER); 2202621Sllai1 ASSERT(dv->sdev_attr || dv->sdev_attrvp); 2212621Sllai1 if (SDEV_IS_GLOBAL(dv) && (dv->sdev_state != SDEV_ZOMBIE)) { 2222621Sllai1 map = sdev_get_map(parent, 0); 2232621Sllai1 dirops = map ? map->dir_ops : NULL; 2242621Sllai1 } 2252621Sllai1 2262621Sllai1 /* 2272621Sllai1 * search order: 2282621Sllai1 * - for persistent nodes (SDEV_PERSIST): backstore 2292621Sllai1 * - for non-persistent nodes: module ops if global, then memory 2302621Sllai1 */ 2312621Sllai1 if (dv->sdev_attrvp) { 2322621Sllai1 rw_exit(&parent->sdev_contents); 233*5331Samw error = VOP_GETATTR(dv->sdev_attrvp, vap, flags, cr, ct); 2342621Sllai1 sdev_vattr_merge(dv, vap); 2352621Sllai1 } else if (dirops && (fn = dirops->devnops_getattr)) { 2362621Sllai1 sdev_vattr_merge(dv, vap); 2372621Sllai1 rw_exit(&parent->sdev_contents); 2382621Sllai1 error = (*fn)(&(dv->sdev_handle), vap, cr); 2392621Sllai1 } else { 2402621Sllai1 ASSERT(dv->sdev_attr); 2412621Sllai1 *vap = *dv->sdev_attr; 2422621Sllai1 sdev_vattr_merge(dv, vap); 2432621Sllai1 rw_exit(&parent->sdev_contents); 2442621Sllai1 } 2452621Sllai1 2462621Sllai1 return (error); 2472621Sllai1 } 2482621Sllai1 2493748Sjg /*ARGSUSED4*/ 2502621Sllai1 static int 2513748Sjg sdev_setattr(struct vnode *vp, struct vattr *vap, int flags, 2523748Sjg struct cred *cred, caller_context_t *ctp) 2532621Sllai1 { 2542621Sllai1 return (devname_setattr_func(vp, vap, flags, cred, NULL, 0)); 2552621Sllai1 } 2562621Sllai1 2572621Sllai1 static int 2582621Sllai1 sdev_getsecattr(struct vnode *vp, struct vsecattr *vsap, int flags, 259*5331Samw struct cred *cr, caller_context_t *ct) 2602621Sllai1 { 2612621Sllai1 int error; 2622621Sllai1 struct sdev_node *dv = VTOSDEV(vp); 2632621Sllai1 struct vnode *avp = dv->sdev_attrvp; 2642621Sllai1 2652621Sllai1 if (avp == NULL) { 2662621Sllai1 /* return fs_fab_acl() if flavor matches, else do nothing */ 2672621Sllai1 if ((SDEV_ACL_FLAVOR(vp) == _ACL_ACLENT_ENABLED && 2682621Sllai1 (vsap->vsa_mask & (VSA_ACLCNT | VSA_DFACLCNT))) || 2692621Sllai1 (SDEV_ACL_FLAVOR(vp) == _ACL_ACE_ENABLED && 2702621Sllai1 (vsap->vsa_mask & (VSA_ACECNT | VSA_ACE)))) 271*5331Samw return (fs_fab_acl(vp, vsap, flags, cr, ct)); 2722621Sllai1 2732621Sllai1 return (ENOSYS); 2742621Sllai1 } 2752621Sllai1 276*5331Samw (void) VOP_RWLOCK(avp, 1, ct); 277*5331Samw error = VOP_GETSECATTR(avp, vsap, flags, cr, ct); 278*5331Samw VOP_RWUNLOCK(avp, 1, ct); 2792621Sllai1 return (error); 2802621Sllai1 } 2812621Sllai1 2822621Sllai1 static int 2832621Sllai1 sdev_setsecattr(struct vnode *vp, struct vsecattr *vsap, int flags, 284*5331Samw struct cred *cr, caller_context_t *ct) 2852621Sllai1 { 2862621Sllai1 int error; 2872621Sllai1 struct sdev_node *dv = VTOSDEV(vp); 2882621Sllai1 struct vnode *avp = dv->sdev_attrvp; 2892621Sllai1 2902621Sllai1 if (dv->sdev_state == SDEV_ZOMBIE) 2912621Sllai1 return (0); 2922621Sllai1 2932621Sllai1 if (avp == NULL) { 2942621Sllai1 if (SDEV_IS_GLOBAL(dv) && !SDEV_IS_PERSIST(dv)) 2952621Sllai1 return (fs_nosys()); 2962621Sllai1 ASSERT(dv->sdev_attr); 2972621Sllai1 /* 2982621Sllai1 * if coming in directly, the acl system call will 2992621Sllai1 * have held the read-write lock via VOP_RWLOCK() 3002621Sllai1 * If coming in via specfs, specfs will have 3012621Sllai1 * held the rw lock on the realvp i.e. us. 3022621Sllai1 */ 3032621Sllai1 ASSERT(RW_WRITE_HELD(&dv->sdev_contents)); 3042621Sllai1 sdev_vattr_merge(dv, dv->sdev_attr); 305*5331Samw error = sdev_shadow_node(dv, cr); 3062621Sllai1 if (error) { 3072621Sllai1 return (fs_nosys()); 3082621Sllai1 } 3092621Sllai1 3102621Sllai1 ASSERT(dv->sdev_attrvp); 3112621Sllai1 /* clean out the memory copy if any */ 3122621Sllai1 if (dv->sdev_attr) { 3132621Sllai1 kmem_free(dv->sdev_attr, sizeof (struct vattr)); 3142621Sllai1 dv->sdev_attr = NULL; 3152621Sllai1 } 3162621Sllai1 avp = dv->sdev_attrvp; 3172621Sllai1 } 3182621Sllai1 ASSERT(avp); 3192621Sllai1 320*5331Samw (void) VOP_RWLOCK(avp, V_WRITELOCK_TRUE, ct); 321*5331Samw error = VOP_SETSECATTR(avp, vsap, flags, cr, ct); 322*5331Samw VOP_RWUNLOCK(avp, V_WRITELOCK_TRUE, ct); 3232621Sllai1 return (error); 3242621Sllai1 } 3252621Sllai1 3262621Sllai1 int 3272621Sllai1 sdev_unlocked_access(void *vdv, int mode, struct cred *cr) 3282621Sllai1 { 3292621Sllai1 struct sdev_node *dv = vdv; 3302621Sllai1 int shift = 0; 3312621Sllai1 uid_t owner = dv->sdev_attr->va_uid; 3322621Sllai1 3332621Sllai1 if (crgetuid(cr) != owner) { 3342621Sllai1 shift += 3; 3352621Sllai1 if (groupmember(dv->sdev_attr->va_gid, cr) == 0) 3362621Sllai1 shift += 3; 3372621Sllai1 } 3382621Sllai1 3392621Sllai1 mode &= ~(dv->sdev_attr->va_mode << shift); 3402621Sllai1 if (mode == 0) 3412621Sllai1 return (0); 3422621Sllai1 3432621Sllai1 return (secpolicy_vnode_access(cr, SDEVTOV(dv), owner, mode)); 3442621Sllai1 } 3452621Sllai1 3462621Sllai1 static int 347*5331Samw sdev_access(struct vnode *vp, int mode, int flags, struct cred *cr, 348*5331Samw caller_context_t *ct) 3492621Sllai1 { 3502621Sllai1 struct sdev_node *dv = VTOSDEV(vp); 3512621Sllai1 int ret = 0; 3522621Sllai1 3532621Sllai1 ASSERT(dv->sdev_attr || dv->sdev_attrvp); 3542621Sllai1 3552621Sllai1 if (dv->sdev_attrvp) { 356*5331Samw ret = VOP_ACCESS(dv->sdev_attrvp, mode, flags, cr, ct); 3572621Sllai1 } else if (dv->sdev_attr) { 3582621Sllai1 rw_enter(&dv->sdev_contents, RW_READER); 3592621Sllai1 ret = sdev_unlocked_access(dv, mode, cr); 3602621Sllai1 if (ret) 3612621Sllai1 ret = EACCES; 3622621Sllai1 rw_exit(&dv->sdev_contents); 3632621Sllai1 } 3642621Sllai1 3652621Sllai1 return (ret); 3662621Sllai1 } 3672621Sllai1 3682621Sllai1 /* 3692621Sllai1 * Lookup 3702621Sllai1 */ 3712621Sllai1 /*ARGSUSED3*/ 3722621Sllai1 static int 3732621Sllai1 sdev_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, 374*5331Samw struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred, 375*5331Samw caller_context_t *ct, int *direntflags, pathname_t *realpnp) 3762621Sllai1 { 3773843Sjg struct sdev_node *parent; 3783843Sjg int error; 3792621Sllai1 3802621Sllai1 parent = VTOSDEV(dvp); 3812621Sllai1 ASSERT(parent); 3822621Sllai1 3833843Sjg /* execute access is required to search the directory */ 384*5331Samw if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) 3853843Sjg return (error); 3863843Sjg 3872621Sllai1 if (!SDEV_IS_GLOBAL(parent)) 3882621Sllai1 return (prof_lookup(dvp, nm, vpp, cred)); 3892621Sllai1 return (devname_lookup_func(parent, nm, vpp, cred, NULL, 0)); 3902621Sllai1 } 3912621Sllai1 3922621Sllai1 /*ARGSUSED2*/ 3932621Sllai1 static int 3942621Sllai1 sdev_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl, 395*5331Samw int mode, struct vnode **vpp, struct cred *cred, int flag, 396*5331Samw caller_context_t *ct, vsecattr_t *vsecp) 3972621Sllai1 { 3982621Sllai1 struct vnode *vp = NULL; 3992621Sllai1 struct vnode *avp; 4002621Sllai1 struct sdev_node *parent; 4012621Sllai1 struct sdev_node *self = NULL; 4022621Sllai1 int error = 0; 4032621Sllai1 vtype_t type = vap->va_type; 4042621Sllai1 4052729Sllai1 ASSERT(type != VNON && type != VBAD); 4062621Sllai1 4072621Sllai1 if ((type == VFIFO) || (type == VSOCK) || 4082621Sllai1 (type == VPROC) || (type == VPORT)) 4092621Sllai1 return (ENOTSUP); 4102621Sllai1 4112621Sllai1 parent = VTOSDEV(dvp); 4122621Sllai1 ASSERT(parent); 4132621Sllai1 4142621Sllai1 rw_enter(&parent->sdev_dotdot->sdev_contents, RW_READER); 4152621Sllai1 if (parent->sdev_state == SDEV_ZOMBIE) { 4162621Sllai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 4172621Sllai1 return (ENOENT); 4182621Sllai1 } 4192621Sllai1 4202621Sllai1 /* non-global do not allow pure node creation */ 4212621Sllai1 if (!SDEV_IS_GLOBAL(parent)) { 4222621Sllai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 4232621Sllai1 return (prof_lookup(dvp, nm, vpp, cred)); 4242621Sllai1 } 4252621Sllai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 4262621Sllai1 4273843Sjg /* execute access is required to search the directory */ 428*5331Samw if ((error = VOP_ACCESS(dvp, VEXEC|VWRITE, 0, cred, ct)) != 0) 4293843Sjg return (error); 4303843Sjg 4312621Sllai1 /* check existing name */ 432*5331Samw /* XXXci - We may need to translate the C-I flags on VOP_LOOKUP */ 433*5331Samw error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL); 4342621Sllai1 4352621Sllai1 /* name found */ 4362621Sllai1 if (error == 0) { 4372621Sllai1 ASSERT(vp); 4382621Sllai1 if (excl == EXCL) { 4392621Sllai1 error = EEXIST; 4402621Sllai1 } else if ((vp->v_type == VDIR) && (mode & VWRITE)) { 4412621Sllai1 /* allowing create/read-only an existing directory */ 4422621Sllai1 error = EISDIR; 4432621Sllai1 } else { 444*5331Samw error = VOP_ACCESS(vp, mode, flag, cred, ct); 4452621Sllai1 } 4462621Sllai1 4472621Sllai1 if (error) { 4482621Sllai1 VN_RELE(vp); 4492621Sllai1 return (error); 4502621Sllai1 } 4512621Sllai1 4522621Sllai1 /* truncation first */ 4532621Sllai1 if ((vp->v_type == VREG) && (vap->va_mask & AT_SIZE) && 4542621Sllai1 (vap->va_size == 0)) { 4552621Sllai1 ASSERT(parent->sdev_attrvp); 4562621Sllai1 error = VOP_CREATE(parent->sdev_attrvp, 457*5331Samw nm, vap, excl, mode, &avp, cred, flag, ct, vsecp); 4582621Sllai1 4592621Sllai1 if (error) { 4602621Sllai1 VN_RELE(vp); 4612621Sllai1 return (error); 4622621Sllai1 } 4632621Sllai1 } 4642621Sllai1 4652621Sllai1 sdev_update_timestamps(vp, kcred, 4662621Sllai1 AT_CTIME|AT_MTIME|AT_ATIME); 4672621Sllai1 *vpp = vp; 4682621Sllai1 return (0); 4692621Sllai1 } 4702621Sllai1 4712621Sllai1 /* bail out early */ 4722621Sllai1 if (error != ENOENT) 4732621Sllai1 return (error); 4742621Sllai1 4752621Sllai1 /* 4762621Sllai1 * For memory-based (ROFS) directory: 4772621Sllai1 * - either disallow node creation; 4782621Sllai1 * - or implement VOP_CREATE of its own 4792621Sllai1 */ 4802621Sllai1 rw_enter(&parent->sdev_contents, RW_WRITER); 4812621Sllai1 if (!SDEV_IS_PERSIST(parent)) { 4822621Sllai1 rw_exit(&parent->sdev_contents); 4832621Sllai1 return (ENOTSUP); 4842621Sllai1 } 4852621Sllai1 ASSERT(parent->sdev_attrvp); 4862621Sllai1 error = sdev_mknode(parent, nm, &self, vap, NULL, NULL, 4872621Sllai1 cred, SDEV_READY); 4882621Sllai1 if (error) { 4892621Sllai1 rw_exit(&parent->sdev_contents); 4902621Sllai1 if (self) 4912621Sllai1 SDEV_RELE(self); 4922621Sllai1 return (error); 4932621Sllai1 } 4942621Sllai1 rw_exit(&parent->sdev_contents); 4952621Sllai1 4962621Sllai1 ASSERT(self); 4972621Sllai1 /* take care the timestamps for the node and its parent */ 4982621Sllai1 sdev_update_timestamps(SDEVTOV(self), kcred, 4992621Sllai1 AT_CTIME|AT_MTIME|AT_ATIME); 5002621Sllai1 sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME); 5012621Sllai1 if (SDEV_IS_GLOBAL(parent)) 5022621Sllai1 atomic_inc_ulong(&parent->sdev_gdir_gen); 5032621Sllai1 5042621Sllai1 /* wake up other threads blocked on looking up this node */ 5052621Sllai1 mutex_enter(&self->sdev_lookup_lock); 5062621Sllai1 SDEV_UNBLOCK_OTHERS(self, SDEV_LOOKUP); 5072621Sllai1 mutex_exit(&self->sdev_lookup_lock); 5082621Sllai1 error = sdev_to_vp(self, vpp); 5092621Sllai1 return (error); 5102621Sllai1 } 5112621Sllai1 5122621Sllai1 static int 513*5331Samw sdev_remove(struct vnode *dvp, char *nm, struct cred *cred, 514*5331Samw caller_context_t *ct, int flags) 5152621Sllai1 { 5162621Sllai1 int error; 5172621Sllai1 struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp); 5182621Sllai1 struct vnode *vp = NULL; 5192621Sllai1 struct sdev_node *dv = NULL; 5202621Sllai1 struct devname_nsmap *map = NULL; 5212621Sllai1 struct devname_ops *dirops = NULL; 5222621Sllai1 int (*fn)(devname_handle_t *); 5232621Sllai1 int len; 5242621Sllai1 int bkstore = 0; 5252621Sllai1 5262621Sllai1 /* bail out early */ 5272621Sllai1 len = strlen(nm); 5282621Sllai1 if (nm[0] == '.') { 5292621Sllai1 if (len == 1) { 5302621Sllai1 return (EINVAL); 5312621Sllai1 } else if (len == 2 && nm[1] == '.') { 5322621Sllai1 return (EEXIST); 5332621Sllai1 } 5342621Sllai1 } 5352621Sllai1 5362621Sllai1 ASSERT(parent); 5372621Sllai1 rw_enter(&parent->sdev_contents, RW_READER); 5382621Sllai1 if (!SDEV_IS_GLOBAL(parent)) { 5392621Sllai1 rw_exit(&parent->sdev_contents); 5402621Sllai1 return (ENOTSUP); 5412621Sllai1 } 5422621Sllai1 5433843Sjg /* execute access is required to search the directory */ 544*5331Samw if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) { 5453843Sjg rw_exit(&parent->sdev_contents); 5463843Sjg return (error); 5473843Sjg } 5483843Sjg 5492621Sllai1 /* check existence first */ 5502621Sllai1 dv = sdev_cache_lookup(parent, nm); 5512621Sllai1 if (dv == NULL) { 5522621Sllai1 rw_exit(&parent->sdev_contents); 5532621Sllai1 return (ENOENT); 5542621Sllai1 } 5552621Sllai1 5562621Sllai1 vp = SDEVTOV(dv); 5572621Sllai1 if ((dv->sdev_state == SDEV_INIT) || 5582621Sllai1 (dv->sdev_state == SDEV_ZOMBIE)) { 5592621Sllai1 rw_exit(&parent->sdev_contents); 5602621Sllai1 VN_RELE(vp); 5612621Sllai1 return (ENOENT); 5622621Sllai1 } 5632621Sllai1 5643843Sjg /* write access is required to remove an entry */ 565*5331Samw if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred, ct)) != 0) { 5663843Sjg rw_exit(&parent->sdev_contents); 5673843Sjg VN_RELE(vp); 5683843Sjg return (error); 5693843Sjg } 5703843Sjg 5712621Sllai1 /* the module may record/reject removing a device node */ 5722621Sllai1 map = sdev_get_map(parent, 0); 5732621Sllai1 dirops = map ? map->dir_ops : NULL; 5742621Sllai1 if (dirops && ((fn = dirops->devnops_remove) != NULL)) { 5752621Sllai1 error = (*fn)(&(dv->sdev_handle)); 5762621Sllai1 if (error) { 5772621Sllai1 rw_exit(&parent->sdev_contents); 5782621Sllai1 VN_RELE(vp); 5792621Sllai1 return (error); 5802621Sllai1 } 5812621Sllai1 } 5822621Sllai1 5832621Sllai1 /* 5842621Sllai1 * sdev_dirdelete does the real job of: 5852621Sllai1 * - make sure no open ref count 5862621Sllai1 * - destroying the sdev_node 5872621Sllai1 * - releasing the hold on attrvp 5882621Sllai1 */ 5892621Sllai1 bkstore = SDEV_IS_PERSIST(dv) ? 1 : 0; 5902621Sllai1 if (!rw_tryupgrade(&parent->sdev_contents)) { 5912621Sllai1 rw_exit(&parent->sdev_contents); 5922621Sllai1 rw_enter(&parent->sdev_contents, RW_WRITER); 5932621Sllai1 } 5942621Sllai1 error = sdev_cache_update(parent, &dv, nm, SDEV_CACHE_DELETE); 5952621Sllai1 rw_exit(&parent->sdev_contents); 5962621Sllai1 5972621Sllai1 sdcmn_err2(("sdev_remove: cache_update error %d\n", error)); 5982621Sllai1 if (error && (error != EBUSY)) { 5992621Sllai1 /* report errors other than EBUSY */ 6002621Sllai1 VN_RELE(vp); 6012621Sllai1 } else { 6022621Sllai1 sdcmn_err2(("sdev_remove: cleaning node %s from cache " 6032621Sllai1 " with error %d\n", nm, error)); 6042621Sllai1 6052621Sllai1 /* 6062621Sllai1 * best efforts clean up the backing store 6072621Sllai1 */ 6082621Sllai1 if (bkstore) { 6092621Sllai1 ASSERT(parent->sdev_attrvp); 610*5331Samw error = VOP_REMOVE(parent->sdev_attrvp, nm, cred, 611*5331Samw ct, flags); 6122621Sllai1 /* 6132621Sllai1 * do not report BUSY error 6142621Sllai1 * because the backing store ref count is released 6152621Sllai1 * when the last ref count on the sdev_node is 6162621Sllai1 * released. 6172621Sllai1 */ 6182621Sllai1 if (error == EBUSY) { 6192621Sllai1 sdcmn_err2(("sdev_remove: device %s is still on" 6202621Sllai1 "disk %s\n", nm, parent->sdev_path)); 6212621Sllai1 error = 0; 6222621Sllai1 } 6232621Sllai1 } 6242621Sllai1 6252621Sllai1 if (error == EBUSY) 6262621Sllai1 error = 0; 6272621Sllai1 } 6282621Sllai1 6292621Sllai1 return (error); 6302621Sllai1 } 6312621Sllai1 6322621Sllai1 /* 6332621Sllai1 * Some restrictions for this file system: 6342621Sllai1 * - both oldnm and newnm are in the scope of /dev file system, 6352621Sllai1 * to simply the namespace management model. 6362621Sllai1 */ 637*5331Samw /*ARGSUSED6*/ 6382621Sllai1 static int 6392621Sllai1 sdev_rename(struct vnode *odvp, char *onm, struct vnode *ndvp, char *nnm, 640*5331Samw struct cred *cred, caller_context_t *ct, int flags) 6412621Sllai1 { 6422621Sllai1 struct sdev_node *fromparent = NULL; 6432621Sllai1 struct vattr vattr; 6442621Sllai1 struct sdev_node *toparent; 6452621Sllai1 struct sdev_node *fromdv = NULL; /* source node */ 6462729Sllai1 struct vnode *ovp = NULL; /* source vnode */ 6472621Sllai1 struct sdev_node *todv = NULL; /* destination node */ 6482729Sllai1 struct vnode *nvp = NULL; /* destination vnode */ 6492621Sllai1 int samedir = 0; /* set if odvp == ndvp */ 6502621Sllai1 struct vnode *realvp; 6512621Sllai1 int len; 6522621Sllai1 char nnm_path[MAXPATHLEN]; 6532621Sllai1 struct devname_nsmap *omap = NULL; 6542621Sllai1 struct devname_ops *odirops = NULL; 6552621Sllai1 int (*fn)(devname_handle_t *, char *); 6562621Sllai1 int (*rmfn)(devname_handle_t *); 6572621Sllai1 int error = 0; 6582621Sllai1 dev_t fsid; 6592621Sllai1 int bkstore = 0; 6602729Sllai1 vtype_t type; 6612621Sllai1 6622621Sllai1 /* prevent modifying "." and ".." */ 6632621Sllai1 if ((onm[0] == '.' && 6642729Sllai1 (onm[1] == '\0' || (onm[1] == '.' && onm[2] == '\0'))) || 6652729Sllai1 (nnm[0] == '.' && 6662729Sllai1 (nnm[1] == '\0' || (nnm[1] == '.' && nnm[2] == '\0')))) { 6672621Sllai1 return (EINVAL); 6682621Sllai1 } 6692621Sllai1 6702621Sllai1 fromparent = VTOSDEV(odvp); 6712621Sllai1 toparent = VTOSDEV(ndvp); 6722621Sllai1 6732621Sllai1 /* ZOMBIE parent doesn't allow new node creation */ 6742621Sllai1 rw_enter(&fromparent->sdev_dotdot->sdev_contents, RW_READER); 6752621Sllai1 if (fromparent->sdev_state == SDEV_ZOMBIE) { 6762621Sllai1 rw_exit(&fromparent->sdev_dotdot->sdev_contents); 6772621Sllai1 return (ENOENT); 6782621Sllai1 } 6792621Sllai1 6802621Sllai1 /* renaming only supported for global device nodes */ 6812621Sllai1 if (!SDEV_IS_GLOBAL(fromparent)) { 6822621Sllai1 rw_exit(&fromparent->sdev_dotdot->sdev_contents); 6832621Sllai1 return (ENOTSUP); 6842621Sllai1 } 6852621Sllai1 rw_exit(&fromparent->sdev_dotdot->sdev_contents); 6862621Sllai1 6872621Sllai1 rw_enter(&toparent->sdev_dotdot->sdev_contents, RW_READER); 6882621Sllai1 if (toparent->sdev_state == SDEV_ZOMBIE) { 6892621Sllai1 rw_exit(&toparent->sdev_dotdot->sdev_contents); 6902621Sllai1 return (ENOENT); 6912621Sllai1 } 6922621Sllai1 rw_exit(&toparent->sdev_dotdot->sdev_contents); 6932621Sllai1 6942729Sllai1 /* 6953843Sjg * acquire the global lock to prevent 6962729Sllai1 * mount/unmount/other rename activities. 6972729Sllai1 */ 6982729Sllai1 mutex_enter(&sdev_lock); 6992729Sllai1 7002621Sllai1 /* check existence of the source node */ 701*5331Samw /* XXXci - We may need to translate the C-I flags on VOP_LOOKUP */ 702*5331Samw error = VOP_LOOKUP(odvp, onm, &ovp, NULL, 0, NULL, cred, ct, 703*5331Samw NULL, NULL); 7042621Sllai1 if (error) { 7052621Sllai1 sdcmn_err2(("sdev_rename: the source node %s exists\n", 7062621Sllai1 onm)); 7072729Sllai1 mutex_exit(&sdev_lock); 7082621Sllai1 return (error); 7092621Sllai1 } 7102621Sllai1 711*5331Samw if (VOP_REALVP(ovp, &realvp, ct) == 0) { 7122621Sllai1 VN_HOLD(realvp); 7132621Sllai1 VN_RELE(ovp); 7142621Sllai1 ovp = realvp; 7152621Sllai1 } 7162621Sllai1 7172621Sllai1 /* check existence of destination */ 718*5331Samw /* XXXci - We may need to translate the C-I flags on VOP_LOOKUP */ 719*5331Samw error = VOP_LOOKUP(ndvp, nnm, &nvp, NULL, 0, NULL, cred, ct, 720*5331Samw NULL, NULL); 7212621Sllai1 if (error && (error != ENOENT)) { 7222729Sllai1 mutex_exit(&sdev_lock); 7232621Sllai1 VN_RELE(ovp); 7242621Sllai1 return (error); 7252621Sllai1 } 7262621Sllai1 727*5331Samw if (nvp && (VOP_REALVP(nvp, &realvp, ct) == 0)) { 7282621Sllai1 VN_HOLD(realvp); 7292621Sllai1 VN_RELE(nvp); 7302621Sllai1 nvp = realvp; 7312621Sllai1 } 7322621Sllai1 7332621Sllai1 /* 7342729Sllai1 * make sure the source and the destination are 7352729Sllai1 * in the same dev filesystem 7362621Sllai1 */ 7372621Sllai1 if (odvp != ndvp) { 7382621Sllai1 vattr.va_mask = AT_FSID; 739*5331Samw if (error = VOP_GETATTR(odvp, &vattr, 0, cred, ct)) { 7402729Sllai1 mutex_exit(&sdev_lock); 7412621Sllai1 VN_RELE(ovp); 7422621Sllai1 return (error); 7432621Sllai1 } 7442621Sllai1 fsid = vattr.va_fsid; 7452621Sllai1 vattr.va_mask = AT_FSID; 746*5331Samw if (error = VOP_GETATTR(ndvp, &vattr, 0, cred, ct)) { 7472729Sllai1 mutex_exit(&sdev_lock); 7482621Sllai1 VN_RELE(ovp); 7492621Sllai1 return (error); 7502621Sllai1 } 7512621Sllai1 if (fsid != vattr.va_fsid) { 7522729Sllai1 mutex_exit(&sdev_lock); 7532621Sllai1 VN_RELE(ovp); 7542621Sllai1 return (EXDEV); 7552621Sllai1 } 7562621Sllai1 } 7572621Sllai1 7582621Sllai1 /* make sure the old entry can be deleted */ 759*5331Samw error = VOP_ACCESS(odvp, VWRITE, 0, cred, ct); 7602621Sllai1 if (error) { 7612729Sllai1 mutex_exit(&sdev_lock); 7622621Sllai1 VN_RELE(ovp); 7632621Sllai1 return (error); 7642621Sllai1 } 7652621Sllai1 7662621Sllai1 /* make sure the destination allows creation */ 7672621Sllai1 samedir = (fromparent == toparent); 7682621Sllai1 if (!samedir) { 769*5331Samw error = VOP_ACCESS(ndvp, VEXEC|VWRITE, 0, cred, ct); 7702621Sllai1 if (error) { 7712729Sllai1 mutex_exit(&sdev_lock); 7722621Sllai1 VN_RELE(ovp); 7732621Sllai1 return (error); 7742621Sllai1 } 7752621Sllai1 } 7762621Sllai1 7772621Sllai1 fromdv = VTOSDEV(ovp); 7782621Sllai1 ASSERT(fromdv); 7792621Sllai1 7802621Sllai1 /* check with the plug-in modules for the source directory */ 7812621Sllai1 rw_enter(&fromparent->sdev_contents, RW_READER); 7822621Sllai1 omap = sdev_get_map(fromparent, 0); 7832621Sllai1 rw_exit(&fromparent->sdev_contents); 7842621Sllai1 odirops = omap ? omap->dir_ops : NULL; 7852621Sllai1 if (odirops && ((fn = odirops->devnops_rename) != NULL)) { 7862621Sllai1 if (samedir) { 7872621Sllai1 error = (*fn)(&(fromdv->sdev_handle), nnm); 7882621Sllai1 } else { 7892621Sllai1 len = strlen(nnm) + strlen(toparent->sdev_name) + 2; 7902621Sllai1 (void) snprintf(nnm_path, len, "%s/%s", 7912621Sllai1 toparent->sdev_name, nnm); 7922621Sllai1 error = (*fn)(&(fromdv->sdev_handle), nnm); 7932621Sllai1 } 7942621Sllai1 7952621Sllai1 if (error) { 7962729Sllai1 mutex_exit(&sdev_lock); 7972729Sllai1 sdcmn_err2(("sdev_rename: DBNR doesn't " 7982729Sllai1 "allow rename, error %d", error)); 7992621Sllai1 VN_RELE(ovp); 8002621Sllai1 return (error); 8012621Sllai1 } 8022621Sllai1 } 8032621Sllai1 8042729Sllai1 /* destination file exists */ 8052621Sllai1 if (nvp) { 8062729Sllai1 todv = VTOSDEV(nvp); 8072729Sllai1 ASSERT(todv); 8082621Sllai1 } 8092621Sllai1 8102621Sllai1 /* 8112621Sllai1 * link source to new target in the memory 8122621Sllai1 */ 8132729Sllai1 error = sdev_rnmnode(fromparent, fromdv, toparent, &todv, nnm, cred); 8142621Sllai1 if (error) { 8152621Sllai1 sdcmn_err2(("sdev_rename: renaming %s to %s failed " 8162621Sllai1 " with error %d\n", onm, nnm, error)); 8172729Sllai1 mutex_exit(&sdev_lock); 8182729Sllai1 if (nvp) 8192729Sllai1 VN_RELE(nvp); 8202621Sllai1 VN_RELE(ovp); 8212729Sllai1 return (error); 8222729Sllai1 } 8232729Sllai1 8242729Sllai1 /* notify the DBNR module the node is going away */ 8252729Sllai1 if (odirops && ((rmfn = odirops->devnops_remove) != NULL)) { 8262729Sllai1 (void) (*rmfn)(&(fromdv->sdev_handle)); 8272729Sllai1 } 8282729Sllai1 8292729Sllai1 /* 8302729Sllai1 * unlink from source 8312729Sllai1 */ 8322729Sllai1 rw_enter(&fromparent->sdev_contents, RW_READER); 8332729Sllai1 fromdv = sdev_cache_lookup(fromparent, onm); 8342729Sllai1 if (fromdv == NULL) { 8352729Sllai1 rw_exit(&fromparent->sdev_contents); 8362729Sllai1 mutex_exit(&sdev_lock); 8372729Sllai1 sdcmn_err2(("sdev_rename: the source is deleted already\n")); 8382729Sllai1 return (0); 8392621Sllai1 } 8402621Sllai1 8412729Sllai1 if (fromdv->sdev_state == SDEV_ZOMBIE) { 8422729Sllai1 rw_exit(&fromparent->sdev_contents); 8432729Sllai1 mutex_exit(&sdev_lock); 8442729Sllai1 VN_RELE(SDEVTOV(fromdv)); 8452729Sllai1 sdcmn_err2(("sdev_rename: the source is being deleted\n")); 8462729Sllai1 return (0); 8472729Sllai1 } 8482729Sllai1 rw_exit(&fromparent->sdev_contents); 8492729Sllai1 ASSERT(SDEVTOV(fromdv) == ovp); 8502729Sllai1 VN_RELE(ovp); 8512729Sllai1 8522729Sllai1 /* clean out the directory contents before it can be removed */ 8532729Sllai1 type = SDEVTOV(fromdv)->v_type; 8542729Sllai1 if (type == VDIR) { 8552729Sllai1 error = sdev_cleandir(fromdv, NULL, 0); 8562729Sllai1 sdcmn_err2(("sdev_rename: cleandir finished with %d\n", 8572729Sllai1 error)); 8582729Sllai1 if (error == EBUSY) 8592729Sllai1 error = 0; 8602729Sllai1 } 8612729Sllai1 8622729Sllai1 rw_enter(&fromparent->sdev_contents, RW_WRITER); 8632729Sllai1 bkstore = SDEV_IS_PERSIST(fromdv) ? 1 : 0; 8642729Sllai1 error = sdev_cache_update(fromparent, &fromdv, onm, 8652729Sllai1 SDEV_CACHE_DELETE); 8662729Sllai1 8672729Sllai1 /* best effforts clean up the backing store */ 8682729Sllai1 if (bkstore) { 8692729Sllai1 ASSERT(fromparent->sdev_attrvp); 8702729Sllai1 if (type != VDIR) { 871*5331Samw /* XXXci - We may need to translate the C-I flags on VOP_REMOVE */ 8722729Sllai1 error = VOP_REMOVE(fromparent->sdev_attrvp, 873*5331Samw onm, kcred, ct, 0); 8742729Sllai1 } else { 875*5331Samw /* XXXci - We may need to translate the C-I flags on VOP_RMDIR */ 8762729Sllai1 error = VOP_RMDIR(fromparent->sdev_attrvp, 877*5331Samw onm, fromparent->sdev_attrvp, kcred, ct, 0); 8782729Sllai1 } 8792729Sllai1 8802729Sllai1 if (error) { 8812729Sllai1 sdcmn_err2(("sdev_rename: device %s is " 8822729Sllai1 "still on disk %s\n", onm, 8832729Sllai1 fromparent->sdev_path)); 8842729Sllai1 error = 0; 8852729Sllai1 } 8862729Sllai1 } 8872729Sllai1 rw_exit(&fromparent->sdev_contents); 8882729Sllai1 mutex_exit(&sdev_lock); 8892729Sllai1 8902729Sllai1 /* once reached to this point, the rename is regarded successful */ 8912729Sllai1 return (0); 8922621Sllai1 } 8932621Sllai1 8942621Sllai1 /* 8952621Sllai1 * dev-fs version of "ln -s path dev-name" 8962621Sllai1 * tnm - path, e.g. /devices/... or /dev/... 8972621Sllai1 * lnm - dev_name 8982621Sllai1 */ 899*5331Samw /*ARGSUSED6*/ 9002621Sllai1 static int 9012621Sllai1 sdev_symlink(struct vnode *dvp, char *lnm, struct vattr *tva, 902*5331Samw char *tnm, struct cred *cred, caller_context_t *ct, int flags) 9032621Sllai1 { 9042621Sllai1 int error; 9052621Sllai1 struct vnode *vp = NULL; 9062621Sllai1 struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp); 9072621Sllai1 struct sdev_node *self = (struct sdev_node *)NULL; 9082621Sllai1 9092621Sllai1 ASSERT(parent); 9102621Sllai1 rw_enter(&parent->sdev_dotdot->sdev_contents, RW_READER); 9112621Sllai1 if (parent->sdev_state == SDEV_ZOMBIE) { 9122621Sllai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 9132621Sllai1 sdcmn_err2(("sdev_symlink: parent %s is ZOMBIED \n", 9142621Sllai1 parent->sdev_name)); 9152621Sllai1 return (ENOENT); 9162621Sllai1 } 9172621Sllai1 9182621Sllai1 if (!SDEV_IS_GLOBAL(parent)) { 9192621Sllai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 9202621Sllai1 return (ENOTSUP); 9212621Sllai1 } 9222621Sllai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 9232621Sllai1 9243843Sjg /* execute access is required to search a directory */ 925*5331Samw if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) 9263843Sjg return (error); 9273843Sjg 9282621Sllai1 /* find existing name */ 929*5331Samw /* XXXci - We may need to translate the C-I flags here */ 930*5331Samw error = VOP_LOOKUP(dvp, lnm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL); 9312621Sllai1 if (error == 0) { 9322621Sllai1 ASSERT(vp); 9332621Sllai1 VN_RELE(vp); 9342621Sllai1 sdcmn_err2(("sdev_symlink: node %s already exists\n", lnm)); 9352621Sllai1 return (EEXIST); 9362621Sllai1 } 9373843Sjg if (error != ENOENT) 9383843Sjg return (error); 9392621Sllai1 9403843Sjg /* write access is required to create a symlink */ 941*5331Samw if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred, ct)) != 0) 9422621Sllai1 return (error); 9432621Sllai1 9442621Sllai1 /* put it into memory cache */ 9452621Sllai1 rw_enter(&parent->sdev_contents, RW_WRITER); 9462621Sllai1 error = sdev_mknode(parent, lnm, &self, tva, NULL, (void *)tnm, 9472621Sllai1 cred, SDEV_READY); 9482621Sllai1 if (error) { 9492621Sllai1 rw_exit(&parent->sdev_contents); 9502621Sllai1 sdcmn_err2(("sdev_symlink: node %s creation failed\n", lnm)); 9512621Sllai1 if (self) 9522621Sllai1 SDEV_RELE(self); 9532621Sllai1 9542621Sllai1 return (error); 9552621Sllai1 } 9562621Sllai1 ASSERT(self && (self->sdev_state == SDEV_READY)); 9572621Sllai1 rw_exit(&parent->sdev_contents); 9582621Sllai1 9592621Sllai1 /* take care the timestamps for the node and its parent */ 9602621Sllai1 sdev_update_timestamps(SDEVTOV(self), kcred, 9612621Sllai1 AT_CTIME|AT_MTIME|AT_ATIME); 9622621Sllai1 sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME); 9632621Sllai1 if (SDEV_IS_GLOBAL(parent)) 9642621Sllai1 atomic_inc_ulong(&parent->sdev_gdir_gen); 9652621Sllai1 9662621Sllai1 /* wake up other threads blocked on looking up this node */ 9672621Sllai1 mutex_enter(&self->sdev_lookup_lock); 9682621Sllai1 SDEV_UNBLOCK_OTHERS(self, SDEV_LOOKUP); 9692621Sllai1 mutex_exit(&self->sdev_lookup_lock); 9702621Sllai1 SDEV_RELE(self); /* don't return with vnode held */ 9712621Sllai1 return (0); 9722621Sllai1 } 9732621Sllai1 974*5331Samw /*ARGSUSED6*/ 9752621Sllai1 static int 9762621Sllai1 sdev_mkdir(struct vnode *dvp, char *nm, struct vattr *va, struct vnode **vpp, 977*5331Samw struct cred *cred, caller_context_t *ct, int flags, vsecattr_t *vsecp) 9782621Sllai1 { 9792621Sllai1 int error; 9802621Sllai1 struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp); 9812621Sllai1 struct sdev_node *self = NULL; 9822621Sllai1 struct vnode *vp = NULL; 9832621Sllai1 9842621Sllai1 ASSERT(parent && parent->sdev_dotdot); 9852621Sllai1 rw_enter(&parent->sdev_dotdot->sdev_contents, RW_READER); 9862621Sllai1 if (parent->sdev_state == SDEV_ZOMBIE) { 9872621Sllai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 9882621Sllai1 return (ENOENT); 9892621Sllai1 } 9902621Sllai1 9912621Sllai1 /* non-global do not allow pure directory creation */ 9922621Sllai1 if (!SDEV_IS_GLOBAL(parent)) { 9932621Sllai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 9942621Sllai1 return (prof_lookup(dvp, nm, vpp, cred)); 9952621Sllai1 } 9962621Sllai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 9972621Sllai1 9983843Sjg /* execute access is required to search the directory */ 999*5331Samw if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) { 10003843Sjg return (error); 10013843Sjg } 10023843Sjg 10032621Sllai1 /* find existing name */ 1004*5331Samw /* XXXci - We may need to translate the C-I flags on VOP_LOOKUP */ 1005*5331Samw error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL); 10062621Sllai1 if (error == 0) { 10072621Sllai1 VN_RELE(vp); 10082621Sllai1 return (EEXIST); 10092621Sllai1 } 10102621Sllai1 if (error != ENOENT) 10112621Sllai1 return (error); 10122621Sllai1 10133843Sjg /* require write access to create a directory */ 1014*5331Samw if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred, ct)) != 0) { 10153843Sjg return (error); 10163843Sjg } 10173843Sjg 10182621Sllai1 /* put it into memory */ 10192621Sllai1 rw_enter(&parent->sdev_contents, RW_WRITER); 10202621Sllai1 error = sdev_mknode(parent, nm, &self, 10212621Sllai1 va, NULL, NULL, cred, SDEV_READY); 10222621Sllai1 if (error) { 10232621Sllai1 rw_exit(&parent->sdev_contents); 10242621Sllai1 if (self) 10252621Sllai1 SDEV_RELE(self); 10262621Sllai1 return (error); 10272621Sllai1 } 10282621Sllai1 ASSERT(self && (self->sdev_state == SDEV_READY)); 10292621Sllai1 rw_exit(&parent->sdev_contents); 10302621Sllai1 10312621Sllai1 /* take care the timestamps for the node and its parent */ 10322621Sllai1 sdev_update_timestamps(SDEVTOV(self), kcred, 10332621Sllai1 AT_CTIME|AT_MTIME|AT_ATIME); 10342621Sllai1 sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME); 10352621Sllai1 if (SDEV_IS_GLOBAL(parent)) 10362621Sllai1 atomic_inc_ulong(&parent->sdev_gdir_gen); 10372621Sllai1 10382621Sllai1 /* wake up other threads blocked on looking up this node */ 10392621Sllai1 mutex_enter(&self->sdev_lookup_lock); 10402621Sllai1 SDEV_UNBLOCK_OTHERS(self, SDEV_LOOKUP); 10412621Sllai1 mutex_exit(&self->sdev_lookup_lock); 10422621Sllai1 *vpp = SDEVTOV(self); 10432621Sllai1 return (0); 10442621Sllai1 } 10452621Sllai1 10462621Sllai1 /* 10472621Sllai1 * allowing removing an empty directory under /dev 10482621Sllai1 */ 10492621Sllai1 /*ARGSUSED*/ 10502621Sllai1 static int 1051*5331Samw sdev_rmdir(struct vnode *dvp, char *nm, struct vnode *cdir, struct cred *cred, 1052*5331Samw caller_context_t *ct, int flags) 10532621Sllai1 { 10542621Sllai1 int error = 0; 10552621Sllai1 struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp); 10562621Sllai1 struct sdev_node *self = NULL; 10572621Sllai1 struct vnode *vp = NULL; 10582621Sllai1 10592621Sllai1 /* bail out early */ 10602621Sllai1 if (strcmp(nm, ".") == 0) 10612621Sllai1 return (EINVAL); 10622621Sllai1 if (strcmp(nm, "..") == 0) 10632621Sllai1 return (EEXIST); /* should be ENOTEMPTY */ 10642621Sllai1 10652621Sllai1 /* no destruction of non-global node */ 10662621Sllai1 ASSERT(parent && parent->sdev_dotdot); 10672621Sllai1 rw_enter(&parent->sdev_dotdot->sdev_contents, RW_READER); 10682621Sllai1 if (!SDEV_IS_GLOBAL(parent)) { 10692621Sllai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 10702621Sllai1 return (ENOTSUP); 10712621Sllai1 } 10722621Sllai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 10732621Sllai1 10743843Sjg /* execute access is required to search the directory */ 1075*5331Samw if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) 10763843Sjg return (error); 10773843Sjg 10782621Sllai1 /* check existing name */ 10792621Sllai1 rw_enter(&parent->sdev_contents, RW_WRITER); 10802621Sllai1 self = sdev_cache_lookup(parent, nm); 10812621Sllai1 if (self == NULL) { 10822621Sllai1 rw_exit(&parent->sdev_contents); 10832621Sllai1 return (ENOENT); 10842621Sllai1 } 10852621Sllai1 10862621Sllai1 vp = SDEVTOV(self); 10872621Sllai1 if ((self->sdev_state == SDEV_INIT) || 10882621Sllai1 (self->sdev_state == SDEV_ZOMBIE)) { 10892621Sllai1 rw_exit(&parent->sdev_contents); 10902621Sllai1 VN_RELE(vp); 10912621Sllai1 return (ENOENT); 10922621Sllai1 } 10932621Sllai1 10943843Sjg /* write access is required to remove a directory */ 1095*5331Samw if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred, ct)) != 0) { 10963843Sjg rw_exit(&parent->sdev_contents); 10973843Sjg VN_RELE(vp); 10983843Sjg return (error); 10993843Sjg } 11003843Sjg 11012621Sllai1 /* some sanity checks */ 11022621Sllai1 if (vp == dvp || vp == cdir) { 11032621Sllai1 rw_exit(&parent->sdev_contents); 11042621Sllai1 VN_RELE(vp); 11052621Sllai1 return (EINVAL); 11062621Sllai1 } 11072621Sllai1 11082621Sllai1 if (vp->v_type != VDIR) { 11092621Sllai1 rw_exit(&parent->sdev_contents); 11102621Sllai1 VN_RELE(vp); 11112621Sllai1 return (ENOTDIR); 11122621Sllai1 } 11132621Sllai1 11142621Sllai1 if (vn_vfswlock(vp)) { 11152621Sllai1 rw_exit(&parent->sdev_contents); 11162621Sllai1 VN_RELE(vp); 11172621Sllai1 return (EBUSY); 11182621Sllai1 } 11192621Sllai1 11202621Sllai1 if (vn_mountedvfs(vp) != NULL) { 11212621Sllai1 rw_exit(&parent->sdev_contents); 11222621Sllai1 vn_vfsunlock(vp); 11232621Sllai1 VN_RELE(vp); 11242621Sllai1 return (EBUSY); 11252621Sllai1 } 11262621Sllai1 11272621Sllai1 self = VTOSDEV(vp); 11282621Sllai1 /* bail out on a non-empty directory */ 11292621Sllai1 rw_enter(&self->sdev_contents, RW_READER); 11302621Sllai1 if (self->sdev_nlink > 2) { 11312621Sllai1 rw_exit(&self->sdev_contents); 11322621Sllai1 rw_exit(&parent->sdev_contents); 11332621Sllai1 vn_vfsunlock(vp); 11342621Sllai1 VN_RELE(vp); 11352621Sllai1 return (ENOTEMPTY); 11362621Sllai1 } 11372621Sllai1 rw_exit(&self->sdev_contents); 11382621Sllai1 11392621Sllai1 /* unlink it from the directory cache */ 11402621Sllai1 error = sdev_cache_update(parent, &self, nm, SDEV_CACHE_DELETE); 11412621Sllai1 rw_exit(&parent->sdev_contents); 11422621Sllai1 vn_vfsunlock(vp); 11432621Sllai1 11442621Sllai1 if (error && (error != EBUSY)) { 11452621Sllai1 VN_RELE(vp); 11462621Sllai1 } else { 11472621Sllai1 sdcmn_err2(("sdev_rmdir: cleaning node %s from directory " 11482621Sllai1 " cache with error %d\n", nm, error)); 11492621Sllai1 11502621Sllai1 /* best effort to clean up the backing store */ 11512621Sllai1 if (SDEV_IS_PERSIST(parent)) { 11522621Sllai1 ASSERT(parent->sdev_attrvp); 11532621Sllai1 error = VOP_RMDIR(parent->sdev_attrvp, nm, 1154*5331Samw parent->sdev_attrvp, kcred, ct, flags); 11552621Sllai1 sdcmn_err2(("sdev_rmdir: cleaning device %s is on" 11562621Sllai1 " disk error %d\n", parent->sdev_path, error)); 11572621Sllai1 } 11582621Sllai1 11592621Sllai1 if (error == EBUSY) 11602621Sllai1 error = 0; 11612621Sllai1 } 11622621Sllai1 11632621Sllai1 return (error); 11642621Sllai1 } 11652621Sllai1 11662621Sllai1 /* 11672621Sllai1 * read the contents of a symbolic link 11682621Sllai1 */ 11692621Sllai1 static int 1170*5331Samw sdev_readlink(struct vnode *vp, struct uio *uiop, struct cred *cred, 1171*5331Samw caller_context_t *ct) 11722621Sllai1 { 11732621Sllai1 struct sdev_node *dv; 11742621Sllai1 int error = 0; 11752621Sllai1 11762621Sllai1 ASSERT(vp->v_type == VLNK); 11772621Sllai1 11782621Sllai1 dv = VTOSDEV(vp); 11792621Sllai1 11802621Sllai1 if (dv->sdev_attrvp) { 11812621Sllai1 /* non-NULL attrvp implys a persisted node at READY state */ 1182*5331Samw return (VOP_READLINK(dv->sdev_attrvp, uiop, cred, ct)); 11832621Sllai1 } else if (dv->sdev_symlink != NULL) { 11842621Sllai1 /* memory nodes, e.g. local nodes */ 11852621Sllai1 rw_enter(&dv->sdev_contents, RW_READER); 11862621Sllai1 sdcmn_err2(("sdev_readlink link is %s\n", dv->sdev_symlink)); 11872621Sllai1 error = uiomove(dv->sdev_symlink, strlen(dv->sdev_symlink), 11882621Sllai1 UIO_READ, uiop); 11892621Sllai1 rw_exit(&dv->sdev_contents); 11902621Sllai1 return (error); 11912621Sllai1 } 11922621Sllai1 11932621Sllai1 return (ENOENT); 11942621Sllai1 } 11952621Sllai1 1196*5331Samw /*ARGSUSED4*/ 11972621Sllai1 static int 1198*5331Samw sdev_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, int *eofp, 1199*5331Samw caller_context_t *ct, int flags) 12002621Sllai1 { 12012679Sszhou struct sdev_node *parent = VTOSDEV(dvp); 12023843Sjg int error; 12033843Sjg 12043843Sjg /* execute access is required to search the directory */ 1205*5331Samw if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) 12063843Sjg return (error); 12072679Sszhou 12082679Sszhou ASSERT(parent); 12092679Sszhou if (!SDEV_IS_GLOBAL(parent)) 12102679Sszhou prof_filldir(parent); 12112621Sllai1 return (devname_readdir_func(dvp, uiop, cred, eofp, SDEV_BROWSE)); 12122621Sllai1 } 12132621Sllai1 12142621Sllai1 /*ARGSUSED1*/ 12152621Sllai1 static void 1216*5331Samw sdev_inactive(struct vnode *vp, struct cred *cred, caller_context_t *ct) 12172621Sllai1 { 12182621Sllai1 int clean; 12192621Sllai1 struct sdev_node *dv = VTOSDEV(vp); 12202621Sllai1 struct sdev_node *ddv = dv->sdev_dotdot; 12212621Sllai1 struct sdev_node *idv; 12222621Sllai1 struct sdev_node *prev = NULL; 12232621Sllai1 int state; 12242621Sllai1 struct devname_nsmap *map = NULL; 12252621Sllai1 struct devname_ops *dirops = NULL; 12262621Sllai1 void (*fn)(devname_handle_t *, struct cred *) = NULL; 12272621Sllai1 12282621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 12292621Sllai1 state = dv->sdev_state; 12302621Sllai1 12312621Sllai1 mutex_enter(&vp->v_lock); 12322621Sllai1 ASSERT(vp->v_count >= 1); 12332621Sllai1 12342621Sllai1 clean = (vp->v_count == 1) && (state == SDEV_ZOMBIE); 12352621Sllai1 12362621Sllai1 /* 12372621Sllai1 * last ref count on the ZOMBIE node is released. 12382621Sllai1 * clean up the sdev_node, and 12392621Sllai1 * release the hold on the backing store node so that 12402621Sllai1 * the ZOMBIE backing stores also cleaned out. 12412621Sllai1 */ 12422621Sllai1 if (clean) { 12432621Sllai1 ASSERT(ddv); 12442621Sllai1 if (SDEV_IS_GLOBAL(dv)) { 12452621Sllai1 map = ddv->sdev_mapinfo; 12462621Sllai1 dirops = map ? map->dir_ops : NULL; 12472621Sllai1 if (dirops && (fn = dirops->devnops_inactive)) 12482621Sllai1 (*fn)(&(dv->sdev_handle), cred); 12492621Sllai1 } 12502621Sllai1 12512621Sllai1 ddv->sdev_nlink--; 12522621Sllai1 if (vp->v_type == VDIR) { 12532621Sllai1 dv->sdev_nlink--; 12542621Sllai1 } 12552621Sllai1 for (idv = ddv->sdev_dot; idv && idv != dv; 12565084Sjohnlev prev = idv, idv = idv->sdev_next) 12575084Sjohnlev ; 12582621Sllai1 ASSERT(idv == dv); 12592621Sllai1 if (prev == NULL) 12602621Sllai1 ddv->sdev_dot = dv->sdev_next; 12612621Sllai1 else 12622621Sllai1 prev->sdev_next = dv->sdev_next; 12632621Sllai1 dv->sdev_next = NULL; 12642621Sllai1 dv->sdev_nlink--; 12652621Sllai1 --vp->v_count; 12662621Sllai1 mutex_exit(&vp->v_lock); 12672621Sllai1 sdev_nodedestroy(dv, 0); 12682621Sllai1 } else { 12692621Sllai1 --vp->v_count; 12702621Sllai1 mutex_exit(&vp->v_lock); 12712621Sllai1 } 12722621Sllai1 rw_exit(&ddv->sdev_contents); 12732621Sllai1 } 12742621Sllai1 1275*5331Samw /*ARGSUSED2*/ 12762621Sllai1 static int 1277*5331Samw sdev_fid(struct vnode *vp, struct fid *fidp, caller_context_t *ct) 12782621Sllai1 { 12792621Sllai1 struct sdev_node *dv = VTOSDEV(vp); 12802621Sllai1 struct sdev_fid *sdev_fid; 12812621Sllai1 12822621Sllai1 if (fidp->fid_len < (sizeof (struct sdev_fid) - sizeof (ushort_t))) { 12832621Sllai1 fidp->fid_len = sizeof (struct sdev_fid) - sizeof (ushort_t); 12842621Sllai1 return (ENOSPC); 12852621Sllai1 } 12862621Sllai1 12872621Sllai1 sdev_fid = (struct sdev_fid *)fidp; 12882621Sllai1 bzero(sdev_fid, sizeof (struct sdev_fid)); 12892621Sllai1 sdev_fid->sdevfid_len = 12902621Sllai1 (int)sizeof (struct sdev_fid) - sizeof (ushort_t); 12912621Sllai1 sdev_fid->sdevfid_ino = dv->sdev_ino; 12922621Sllai1 12932621Sllai1 return (0); 12942621Sllai1 } 12952621Sllai1 12962621Sllai1 /* 12972621Sllai1 * This pair of routines bracket all VOP_READ, VOP_WRITE 12982621Sllai1 * and VOP_READDIR requests. The contents lock stops things 12992621Sllai1 * moving around while we're looking at them. 13002621Sllai1 */ 13013748Sjg /*ARGSUSED2*/ 13023748Sjg static int 13033748Sjg sdev_rwlock(struct vnode *vp, int write_flag, caller_context_t *ctp) 13042621Sllai1 { 13053748Sjg rw_enter(&VTOSDEV(vp)->sdev_contents, 13063748Sjg write_flag ? RW_WRITER : RW_READER); 13073748Sjg return (write_flag ? V_WRITELOCK_TRUE : V_WRITELOCK_FALSE); 13082621Sllai1 } 13092621Sllai1 13102621Sllai1 /*ARGSUSED1*/ 13112621Sllai1 static void 13123748Sjg sdev_rwunlock(struct vnode *vp, int write_flag, caller_context_t *ctp) 13132621Sllai1 { 13142621Sllai1 rw_exit(&VTOSDEV(vp)->sdev_contents); 13152621Sllai1 } 13162621Sllai1 13172621Sllai1 /*ARGSUSED1*/ 13182621Sllai1 static int 1319*5331Samw sdev_seek(struct vnode *vp, offset_t ooff, offset_t *noffp, 1320*5331Samw caller_context_t *ct) 13212621Sllai1 { 13222621Sllai1 struct vnode *attrvp = VTOSDEV(vp)->sdev_attrvp; 13232621Sllai1 13242621Sllai1 ASSERT(vp->v_type != VCHR && 13252621Sllai1 vp->v_type != VBLK && vp->v_type != VLNK); 13262621Sllai1 13272621Sllai1 if (vp->v_type == VDIR) 1328*5331Samw return (fs_seek(vp, ooff, noffp, ct)); 13292621Sllai1 13302621Sllai1 ASSERT(attrvp); 1331*5331Samw return (VOP_SEEK(attrvp, ooff, noffp, ct)); 13322621Sllai1 } 13332621Sllai1 13342621Sllai1 /*ARGSUSED1*/ 13352621Sllai1 static int 13362621Sllai1 sdev_frlock(struct vnode *vp, int cmd, struct flock64 *bfp, int flag, 1337*5331Samw offset_t offset, struct flk_callback *flk_cbp, struct cred *cr, 1338*5331Samw caller_context_t *ct) 13392621Sllai1 { 13402621Sllai1 int error; 13412621Sllai1 struct sdev_node *dv = VTOSDEV(vp); 13422621Sllai1 13432621Sllai1 ASSERT(dv); 13442621Sllai1 ASSERT(dv->sdev_attrvp); 13452621Sllai1 error = VOP_FRLOCK(dv->sdev_attrvp, cmd, bfp, flag, offset, 1346*5331Samw flk_cbp, cr, ct); 13472621Sllai1 13482621Sllai1 return (error); 13492621Sllai1 } 13502621Sllai1 13512621Sllai1 static int 1352*5331Samw sdev_setfl(struct vnode *vp, int oflags, int nflags, cred_t *cr, 1353*5331Samw caller_context_t *ct) 13542621Sllai1 { 13552621Sllai1 struct sdev_node *dv = VTOSDEV(vp); 13562621Sllai1 ASSERT(dv); 13572621Sllai1 ASSERT(dv->sdev_attrvp); 13582621Sllai1 1359*5331Samw return (VOP_SETFL(dv->sdev_attrvp, oflags, nflags, cr, ct)); 13602621Sllai1 } 13612621Sllai1 13622621Sllai1 static int 1363*5331Samw sdev_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr, 1364*5331Samw caller_context_t *ct) 13652621Sllai1 { 13662621Sllai1 switch (cmd) { 13672621Sllai1 case _PC_ACL_ENABLED: 13682621Sllai1 *valp = SDEV_ACL_FLAVOR(vp); 13692621Sllai1 return (0); 13702621Sllai1 } 13712621Sllai1 1372*5331Samw return (fs_pathconf(vp, cmd, valp, cr, ct)); 13732621Sllai1 } 13742621Sllai1 13752621Sllai1 vnodeops_t *sdev_vnodeops; 13762621Sllai1 13772621Sllai1 const fs_operation_def_t sdev_vnodeops_tbl[] = { 13783898Srsb VOPNAME_OPEN, { .vop_open = sdev_open }, 13793898Srsb VOPNAME_CLOSE, { .vop_close = sdev_close }, 13803898Srsb VOPNAME_READ, { .vop_read = sdev_read }, 13813898Srsb VOPNAME_WRITE, { .vop_write = sdev_write }, 13823898Srsb VOPNAME_IOCTL, { .vop_ioctl = sdev_ioctl }, 13833898Srsb VOPNAME_GETATTR, { .vop_getattr = sdev_getattr }, 13843898Srsb VOPNAME_SETATTR, { .vop_setattr = sdev_setattr }, 13853898Srsb VOPNAME_ACCESS, { .vop_access = sdev_access }, 13863898Srsb VOPNAME_LOOKUP, { .vop_lookup = sdev_lookup }, 13873898Srsb VOPNAME_CREATE, { .vop_create = sdev_create }, 13883898Srsb VOPNAME_RENAME, { .vop_rename = sdev_rename }, 13893898Srsb VOPNAME_REMOVE, { .vop_remove = sdev_remove }, 13903898Srsb VOPNAME_MKDIR, { .vop_mkdir = sdev_mkdir }, 13913898Srsb VOPNAME_RMDIR, { .vop_rmdir = sdev_rmdir }, 13923898Srsb VOPNAME_READDIR, { .vop_readdir = sdev_readdir }, 13933898Srsb VOPNAME_SYMLINK, { .vop_symlink = sdev_symlink }, 13943898Srsb VOPNAME_READLINK, { .vop_readlink = sdev_readlink }, 13953898Srsb VOPNAME_INACTIVE, { .vop_inactive = sdev_inactive }, 13963898Srsb VOPNAME_FID, { .vop_fid = sdev_fid }, 13973898Srsb VOPNAME_RWLOCK, { .vop_rwlock = sdev_rwlock }, 13983898Srsb VOPNAME_RWUNLOCK, { .vop_rwunlock = sdev_rwunlock }, 13993898Srsb VOPNAME_SEEK, { .vop_seek = sdev_seek }, 14003898Srsb VOPNAME_FRLOCK, { .vop_frlock = sdev_frlock }, 14013898Srsb VOPNAME_PATHCONF, { .vop_pathconf = sdev_pathconf }, 14023898Srsb VOPNAME_SETFL, { .vop_setfl = sdev_setfl }, 14033898Srsb VOPNAME_SETSECATTR, { .vop_setsecattr = sdev_setsecattr }, 14043898Srsb VOPNAME_GETSECATTR, { .vop_getsecattr = sdev_getsecattr }, 14053898Srsb NULL, NULL 14062621Sllai1 }; 14072621Sllai1 14082621Sllai1 int sdev_vnodeops_tbl_size = sizeof (sdev_vnodeops_tbl); 1409