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