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 * utility routines for the /dev fs 30*2621Sllai1 */ 31*2621Sllai1 32*2621Sllai1 #include <sys/types.h> 33*2621Sllai1 #include <sys/param.h> 34*2621Sllai1 #include <sys/t_lock.h> 35*2621Sllai1 #include <sys/systm.h> 36*2621Sllai1 #include <sys/sysmacros.h> 37*2621Sllai1 #include <sys/user.h> 38*2621Sllai1 #include <sys/time.h> 39*2621Sllai1 #include <sys/vfs.h> 40*2621Sllai1 #include <sys/vnode.h> 41*2621Sllai1 #include <sys/file.h> 42*2621Sllai1 #include <sys/fcntl.h> 43*2621Sllai1 #include <sys/flock.h> 44*2621Sllai1 #include <sys/kmem.h> 45*2621Sllai1 #include <sys/uio.h> 46*2621Sllai1 #include <sys/errno.h> 47*2621Sllai1 #include <sys/stat.h> 48*2621Sllai1 #include <sys/cred.h> 49*2621Sllai1 #include <sys/dirent.h> 50*2621Sllai1 #include <sys/pathname.h> 51*2621Sllai1 #include <sys/cmn_err.h> 52*2621Sllai1 #include <sys/debug.h> 53*2621Sllai1 #include <sys/mode.h> 54*2621Sllai1 #include <sys/policy.h> 55*2621Sllai1 #include <fs/fs_subr.h> 56*2621Sllai1 #include <sys/mount.h> 57*2621Sllai1 #include <sys/fs/snode.h> 58*2621Sllai1 #include <sys/fs/dv_node.h> 59*2621Sllai1 #include <sys/fs/sdev_impl.h> 60*2621Sllai1 #include <sys/fs/sdev_node.h> 61*2621Sllai1 #include <sys/sunndi.h> 62*2621Sllai1 #include <sys/sunmdi.h> 63*2621Sllai1 #include <sys/conf.h> 64*2621Sllai1 #include <sys/proc.h> 65*2621Sllai1 #include <sys/user.h> 66*2621Sllai1 #include <sys/modctl.h> 67*2621Sllai1 68*2621Sllai1 #ifdef DEBUG 69*2621Sllai1 int sdev_debug = 0x00000001; 70*2621Sllai1 int sdev_debug_cache_flags = 0; 71*2621Sllai1 #endif 72*2621Sllai1 73*2621Sllai1 /* 74*2621Sllai1 * globals 75*2621Sllai1 */ 76*2621Sllai1 /* prototype memory vattrs */ 77*2621Sllai1 vattr_t sdev_vattr_dir = { 78*2621Sllai1 AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */ 79*2621Sllai1 VDIR, /* va_type */ 80*2621Sllai1 SDEV_DIRMODE_DEFAULT, /* va_mode */ 81*2621Sllai1 SDEV_UID_DEFAULT, /* va_uid */ 82*2621Sllai1 SDEV_GID_DEFAULT, /* va_gid */ 83*2621Sllai1 0, /* va_fsid */ 84*2621Sllai1 0, /* va_nodeid */ 85*2621Sllai1 0, /* va_nlink */ 86*2621Sllai1 0, /* va_size */ 87*2621Sllai1 0, /* va_atime */ 88*2621Sllai1 0, /* va_mtime */ 89*2621Sllai1 0, /* va_ctime */ 90*2621Sllai1 0, /* va_rdev */ 91*2621Sllai1 0, /* va_blksize */ 92*2621Sllai1 0, /* va_nblocks */ 93*2621Sllai1 0 /* va_vcode */ 94*2621Sllai1 }; 95*2621Sllai1 96*2621Sllai1 vattr_t sdev_vattr_lnk = { 97*2621Sllai1 AT_TYPE|AT_MODE, /* va_mask */ 98*2621Sllai1 VLNK, /* va_type */ 99*2621Sllai1 SDEV_LNKMODE_DEFAULT, /* va_mode */ 100*2621Sllai1 SDEV_UID_DEFAULT, /* va_uid */ 101*2621Sllai1 SDEV_GID_DEFAULT, /* va_gid */ 102*2621Sllai1 0, /* va_fsid */ 103*2621Sllai1 0, /* va_nodeid */ 104*2621Sllai1 0, /* va_nlink */ 105*2621Sllai1 0, /* va_size */ 106*2621Sllai1 0, /* va_atime */ 107*2621Sllai1 0, /* va_mtime */ 108*2621Sllai1 0, /* va_ctime */ 109*2621Sllai1 0, /* va_rdev */ 110*2621Sllai1 0, /* va_blksize */ 111*2621Sllai1 0, /* va_nblocks */ 112*2621Sllai1 0 /* va_vcode */ 113*2621Sllai1 }; 114*2621Sllai1 115*2621Sllai1 vattr_t sdev_vattr_blk = { 116*2621Sllai1 AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */ 117*2621Sllai1 VBLK, /* va_type */ 118*2621Sllai1 S_IFBLK | SDEV_DEVMODE_DEFAULT, /* va_mode */ 119*2621Sllai1 SDEV_UID_DEFAULT, /* va_uid */ 120*2621Sllai1 SDEV_GID_DEFAULT, /* va_gid */ 121*2621Sllai1 0, /* va_fsid */ 122*2621Sllai1 0, /* va_nodeid */ 123*2621Sllai1 0, /* va_nlink */ 124*2621Sllai1 0, /* va_size */ 125*2621Sllai1 0, /* va_atime */ 126*2621Sllai1 0, /* va_mtime */ 127*2621Sllai1 0, /* va_ctime */ 128*2621Sllai1 0, /* va_rdev */ 129*2621Sllai1 0, /* va_blksize */ 130*2621Sllai1 0, /* va_nblocks */ 131*2621Sllai1 0 /* va_vcode */ 132*2621Sllai1 }; 133*2621Sllai1 134*2621Sllai1 vattr_t sdev_vattr_chr = { 135*2621Sllai1 AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */ 136*2621Sllai1 VCHR, /* va_type */ 137*2621Sllai1 S_IFCHR | SDEV_DEVMODE_DEFAULT, /* va_mode */ 138*2621Sllai1 SDEV_UID_DEFAULT, /* va_uid */ 139*2621Sllai1 SDEV_GID_DEFAULT, /* va_gid */ 140*2621Sllai1 0, /* va_fsid */ 141*2621Sllai1 0, /* va_nodeid */ 142*2621Sllai1 0, /* va_nlink */ 143*2621Sllai1 0, /* va_size */ 144*2621Sllai1 0, /* va_atime */ 145*2621Sllai1 0, /* va_mtime */ 146*2621Sllai1 0, /* va_ctime */ 147*2621Sllai1 0, /* va_rdev */ 148*2621Sllai1 0, /* va_blksize */ 149*2621Sllai1 0, /* va_nblocks */ 150*2621Sllai1 0 /* va_vcode */ 151*2621Sllai1 }; 152*2621Sllai1 153*2621Sllai1 kmem_cache_t *sdev_node_cache; /* sdev_node cache */ 154*2621Sllai1 int devtype; /* fstype */ 155*2621Sllai1 156*2621Sllai1 struct devname_ops *devname_ns_ops; /* default name service directory ops */ 157*2621Sllai1 kmutex_t devname_nsmaps_lock; /* protect devname_nsmaps */ 158*2621Sllai1 159*2621Sllai1 /* static */ 160*2621Sllai1 static struct devname_nsmap *devname_nsmaps = NULL; 161*2621Sllai1 /* contents from /etc/dev/devname_master */ 162*2621Sllai1 static int devname_nsmaps_invalidated = 0; /* "devfsadm -m" has run */ 163*2621Sllai1 164*2621Sllai1 static struct vnodeops *sdev_get_vop(struct sdev_node *); 165*2621Sllai1 static void sdev_set_no_nocache(struct sdev_node *); 166*2621Sllai1 static int sdev_get_moduleops(struct sdev_node *); 167*2621Sllai1 static void sdev_handle_alloc(struct sdev_node *); 168*2621Sllai1 static fs_operation_def_t *sdev_merge_vtab(const fs_operation_def_t []); 169*2621Sllai1 static void sdev_free_vtab(fs_operation_def_t *); 170*2621Sllai1 171*2621Sllai1 static void 172*2621Sllai1 sdev_prof_free(struct sdev_node *dv) 173*2621Sllai1 { 174*2621Sllai1 ASSERT(!SDEV_IS_GLOBAL(dv)); 175*2621Sllai1 if (dv->sdev_prof.dev_name) 176*2621Sllai1 nvlist_free(dv->sdev_prof.dev_name); 177*2621Sllai1 if (dv->sdev_prof.dev_map) 178*2621Sllai1 nvlist_free(dv->sdev_prof.dev_map); 179*2621Sllai1 if (dv->sdev_prof.dev_symlink) 180*2621Sllai1 nvlist_free(dv->sdev_prof.dev_symlink); 181*2621Sllai1 if (dv->sdev_prof.dev_glob_incdir) 182*2621Sllai1 nvlist_free(dv->sdev_prof.dev_glob_incdir); 183*2621Sllai1 if (dv->sdev_prof.dev_glob_excdir) 184*2621Sllai1 nvlist_free(dv->sdev_prof.dev_glob_excdir); 185*2621Sllai1 bzero(&dv->sdev_prof, sizeof (dv->sdev_prof)); 186*2621Sllai1 } 187*2621Sllai1 188*2621Sllai1 /* 189*2621Sllai1 * sdev_node cache constructor 190*2621Sllai1 */ 191*2621Sllai1 /*ARGSUSED1*/ 192*2621Sllai1 static int 193*2621Sllai1 i_sdev_node_ctor(void *buf, void *cfarg, int flag) 194*2621Sllai1 { 195*2621Sllai1 struct sdev_node *dv = (struct sdev_node *)buf; 196*2621Sllai1 struct vnode *vp; 197*2621Sllai1 198*2621Sllai1 ASSERT(flag == KM_SLEEP); 199*2621Sllai1 200*2621Sllai1 bzero(buf, sizeof (struct sdev_node)); 201*2621Sllai1 rw_init(&dv->sdev_contents, NULL, RW_DEFAULT, NULL); 202*2621Sllai1 dv->sdev_vnode = vn_alloc(KM_SLEEP); 203*2621Sllai1 vp = SDEVTOV(dv); 204*2621Sllai1 vp->v_data = (caddr_t)dv; 205*2621Sllai1 return (0); 206*2621Sllai1 } 207*2621Sllai1 208*2621Sllai1 /* sdev_node destructor for kmem cache */ 209*2621Sllai1 /*ARGSUSED1*/ 210*2621Sllai1 static void 211*2621Sllai1 i_sdev_node_dtor(void *buf, void *arg) 212*2621Sllai1 { 213*2621Sllai1 struct sdev_node *dv = (struct sdev_node *)buf; 214*2621Sllai1 struct vnode *vp = SDEVTOV(dv); 215*2621Sllai1 216*2621Sllai1 rw_destroy(&dv->sdev_contents); 217*2621Sllai1 vn_free(vp); 218*2621Sllai1 } 219*2621Sllai1 220*2621Sllai1 /* initialize sdev_node cache */ 221*2621Sllai1 void 222*2621Sllai1 sdev_node_cache_init() 223*2621Sllai1 { 224*2621Sllai1 int flags = 0; 225*2621Sllai1 226*2621Sllai1 #ifdef DEBUG 227*2621Sllai1 flags = sdev_debug_cache_flags; 228*2621Sllai1 if (flags) 229*2621Sllai1 sdcmn_err(("cache debug flags 0x%x\n", flags)); 230*2621Sllai1 #endif /* DEBUG */ 231*2621Sllai1 232*2621Sllai1 ASSERT(sdev_node_cache == NULL); 233*2621Sllai1 sdev_node_cache = kmem_cache_create("sdev_node_cache", 234*2621Sllai1 sizeof (struct sdev_node), 0, i_sdev_node_ctor, i_sdev_node_dtor, 235*2621Sllai1 NULL, NULL, NULL, flags); 236*2621Sllai1 } 237*2621Sllai1 238*2621Sllai1 /* destroy sdev_node cache */ 239*2621Sllai1 void 240*2621Sllai1 sdev_node_cache_fini() 241*2621Sllai1 { 242*2621Sllai1 ASSERT(sdev_node_cache != NULL); 243*2621Sllai1 kmem_cache_destroy(sdev_node_cache); 244*2621Sllai1 sdev_node_cache = NULL; 245*2621Sllai1 } 246*2621Sllai1 247*2621Sllai1 void 248*2621Sllai1 sdev_set_nodestate(struct sdev_node *dv, sdev_node_state_t state) 249*2621Sllai1 { 250*2621Sllai1 ASSERT(dv); 251*2621Sllai1 ASSERT(RW_WRITE_HELD(&dv->sdev_contents)); 252*2621Sllai1 dv->sdev_state = state; 253*2621Sllai1 } 254*2621Sllai1 255*2621Sllai1 static void 256*2621Sllai1 sdev_attrinit(struct sdev_node *dv, vattr_t *vap) 257*2621Sllai1 { 258*2621Sllai1 timestruc_t now; 259*2621Sllai1 260*2621Sllai1 ASSERT(vap); 261*2621Sllai1 262*2621Sllai1 dv->sdev_attr = kmem_zalloc(sizeof (struct vattr), KM_SLEEP); 263*2621Sllai1 *dv->sdev_attr = *vap; 264*2621Sllai1 265*2621Sllai1 dv->sdev_attr->va_mode = MAKEIMODE(vap->va_type, vap->va_mode); 266*2621Sllai1 267*2621Sllai1 gethrestime(&now); 268*2621Sllai1 dv->sdev_attr->va_atime = now; 269*2621Sllai1 dv->sdev_attr->va_mtime = now; 270*2621Sllai1 dv->sdev_attr->va_ctime = now; 271*2621Sllai1 } 272*2621Sllai1 273*2621Sllai1 /* alloc and initialize a sdev_node */ 274*2621Sllai1 int 275*2621Sllai1 sdev_nodeinit(struct sdev_node *ddv, char *nm, struct sdev_node **newdv, 276*2621Sllai1 vattr_t *vap) 277*2621Sllai1 { 278*2621Sllai1 struct sdev_node *dv = NULL; 279*2621Sllai1 struct vnode *vp; 280*2621Sllai1 size_t nmlen, len; 281*2621Sllai1 devname_handle_t *dhl; 282*2621Sllai1 283*2621Sllai1 nmlen = strlen(nm) + 1; 284*2621Sllai1 if (nmlen > MAXNAMELEN) { 285*2621Sllai1 sdcmn_err9(("sdev_nodeinit: node name %s" 286*2621Sllai1 " too long\n", nm)); 287*2621Sllai1 *newdv = NULL; 288*2621Sllai1 return (ENAMETOOLONG); 289*2621Sllai1 } 290*2621Sllai1 291*2621Sllai1 dv = kmem_cache_alloc(sdev_node_cache, KM_SLEEP); 292*2621Sllai1 293*2621Sllai1 dv->sdev_name = kmem_alloc(nmlen, KM_SLEEP); 294*2621Sllai1 bcopy(nm, dv->sdev_name, nmlen); 295*2621Sllai1 dv->sdev_namelen = nmlen - 1; /* '\0' not included */ 296*2621Sllai1 len = strlen(ddv->sdev_path) + strlen(nm) + 2; 297*2621Sllai1 dv->sdev_path = kmem_alloc(len, KM_SLEEP); 298*2621Sllai1 (void) snprintf(dv->sdev_path, len, "%s/%s", ddv->sdev_path, nm); 299*2621Sllai1 /* overwritten for VLNK nodes */ 300*2621Sllai1 dv->sdev_symlink = NULL; 301*2621Sllai1 302*2621Sllai1 vp = SDEVTOV(dv); 303*2621Sllai1 vn_reinit(vp); 304*2621Sllai1 vp->v_vfsp = SDEVTOV(ddv)->v_vfsp; 305*2621Sllai1 if (vap) 306*2621Sllai1 vp->v_type = vap->va_type; 307*2621Sllai1 308*2621Sllai1 /* 309*2621Sllai1 * initialized to the parent's vnodeops. 310*2621Sllai1 * maybe overwriten for a VDIR 311*2621Sllai1 */ 312*2621Sllai1 vn_setops(vp, vn_getops(SDEVTOV(ddv))); 313*2621Sllai1 vn_exists(vp); 314*2621Sllai1 315*2621Sllai1 dv->sdev_dotdot = NULL; 316*2621Sllai1 dv->sdev_dot = NULL; 317*2621Sllai1 dv->sdev_next = NULL; 318*2621Sllai1 dv->sdev_attrvp = NULL; 319*2621Sllai1 if (vap) { 320*2621Sllai1 sdev_attrinit(dv, vap); 321*2621Sllai1 } else { 322*2621Sllai1 dv->sdev_attr = NULL; 323*2621Sllai1 } 324*2621Sllai1 325*2621Sllai1 dv->sdev_ino = sdev_mkino(dv); 326*2621Sllai1 dv->sdev_nlink = 0; /* updated on insert */ 327*2621Sllai1 dv->sdev_flags = ddv->sdev_flags; /* inherit from the parent first */ 328*2621Sllai1 dv->sdev_flags |= SDEV_BUILD; 329*2621Sllai1 mutex_init(&dv->sdev_lookup_lock, NULL, MUTEX_DEFAULT, NULL); 330*2621Sllai1 cv_init(&dv->sdev_lookup_cv, NULL, CV_DEFAULT, NULL); 331*2621Sllai1 if (SDEV_IS_GLOBAL(ddv)) { 332*2621Sllai1 dv->sdev_flags |= SDEV_GLOBAL; 333*2621Sllai1 dv->sdev_mapinfo = NULL; 334*2621Sllai1 dhl = &(dv->sdev_handle); 335*2621Sllai1 dhl->dh_data = dv; 336*2621Sllai1 dhl->dh_spec = DEVNAME_NS_NONE; 337*2621Sllai1 dhl->dh_args = NULL; 338*2621Sllai1 sdev_set_no_nocache(dv); 339*2621Sllai1 dv->sdev_gdir_gen = 0; 340*2621Sllai1 } else { 341*2621Sllai1 dv->sdev_flags &= ~SDEV_GLOBAL; 342*2621Sllai1 dv->sdev_origin = NULL; /* set later */ 343*2621Sllai1 bzero(&dv->sdev_prof, sizeof (dv->sdev_prof)); 344*2621Sllai1 dv->sdev_ldir_gen = 0; 345*2621Sllai1 dv->sdev_devtree_gen = 0; 346*2621Sllai1 } 347*2621Sllai1 348*2621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 349*2621Sllai1 sdev_set_nodestate(dv, SDEV_INIT); 350*2621Sllai1 rw_exit(&dv->sdev_contents); 351*2621Sllai1 *newdv = dv; 352*2621Sllai1 353*2621Sllai1 return (0); 354*2621Sllai1 } 355*2621Sllai1 356*2621Sllai1 /* 357*2621Sllai1 * transition a sdev_node into SDEV_READY state 358*2621Sllai1 */ 359*2621Sllai1 int 360*2621Sllai1 sdev_nodeready(struct sdev_node *dv, struct vattr *vap, struct vnode *avp, 361*2621Sllai1 void *args, struct cred *cred) 362*2621Sllai1 { 363*2621Sllai1 int error = 0; 364*2621Sllai1 struct vnode *vp = SDEVTOV(dv); 365*2621Sllai1 vtype_t type; 366*2621Sllai1 367*2621Sllai1 ASSERT(dv && (dv->sdev_state != SDEV_READY) && vap); 368*2621Sllai1 369*2621Sllai1 type = vap->va_type; 370*2621Sllai1 vp->v_type = type; 371*2621Sllai1 vp->v_rdev = vap->va_rdev; 372*2621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 373*2621Sllai1 if (type == VDIR) { 374*2621Sllai1 dv->sdev_nlink = 2; 375*2621Sllai1 dv->sdev_flags &= ~SDEV_PERSIST; 376*2621Sllai1 dv->sdev_flags &= ~SDEV_DYNAMIC; 377*2621Sllai1 vn_setops(vp, sdev_get_vop(dv)); /* from internal vtab */ 378*2621Sllai1 error = sdev_get_moduleops(dv); /* from plug-in module */ 379*2621Sllai1 ASSERT(dv->sdev_dotdot); 380*2621Sllai1 ASSERT(SDEVTOV(dv->sdev_dotdot)->v_type == VDIR); 381*2621Sllai1 vp->v_rdev = SDEVTOV(dv->sdev_dotdot)->v_rdev; 382*2621Sllai1 } else if (type == VLNK) { 383*2621Sllai1 ASSERT(args); 384*2621Sllai1 dv->sdev_nlink = 1; 385*2621Sllai1 dv->sdev_symlink = i_ddi_strdup((char *)args, KM_SLEEP); 386*2621Sllai1 } else { 387*2621Sllai1 dv->sdev_nlink = 1; 388*2621Sllai1 } 389*2621Sllai1 390*2621Sllai1 if (!(SDEV_IS_GLOBAL(dv))) { 391*2621Sllai1 dv->sdev_origin = (struct sdev_node *)args; 392*2621Sllai1 dv->sdev_flags &= ~SDEV_PERSIST; 393*2621Sllai1 } 394*2621Sllai1 395*2621Sllai1 /* 396*2621Sllai1 * shadow node is created here OR 397*2621Sllai1 * if failed (indicated by dv->sdev_attrvp == NULL), 398*2621Sllai1 * created later in sdev_setattr 399*2621Sllai1 */ 400*2621Sllai1 if (avp) { 401*2621Sllai1 dv->sdev_attrvp = avp; 402*2621Sllai1 } else { 403*2621Sllai1 if (dv->sdev_attr == NULL) 404*2621Sllai1 sdev_attrinit(dv, vap); 405*2621Sllai1 else 406*2621Sllai1 *dv->sdev_attr = *vap; 407*2621Sllai1 408*2621Sllai1 if ((SDEV_IS_PERSIST(dv) && (dv->sdev_attrvp == NULL)) || 409*2621Sllai1 ((SDEVTOV(dv)->v_type == VDIR) && 410*2621Sllai1 (dv->sdev_attrvp == NULL))) 411*2621Sllai1 error = sdev_shadow_node(dv, cred); 412*2621Sllai1 } 413*2621Sllai1 414*2621Sllai1 /* transition to READY state */ 415*2621Sllai1 sdev_set_nodestate(dv, SDEV_READY); 416*2621Sllai1 sdev_nc_node_exists(dv); 417*2621Sllai1 rw_exit(&dv->sdev_contents); 418*2621Sllai1 return (error); 419*2621Sllai1 } 420*2621Sllai1 421*2621Sllai1 /* 422*2621Sllai1 * setting ZOMBIE state 423*2621Sllai1 */ 424*2621Sllai1 static int 425*2621Sllai1 sdev_nodezombied(struct sdev_node *dv) 426*2621Sllai1 { 427*2621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 428*2621Sllai1 sdev_set_nodestate(dv, SDEV_ZOMBIE); 429*2621Sllai1 rw_exit(&dv->sdev_contents); 430*2621Sllai1 return (0); 431*2621Sllai1 } 432*2621Sllai1 433*2621Sllai1 /* 434*2621Sllai1 * Build the VROOT sdev_node. 435*2621Sllai1 */ 436*2621Sllai1 /*ARGSUSED*/ 437*2621Sllai1 struct sdev_node * 438*2621Sllai1 sdev_mkroot(struct vfs *vfsp, dev_t devdev, struct vnode *mvp, 439*2621Sllai1 struct vnode *avp, struct cred *cred) 440*2621Sllai1 { 441*2621Sllai1 struct sdev_node *dv; 442*2621Sllai1 struct vnode *vp; 443*2621Sllai1 char devdir[] = "/dev"; 444*2621Sllai1 445*2621Sllai1 ASSERT(sdev_node_cache != NULL); 446*2621Sllai1 ASSERT(avp); 447*2621Sllai1 dv = kmem_cache_alloc(sdev_node_cache, KM_SLEEP); 448*2621Sllai1 vp = SDEVTOV(dv); 449*2621Sllai1 vn_reinit(vp); 450*2621Sllai1 vp->v_flag |= VROOT; 451*2621Sllai1 vp->v_vfsp = vfsp; 452*2621Sllai1 vp->v_type = VDIR; 453*2621Sllai1 vp->v_rdev = devdev; 454*2621Sllai1 vn_setops(vp, sdev_vnodeops); /* apply the default vnodeops at /dev */ 455*2621Sllai1 vn_exists(vp); 456*2621Sllai1 457*2621Sllai1 if (vfsp->vfs_mntpt) 458*2621Sllai1 dv->sdev_name = i_ddi_strdup( 459*2621Sllai1 (char *)refstr_value(vfsp->vfs_mntpt), KM_SLEEP); 460*2621Sllai1 else 461*2621Sllai1 /* vfs_mountdev1 set mount point later */ 462*2621Sllai1 dv->sdev_name = i_ddi_strdup("/dev", KM_SLEEP); 463*2621Sllai1 dv->sdev_namelen = strlen(dv->sdev_name); /* '\0' not included */ 464*2621Sllai1 dv->sdev_path = i_ddi_strdup(devdir, KM_SLEEP); 465*2621Sllai1 dv->sdev_ino = SDEV_ROOTINO; 466*2621Sllai1 dv->sdev_nlink = 2; /* name + . (no sdev_insert) */ 467*2621Sllai1 dv->sdev_dotdot = dv; /* .. == self */ 468*2621Sllai1 dv->sdev_attrvp = avp; 469*2621Sllai1 dv->sdev_attr = NULL; 470*2621Sllai1 mutex_init(&dv->sdev_lookup_lock, NULL, MUTEX_DEFAULT, NULL); 471*2621Sllai1 cv_init(&dv->sdev_lookup_cv, NULL, CV_DEFAULT, NULL); 472*2621Sllai1 if (strcmp(dv->sdev_name, "/dev") == 0) { 473*2621Sllai1 mutex_init(&devname_nsmaps_lock, NULL, MUTEX_DEFAULT, NULL); 474*2621Sllai1 dv->sdev_mapinfo = NULL; 475*2621Sllai1 dv->sdev_flags = SDEV_BUILD|SDEV_GLOBAL|SDEV_PERSIST; 476*2621Sllai1 bzero(&dv->sdev_handle, sizeof (dv->sdev_handle)); 477*2621Sllai1 dv->sdev_gdir_gen = 0; 478*2621Sllai1 } else { 479*2621Sllai1 dv->sdev_flags = SDEV_BUILD; 480*2621Sllai1 dv->sdev_flags &= ~SDEV_PERSIST; 481*2621Sllai1 bzero(&dv->sdev_prof, sizeof (dv->sdev_prof)); 482*2621Sllai1 dv->sdev_ldir_gen = 0; 483*2621Sllai1 dv->sdev_devtree_gen = 0; 484*2621Sllai1 } 485*2621Sllai1 486*2621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 487*2621Sllai1 sdev_set_nodestate(dv, SDEV_READY); 488*2621Sllai1 rw_exit(&dv->sdev_contents); 489*2621Sllai1 sdev_nc_node_exists(dv); 490*2621Sllai1 return (dv); 491*2621Sllai1 } 492*2621Sllai1 493*2621Sllai1 /* 494*2621Sllai1 * 1. load the module 495*2621Sllai1 * 2. modload invokes sdev_module_register, which in turn sets 496*2621Sllai1 * the dv->sdev_mapinfo->dir_ops 497*2621Sllai1 * 498*2621Sllai1 * note: locking order: 499*2621Sllai1 * dv->sdev_contents -> map->dir_lock 500*2621Sllai1 */ 501*2621Sllai1 static int 502*2621Sllai1 sdev_get_moduleops(struct sdev_node *dv) 503*2621Sllai1 { 504*2621Sllai1 int error = 0; 505*2621Sllai1 struct devname_nsmap *map = NULL; 506*2621Sllai1 char *module; 507*2621Sllai1 char *path; 508*2621Sllai1 int load = 1; 509*2621Sllai1 510*2621Sllai1 ASSERT(SDEVTOV(dv)->v_type == VDIR); 511*2621Sllai1 512*2621Sllai1 if (devname_nsmaps == NULL) 513*2621Sllai1 return (0); 514*2621Sllai1 515*2621Sllai1 if (!sdev_nsmaps_loaded() && !sdev_nsmaps_reloaded()) 516*2621Sllai1 return (0); 517*2621Sllai1 518*2621Sllai1 519*2621Sllai1 path = dv->sdev_path; 520*2621Sllai1 if ((map = sdev_get_nsmap_by_dir(path, 0))) { 521*2621Sllai1 rw_enter(&map->dir_lock, RW_READER); 522*2621Sllai1 if (map->dir_invalid) { 523*2621Sllai1 if (map->dir_module && map->dir_newmodule && 524*2621Sllai1 (strcmp(map->dir_module, 525*2621Sllai1 map->dir_newmodule) == 0)) { 526*2621Sllai1 load = 0; 527*2621Sllai1 } 528*2621Sllai1 sdev_replace_nsmap(map, map->dir_newmodule, 529*2621Sllai1 map->dir_newmap); 530*2621Sllai1 } 531*2621Sllai1 532*2621Sllai1 module = map->dir_module; 533*2621Sllai1 if (module && load) { 534*2621Sllai1 sdcmn_err6(("sdev_get_moduleops: " 535*2621Sllai1 "load module %s", module)); 536*2621Sllai1 rw_exit(&map->dir_lock); 537*2621Sllai1 error = modload("devname", module); 538*2621Sllai1 sdcmn_err6(("sdev_get_moduleops: error %d\n", error)); 539*2621Sllai1 if (error < 0) { 540*2621Sllai1 return (-1); 541*2621Sllai1 } 542*2621Sllai1 } else if (module == NULL) { 543*2621Sllai1 /* 544*2621Sllai1 * loading the module ops for name services 545*2621Sllai1 */ 546*2621Sllai1 if (devname_ns_ops == NULL) { 547*2621Sllai1 sdcmn_err6(( 548*2621Sllai1 "sdev_get_moduleops: modload default\n")); 549*2621Sllai1 error = modload("devname", DEVNAME_NSCONFIG); 550*2621Sllai1 sdcmn_err6(( 551*2621Sllai1 "sdev_get_moduleops: error %d\n", error)); 552*2621Sllai1 if (error < 0) { 553*2621Sllai1 return (-1); 554*2621Sllai1 } 555*2621Sllai1 } 556*2621Sllai1 557*2621Sllai1 if (!rw_tryupgrade(&map->dir_lock)) { 558*2621Sllai1 rw_exit(&map->dir_lock); 559*2621Sllai1 rw_enter(&map->dir_lock, RW_WRITER); 560*2621Sllai1 } 561*2621Sllai1 ASSERT(devname_ns_ops); 562*2621Sllai1 map->dir_ops = devname_ns_ops; 563*2621Sllai1 rw_exit(&map->dir_lock); 564*2621Sllai1 } 565*2621Sllai1 } 566*2621Sllai1 567*2621Sllai1 dv->sdev_mapinfo = map; 568*2621Sllai1 return (0); 569*2621Sllai1 } 570*2621Sllai1 571*2621Sllai1 /* directory dependent vop table */ 572*2621Sllai1 struct sdev_vop_table { 573*2621Sllai1 char *vt_name; /* subdirectory name */ 574*2621Sllai1 const fs_operation_def_t *vt_service; /* vnodeops table */ 575*2621Sllai1 struct vnodeops *vt_vops; /* constructed vop */ 576*2621Sllai1 struct vnodeops **vt_global_vops; /* global container for vop */ 577*2621Sllai1 int (*vt_vtor)(struct sdev_node *); /* validate sdev_node */ 578*2621Sllai1 int vt_flags; 579*2621Sllai1 }; 580*2621Sllai1 581*2621Sllai1 /* 582*2621Sllai1 * A nice improvement would be to provide a plug-in mechanism 583*2621Sllai1 * for this table instead of a const table. 584*2621Sllai1 */ 585*2621Sllai1 static struct sdev_vop_table vtab[] = 586*2621Sllai1 { 587*2621Sllai1 { "pts", devpts_vnodeops_tbl, NULL, &devpts_vnodeops, devpts_validate, 588*2621Sllai1 SDEV_DYNAMIC | SDEV_VTOR }, 589*2621Sllai1 590*2621Sllai1 { "zcons", NULL, NULL, NULL, NULL, SDEV_NO_NCACHE }, 591*2621Sllai1 592*2621Sllai1 { NULL, NULL, NULL, NULL, NULL, 0} 593*2621Sllai1 }; 594*2621Sllai1 595*2621Sllai1 596*2621Sllai1 /* 597*2621Sllai1 * sets a directory's vnodeops if the directory is in the vtab; 598*2621Sllai1 */ 599*2621Sllai1 static struct vnodeops * 600*2621Sllai1 sdev_get_vop(struct sdev_node *dv) 601*2621Sllai1 { 602*2621Sllai1 int i; 603*2621Sllai1 char *path; 604*2621Sllai1 605*2621Sllai1 path = dv->sdev_path; 606*2621Sllai1 ASSERT(path); 607*2621Sllai1 608*2621Sllai1 /* gets the relative path to /dev/ */ 609*2621Sllai1 path += 5; 610*2621Sllai1 611*2621Sllai1 /* gets the vtab entry if matches */ 612*2621Sllai1 for (i = 0; vtab[i].vt_name; i++) { 613*2621Sllai1 if (strcmp(vtab[i].vt_name, path) != 0) 614*2621Sllai1 continue; 615*2621Sllai1 dv->sdev_flags |= vtab[i].vt_flags; 616*2621Sllai1 617*2621Sllai1 if (vtab[i].vt_vops) { 618*2621Sllai1 if (vtab[i].vt_global_vops) 619*2621Sllai1 *(vtab[i].vt_global_vops) = vtab[i].vt_vops; 620*2621Sllai1 return (vtab[i].vt_vops); 621*2621Sllai1 } 622*2621Sllai1 623*2621Sllai1 if (vtab[i].vt_service) { 624*2621Sllai1 fs_operation_def_t *templ; 625*2621Sllai1 templ = sdev_merge_vtab(vtab[i].vt_service); 626*2621Sllai1 if (vn_make_ops(vtab[i].vt_name, 627*2621Sllai1 (const fs_operation_def_t *)templ, 628*2621Sllai1 &vtab[i].vt_vops) != 0) { 629*2621Sllai1 cmn_err(CE_PANIC, "%s: malformed vnode ops\n", 630*2621Sllai1 vtab[i].vt_name); 631*2621Sllai1 /*NOTREACHED*/ 632*2621Sllai1 } 633*2621Sllai1 if (vtab[i].vt_global_vops) { 634*2621Sllai1 *(vtab[i].vt_global_vops) = vtab[i].vt_vops; 635*2621Sllai1 } 636*2621Sllai1 sdev_free_vtab(templ); 637*2621Sllai1 return (vtab[i].vt_vops); 638*2621Sllai1 } 639*2621Sllai1 return (sdev_vnodeops); 640*2621Sllai1 } 641*2621Sllai1 642*2621Sllai1 /* child inherits the persistence of the parent */ 643*2621Sllai1 if (SDEV_IS_PERSIST(dv->sdev_dotdot)) 644*2621Sllai1 dv->sdev_flags |= SDEV_PERSIST; 645*2621Sllai1 646*2621Sllai1 return (sdev_vnodeops); 647*2621Sllai1 } 648*2621Sllai1 649*2621Sllai1 static void 650*2621Sllai1 sdev_set_no_nocache(struct sdev_node *dv) 651*2621Sllai1 { 652*2621Sllai1 int i; 653*2621Sllai1 char *path; 654*2621Sllai1 655*2621Sllai1 ASSERT(dv->sdev_path); 656*2621Sllai1 path = dv->sdev_path + strlen("/dev/"); 657*2621Sllai1 658*2621Sllai1 for (i = 0; vtab[i].vt_name; i++) { 659*2621Sllai1 if (strcmp(vtab[i].vt_name, path) == 0) { 660*2621Sllai1 if (vtab[i].vt_flags & SDEV_NO_NCACHE) 661*2621Sllai1 dv->sdev_flags |= SDEV_NO_NCACHE; 662*2621Sllai1 break; 663*2621Sllai1 } 664*2621Sllai1 } 665*2621Sllai1 } 666*2621Sllai1 667*2621Sllai1 void * 668*2621Sllai1 sdev_get_vtor(struct sdev_node *dv) 669*2621Sllai1 { 670*2621Sllai1 int i; 671*2621Sllai1 672*2621Sllai1 for (i = 0; vtab[i].vt_name; i++) { 673*2621Sllai1 if (strcmp(vtab[i].vt_name, dv->sdev_name) != 0) 674*2621Sllai1 continue; 675*2621Sllai1 return ((void *)vtab[i].vt_vtor); 676*2621Sllai1 } 677*2621Sllai1 return (NULL); 678*2621Sllai1 } 679*2621Sllai1 680*2621Sllai1 /* 681*2621Sllai1 * Build the base root inode 682*2621Sllai1 */ 683*2621Sllai1 ino_t 684*2621Sllai1 sdev_mkino(struct sdev_node *dv) 685*2621Sllai1 { 686*2621Sllai1 ino_t ino; 687*2621Sllai1 688*2621Sllai1 /* 689*2621Sllai1 * for now, follow the lead of tmpfs here 690*2621Sllai1 * need to someday understand the requirements here 691*2621Sllai1 */ 692*2621Sllai1 ino = (ino_t)(uint32_t)((uintptr_t)dv >> 3); 693*2621Sllai1 ino += SDEV_ROOTINO + 1; 694*2621Sllai1 695*2621Sllai1 return (ino); 696*2621Sllai1 } 697*2621Sllai1 698*2621Sllai1 static int 699*2621Sllai1 sdev_getlink(struct vnode *linkvp, char **link) 700*2621Sllai1 { 701*2621Sllai1 int err; 702*2621Sllai1 char *buf; 703*2621Sllai1 struct uio uio = {0}; 704*2621Sllai1 struct iovec iov = {0}; 705*2621Sllai1 706*2621Sllai1 if (linkvp == NULL) 707*2621Sllai1 return (ENOENT); 708*2621Sllai1 ASSERT(linkvp->v_type == VLNK); 709*2621Sllai1 710*2621Sllai1 buf = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 711*2621Sllai1 iov.iov_base = buf; 712*2621Sllai1 iov.iov_len = MAXPATHLEN; 713*2621Sllai1 uio.uio_iov = &iov; 714*2621Sllai1 uio.uio_iovcnt = 1; 715*2621Sllai1 uio.uio_resid = MAXPATHLEN; 716*2621Sllai1 uio.uio_segflg = UIO_SYSSPACE; 717*2621Sllai1 uio.uio_llimit = MAXOFFSET_T; 718*2621Sllai1 719*2621Sllai1 err = VOP_READLINK(linkvp, &uio, kcred); 720*2621Sllai1 if (err) { 721*2621Sllai1 cmn_err(CE_WARN, "readlink %s failed in dev\n", buf); 722*2621Sllai1 kmem_free(buf, MAXPATHLEN); 723*2621Sllai1 return (ENOENT); 724*2621Sllai1 } 725*2621Sllai1 726*2621Sllai1 /* mission complete */ 727*2621Sllai1 *link = i_ddi_strdup(buf, KM_SLEEP); 728*2621Sllai1 kmem_free(buf, MAXPATHLEN); 729*2621Sllai1 return (0); 730*2621Sllai1 } 731*2621Sllai1 732*2621Sllai1 /* 733*2621Sllai1 * A convenient wrapper to get the devfs node vnode for a device 734*2621Sllai1 * minor functionality: readlink() of a /dev symlink 735*2621Sllai1 * Place the link into dv->sdev_symlink 736*2621Sllai1 */ 737*2621Sllai1 static int 738*2621Sllai1 sdev_follow_link(struct sdev_node *dv) 739*2621Sllai1 { 740*2621Sllai1 int err; 741*2621Sllai1 struct vnode *linkvp; 742*2621Sllai1 char *link = NULL; 743*2621Sllai1 744*2621Sllai1 linkvp = SDEVTOV(dv); 745*2621Sllai1 if (linkvp == NULL) 746*2621Sllai1 return (ENOENT); 747*2621Sllai1 ASSERT(linkvp->v_type == VLNK); 748*2621Sllai1 err = sdev_getlink(linkvp, &link); 749*2621Sllai1 if (err) { 750*2621Sllai1 (void) sdev_nodezombied(dv); 751*2621Sllai1 dv->sdev_symlink = NULL; 752*2621Sllai1 return (ENOENT); 753*2621Sllai1 } 754*2621Sllai1 755*2621Sllai1 ASSERT(link != NULL); 756*2621Sllai1 dv->sdev_symlink = link; 757*2621Sllai1 return (0); 758*2621Sllai1 } 759*2621Sllai1 760*2621Sllai1 static int 761*2621Sllai1 sdev_node_check(struct sdev_node *dv, struct vattr *nvap, void *nargs) 762*2621Sllai1 { 763*2621Sllai1 vtype_t otype = SDEVTOV(dv)->v_type; 764*2621Sllai1 765*2621Sllai1 /* 766*2621Sllai1 * existing sdev_node has a different type. 767*2621Sllai1 */ 768*2621Sllai1 if (otype != nvap->va_type) { 769*2621Sllai1 sdcmn_err9(("sdev_node_check: existing node " 770*2621Sllai1 " %s type %d does not match new node type %d\n", 771*2621Sllai1 dv->sdev_name, otype, nvap->va_type)); 772*2621Sllai1 return (EEXIST); 773*2621Sllai1 } 774*2621Sllai1 775*2621Sllai1 /* 776*2621Sllai1 * For a symlink, the target should be the same. 777*2621Sllai1 */ 778*2621Sllai1 if (otype == VLNK) { 779*2621Sllai1 ASSERT(nargs != NULL); 780*2621Sllai1 ASSERT(dv->sdev_symlink != NULL); 781*2621Sllai1 if (strcmp(dv->sdev_symlink, (char *)nargs) != 0) { 782*2621Sllai1 sdcmn_err9(("sdev_node_check: existing node " 783*2621Sllai1 " %s has different symlink %s as new node " 784*2621Sllai1 " %s\n", dv->sdev_name, dv->sdev_symlink, 785*2621Sllai1 (char *)nargs)); 786*2621Sllai1 return (EEXIST); 787*2621Sllai1 } 788*2621Sllai1 } 789*2621Sllai1 790*2621Sllai1 return (0); 791*2621Sllai1 } 792*2621Sllai1 793*2621Sllai1 /* 794*2621Sllai1 * sdev_mknode - a wrapper for sdev_nodeinit(), sdev_nodeready() 795*2621Sllai1 * 796*2621Sllai1 * arguments: 797*2621Sllai1 * - ddv (parent) 798*2621Sllai1 * - nm (child name) 799*2621Sllai1 * - newdv (sdev_node for nm is returned here) 800*2621Sllai1 * - vap (vattr for the node to be created, va_type should be set. 801*2621Sllai1 * the defaults should be used if unknown) 802*2621Sllai1 * - cred 803*2621Sllai1 * - args 804*2621Sllai1 * . tnm (for VLNK) 805*2621Sllai1 * . global sdev_node (for !SDEV_GLOBAL) 806*2621Sllai1 * - state: SDEV_INIT, SDEV_READY 807*2621Sllai1 * 808*2621Sllai1 * only ddv, nm, newddv, vap, cred are required for sdev_mknode(SDEV_INIT) 809*2621Sllai1 * 810*2621Sllai1 * NOTE: directory contents writers lock needs to be held before 811*2621Sllai1 * calling this routine. 812*2621Sllai1 */ 813*2621Sllai1 int 814*2621Sllai1 sdev_mknode(struct sdev_node *ddv, char *nm, struct sdev_node **newdv, 815*2621Sllai1 struct vattr *vap, struct vnode *avp, void *args, struct cred *cred, 816*2621Sllai1 sdev_node_state_t state) 817*2621Sllai1 { 818*2621Sllai1 int error = 0; 819*2621Sllai1 sdev_node_state_t node_state; 820*2621Sllai1 struct sdev_node *dv = NULL; 821*2621Sllai1 822*2621Sllai1 ASSERT(state != SDEV_ZOMBIE); 823*2621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 824*2621Sllai1 825*2621Sllai1 if (*newdv) { 826*2621Sllai1 dv = *newdv; 827*2621Sllai1 } else { 828*2621Sllai1 /* allocate and initialize a sdev_node */ 829*2621Sllai1 if (ddv->sdev_state == SDEV_ZOMBIE) { 830*2621Sllai1 sdcmn_err9(("sdev_mknode: parent %s ZOMBIEd\n", 831*2621Sllai1 ddv->sdev_path)); 832*2621Sllai1 return (ENOENT); 833*2621Sllai1 } 834*2621Sllai1 835*2621Sllai1 error = sdev_nodeinit(ddv, nm, &dv, vap); 836*2621Sllai1 if (error != 0) { 837*2621Sllai1 sdcmn_err9(("sdev_mknode: error %d," 838*2621Sllai1 " name %s can not be initialized\n", 839*2621Sllai1 error, nm)); 840*2621Sllai1 return (ENOENT); 841*2621Sllai1 } 842*2621Sllai1 ASSERT(dv); 843*2621Sllai1 844*2621Sllai1 /* insert into the directory cache */ 845*2621Sllai1 error = sdev_cache_update(ddv, &dv, nm, SDEV_CACHE_ADD); 846*2621Sllai1 if (error) { 847*2621Sllai1 sdcmn_err9(("sdev_mknode: node %s can not" 848*2621Sllai1 " be added into directory cache\n", nm)); 849*2621Sllai1 return (ENOENT); 850*2621Sllai1 } 851*2621Sllai1 } 852*2621Sllai1 853*2621Sllai1 ASSERT(dv); 854*2621Sllai1 node_state = dv->sdev_state; 855*2621Sllai1 ASSERT(node_state != SDEV_ZOMBIE); 856*2621Sllai1 857*2621Sllai1 if (state == SDEV_READY) { 858*2621Sllai1 switch (node_state) { 859*2621Sllai1 case SDEV_INIT: 860*2621Sllai1 error = sdev_nodeready(dv, vap, avp, args, cred); 861*2621Sllai1 /* 862*2621Sllai1 * masking the errors with ENOENT 863*2621Sllai1 */ 864*2621Sllai1 if (error) { 865*2621Sllai1 sdcmn_err9(("sdev_mknode: node %s can NOT" 866*2621Sllai1 " be transitioned into READY state, " 867*2621Sllai1 "error %d\n", nm, error)); 868*2621Sllai1 error = ENOENT; 869*2621Sllai1 } 870*2621Sllai1 break; 871*2621Sllai1 case SDEV_READY: 872*2621Sllai1 /* 873*2621Sllai1 * Do some sanity checking to make sure 874*2621Sllai1 * the existing sdev_node is what has been 875*2621Sllai1 * asked for. 876*2621Sllai1 */ 877*2621Sllai1 error = sdev_node_check(dv, vap, args); 878*2621Sllai1 break; 879*2621Sllai1 default: 880*2621Sllai1 break; 881*2621Sllai1 } 882*2621Sllai1 } 883*2621Sllai1 884*2621Sllai1 if (!error) { 885*2621Sllai1 *newdv = dv; 886*2621Sllai1 ASSERT((*newdv)->sdev_state != SDEV_ZOMBIE); 887*2621Sllai1 } else { 888*2621Sllai1 SDEV_SIMPLE_RELE(dv); 889*2621Sllai1 *newdv = NULL; 890*2621Sllai1 } 891*2621Sllai1 892*2621Sllai1 return (error); 893*2621Sllai1 } 894*2621Sllai1 895*2621Sllai1 /* 896*2621Sllai1 * convenient wrapper to change vp's ATIME, CTIME and ATIME 897*2621Sllai1 */ 898*2621Sllai1 void 899*2621Sllai1 sdev_update_timestamps(struct vnode *vp, cred_t *cred, uint_t mask) 900*2621Sllai1 { 901*2621Sllai1 struct vattr attr; 902*2621Sllai1 timestruc_t now; 903*2621Sllai1 int err; 904*2621Sllai1 905*2621Sllai1 ASSERT(vp); 906*2621Sllai1 gethrestime(&now); 907*2621Sllai1 if (mask & AT_CTIME) 908*2621Sllai1 attr.va_ctime = now; 909*2621Sllai1 if (mask & AT_MTIME) 910*2621Sllai1 attr.va_mtime = now; 911*2621Sllai1 if (mask & AT_ATIME) 912*2621Sllai1 attr.va_atime = now; 913*2621Sllai1 914*2621Sllai1 attr.va_mask = (mask & AT_TIMES); 915*2621Sllai1 err = VOP_SETATTR(vp, &attr, 0, cred, NULL); 916*2621Sllai1 if (err && (err != EROFS)) { 917*2621Sllai1 sdcmn_err(("update timestamps error %d\n", err)); 918*2621Sllai1 } 919*2621Sllai1 } 920*2621Sllai1 921*2621Sllai1 /* 922*2621Sllai1 * the backing store vnode is released here 923*2621Sllai1 */ 924*2621Sllai1 /*ARGSUSED1*/ 925*2621Sllai1 void 926*2621Sllai1 sdev_nodedestroy(struct sdev_node *dv, uint_t flags) 927*2621Sllai1 { 928*2621Sllai1 /* no references */ 929*2621Sllai1 ASSERT(dv->sdev_nlink == 0); 930*2621Sllai1 931*2621Sllai1 if (dv->sdev_attrvp != NULLVP) { 932*2621Sllai1 VN_RELE(dv->sdev_attrvp); 933*2621Sllai1 /* 934*2621Sllai1 * reset the attrvp so that no more 935*2621Sllai1 * references can be made on this already 936*2621Sllai1 * vn_rele() vnode 937*2621Sllai1 */ 938*2621Sllai1 dv->sdev_attrvp = NULLVP; 939*2621Sllai1 } 940*2621Sllai1 941*2621Sllai1 if (dv->sdev_attr != NULL) { 942*2621Sllai1 kmem_free(dv->sdev_attr, sizeof (struct vattr)); 943*2621Sllai1 dv->sdev_attr = NULL; 944*2621Sllai1 } 945*2621Sllai1 946*2621Sllai1 if (dv->sdev_name != NULL) { 947*2621Sllai1 kmem_free(dv->sdev_name, dv->sdev_namelen + 1); 948*2621Sllai1 dv->sdev_name = NULL; 949*2621Sllai1 } 950*2621Sllai1 951*2621Sllai1 if (dv->sdev_symlink != NULL) { 952*2621Sllai1 kmem_free(dv->sdev_symlink, strlen(dv->sdev_symlink) + 1); 953*2621Sllai1 dv->sdev_symlink = NULL; 954*2621Sllai1 } 955*2621Sllai1 956*2621Sllai1 if (dv->sdev_path) { 957*2621Sllai1 kmem_free(dv->sdev_path, strlen(dv->sdev_path) + 1); 958*2621Sllai1 dv->sdev_path = NULL; 959*2621Sllai1 } 960*2621Sllai1 961*2621Sllai1 if (!SDEV_IS_GLOBAL(dv)) 962*2621Sllai1 sdev_prof_free(dv); 963*2621Sllai1 964*2621Sllai1 mutex_destroy(&dv->sdev_lookup_lock); 965*2621Sllai1 cv_destroy(&dv->sdev_lookup_cv); 966*2621Sllai1 967*2621Sllai1 /* return node to initial state as per constructor */ 968*2621Sllai1 (void) memset((void *)&dv->sdev_instance_data, 0, 969*2621Sllai1 sizeof (dv->sdev_instance_data)); 970*2621Sllai1 971*2621Sllai1 vn_invalid(SDEVTOV(dv)); 972*2621Sllai1 kmem_cache_free(sdev_node_cache, dv); 973*2621Sllai1 } 974*2621Sllai1 975*2621Sllai1 /* 976*2621Sllai1 * DIRECTORY CACHE lookup 977*2621Sllai1 */ 978*2621Sllai1 struct sdev_node * 979*2621Sllai1 sdev_findbyname(struct sdev_node *ddv, char *nm) 980*2621Sllai1 { 981*2621Sllai1 struct sdev_node *dv; 982*2621Sllai1 size_t nmlen = strlen(nm); 983*2621Sllai1 984*2621Sllai1 ASSERT(RW_LOCK_HELD(&ddv->sdev_contents)); 985*2621Sllai1 for (dv = ddv->sdev_dot; dv; dv = dv->sdev_next) { 986*2621Sllai1 if (dv->sdev_namelen != nmlen) { 987*2621Sllai1 continue; 988*2621Sllai1 } 989*2621Sllai1 990*2621Sllai1 /* 991*2621Sllai1 * Can't lookup stale nodes 992*2621Sllai1 */ 993*2621Sllai1 if (dv->sdev_flags & SDEV_STALE) { 994*2621Sllai1 sdcmn_err9(( 995*2621Sllai1 "sdev_findbyname: skipped stale node: %s\n", 996*2621Sllai1 dv->sdev_name)); 997*2621Sllai1 continue; 998*2621Sllai1 } 999*2621Sllai1 1000*2621Sllai1 if (strcmp(dv->sdev_name, nm) == 0) { 1001*2621Sllai1 SDEV_HOLD(dv); 1002*2621Sllai1 return (dv); 1003*2621Sllai1 } 1004*2621Sllai1 } 1005*2621Sllai1 return (NULL); 1006*2621Sllai1 } 1007*2621Sllai1 1008*2621Sllai1 /* 1009*2621Sllai1 * Inserts a new sdev_node in a parent directory 1010*2621Sllai1 */ 1011*2621Sllai1 void 1012*2621Sllai1 sdev_direnter(struct sdev_node *ddv, struct sdev_node *dv) 1013*2621Sllai1 { 1014*2621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 1015*2621Sllai1 ASSERT(SDEVTOV(ddv)->v_type == VDIR); 1016*2621Sllai1 ASSERT(ddv->sdev_nlink >= 2); 1017*2621Sllai1 ASSERT(dv->sdev_nlink == 0); 1018*2621Sllai1 1019*2621Sllai1 dv->sdev_dotdot = ddv; 1020*2621Sllai1 dv->sdev_next = ddv->sdev_dot; 1021*2621Sllai1 ddv->sdev_dot = dv; 1022*2621Sllai1 ddv->sdev_nlink++; 1023*2621Sllai1 } 1024*2621Sllai1 1025*2621Sllai1 /* 1026*2621Sllai1 * The following check is needed because while sdev_nodes are linked 1027*2621Sllai1 * in SDEV_INIT state, they have their link counts incremented only 1028*2621Sllai1 * in SDEV_READY state. 1029*2621Sllai1 */ 1030*2621Sllai1 static void 1031*2621Sllai1 decr_link(struct sdev_node *dv) 1032*2621Sllai1 { 1033*2621Sllai1 if (dv->sdev_state != SDEV_INIT) 1034*2621Sllai1 dv->sdev_nlink--; 1035*2621Sllai1 else 1036*2621Sllai1 ASSERT(dv->sdev_nlink == 0); 1037*2621Sllai1 } 1038*2621Sllai1 1039*2621Sllai1 /* 1040*2621Sllai1 * Delete an existing dv from directory cache 1041*2621Sllai1 * 1042*2621Sllai1 * In the case of a node is still held by non-zero reference count, 1043*2621Sllai1 * the node is put into ZOMBIE state. Once the reference count 1044*2621Sllai1 * reaches "0", the node is unlinked and destroyed, 1045*2621Sllai1 * in sdev_inactive(). 1046*2621Sllai1 */ 1047*2621Sllai1 static int 1048*2621Sllai1 sdev_dirdelete(struct sdev_node *ddv, struct sdev_node *dv) 1049*2621Sllai1 { 1050*2621Sllai1 struct sdev_node *idv; 1051*2621Sllai1 struct sdev_node *prev = NULL; 1052*2621Sllai1 struct vnode *vp; 1053*2621Sllai1 1054*2621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 1055*2621Sllai1 1056*2621Sllai1 vp = SDEVTOV(dv); 1057*2621Sllai1 mutex_enter(&vp->v_lock); 1058*2621Sllai1 1059*2621Sllai1 /* dv is held still */ 1060*2621Sllai1 if (vp->v_count > 1) { 1061*2621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 1062*2621Sllai1 if (dv->sdev_state == SDEV_READY) { 1063*2621Sllai1 sdcmn_err9(( 1064*2621Sllai1 "sdev_delete: node %s busy with count %d\n", 1065*2621Sllai1 dv->sdev_name, vp->v_count)); 1066*2621Sllai1 dv->sdev_state = SDEV_ZOMBIE; 1067*2621Sllai1 } 1068*2621Sllai1 rw_exit(&dv->sdev_contents); 1069*2621Sllai1 --vp->v_count; 1070*2621Sllai1 mutex_exit(&vp->v_lock); 1071*2621Sllai1 return (EBUSY); 1072*2621Sllai1 } 1073*2621Sllai1 ASSERT(vp->v_count == 1); 1074*2621Sllai1 1075*2621Sllai1 /* unlink from the memory cache */ 1076*2621Sllai1 ddv->sdev_nlink--; /* .. to above */ 1077*2621Sllai1 if (vp->v_type == VDIR) { 1078*2621Sllai1 decr_link(dv); /* . to self */ 1079*2621Sllai1 } 1080*2621Sllai1 1081*2621Sllai1 for (idv = ddv->sdev_dot; idv && idv != dv; 1082*2621Sllai1 prev = idv, idv = idv->sdev_next) 1083*2621Sllai1 ; 1084*2621Sllai1 ASSERT(idv == dv); /* node to be deleted must exist */ 1085*2621Sllai1 if (prev == NULL) 1086*2621Sllai1 ddv->sdev_dot = dv->sdev_next; 1087*2621Sllai1 else 1088*2621Sllai1 prev->sdev_next = dv->sdev_next; 1089*2621Sllai1 dv->sdev_next = NULL; 1090*2621Sllai1 decr_link(dv); /* name, back to zero */ 1091*2621Sllai1 vp->v_count--; 1092*2621Sllai1 mutex_exit(&vp->v_lock); 1093*2621Sllai1 1094*2621Sllai1 /* destroy the node */ 1095*2621Sllai1 sdev_nodedestroy(dv, 0); 1096*2621Sllai1 return (0); 1097*2621Sllai1 } 1098*2621Sllai1 1099*2621Sllai1 /* 1100*2621Sllai1 * check if the source is in the path of the target 1101*2621Sllai1 * 1102*2621Sllai1 * source and target are different 1103*2621Sllai1 */ 1104*2621Sllai1 /*ARGSUSED2*/ 1105*2621Sllai1 static int 1106*2621Sllai1 sdev_checkpath(struct sdev_node *sdv, struct sdev_node *tdv, struct cred *cred) 1107*2621Sllai1 { 1108*2621Sllai1 int error = 0; 1109*2621Sllai1 struct sdev_node *dotdot, *dir; 1110*2621Sllai1 1111*2621Sllai1 rw_enter(&tdv->sdev_contents, RW_READER); 1112*2621Sllai1 dotdot = tdv->sdev_dotdot; 1113*2621Sllai1 ASSERT(dotdot); 1114*2621Sllai1 1115*2621Sllai1 /* fs root */ 1116*2621Sllai1 if (dotdot == tdv) { 1117*2621Sllai1 rw_exit(&tdv->sdev_contents); 1118*2621Sllai1 return (0); 1119*2621Sllai1 } 1120*2621Sllai1 1121*2621Sllai1 for (;;) { 1122*2621Sllai1 /* 1123*2621Sllai1 * avoid error cases like 1124*2621Sllai1 * mv a a/b 1125*2621Sllai1 * mv a a/b/c 1126*2621Sllai1 * etc. 1127*2621Sllai1 */ 1128*2621Sllai1 if (dotdot == sdv) { 1129*2621Sllai1 error = EINVAL; 1130*2621Sllai1 break; 1131*2621Sllai1 } 1132*2621Sllai1 1133*2621Sllai1 dir = dotdot; 1134*2621Sllai1 dotdot = dir->sdev_dotdot; 1135*2621Sllai1 1136*2621Sllai1 /* done checking because root is reached */ 1137*2621Sllai1 if (dir == dotdot) { 1138*2621Sllai1 break; 1139*2621Sllai1 } 1140*2621Sllai1 } 1141*2621Sllai1 rw_exit(&tdv->sdev_contents); 1142*2621Sllai1 return (error); 1143*2621Sllai1 } 1144*2621Sllai1 1145*2621Sllai1 /* 1146*2621Sllai1 * Renaming a directory to a different parent 1147*2621Sllai1 * requires modifying the ".." reference. 1148*2621Sllai1 */ 1149*2621Sllai1 static void 1150*2621Sllai1 sdev_fixdotdot(struct sdev_node *dv, struct sdev_node *oparent, 1151*2621Sllai1 struct sdev_node *nparent) 1152*2621Sllai1 { 1153*2621Sllai1 ASSERT(SDEVTOV(dv)->v_type == VDIR); 1154*2621Sllai1 ASSERT(nparent); 1155*2621Sllai1 ASSERT(oparent); 1156*2621Sllai1 1157*2621Sllai1 rw_enter(&nparent->sdev_contents, RW_WRITER); 1158*2621Sllai1 nparent->sdev_nlink++; 1159*2621Sllai1 ASSERT(dv->sdev_dotdot == oparent); 1160*2621Sllai1 dv->sdev_dotdot = nparent; 1161*2621Sllai1 rw_exit(&nparent->sdev_contents); 1162*2621Sllai1 1163*2621Sllai1 rw_enter(&oparent->sdev_contents, RW_WRITER); 1164*2621Sllai1 oparent->sdev_nlink--; 1165*2621Sllai1 rw_exit(&oparent->sdev_contents); 1166*2621Sllai1 } 1167*2621Sllai1 1168*2621Sllai1 int 1169*2621Sllai1 sdev_rnmnode(struct sdev_node *oddv, struct sdev_node *odv, 1170*2621Sllai1 struct sdev_node *nddv, struct sdev_node **ndvp, char *nnm, 1171*2621Sllai1 struct cred *cred) 1172*2621Sllai1 { 1173*2621Sllai1 int error = 0; 1174*2621Sllai1 struct vnode *ovp = SDEVTOV(odv); 1175*2621Sllai1 struct vnode *nvp; 1176*2621Sllai1 struct vattr vattr; 1177*2621Sllai1 int doingdir = (ovp->v_type == VDIR); 1178*2621Sllai1 char *link = NULL; 1179*2621Sllai1 1180*2621Sllai1 /* 1181*2621Sllai1 * If renaming a directory, and the parents are different (".." must be 1182*2621Sllai1 * changed) then the source dir must not be in the dir hierarchy above 1183*2621Sllai1 * the target since it would orphan everything below the source dir. 1184*2621Sllai1 */ 1185*2621Sllai1 if (doingdir && (oddv != nddv)) { 1186*2621Sllai1 error = sdev_checkpath(odv, nddv, cred); 1187*2621Sllai1 if (error) 1188*2621Sllai1 return (error); 1189*2621Sllai1 } 1190*2621Sllai1 1191*2621Sllai1 vattr.va_mask = AT_MODE|AT_UID|AT_GID; 1192*2621Sllai1 error = VOP_GETATTR(ovp, &vattr, 0, cred); 1193*2621Sllai1 if (error) 1194*2621Sllai1 return (error); 1195*2621Sllai1 1196*2621Sllai1 if (*ndvp) { 1197*2621Sllai1 /* destination existing */ 1198*2621Sllai1 nvp = SDEVTOV(*ndvp); 1199*2621Sllai1 ASSERT(nvp); 1200*2621Sllai1 1201*2621Sllai1 /* handling renaming to itself */ 1202*2621Sllai1 if (odv == *ndvp) 1203*2621Sllai1 return (0); 1204*2621Sllai1 1205*2621Sllai1 /* special handling directory renaming */ 1206*2621Sllai1 if (doingdir) { 1207*2621Sllai1 if (nvp->v_type != VDIR) 1208*2621Sllai1 return (ENOTDIR); 1209*2621Sllai1 1210*2621Sllai1 /* 1211*2621Sllai1 * Renaming a directory with the parent different 1212*2621Sllai1 * requires that ".." be re-written. 1213*2621Sllai1 */ 1214*2621Sllai1 if (oddv != nddv) { 1215*2621Sllai1 sdev_fixdotdot(*ndvp, oddv, nddv); 1216*2621Sllai1 } 1217*2621Sllai1 } 1218*2621Sllai1 } else { 1219*2621Sllai1 /* creating the destination node with the source attr */ 1220*2621Sllai1 rw_enter(&nddv->sdev_contents, RW_WRITER); 1221*2621Sllai1 error = sdev_mknode(nddv, nnm, ndvp, &vattr, NULL, NULL, 1222*2621Sllai1 cred, SDEV_INIT); 1223*2621Sllai1 rw_exit(&nddv->sdev_contents); 1224*2621Sllai1 if (error) 1225*2621Sllai1 return (error); 1226*2621Sllai1 1227*2621Sllai1 ASSERT(*ndvp); 1228*2621Sllai1 nvp = SDEVTOV(*ndvp); 1229*2621Sllai1 } 1230*2621Sllai1 1231*2621Sllai1 /* fix the source for a symlink */ 1232*2621Sllai1 if (vattr.va_type == VLNK) { 1233*2621Sllai1 if (odv->sdev_symlink == NULL) { 1234*2621Sllai1 error = sdev_follow_link(odv); 1235*2621Sllai1 if (error) 1236*2621Sllai1 return (ENOENT); 1237*2621Sllai1 } 1238*2621Sllai1 ASSERT(odv->sdev_symlink); 1239*2621Sllai1 link = i_ddi_strdup(odv->sdev_symlink, KM_SLEEP); 1240*2621Sllai1 } 1241*2621Sllai1 1242*2621Sllai1 rw_enter(&nddv->sdev_contents, RW_WRITER); 1243*2621Sllai1 error = sdev_mknode(nddv, nnm, ndvp, &vattr, NULL, (void *)link, 1244*2621Sllai1 cred, SDEV_READY); 1245*2621Sllai1 rw_exit(&nddv->sdev_contents); 1246*2621Sllai1 1247*2621Sllai1 if (link) 1248*2621Sllai1 kmem_free(link, strlen(link) + 1); 1249*2621Sllai1 1250*2621Sllai1 /* update timestamps */ 1251*2621Sllai1 sdev_update_timestamps(nvp, kcred, AT_CTIME|AT_ATIME); 1252*2621Sllai1 sdev_update_timestamps(SDEVTOV(nddv), kcred, AT_MTIME|AT_ATIME); 1253*2621Sllai1 SDEV_RELE(*ndvp); 1254*2621Sllai1 return (0); 1255*2621Sllai1 } 1256*2621Sllai1 1257*2621Sllai1 /* 1258*2621Sllai1 * Merge sdev_node specific information into an attribute structure. 1259*2621Sllai1 * 1260*2621Sllai1 * note: sdev_node is not locked here 1261*2621Sllai1 */ 1262*2621Sllai1 void 1263*2621Sllai1 sdev_vattr_merge(struct sdev_node *dv, struct vattr *vap) 1264*2621Sllai1 { 1265*2621Sllai1 struct vnode *vp = SDEVTOV(dv); 1266*2621Sllai1 1267*2621Sllai1 vap->va_nlink = dv->sdev_nlink; 1268*2621Sllai1 vap->va_nodeid = dv->sdev_ino; 1269*2621Sllai1 vap->va_fsid = SDEVTOV(dv->sdev_dotdot)->v_rdev; 1270*2621Sllai1 vap->va_type = vp->v_type; 1271*2621Sllai1 1272*2621Sllai1 if (vp->v_type == VDIR) { 1273*2621Sllai1 vap->va_rdev = 0; 1274*2621Sllai1 vap->va_fsid = vp->v_rdev; 1275*2621Sllai1 } else if (vp->v_type == VLNK) { 1276*2621Sllai1 vap->va_rdev = 0; 1277*2621Sllai1 vap->va_mode &= ~S_IFMT; 1278*2621Sllai1 vap->va_mode |= S_IFLNK; 1279*2621Sllai1 } else if ((vp->v_type == VCHR) || (vp->v_type == VBLK)) { 1280*2621Sllai1 vap->va_rdev = vp->v_rdev; 1281*2621Sllai1 vap->va_mode &= ~S_IFMT; 1282*2621Sllai1 if (vap->va_type == VCHR) 1283*2621Sllai1 vap->va_mode |= S_IFCHR; 1284*2621Sllai1 else 1285*2621Sllai1 vap->va_mode |= S_IFBLK; 1286*2621Sllai1 } else { 1287*2621Sllai1 vap->va_rdev = 0; 1288*2621Sllai1 } 1289*2621Sllai1 } 1290*2621Sllai1 1291*2621Sllai1 static struct vattr * 1292*2621Sllai1 sdev_getdefault_attr(enum vtype type) 1293*2621Sllai1 { 1294*2621Sllai1 if (type == VDIR) 1295*2621Sllai1 return (&sdev_vattr_dir); 1296*2621Sllai1 else if (type == VCHR) 1297*2621Sllai1 return (&sdev_vattr_chr); 1298*2621Sllai1 else if (type == VBLK) 1299*2621Sllai1 return (&sdev_vattr_blk); 1300*2621Sllai1 else if (type == VLNK) 1301*2621Sllai1 return (&sdev_vattr_lnk); 1302*2621Sllai1 else 1303*2621Sllai1 return (NULL); 1304*2621Sllai1 } 1305*2621Sllai1 int 1306*2621Sllai1 sdev_to_vp(struct sdev_node *dv, struct vnode **vpp) 1307*2621Sllai1 { 1308*2621Sllai1 int rv = 0; 1309*2621Sllai1 struct vnode *vp = SDEVTOV(dv); 1310*2621Sllai1 1311*2621Sllai1 switch (vp->v_type) { 1312*2621Sllai1 case VCHR: 1313*2621Sllai1 case VBLK: 1314*2621Sllai1 /* 1315*2621Sllai1 * If vnode is a device, return special vnode instead 1316*2621Sllai1 * (though it knows all about -us- via sp->s_realvp) 1317*2621Sllai1 */ 1318*2621Sllai1 *vpp = specvp(vp, vp->v_rdev, vp->v_type, kcred); 1319*2621Sllai1 VN_RELE(vp); 1320*2621Sllai1 if (*vpp == NULLVP) 1321*2621Sllai1 rv = ENOSYS; 1322*2621Sllai1 break; 1323*2621Sllai1 default: /* most types are returned as is */ 1324*2621Sllai1 *vpp = vp; 1325*2621Sllai1 break; 1326*2621Sllai1 } 1327*2621Sllai1 return (rv); 1328*2621Sllai1 } 1329*2621Sllai1 1330*2621Sllai1 /* 1331*2621Sllai1 * loopback into sdev_lookup() 1332*2621Sllai1 */ 1333*2621Sllai1 static struct vnode * 1334*2621Sllai1 devname_find_by_devpath(char *devpath, struct vattr *vattr) 1335*2621Sllai1 { 1336*2621Sllai1 int error = 0; 1337*2621Sllai1 struct vnode *vp; 1338*2621Sllai1 1339*2621Sllai1 error = lookupname(devpath, UIO_SYSSPACE, NO_FOLLOW, NULLVPP, &vp); 1340*2621Sllai1 if (error) { 1341*2621Sllai1 return (NULL); 1342*2621Sllai1 } 1343*2621Sllai1 1344*2621Sllai1 if (vattr) 1345*2621Sllai1 (void) VOP_GETATTR(vp, vattr, 0, kcred); 1346*2621Sllai1 return (vp); 1347*2621Sllai1 } 1348*2621Sllai1 1349*2621Sllai1 /* 1350*2621Sllai1 * the junction between devname and devfs 1351*2621Sllai1 */ 1352*2621Sllai1 static struct vnode * 1353*2621Sllai1 devname_configure_by_path(char *physpath, struct vattr *vattr) 1354*2621Sllai1 { 1355*2621Sllai1 int error = 0; 1356*2621Sllai1 struct vnode *vp; 1357*2621Sllai1 1358*2621Sllai1 ASSERT(strncmp(physpath, "/devices/", sizeof ("/devices/" - 1)) 1359*2621Sllai1 == 0); 1360*2621Sllai1 1361*2621Sllai1 error = devfs_lookupname(physpath + sizeof ("/devices/") - 1, 1362*2621Sllai1 NULLVPP, &vp); 1363*2621Sllai1 if (error != 0) { 1364*2621Sllai1 if (error == ENODEV) { 1365*2621Sllai1 cmn_err(CE_CONT, "%s: not found (line %d)\n", 1366*2621Sllai1 physpath, __LINE__); 1367*2621Sllai1 } 1368*2621Sllai1 1369*2621Sllai1 return (NULL); 1370*2621Sllai1 } 1371*2621Sllai1 1372*2621Sllai1 if (vattr) 1373*2621Sllai1 (void) VOP_GETATTR(vp, vattr, 0, kcred); 1374*2621Sllai1 return (vp); 1375*2621Sllai1 } 1376*2621Sllai1 1377*2621Sllai1 /* 1378*2621Sllai1 * junction between devname and root file system, e.g. ufs 1379*2621Sllai1 */ 1380*2621Sllai1 int 1381*2621Sllai1 devname_backstore_lookup(struct sdev_node *ddv, char *nm, struct vnode **rvp) 1382*2621Sllai1 { 1383*2621Sllai1 struct vnode *rdvp = ddv->sdev_attrvp; 1384*2621Sllai1 int rval = 0; 1385*2621Sllai1 1386*2621Sllai1 ASSERT(rdvp); 1387*2621Sllai1 1388*2621Sllai1 rval = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, kcred); 1389*2621Sllai1 return (rval); 1390*2621Sllai1 } 1391*2621Sllai1 1392*2621Sllai1 static int 1393*2621Sllai1 sdev_filldir_from_store(struct sdev_node *ddv, int dlen, struct cred *cred) 1394*2621Sllai1 { 1395*2621Sllai1 struct sdev_node *dv = NULL; 1396*2621Sllai1 char *nm; 1397*2621Sllai1 struct vnode *dirvp; 1398*2621Sllai1 int error; 1399*2621Sllai1 vnode_t *vp; 1400*2621Sllai1 int eof; 1401*2621Sllai1 struct iovec iov; 1402*2621Sllai1 struct uio uio; 1403*2621Sllai1 struct dirent64 *dp; 1404*2621Sllai1 dirent64_t *dbuf; 1405*2621Sllai1 size_t dbuflen; 1406*2621Sllai1 struct vattr vattr; 1407*2621Sllai1 char *link = NULL; 1408*2621Sllai1 1409*2621Sllai1 if (ddv->sdev_attrvp == NULL) 1410*2621Sllai1 return (0); 1411*2621Sllai1 if (!(ddv->sdev_flags & SDEV_BUILD)) 1412*2621Sllai1 return (0); 1413*2621Sllai1 1414*2621Sllai1 dirvp = ddv->sdev_attrvp; 1415*2621Sllai1 VN_HOLD(dirvp); 1416*2621Sllai1 dbuf = kmem_zalloc(dlen, KM_SLEEP); 1417*2621Sllai1 1418*2621Sllai1 uio.uio_iov = &iov; 1419*2621Sllai1 uio.uio_iovcnt = 1; 1420*2621Sllai1 uio.uio_segflg = UIO_SYSSPACE; 1421*2621Sllai1 uio.uio_fmode = 0; 1422*2621Sllai1 uio.uio_extflg = UIO_COPY_CACHED; 1423*2621Sllai1 uio.uio_loffset = 0; 1424*2621Sllai1 uio.uio_llimit = MAXOFFSET_T; 1425*2621Sllai1 1426*2621Sllai1 eof = 0; 1427*2621Sllai1 error = 0; 1428*2621Sllai1 while (!error && !eof) { 1429*2621Sllai1 uio.uio_resid = dlen; 1430*2621Sllai1 iov.iov_base = (char *)dbuf; 1431*2621Sllai1 iov.iov_len = dlen; 1432*2621Sllai1 (void) VOP_RWLOCK(dirvp, V_WRITELOCK_FALSE, NULL); 1433*2621Sllai1 error = VOP_READDIR(dirvp, &uio, kcred, &eof); 1434*2621Sllai1 VOP_RWUNLOCK(dirvp, V_WRITELOCK_FALSE, NULL); 1435*2621Sllai1 1436*2621Sllai1 dbuflen = dlen - uio.uio_resid; 1437*2621Sllai1 if (error || dbuflen == 0) 1438*2621Sllai1 break; 1439*2621Sllai1 1440*2621Sllai1 if (!(ddv->sdev_flags & SDEV_BUILD)) { 1441*2621Sllai1 error = 0; 1442*2621Sllai1 break; 1443*2621Sllai1 } 1444*2621Sllai1 1445*2621Sllai1 for (dp = dbuf; ((intptr_t)dp < 1446*2621Sllai1 (intptr_t)dbuf + dbuflen); 1447*2621Sllai1 dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) { 1448*2621Sllai1 nm = dp->d_name; 1449*2621Sllai1 1450*2621Sllai1 if (strcmp(nm, ".") == 0 || 1451*2621Sllai1 strcmp(nm, "..") == 0) 1452*2621Sllai1 continue; 1453*2621Sllai1 1454*2621Sllai1 vp = NULLVP; 1455*2621Sllai1 dv = sdev_cache_lookup(ddv, nm); 1456*2621Sllai1 if (dv) { 1457*2621Sllai1 if (dv->sdev_state != SDEV_ZOMBIE) { 1458*2621Sllai1 SDEV_SIMPLE_RELE(dv); 1459*2621Sllai1 } else { 1460*2621Sllai1 /* 1461*2621Sllai1 * A ZOMBIE node may not have been 1462*2621Sllai1 * cleaned up from the backing store, 1463*2621Sllai1 * bypass this entry in this case, 1464*2621Sllai1 * and clean it up from the directory 1465*2621Sllai1 * cache if this is the last call. 1466*2621Sllai1 */ 1467*2621Sllai1 (void) sdev_dirdelete(ddv, dv); 1468*2621Sllai1 } 1469*2621Sllai1 continue; 1470*2621Sllai1 } 1471*2621Sllai1 1472*2621Sllai1 /* refill the cache if not already */ 1473*2621Sllai1 error = devname_backstore_lookup(ddv, nm, &vp); 1474*2621Sllai1 if (error) 1475*2621Sllai1 continue; 1476*2621Sllai1 1477*2621Sllai1 vattr.va_mask = AT_MODE|AT_UID|AT_GID; 1478*2621Sllai1 error = VOP_GETATTR(vp, &vattr, 0, cred); 1479*2621Sllai1 if (error) 1480*2621Sllai1 continue; 1481*2621Sllai1 1482*2621Sllai1 if (vattr.va_type == VLNK) { 1483*2621Sllai1 error = sdev_getlink(vp, &link); 1484*2621Sllai1 if (error) { 1485*2621Sllai1 continue; 1486*2621Sllai1 } 1487*2621Sllai1 ASSERT(link != NULL); 1488*2621Sllai1 } 1489*2621Sllai1 1490*2621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 1491*2621Sllai1 rw_exit(&ddv->sdev_contents); 1492*2621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 1493*2621Sllai1 } 1494*2621Sllai1 error = sdev_mknode(ddv, nm, &dv, &vattr, vp, link, 1495*2621Sllai1 cred, SDEV_READY); 1496*2621Sllai1 rw_downgrade(&ddv->sdev_contents); 1497*2621Sllai1 1498*2621Sllai1 if (link != NULL) { 1499*2621Sllai1 kmem_free(link, strlen(link) + 1); 1500*2621Sllai1 link = NULL; 1501*2621Sllai1 } 1502*2621Sllai1 1503*2621Sllai1 if (!error) { 1504*2621Sllai1 ASSERT(dv); 1505*2621Sllai1 ASSERT(dv->sdev_state != SDEV_ZOMBIE); 1506*2621Sllai1 SDEV_SIMPLE_RELE(dv); 1507*2621Sllai1 } 1508*2621Sllai1 vp = NULL; 1509*2621Sllai1 dv = NULL; 1510*2621Sllai1 } 1511*2621Sllai1 } 1512*2621Sllai1 1513*2621Sllai1 done: 1514*2621Sllai1 VN_RELE(dirvp); 1515*2621Sllai1 kmem_free(dbuf, dlen); 1516*2621Sllai1 1517*2621Sllai1 return (error); 1518*2621Sllai1 } 1519*2621Sllai1 1520*2621Sllai1 static int 1521*2621Sllai1 sdev_filldir_dynamic(struct sdev_node *ddv) 1522*2621Sllai1 { 1523*2621Sllai1 int error; 1524*2621Sllai1 int i; 1525*2621Sllai1 struct vattr *vap; 1526*2621Sllai1 char *nm = NULL; 1527*2621Sllai1 struct sdev_node *dv = NULL; 1528*2621Sllai1 1529*2621Sllai1 if (!(ddv->sdev_flags & SDEV_BUILD)) { 1530*2621Sllai1 return (0); 1531*2621Sllai1 } 1532*2621Sllai1 1533*2621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 1534*2621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 1535*2621Sllai1 rw_exit(&ddv->sdev_contents); 1536*2621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 1537*2621Sllai1 } 1538*2621Sllai1 1539*2621Sllai1 vap = sdev_getdefault_attr(VDIR); 1540*2621Sllai1 for (i = 0; vtab[i].vt_name != NULL; i++) { 1541*2621Sllai1 nm = vtab[i].vt_name; 1542*2621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 1543*2621Sllai1 error = sdev_mknode(ddv, nm, &dv, vap, NULL, 1544*2621Sllai1 NULL, kcred, SDEV_READY); 1545*2621Sllai1 if (error) 1546*2621Sllai1 continue; 1547*2621Sllai1 ASSERT(dv); 1548*2621Sllai1 ASSERT(dv->sdev_state != SDEV_ZOMBIE); 1549*2621Sllai1 SDEV_SIMPLE_RELE(dv); 1550*2621Sllai1 dv = NULL; 1551*2621Sllai1 } 1552*2621Sllai1 rw_downgrade(&ddv->sdev_contents); 1553*2621Sllai1 return (0); 1554*2621Sllai1 } 1555*2621Sllai1 1556*2621Sllai1 /* 1557*2621Sllai1 * Creating a backing store entry based on sdev_attr. 1558*2621Sllai1 * This is called either as part of node creation in a persistent directory 1559*2621Sllai1 * or from setattr/setsecattr to persist access attributes across reboot. 1560*2621Sllai1 */ 1561*2621Sllai1 int 1562*2621Sllai1 sdev_shadow_node(struct sdev_node *dv, struct cred *cred) 1563*2621Sllai1 { 1564*2621Sllai1 int error = 0; 1565*2621Sllai1 struct vnode *dvp = SDEVTOV(dv->sdev_dotdot); 1566*2621Sllai1 struct vnode *rdvp = VTOSDEV(dvp)->sdev_attrvp; 1567*2621Sllai1 struct vattr *vap = dv->sdev_attr; 1568*2621Sllai1 char *nm = dv->sdev_name; 1569*2621Sllai1 struct vnode *tmpvp, **rvp = &tmpvp, *rrvp = NULL; 1570*2621Sllai1 1571*2621Sllai1 ASSERT(dv && dv->sdev_name && rdvp); 1572*2621Sllai1 ASSERT(RW_WRITE_HELD(&dv->sdev_contents) && dv->sdev_attrvp == NULL); 1573*2621Sllai1 1574*2621Sllai1 lookup: 1575*2621Sllai1 /* try to find it in the backing store */ 1576*2621Sllai1 error = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, cred); 1577*2621Sllai1 if (error == 0) { 1578*2621Sllai1 if (VOP_REALVP(*rvp, &rrvp) == 0) { 1579*2621Sllai1 VN_HOLD(rrvp); 1580*2621Sllai1 VN_RELE(*rvp); 1581*2621Sllai1 *rvp = rrvp; 1582*2621Sllai1 } 1583*2621Sllai1 1584*2621Sllai1 kmem_free(dv->sdev_attr, sizeof (vattr_t)); 1585*2621Sllai1 dv->sdev_attr = NULL; 1586*2621Sllai1 dv->sdev_attrvp = *rvp; 1587*2621Sllai1 return (0); 1588*2621Sllai1 } 1589*2621Sllai1 1590*2621Sllai1 /* let's try to persist the node */ 1591*2621Sllai1 gethrestime(&vap->va_atime); 1592*2621Sllai1 vap->va_mtime = vap->va_atime; 1593*2621Sllai1 vap->va_ctime = vap->va_atime; 1594*2621Sllai1 vap->va_mask |= AT_TYPE|AT_MODE; 1595*2621Sllai1 switch (vap->va_type) { 1596*2621Sllai1 case VDIR: 1597*2621Sllai1 error = VOP_MKDIR(rdvp, nm, vap, rvp, cred); 1598*2621Sllai1 sdcmn_err9(("sdev_shadow_node: mkdir vp %p error %d\n", 1599*2621Sllai1 (void *)(*rvp), error)); 1600*2621Sllai1 break; 1601*2621Sllai1 case VCHR: 1602*2621Sllai1 case VBLK: 1603*2621Sllai1 case VREG: 1604*2621Sllai1 case VDOOR: 1605*2621Sllai1 error = VOP_CREATE(rdvp, nm, vap, NONEXCL, VREAD|VWRITE, 1606*2621Sllai1 rvp, cred, 0); 1607*2621Sllai1 sdcmn_err9(("sdev_shadow_node: create vp %p, error %d\n", 1608*2621Sllai1 (void *)(*rvp), error)); 1609*2621Sllai1 if (!error) 1610*2621Sllai1 VN_RELE(*rvp); 1611*2621Sllai1 break; 1612*2621Sllai1 case VLNK: 1613*2621Sllai1 ASSERT(dv->sdev_symlink); 1614*2621Sllai1 error = VOP_SYMLINK(rdvp, nm, vap, dv->sdev_symlink, cred); 1615*2621Sllai1 sdcmn_err9(("sdev_shadow_node: create symlink error %d\n", 1616*2621Sllai1 error)); 1617*2621Sllai1 break; 1618*2621Sllai1 default: 1619*2621Sllai1 cmn_err(CE_PANIC, "dev: %s: sdev_shadow_node " 1620*2621Sllai1 "create\n", nm); 1621*2621Sllai1 /*NOTREACHED*/ 1622*2621Sllai1 } 1623*2621Sllai1 1624*2621Sllai1 /* go back to lookup to factor out spec node and set attrvp */ 1625*2621Sllai1 if (error == 0) 1626*2621Sllai1 goto lookup; 1627*2621Sllai1 1628*2621Sllai1 return (error); 1629*2621Sllai1 } 1630*2621Sllai1 1631*2621Sllai1 static int 1632*2621Sllai1 sdev_cache_add(struct sdev_node *ddv, struct sdev_node **dv, char *nm) 1633*2621Sllai1 { 1634*2621Sllai1 int error = 0; 1635*2621Sllai1 struct sdev_node *dup = NULL; 1636*2621Sllai1 1637*2621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 1638*2621Sllai1 if ((dup = sdev_findbyname(ddv, nm)) == NULL) { 1639*2621Sllai1 sdev_direnter(ddv, *dv); 1640*2621Sllai1 } else { 1641*2621Sllai1 if (dup->sdev_state == SDEV_ZOMBIE) { 1642*2621Sllai1 error = sdev_dirdelete(ddv, dup); 1643*2621Sllai1 /* 1644*2621Sllai1 * The ZOMBIE node is still hanging 1645*2621Sllai1 * around with more than one reference counts. 1646*2621Sllai1 * Fail the new node creation so that 1647*2621Sllai1 * the directory cache won't have 1648*2621Sllai1 * duplicate entries for the same named node 1649*2621Sllai1 */ 1650*2621Sllai1 if (error == EBUSY) { 1651*2621Sllai1 SDEV_SIMPLE_RELE(*dv); 1652*2621Sllai1 sdev_nodedestroy(*dv, 0); 1653*2621Sllai1 *dv = NULL; 1654*2621Sllai1 return (error); 1655*2621Sllai1 } 1656*2621Sllai1 sdev_direnter(ddv, *dv); 1657*2621Sllai1 } else { 1658*2621Sllai1 ASSERT((*dv)->sdev_state != SDEV_ZOMBIE); 1659*2621Sllai1 SDEV_SIMPLE_RELE(*dv); 1660*2621Sllai1 sdev_nodedestroy(*dv, 0); 1661*2621Sllai1 *dv = dup; 1662*2621Sllai1 } 1663*2621Sllai1 } 1664*2621Sllai1 1665*2621Sllai1 return (0); 1666*2621Sllai1 } 1667*2621Sllai1 1668*2621Sllai1 static int 1669*2621Sllai1 sdev_cache_delete(struct sdev_node *ddv, struct sdev_node **dv) 1670*2621Sllai1 { 1671*2621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 1672*2621Sllai1 return (sdev_dirdelete(ddv, *dv)); 1673*2621Sllai1 } 1674*2621Sllai1 1675*2621Sllai1 /* 1676*2621Sllai1 * update the in-core directory cache 1677*2621Sllai1 */ 1678*2621Sllai1 int 1679*2621Sllai1 sdev_cache_update(struct sdev_node *ddv, struct sdev_node **dv, char *nm, 1680*2621Sllai1 sdev_cache_ops_t ops) 1681*2621Sllai1 { 1682*2621Sllai1 int error = 0; 1683*2621Sllai1 1684*2621Sllai1 ASSERT((SDEV_HELD(*dv))); 1685*2621Sllai1 1686*2621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 1687*2621Sllai1 switch (ops) { 1688*2621Sllai1 case SDEV_CACHE_ADD: 1689*2621Sllai1 error = sdev_cache_add(ddv, dv, nm); 1690*2621Sllai1 break; 1691*2621Sllai1 case SDEV_CACHE_DELETE: 1692*2621Sllai1 error = sdev_cache_delete(ddv, dv); 1693*2621Sllai1 break; 1694*2621Sllai1 default: 1695*2621Sllai1 break; 1696*2621Sllai1 } 1697*2621Sllai1 1698*2621Sllai1 return (error); 1699*2621Sllai1 } 1700*2621Sllai1 1701*2621Sllai1 /* 1702*2621Sllai1 * retrive the named entry from the directory cache 1703*2621Sllai1 */ 1704*2621Sllai1 struct sdev_node * 1705*2621Sllai1 sdev_cache_lookup(struct sdev_node *ddv, char *nm) 1706*2621Sllai1 { 1707*2621Sllai1 struct sdev_node *dv = NULL; 1708*2621Sllai1 1709*2621Sllai1 ASSERT(RW_LOCK_HELD(&ddv->sdev_contents)); 1710*2621Sllai1 dv = sdev_findbyname(ddv, nm); 1711*2621Sllai1 1712*2621Sllai1 return (dv); 1713*2621Sllai1 } 1714*2621Sllai1 1715*2621Sllai1 /* 1716*2621Sllai1 * Implicit reconfig for nodes constructed by a link generator 1717*2621Sllai1 * Start devfsadm if needed, or if devfsadm is in progress, 1718*2621Sllai1 * prepare to block on devfsadm either completing or 1719*2621Sllai1 * constructing the desired node. As devfsadmd is global 1720*2621Sllai1 * in scope, constructing all necessary nodes, we only 1721*2621Sllai1 * need to initiate it once. 1722*2621Sllai1 */ 1723*2621Sllai1 static int 1724*2621Sllai1 sdev_call_devfsadmd(struct sdev_node *ddv, struct sdev_node *dv, char *nm) 1725*2621Sllai1 { 1726*2621Sllai1 int error = 0; 1727*2621Sllai1 1728*2621Sllai1 if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) { 1729*2621Sllai1 sdcmn_err6(("lookup: waiting for %s/%s, 0x%x\n", 1730*2621Sllai1 ddv->sdev_name, nm, devfsadm_state)); 1731*2621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 1732*2621Sllai1 SDEV_BLOCK_OTHERS(dv, (SDEV_LOOKUP | SDEV_LGWAITING)); 1733*2621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 1734*2621Sllai1 error = 0; 1735*2621Sllai1 } else if (!DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state)) { 1736*2621Sllai1 sdcmn_err6(("lookup %s/%s starting devfsadm, 0x%x\n", 1737*2621Sllai1 ddv->sdev_name, nm, devfsadm_state)); 1738*2621Sllai1 1739*2621Sllai1 sdev_devfsadmd_thread(ddv, dv, kcred); 1740*2621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 1741*2621Sllai1 SDEV_BLOCK_OTHERS(dv, 1742*2621Sllai1 (SDEV_LOOKUP | SDEV_LGWAITING)); 1743*2621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 1744*2621Sllai1 error = 0; 1745*2621Sllai1 } else { 1746*2621Sllai1 error = -1; 1747*2621Sllai1 } 1748*2621Sllai1 1749*2621Sllai1 return (error); 1750*2621Sllai1 } 1751*2621Sllai1 1752*2621Sllai1 static int 1753*2621Sllai1 sdev_call_modulelookup(struct sdev_node *ddv, struct sdev_node **dvp, char *nm, 1754*2621Sllai1 int (*fn)(char *, devname_handle_t *, struct cred *), struct cred *cred) 1755*2621Sllai1 { 1756*2621Sllai1 struct vnode *rvp = NULL; 1757*2621Sllai1 int error = 0; 1758*2621Sllai1 struct vattr *vap; 1759*2621Sllai1 devname_spec_t spec; 1760*2621Sllai1 devname_handle_t *hdl; 1761*2621Sllai1 void *args = NULL; 1762*2621Sllai1 struct sdev_node *dv = *dvp; 1763*2621Sllai1 1764*2621Sllai1 ASSERT(dv && ddv); 1765*2621Sllai1 hdl = &(dv->sdev_handle); 1766*2621Sllai1 ASSERT(hdl->dh_data == dv); 1767*2621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 1768*2621Sllai1 SDEV_BLOCK_OTHERS(dv, SDEV_LOOKUP); 1769*2621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 1770*2621Sllai1 error = (*fn)(nm, hdl, cred); 1771*2621Sllai1 if (error) { 1772*2621Sllai1 return (error); 1773*2621Sllai1 } 1774*2621Sllai1 1775*2621Sllai1 spec = hdl->dh_spec; 1776*2621Sllai1 args = hdl->dh_args; 1777*2621Sllai1 ASSERT(args); 1778*2621Sllai1 1779*2621Sllai1 switch (spec) { 1780*2621Sllai1 case DEVNAME_NS_PATH: 1781*2621Sllai1 /* 1782*2621Sllai1 * symlink of: 1783*2621Sllai1 * /dev/dir/nm -> /device/... 1784*2621Sllai1 */ 1785*2621Sllai1 rvp = devname_configure_by_path((char *)args, NULL); 1786*2621Sllai1 break; 1787*2621Sllai1 case DEVNAME_NS_DEV: 1788*2621Sllai1 /* 1789*2621Sllai1 * symlink of: 1790*2621Sllai1 * /dev/dir/nm -> /dev/... 1791*2621Sllai1 */ 1792*2621Sllai1 rvp = devname_find_by_devpath((char *)args, NULL); 1793*2621Sllai1 break; 1794*2621Sllai1 default: 1795*2621Sllai1 if (args) 1796*2621Sllai1 kmem_free((char *)args, strlen(args) + 1); 1797*2621Sllai1 return (ENOENT); 1798*2621Sllai1 1799*2621Sllai1 } 1800*2621Sllai1 1801*2621Sllai1 if (rvp == NULL) { 1802*2621Sllai1 if (args) 1803*2621Sllai1 kmem_free((char *)args, strlen(args) + 1); 1804*2621Sllai1 return (ENOENT); 1805*2621Sllai1 } else { 1806*2621Sllai1 vap = sdev_getdefault_attr(VLNK); 1807*2621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 1808*2621Sllai1 /* 1809*2621Sllai1 * Could sdev_mknode return a different dv_node 1810*2621Sllai1 * once the lock is dropped? 1811*2621Sllai1 */ 1812*2621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 1813*2621Sllai1 rw_exit(&ddv->sdev_contents); 1814*2621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 1815*2621Sllai1 } 1816*2621Sllai1 error = sdev_mknode(ddv, nm, &dv, vap, NULL, args, cred, 1817*2621Sllai1 SDEV_READY); 1818*2621Sllai1 rw_downgrade(&ddv->sdev_contents); 1819*2621Sllai1 if (error) { 1820*2621Sllai1 if (args) 1821*2621Sllai1 kmem_free((char *)args, strlen(args) + 1); 1822*2621Sllai1 return (error); 1823*2621Sllai1 } else { 1824*2621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 1825*2621Sllai1 SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP); 1826*2621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 1827*2621Sllai1 error = 0; 1828*2621Sllai1 } 1829*2621Sllai1 } 1830*2621Sllai1 1831*2621Sllai1 if (args) 1832*2621Sllai1 kmem_free((char *)args, strlen(args) + 1); 1833*2621Sllai1 1834*2621Sllai1 *dvp = dv; 1835*2621Sllai1 return (0); 1836*2621Sllai1 } 1837*2621Sllai1 1838*2621Sllai1 /* 1839*2621Sllai1 * Support for specialized device naming construction mechanisms 1840*2621Sllai1 */ 1841*2621Sllai1 static int 1842*2621Sllai1 sdev_call_dircallback(struct sdev_node *ddv, struct sdev_node **dvp, char *nm, 1843*2621Sllai1 int (*callback)(struct sdev_node *, char *, void **, struct cred *, 1844*2621Sllai1 void *, char *), int flags, struct cred *cred) 1845*2621Sllai1 { 1846*2621Sllai1 int rv = 0; 1847*2621Sllai1 char *physpath = NULL; 1848*2621Sllai1 struct vnode *rvp = NULL; 1849*2621Sllai1 struct vattr vattr; 1850*2621Sllai1 struct vattr *vap; 1851*2621Sllai1 struct sdev_node *dv = *dvp; 1852*2621Sllai1 1853*2621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 1854*2621Sllai1 SDEV_BLOCK_OTHERS(dv, SDEV_LOOKUP); 1855*2621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 1856*2621Sllai1 1857*2621Sllai1 /* for non-devfsadm devices */ 1858*2621Sllai1 if (flags & SDEV_PATH) { 1859*2621Sllai1 physpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 1860*2621Sllai1 rv = callback(ddv, nm, (void *)&physpath, kcred, NULL, 1861*2621Sllai1 NULL); 1862*2621Sllai1 if (rv) { 1863*2621Sllai1 kmem_free(physpath, MAXPATHLEN); 1864*2621Sllai1 return (-1); 1865*2621Sllai1 } 1866*2621Sllai1 1867*2621Sllai1 ASSERT(physpath); 1868*2621Sllai1 rvp = devname_configure_by_path(physpath, NULL); 1869*2621Sllai1 if (rvp == NULL) { 1870*2621Sllai1 sdcmn_err3(("devname_configure_by_path: " 1871*2621Sllai1 "failed for /dev/%s/%s\n", 1872*2621Sllai1 ddv->sdev_name, nm)); 1873*2621Sllai1 kmem_free(physpath, MAXPATHLEN); 1874*2621Sllai1 rv = -1; 1875*2621Sllai1 } else { 1876*2621Sllai1 vap = sdev_getdefault_attr(VLNK); 1877*2621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 1878*2621Sllai1 1879*2621Sllai1 /* 1880*2621Sllai1 * Sdev_mknode may return back a different sdev_node 1881*2621Sllai1 * that was created by another thread that 1882*2621Sllai1 * raced to the directroy cache before this thread. 1883*2621Sllai1 * 1884*2621Sllai1 * With current directory cache mechanism 1885*2621Sllai1 * (linked list with the sdev_node name as 1886*2621Sllai1 * the entity key), this is a way to make sure 1887*2621Sllai1 * only one entry exists for the same name 1888*2621Sllai1 * in the same directory. The outcome is 1889*2621Sllai1 * the winner wins. 1890*2621Sllai1 */ 1891*2621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 1892*2621Sllai1 rw_exit(&ddv->sdev_contents); 1893*2621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 1894*2621Sllai1 } 1895*2621Sllai1 rv = sdev_mknode(ddv, nm, &dv, vap, NULL, 1896*2621Sllai1 (void *)physpath, cred, SDEV_READY); 1897*2621Sllai1 rw_downgrade(&ddv->sdev_contents); 1898*2621Sllai1 kmem_free(physpath, MAXPATHLEN); 1899*2621Sllai1 if (rv) { 1900*2621Sllai1 return (rv); 1901*2621Sllai1 } else { 1902*2621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 1903*2621Sllai1 SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP); 1904*2621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 1905*2621Sllai1 return (0); 1906*2621Sllai1 } 1907*2621Sllai1 } 1908*2621Sllai1 } else if (flags & SDEV_VNODE) { 1909*2621Sllai1 /* 1910*2621Sllai1 * DBNR has its own way to create the device 1911*2621Sllai1 * and return a backing store vnode in rvp 1912*2621Sllai1 */ 1913*2621Sllai1 ASSERT(callback); 1914*2621Sllai1 rv = callback(ddv, nm, (void *)&rvp, kcred, NULL, NULL); 1915*2621Sllai1 if (rv || (rvp == NULL)) { 1916*2621Sllai1 sdcmn_err3(("devname_lookup_func: SDEV_VNODE " 1917*2621Sllai1 "callback failed \n")); 1918*2621Sllai1 return (-1); 1919*2621Sllai1 } 1920*2621Sllai1 vap = sdev_getdefault_attr(rvp->v_type); 1921*2621Sllai1 if (vap == NULL) 1922*2621Sllai1 return (-1); 1923*2621Sllai1 1924*2621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 1925*2621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 1926*2621Sllai1 rw_exit(&ddv->sdev_contents); 1927*2621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 1928*2621Sllai1 } 1929*2621Sllai1 rv = sdev_mknode(ddv, nm, &dv, vap, rvp, NULL, 1930*2621Sllai1 cred, SDEV_READY); 1931*2621Sllai1 rw_downgrade(&ddv->sdev_contents); 1932*2621Sllai1 if (rv) 1933*2621Sllai1 return (rv); 1934*2621Sllai1 1935*2621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 1936*2621Sllai1 SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP); 1937*2621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 1938*2621Sllai1 return (0); 1939*2621Sllai1 } else if (flags & SDEV_VATTR) { 1940*2621Sllai1 /* 1941*2621Sllai1 * /dev/pts 1942*2621Sllai1 * 1943*2621Sllai1 * DBNR has its own way to create the device 1944*2621Sllai1 * "0" is returned upon success. 1945*2621Sllai1 * 1946*2621Sllai1 * callback is responsible to set the basic attributes, 1947*2621Sllai1 * e.g. va_type/va_uid/va_gid/ 1948*2621Sllai1 * dev_t if VCHR or VBLK/ 1949*2621Sllai1 */ 1950*2621Sllai1 ASSERT(callback); 1951*2621Sllai1 rv = callback(ddv, nm, (void *)&vattr, kcred, NULL, NULL); 1952*2621Sllai1 if (rv) { 1953*2621Sllai1 sdcmn_err3(("devname_lookup_func: SDEV_NONE " 1954*2621Sllai1 "callback failed \n")); 1955*2621Sllai1 return (-1); 1956*2621Sllai1 } 1957*2621Sllai1 1958*2621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 1959*2621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 1960*2621Sllai1 rw_exit(&ddv->sdev_contents); 1961*2621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 1962*2621Sllai1 } 1963*2621Sllai1 rv = sdev_mknode(ddv, nm, &dv, &vattr, NULL, NULL, 1964*2621Sllai1 cred, SDEV_READY); 1965*2621Sllai1 rw_downgrade(&ddv->sdev_contents); 1966*2621Sllai1 1967*2621Sllai1 if (rv) 1968*2621Sllai1 return (rv); 1969*2621Sllai1 1970*2621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 1971*2621Sllai1 SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP); 1972*2621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 1973*2621Sllai1 return (0); 1974*2621Sllai1 } else { 1975*2621Sllai1 impossible(("lookup: %s/%s by %s not supported (%d)\n", 1976*2621Sllai1 SDEVTOV(ddv)->v_path, nm, curproc->p_user.u_comm, 1977*2621Sllai1 __LINE__)); 1978*2621Sllai1 rv = -1; 1979*2621Sllai1 } 1980*2621Sllai1 1981*2621Sllai1 *dvp = dv; 1982*2621Sllai1 return (rv); 1983*2621Sllai1 } 1984*2621Sllai1 1985*2621Sllai1 static int 1986*2621Sllai1 is_devfsadm_thread(char *exec_name) 1987*2621Sllai1 { 1988*2621Sllai1 /* 1989*2621Sllai1 * note: because devfsadmd -> /usr/sbin/devfsadm 1990*2621Sllai1 * it is safe to use "devfsadm" to capture the lookups 1991*2621Sllai1 * from devfsadm and its daemon version. 1992*2621Sllai1 */ 1993*2621Sllai1 if (strcmp(exec_name, "devfsadm") == 0) 1994*2621Sllai1 return (1); 1995*2621Sllai1 return (0); 1996*2621Sllai1 } 1997*2621Sllai1 1998*2621Sllai1 1999*2621Sllai1 /* 2000*2621Sllai1 * Lookup Order: 2001*2621Sllai1 * sdev_node cache; 2002*2621Sllai1 * backing store (SDEV_PERSIST); 2003*2621Sllai1 * DBNR: a. dir_ops implemented in the loadable modules; 2004*2621Sllai1 * b. vnode ops in vtab. 2005*2621Sllai1 */ 2006*2621Sllai1 int 2007*2621Sllai1 devname_lookup_func(struct sdev_node *ddv, char *nm, struct vnode **vpp, 2008*2621Sllai1 struct cred *cred, int (*callback)(struct sdev_node *, char *, void **, 2009*2621Sllai1 struct cred *, void *, char *), int flags) 2010*2621Sllai1 { 2011*2621Sllai1 int rv = 0, nmlen; 2012*2621Sllai1 struct vnode *rvp = NULL; 2013*2621Sllai1 struct sdev_node *dv = NULL; 2014*2621Sllai1 int retried = 0; 2015*2621Sllai1 int error = 0; 2016*2621Sllai1 struct devname_nsmap *map = NULL; 2017*2621Sllai1 struct devname_ops *dirops = NULL; 2018*2621Sllai1 int (*fn)(char *, devname_handle_t *, struct cred *) = NULL; 2019*2621Sllai1 struct vattr vattr; 2020*2621Sllai1 char *lookup_thread = curproc->p_user.u_comm; 2021*2621Sllai1 int failed_flags = 0; 2022*2621Sllai1 int (*vtor)(struct sdev_node *) = NULL; 2023*2621Sllai1 int state; 2024*2621Sllai1 int parent_state; 2025*2621Sllai1 char *link = NULL; 2026*2621Sllai1 2027*2621Sllai1 if (SDEVTOV(ddv)->v_type != VDIR) 2028*2621Sllai1 return (ENOTDIR); 2029*2621Sllai1 2030*2621Sllai1 /* 2031*2621Sllai1 * Empty name or ., return node itself. 2032*2621Sllai1 */ 2033*2621Sllai1 nmlen = strlen(nm); 2034*2621Sllai1 if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) { 2035*2621Sllai1 *vpp = SDEVTOV(ddv); 2036*2621Sllai1 VN_HOLD(*vpp); 2037*2621Sllai1 return (0); 2038*2621Sllai1 } 2039*2621Sllai1 2040*2621Sllai1 /* 2041*2621Sllai1 * .., return the parent directory 2042*2621Sllai1 */ 2043*2621Sllai1 if ((nmlen == 2) && (strcmp(nm, "..") == 0)) { 2044*2621Sllai1 *vpp = SDEVTOV(ddv->sdev_dotdot); 2045*2621Sllai1 VN_HOLD(*vpp); 2046*2621Sllai1 return (0); 2047*2621Sllai1 } 2048*2621Sllai1 2049*2621Sllai1 rw_enter(&ddv->sdev_contents, RW_READER); 2050*2621Sllai1 if (ddv->sdev_flags & SDEV_VTOR) { 2051*2621Sllai1 vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv); 2052*2621Sllai1 ASSERT(vtor); 2053*2621Sllai1 } 2054*2621Sllai1 2055*2621Sllai1 tryagain: 2056*2621Sllai1 /* 2057*2621Sllai1 * (a) directory cache lookup: 2058*2621Sllai1 */ 2059*2621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 2060*2621Sllai1 parent_state = ddv->sdev_state; 2061*2621Sllai1 dv = sdev_cache_lookup(ddv, nm); 2062*2621Sllai1 if (dv) { 2063*2621Sllai1 state = dv->sdev_state; 2064*2621Sllai1 switch (state) { 2065*2621Sllai1 case SDEV_INIT: 2066*2621Sllai1 if (is_devfsadm_thread(lookup_thread)) 2067*2621Sllai1 break; 2068*2621Sllai1 2069*2621Sllai1 /* ZOMBIED parent won't allow node creation */ 2070*2621Sllai1 if (parent_state == SDEV_ZOMBIE) { 2071*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, 2072*2621Sllai1 retried); 2073*2621Sllai1 goto nolock_notfound; 2074*2621Sllai1 } 2075*2621Sllai1 2076*2621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 2077*2621Sllai1 /* compensate the threads started after devfsadm */ 2078*2621Sllai1 if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) && 2079*2621Sllai1 !(SDEV_IS_LOOKUP(dv))) 2080*2621Sllai1 SDEV_BLOCK_OTHERS(dv, 2081*2621Sllai1 (SDEV_LOOKUP | SDEV_LGWAITING)); 2082*2621Sllai1 2083*2621Sllai1 if (SDEV_IS_LOOKUP(dv)) { 2084*2621Sllai1 failed_flags |= SLF_REBUILT; 2085*2621Sllai1 rw_exit(&ddv->sdev_contents); 2086*2621Sllai1 error = sdev_wait4lookup(dv, SDEV_LOOKUP); 2087*2621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 2088*2621Sllai1 rw_enter(&ddv->sdev_contents, RW_READER); 2089*2621Sllai1 2090*2621Sllai1 if (error != 0) { 2091*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, 2092*2621Sllai1 retried); 2093*2621Sllai1 goto nolock_notfound; 2094*2621Sllai1 } 2095*2621Sllai1 2096*2621Sllai1 state = dv->sdev_state; 2097*2621Sllai1 if (state == SDEV_INIT) { 2098*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, 2099*2621Sllai1 retried); 2100*2621Sllai1 goto nolock_notfound; 2101*2621Sllai1 } else if (state == SDEV_READY) { 2102*2621Sllai1 goto found; 2103*2621Sllai1 } else if (state == SDEV_ZOMBIE) { 2104*2621Sllai1 rw_exit(&ddv->sdev_contents); 2105*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, 2106*2621Sllai1 retried); 2107*2621Sllai1 SDEV_RELE(dv); 2108*2621Sllai1 goto lookup_failed; 2109*2621Sllai1 } 2110*2621Sllai1 } else { 2111*2621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 2112*2621Sllai1 } 2113*2621Sllai1 break; 2114*2621Sllai1 case SDEV_READY: 2115*2621Sllai1 goto found; 2116*2621Sllai1 case SDEV_ZOMBIE: 2117*2621Sllai1 rw_exit(&ddv->sdev_contents); 2118*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2119*2621Sllai1 SDEV_RELE(dv); 2120*2621Sllai1 goto lookup_failed; 2121*2621Sllai1 default: 2122*2621Sllai1 rw_exit(&ddv->sdev_contents); 2123*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2124*2621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags); 2125*2621Sllai1 *vpp = NULLVP; 2126*2621Sllai1 return (ENOENT); 2127*2621Sllai1 } 2128*2621Sllai1 } 2129*2621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 2130*2621Sllai1 2131*2621Sllai1 /* 2132*2621Sllai1 * ZOMBIED parent does not allow new node creation. 2133*2621Sllai1 * bail out early 2134*2621Sllai1 */ 2135*2621Sllai1 if (parent_state == SDEV_ZOMBIE) { 2136*2621Sllai1 rw_exit(&ddv->sdev_contents); 2137*2621Sllai1 *vpp = NULL; 2138*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2139*2621Sllai1 return (ENOENT); 2140*2621Sllai1 } 2141*2621Sllai1 2142*2621Sllai1 /* 2143*2621Sllai1 * (b0): backing store lookup 2144*2621Sllai1 * SDEV_PERSIST is default except: 2145*2621Sllai1 * 1) pts nodes 2146*2621Sllai1 * 2) non-chmod'ed local nodes 2147*2621Sllai1 */ 2148*2621Sllai1 if (SDEV_IS_PERSIST(ddv)) { 2149*2621Sllai1 error = devname_backstore_lookup(ddv, nm, &rvp); 2150*2621Sllai1 2151*2621Sllai1 if (!error) { 2152*2621Sllai1 sdcmn_err3(("devname_backstore_lookup: " 2153*2621Sllai1 "found attrvp %p for %s\n", (void *)rvp, nm)); 2154*2621Sllai1 2155*2621Sllai1 vattr.va_mask = AT_MODE|AT_UID|AT_GID; 2156*2621Sllai1 error = VOP_GETATTR(rvp, &vattr, 0, cred); 2157*2621Sllai1 if (error) { 2158*2621Sllai1 rw_exit(&ddv->sdev_contents); 2159*2621Sllai1 if (dv) 2160*2621Sllai1 SDEV_RELE(dv); 2161*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2162*2621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags); 2163*2621Sllai1 *vpp = NULLVP; 2164*2621Sllai1 return (ENOENT); 2165*2621Sllai1 } 2166*2621Sllai1 2167*2621Sllai1 if (vattr.va_type == VLNK) { 2168*2621Sllai1 error = sdev_getlink(rvp, &link); 2169*2621Sllai1 if (error) { 2170*2621Sllai1 rw_exit(&ddv->sdev_contents); 2171*2621Sllai1 if (dv) 2172*2621Sllai1 SDEV_RELE(dv); 2173*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, 2174*2621Sllai1 retried); 2175*2621Sllai1 sdev_lookup_failed(ddv, nm, 2176*2621Sllai1 failed_flags); 2177*2621Sllai1 *vpp = NULLVP; 2178*2621Sllai1 return (ENOENT); 2179*2621Sllai1 } 2180*2621Sllai1 ASSERT(link != NULL); 2181*2621Sllai1 } 2182*2621Sllai1 2183*2621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 2184*2621Sllai1 rw_exit(&ddv->sdev_contents); 2185*2621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 2186*2621Sllai1 } 2187*2621Sllai1 error = sdev_mknode(ddv, nm, &dv, &vattr, 2188*2621Sllai1 rvp, link, cred, SDEV_READY); 2189*2621Sllai1 rw_downgrade(&ddv->sdev_contents); 2190*2621Sllai1 2191*2621Sllai1 if (link != NULL) { 2192*2621Sllai1 kmem_free(link, strlen(link) + 1); 2193*2621Sllai1 link = NULL; 2194*2621Sllai1 } 2195*2621Sllai1 2196*2621Sllai1 if (error) { 2197*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2198*2621Sllai1 rw_exit(&ddv->sdev_contents); 2199*2621Sllai1 if (dv) 2200*2621Sllai1 SDEV_RELE(dv); 2201*2621Sllai1 goto lookup_failed; 2202*2621Sllai1 } else { 2203*2621Sllai1 goto found; 2204*2621Sllai1 } 2205*2621Sllai1 } else if (retried) { 2206*2621Sllai1 rw_exit(&ddv->sdev_contents); 2207*2621Sllai1 sdcmn_err3(("retry of lookup of %s/%s: failed\n", 2208*2621Sllai1 ddv->sdev_name, nm)); 2209*2621Sllai1 if (dv) 2210*2621Sllai1 SDEV_RELE(dv); 2211*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2212*2621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags); 2213*2621Sllai1 *vpp = NULLVP; 2214*2621Sllai1 return (ENOENT); 2215*2621Sllai1 } 2216*2621Sllai1 } 2217*2621Sllai1 2218*2621Sllai1 2219*2621Sllai1 /* first thread that is doing the lookup on this node */ 2220*2621Sllai1 if (!dv) { 2221*2621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 2222*2621Sllai1 rw_exit(&ddv->sdev_contents); 2223*2621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 2224*2621Sllai1 } 2225*2621Sllai1 error = sdev_mknode(ddv, nm, &dv, NULL, NULL, NULL, 2226*2621Sllai1 cred, SDEV_INIT); 2227*2621Sllai1 if (!dv) { 2228*2621Sllai1 rw_exit(&ddv->sdev_contents); 2229*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2230*2621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags); 2231*2621Sllai1 *vpp = NULLVP; 2232*2621Sllai1 return (ENOENT); 2233*2621Sllai1 } 2234*2621Sllai1 rw_downgrade(&ddv->sdev_contents); 2235*2621Sllai1 } 2236*2621Sllai1 ASSERT(dv); 2237*2621Sllai1 ASSERT(SDEV_HELD(dv)); 2238*2621Sllai1 2239*2621Sllai1 if (SDEV_IS_NO_NCACHE(dv)) { 2240*2621Sllai1 failed_flags |= SLF_NO_NCACHE; 2241*2621Sllai1 } 2242*2621Sllai1 2243*2621Sllai1 if (SDEV_IS_GLOBAL(ddv)) { 2244*2621Sllai1 map = sdev_get_map(ddv, 1); 2245*2621Sllai1 dirops = map ? map->dir_ops : NULL; 2246*2621Sllai1 fn = dirops ? dirops->devnops_lookup : NULL; 2247*2621Sllai1 } 2248*2621Sllai1 2249*2621Sllai1 /* 2250*2621Sllai1 * (b1) invoking devfsadm once per life time for devfsadm nodes 2251*2621Sllai1 */ 2252*2621Sllai1 if ((fn == NULL) && !callback) { 2253*2621Sllai1 2254*2621Sllai1 if (sdev_reconfig_boot || !i_ddi_io_initialized() || 2255*2621Sllai1 SDEV_IS_DYNAMIC(ddv) || SDEV_IS_NO_NCACHE(dv) || 2256*2621Sllai1 ((moddebug & MODDEBUG_FINI_EBUSY) != 0)) { 2257*2621Sllai1 ASSERT(SDEV_HELD(dv)); 2258*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2259*2621Sllai1 goto nolock_notfound; 2260*2621Sllai1 } 2261*2621Sllai1 2262*2621Sllai1 /* 2263*2621Sllai1 * filter out known non-existent devices recorded 2264*2621Sllai1 * during initial reconfiguration boot for which 2265*2621Sllai1 * reconfig should not be done and lookup may 2266*2621Sllai1 * be short-circuited now. 2267*2621Sllai1 */ 2268*2621Sllai1 if (sdev_lookup_filter(ddv, nm)) { 2269*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2270*2621Sllai1 goto nolock_notfound; 2271*2621Sllai1 } 2272*2621Sllai1 2273*2621Sllai1 /* bypassing devfsadm internal nodes */ 2274*2621Sllai1 if (is_devfsadm_thread(lookup_thread)) { 2275*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2276*2621Sllai1 goto nolock_notfound; 2277*2621Sllai1 } 2278*2621Sllai1 2279*2621Sllai1 if (sdev_reconfig_disable) { 2280*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2281*2621Sllai1 goto nolock_notfound; 2282*2621Sllai1 } 2283*2621Sllai1 2284*2621Sllai1 error = sdev_call_devfsadmd(ddv, dv, nm); 2285*2621Sllai1 if (error == 0) { 2286*2621Sllai1 sdcmn_err8(("lookup of %s/%s by %s: reconfig\n", 2287*2621Sllai1 ddv->sdev_name, nm, curproc->p_user.u_comm)); 2288*2621Sllai1 if (sdev_reconfig_verbose) { 2289*2621Sllai1 cmn_err(CE_CONT, 2290*2621Sllai1 "?lookup of %s/%s by %s: reconfig\n", 2291*2621Sllai1 ddv->sdev_name, nm, curproc->p_user.u_comm); 2292*2621Sllai1 } 2293*2621Sllai1 retried = 1; 2294*2621Sllai1 failed_flags |= SLF_REBUILT; 2295*2621Sllai1 ASSERT(dv->sdev_state != SDEV_ZOMBIE); 2296*2621Sllai1 SDEV_SIMPLE_RELE(dv); 2297*2621Sllai1 goto tryagain; 2298*2621Sllai1 } else { 2299*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2300*2621Sllai1 goto nolock_notfound; 2301*2621Sllai1 } 2302*2621Sllai1 } 2303*2621Sllai1 2304*2621Sllai1 /* 2305*2621Sllai1 * (b2) Directory Based Name Resolution (DBNR): 2306*2621Sllai1 * ddv - parent 2307*2621Sllai1 * nm - /dev/(ddv->sdev_name)/nm 2308*2621Sllai1 * 2309*2621Sllai1 * note: module vnode ops take precedence than the build-in ones 2310*2621Sllai1 */ 2311*2621Sllai1 if (fn) { 2312*2621Sllai1 error = sdev_call_modulelookup(ddv, &dv, nm, fn, cred); 2313*2621Sllai1 if (error) { 2314*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2315*2621Sllai1 goto notfound; 2316*2621Sllai1 } else { 2317*2621Sllai1 goto found; 2318*2621Sllai1 } 2319*2621Sllai1 } else if (callback) { 2320*2621Sllai1 error = sdev_call_dircallback(ddv, &dv, nm, callback, 2321*2621Sllai1 flags, cred); 2322*2621Sllai1 if (error == 0) { 2323*2621Sllai1 goto found; 2324*2621Sllai1 } else { 2325*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2326*2621Sllai1 goto notfound; 2327*2621Sllai1 } 2328*2621Sllai1 } 2329*2621Sllai1 ASSERT(rvp); 2330*2621Sllai1 2331*2621Sllai1 found: 2332*2621Sllai1 ASSERT(!(dv->sdev_flags & SDEV_STALE)); 2333*2621Sllai1 ASSERT(dv->sdev_state == SDEV_READY); 2334*2621Sllai1 if (vtor) { 2335*2621Sllai1 /* 2336*2621Sllai1 * Check validity of returned node 2337*2621Sllai1 */ 2338*2621Sllai1 switch (vtor(dv)) { 2339*2621Sllai1 case SDEV_VTOR_VALID: 2340*2621Sllai1 break; 2341*2621Sllai1 case SDEV_VTOR_INVALID: 2342*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2343*2621Sllai1 sdcmn_err7(("lookup: destroy invalid " 2344*2621Sllai1 "node: %s(%p)\n", dv->sdev_name, (void *)dv)); 2345*2621Sllai1 goto nolock_notfound; 2346*2621Sllai1 case SDEV_VTOR_SKIP: 2347*2621Sllai1 sdcmn_err7(("lookup: node not applicable - " 2348*2621Sllai1 "skipping: %s(%p)\n", dv->sdev_name, (void *)dv)); 2349*2621Sllai1 rw_exit(&ddv->sdev_contents); 2350*2621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2351*2621Sllai1 SDEV_RELE(dv); 2352*2621Sllai1 goto lookup_failed; 2353*2621Sllai1 default: 2354*2621Sllai1 cmn_err(CE_PANIC, 2355*2621Sllai1 "dev fs: validator failed: %s(%p)\n", 2356*2621Sllai1 dv->sdev_name, (void *)dv); 2357*2621Sllai1 break; 2358*2621Sllai1 /*NOTREACHED*/ 2359*2621Sllai1 } 2360*2621Sllai1 } 2361*2621Sllai1 2362*2621Sllai1 if ((SDEVTOV(dv)->v_type == VDIR) && SDEV_IS_GLOBAL(dv)) { 2363*2621Sllai1 rw_enter(&dv->sdev_contents, RW_READER); 2364*2621Sllai1 (void) sdev_get_map(dv, 1); 2365*2621Sllai1 rw_exit(&dv->sdev_contents); 2366*2621Sllai1 } 2367*2621Sllai1 rw_exit(&ddv->sdev_contents); 2368*2621Sllai1 rv = sdev_to_vp(dv, vpp); 2369*2621Sllai1 sdcmn_err3(("devname_lookup_func: returning vp %p v_count %d state %d " 2370*2621Sllai1 "for nm %s, error %d\n", (void *)*vpp, (*vpp)->v_count, 2371*2621Sllai1 dv->sdev_state, nm, rv)); 2372*2621Sllai1 return (rv); 2373*2621Sllai1 2374*2621Sllai1 notfound: 2375*2621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 2376*2621Sllai1 SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP); 2377*2621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 2378*2621Sllai1 nolock_notfound: 2379*2621Sllai1 /* 2380*2621Sllai1 * Destroy the node that is created for synchronization purposes. 2381*2621Sllai1 */ 2382*2621Sllai1 sdcmn_err3(("devname_lookup_func: %s with state %d\n", 2383*2621Sllai1 nm, dv->sdev_state)); 2384*2621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 2385*2621Sllai1 if (dv->sdev_state == SDEV_INIT) { 2386*2621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 2387*2621Sllai1 rw_exit(&ddv->sdev_contents); 2388*2621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 2389*2621Sllai1 } 2390*2621Sllai1 2391*2621Sllai1 /* 2392*2621Sllai1 * Node state may have changed during the lock 2393*2621Sllai1 * changes. Re-check. 2394*2621Sllai1 */ 2395*2621Sllai1 if (dv->sdev_state == SDEV_INIT) { 2396*2621Sllai1 (void) sdev_dirdelete(ddv, dv); 2397*2621Sllai1 rw_exit(&ddv->sdev_contents); 2398*2621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags); 2399*2621Sllai1 *vpp = NULL; 2400*2621Sllai1 return (ENOENT); 2401*2621Sllai1 } 2402*2621Sllai1 } 2403*2621Sllai1 2404*2621Sllai1 rw_exit(&ddv->sdev_contents); 2405*2621Sllai1 SDEV_RELE(dv); 2406*2621Sllai1 2407*2621Sllai1 lookup_failed: 2408*2621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags); 2409*2621Sllai1 *vpp = NULL; 2410*2621Sllai1 return (ENOENT); 2411*2621Sllai1 } 2412*2621Sllai1 2413*2621Sllai1 /* 2414*2621Sllai1 * Given a directory node, mark all nodes beneath as 2415*2621Sllai1 * STALE, i.e. nodes that don't exist as far as new 2416*2621Sllai1 * consumers are concerned 2417*2621Sllai1 */ 2418*2621Sllai1 void 2419*2621Sllai1 sdev_stale(struct sdev_node *ddv) 2420*2621Sllai1 { 2421*2621Sllai1 struct sdev_node *dv; 2422*2621Sllai1 struct vnode *vp; 2423*2621Sllai1 2424*2621Sllai1 ASSERT(SDEVTOV(ddv)->v_type == VDIR); 2425*2621Sllai1 2426*2621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 2427*2621Sllai1 for (dv = ddv->sdev_dot; dv; dv = dv->sdev_next) { 2428*2621Sllai1 vp = SDEVTOV(dv); 2429*2621Sllai1 if (vp->v_type == VDIR) 2430*2621Sllai1 sdev_stale(dv); 2431*2621Sllai1 2432*2621Sllai1 sdcmn_err9(("sdev_stale: setting stale %s\n", 2433*2621Sllai1 dv->sdev_name)); 2434*2621Sllai1 dv->sdev_flags |= SDEV_STALE; 2435*2621Sllai1 } 2436*2621Sllai1 ddv->sdev_flags |= SDEV_BUILD; 2437*2621Sllai1 rw_exit(&ddv->sdev_contents); 2438*2621Sllai1 } 2439*2621Sllai1 2440*2621Sllai1 /* 2441*2621Sllai1 * Given a directory node, clean out all the nodes beneath. 2442*2621Sllai1 * If expr is specified, clean node with names matching expr. 2443*2621Sllai1 * If SDEV_ENFORCE is specified in flags, busy nodes are made stale, 2444*2621Sllai1 * so they are excluded from future lookups. 2445*2621Sllai1 */ 2446*2621Sllai1 int 2447*2621Sllai1 sdev_cleandir(struct sdev_node *ddv, char *expr, uint_t flags) 2448*2621Sllai1 { 2449*2621Sllai1 int error = 0; 2450*2621Sllai1 int busy = 0; 2451*2621Sllai1 struct vnode *vp; 2452*2621Sllai1 struct sdev_node *dv, *next = NULL; 2453*2621Sllai1 int bkstore = 0; 2454*2621Sllai1 int len = 0; 2455*2621Sllai1 char *bks_name = NULL; 2456*2621Sllai1 2457*2621Sllai1 ASSERT(SDEVTOV(ddv)->v_type == VDIR); 2458*2621Sllai1 2459*2621Sllai1 /* 2460*2621Sllai1 * We try our best to destroy all unused sdev_node's 2461*2621Sllai1 */ 2462*2621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 2463*2621Sllai1 for (dv = ddv->sdev_dot; dv; dv = next) { 2464*2621Sllai1 next = dv->sdev_next; 2465*2621Sllai1 vp = SDEVTOV(dv); 2466*2621Sllai1 2467*2621Sllai1 if (expr && gmatch(dv->sdev_name, expr) == 0) 2468*2621Sllai1 continue; 2469*2621Sllai1 2470*2621Sllai1 if (vp->v_type == VDIR && 2471*2621Sllai1 sdev_cleandir(dv, NULL, flags) != 0) { 2472*2621Sllai1 sdcmn_err9(("sdev_cleandir: dir %s busy\n", 2473*2621Sllai1 dv->sdev_name)); 2474*2621Sllai1 busy++; 2475*2621Sllai1 continue; 2476*2621Sllai1 } 2477*2621Sllai1 2478*2621Sllai1 if (vp->v_count > 0 && (flags & SDEV_ENFORCE) == 0) { 2479*2621Sllai1 sdcmn_err9(("sdev_cleandir: dir %s busy\n", 2480*2621Sllai1 dv->sdev_name)); 2481*2621Sllai1 busy++; 2482*2621Sllai1 continue; 2483*2621Sllai1 } 2484*2621Sllai1 2485*2621Sllai1 /* 2486*2621Sllai1 * at this point, either dv is not held or SDEV_ENFORCE 2487*2621Sllai1 * is specified. In either case, dv needs to be deleted 2488*2621Sllai1 */ 2489*2621Sllai1 SDEV_HOLD(dv); 2490*2621Sllai1 2491*2621Sllai1 bkstore = SDEV_IS_PERSIST(dv) ? 1 : 0; 2492*2621Sllai1 if (bkstore && (vp->v_type == VDIR)) 2493*2621Sllai1 bkstore += 1; 2494*2621Sllai1 2495*2621Sllai1 if (bkstore) { 2496*2621Sllai1 len = strlen(dv->sdev_name) + 1; 2497*2621Sllai1 bks_name = kmem_alloc(len, KM_SLEEP); 2498*2621Sllai1 bcopy(dv->sdev_name, bks_name, len); 2499*2621Sllai1 } 2500*2621Sllai1 2501*2621Sllai1 error = sdev_dirdelete(ddv, dv); 2502*2621Sllai1 2503*2621Sllai1 if (error == EBUSY) { 2504*2621Sllai1 sdcmn_err9(("sdev_cleandir: dir busy\n")); 2505*2621Sllai1 busy++; 2506*2621Sllai1 } 2507*2621Sllai1 2508*2621Sllai1 /* take care the backing store clean up */ 2509*2621Sllai1 if (bkstore && (error == 0)) { 2510*2621Sllai1 ASSERT(bks_name); 2511*2621Sllai1 ASSERT(ddv->sdev_attrvp); 2512*2621Sllai1 2513*2621Sllai1 if (bkstore == 1) { 2514*2621Sllai1 error = VOP_REMOVE(ddv->sdev_attrvp, 2515*2621Sllai1 bks_name, kcred); 2516*2621Sllai1 } else if (bkstore == 2) { 2517*2621Sllai1 error = VOP_RMDIR(ddv->sdev_attrvp, 2518*2621Sllai1 bks_name, ddv->sdev_attrvp, kcred); 2519*2621Sllai1 } 2520*2621Sllai1 2521*2621Sllai1 /* do not propagate the backing store errors */ 2522*2621Sllai1 if (error) { 2523*2621Sllai1 sdcmn_err9(("sdev_cleandir: backing store" 2524*2621Sllai1 "not cleaned\n")); 2525*2621Sllai1 error = 0; 2526*2621Sllai1 } 2527*2621Sllai1 2528*2621Sllai1 bkstore = 0; 2529*2621Sllai1 kmem_free(bks_name, len); 2530*2621Sllai1 bks_name = NULL; 2531*2621Sllai1 len = 0; 2532*2621Sllai1 } 2533*2621Sllai1 } 2534*2621Sllai1 2535*2621Sllai1 ddv->sdev_flags |= SDEV_BUILD; 2536*2621Sllai1 rw_exit(&ddv->sdev_contents); 2537*2621Sllai1 2538*2621Sllai1 if (busy) { 2539*2621Sllai1 error = EBUSY; 2540*2621Sllai1 } 2541*2621Sllai1 2542*2621Sllai1 return (error); 2543*2621Sllai1 } 2544*2621Sllai1 2545*2621Sllai1 /* 2546*2621Sllai1 * a convenient wrapper for readdir() funcs 2547*2621Sllai1 */ 2548*2621Sllai1 size_t 2549*2621Sllai1 add_dir_entry(dirent64_t *de, char *nm, size_t size, ino_t ino, offset_t off) 2550*2621Sllai1 { 2551*2621Sllai1 size_t reclen = DIRENT64_RECLEN(strlen(nm)); 2552*2621Sllai1 if (reclen > size) 2553*2621Sllai1 return (0); 2554*2621Sllai1 2555*2621Sllai1 de->d_ino = (ino64_t)ino; 2556*2621Sllai1 de->d_off = (off64_t)off + 1; 2557*2621Sllai1 de->d_reclen = (ushort_t)reclen; 2558*2621Sllai1 (void) strncpy(de->d_name, nm, DIRENT64_NAMELEN(reclen)); 2559*2621Sllai1 return (reclen); 2560*2621Sllai1 } 2561*2621Sllai1 2562*2621Sllai1 /* 2563*2621Sllai1 * sdev_mount service routines 2564*2621Sllai1 */ 2565*2621Sllai1 int 2566*2621Sllai1 sdev_copyin_mountargs(struct mounta *uap, struct sdev_mountargs *args) 2567*2621Sllai1 { 2568*2621Sllai1 int error; 2569*2621Sllai1 2570*2621Sllai1 if (uap->datalen != sizeof (*args)) 2571*2621Sllai1 return (EINVAL); 2572*2621Sllai1 2573*2621Sllai1 if (error = copyin(uap->dataptr, args, sizeof (*args))) { 2574*2621Sllai1 cmn_err(CE_WARN, "sdev_copyin_mountargs: can not" 2575*2621Sllai1 "get user data. error %d\n", error); 2576*2621Sllai1 return (EFAULT); 2577*2621Sllai1 } 2578*2621Sllai1 2579*2621Sllai1 return (0); 2580*2621Sllai1 } 2581*2621Sllai1 2582*2621Sllai1 #ifdef nextdp 2583*2621Sllai1 #undef nextdp 2584*2621Sllai1 #endif 2585*2621Sllai1 #define nextdp(dp) ((struct dirent64 *)((char *)(dp) + (dp)->d_reclen)) 2586*2621Sllai1 2587*2621Sllai1 /* 2588*2621Sllai1 * readdir helper func 2589*2621Sllai1 */ 2590*2621Sllai1 int 2591*2621Sllai1 devname_readdir_func(vnode_t *vp, uio_t *uiop, cred_t *cred, int *eofp, 2592*2621Sllai1 int flags) 2593*2621Sllai1 { 2594*2621Sllai1 struct sdev_node *ddv = VTOSDEV(vp); 2595*2621Sllai1 struct sdev_node *dv; 2596*2621Sllai1 dirent64_t *dp; 2597*2621Sllai1 ulong_t outcount = 0; 2598*2621Sllai1 size_t namelen; 2599*2621Sllai1 ulong_t alloc_count; 2600*2621Sllai1 void *outbuf; 2601*2621Sllai1 struct iovec *iovp; 2602*2621Sllai1 int error = 0; 2603*2621Sllai1 size_t reclen; 2604*2621Sllai1 offset_t diroff; 2605*2621Sllai1 offset_t soff; 2606*2621Sllai1 int this_reclen; 2607*2621Sllai1 struct devname_nsmap *map = NULL; 2608*2621Sllai1 struct devname_ops *dirops = NULL; 2609*2621Sllai1 int (*fn)(devname_handle_t *, struct cred *) = NULL; 2610*2621Sllai1 int (*vtor)(struct sdev_node *) = NULL; 2611*2621Sllai1 struct vattr attr; 2612*2621Sllai1 timestruc_t now; 2613*2621Sllai1 2614*2621Sllai1 ASSERT(ddv->sdev_attr || ddv->sdev_attrvp); 2615*2621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 2616*2621Sllai1 2617*2621Sllai1 if (uiop->uio_loffset >= MAXOFF_T) { 2618*2621Sllai1 if (eofp) 2619*2621Sllai1 *eofp = 1; 2620*2621Sllai1 return (0); 2621*2621Sllai1 } 2622*2621Sllai1 2623*2621Sllai1 if (uiop->uio_iovcnt != 1) 2624*2621Sllai1 return (EINVAL); 2625*2621Sllai1 2626*2621Sllai1 if (vp->v_type != VDIR) 2627*2621Sllai1 return (ENOTDIR); 2628*2621Sllai1 2629*2621Sllai1 if (ddv->sdev_flags & SDEV_VTOR) { 2630*2621Sllai1 vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv); 2631*2621Sllai1 ASSERT(vtor); 2632*2621Sllai1 } 2633*2621Sllai1 2634*2621Sllai1 if (eofp != NULL) 2635*2621Sllai1 *eofp = 0; 2636*2621Sllai1 2637*2621Sllai1 soff = uiop->uio_offset; 2638*2621Sllai1 iovp = uiop->uio_iov; 2639*2621Sllai1 alloc_count = iovp->iov_len; 2640*2621Sllai1 dp = outbuf = kmem_alloc(alloc_count, KM_SLEEP); 2641*2621Sllai1 outcount = 0; 2642*2621Sllai1 2643*2621Sllai1 if (ddv->sdev_state == SDEV_ZOMBIE) 2644*2621Sllai1 goto get_cache; 2645*2621Sllai1 2646*2621Sllai1 if (!SDEV_IS_GLOBAL(ddv)) { 2647*2621Sllai1 /* make sure directory content is up to date */ 2648*2621Sllai1 prof_filldir(ddv); 2649*2621Sllai1 } else { 2650*2621Sllai1 map = sdev_get_map(ddv, 0); 2651*2621Sllai1 dirops = map ? map->dir_ops : NULL; 2652*2621Sllai1 fn = dirops ? dirops->devnops_readdir : NULL; 2653*2621Sllai1 2654*2621Sllai1 if (map && map->dir_map) { 2655*2621Sllai1 /* 2656*2621Sllai1 * load the name mapping rule database 2657*2621Sllai1 * through invoking devfsadm and symlink 2658*2621Sllai1 * all the entries in the map 2659*2621Sllai1 */ 2660*2621Sllai1 devname_rdr_result_t rdr_result; 2661*2621Sllai1 int do_thread = 0; 2662*2621Sllai1 2663*2621Sllai1 rw_enter(&map->dir_lock, RW_READER); 2664*2621Sllai1 do_thread = map->dir_maploaded ? 0 : 1; 2665*2621Sllai1 rw_exit(&map->dir_lock); 2666*2621Sllai1 2667*2621Sllai1 if (do_thread) { 2668*2621Sllai1 mutex_enter(&ddv->sdev_lookup_lock); 2669*2621Sllai1 SDEV_BLOCK_OTHERS(ddv, SDEV_READDIR); 2670*2621Sllai1 mutex_exit(&ddv->sdev_lookup_lock); 2671*2621Sllai1 2672*2621Sllai1 sdev_dispatch_to_nsrdr_thread(ddv, 2673*2621Sllai1 map->dir_map, &rdr_result); 2674*2621Sllai1 } 2675*2621Sllai1 } else if ((sdev_boot_state == SDEV_BOOT_STATE_COMPLETE) && 2676*2621Sllai1 !sdev_reconfig_boot && (flags & SDEV_BROWSE) && 2677*2621Sllai1 !SDEV_IS_DYNAMIC(ddv) && !SDEV_IS_NO_NCACHE(ddv) && 2678*2621Sllai1 ((moddebug & MODDEBUG_FINI_EBUSY) == 0) && 2679*2621Sllai1 !DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state) && 2680*2621Sllai1 !DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) && 2681*2621Sllai1 !sdev_reconfig_disable) { 2682*2621Sllai1 /* 2683*2621Sllai1 * invoking "devfsadm" to do system device reconfig 2684*2621Sllai1 */ 2685*2621Sllai1 mutex_enter(&ddv->sdev_lookup_lock); 2686*2621Sllai1 SDEV_BLOCK_OTHERS(ddv, 2687*2621Sllai1 (SDEV_READDIR|SDEV_LGWAITING)); 2688*2621Sllai1 mutex_exit(&ddv->sdev_lookup_lock); 2689*2621Sllai1 2690*2621Sllai1 sdcmn_err8(("readdir of %s by %s: reconfig\n", 2691*2621Sllai1 ddv->sdev_path, curproc->p_user.u_comm)); 2692*2621Sllai1 if (sdev_reconfig_verbose) { 2693*2621Sllai1 cmn_err(CE_CONT, 2694*2621Sllai1 "?readdir of %s by %s: reconfig\n", 2695*2621Sllai1 ddv->sdev_path, curproc->p_user.u_comm); 2696*2621Sllai1 } 2697*2621Sllai1 2698*2621Sllai1 sdev_devfsadmd_thread(ddv, NULL, kcred); 2699*2621Sllai1 } else if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) { 2700*2621Sllai1 /* 2701*2621Sllai1 * compensate the "ls" started later than "devfsadm" 2702*2621Sllai1 */ 2703*2621Sllai1 mutex_enter(&ddv->sdev_lookup_lock); 2704*2621Sllai1 SDEV_BLOCK_OTHERS(ddv, (SDEV_READDIR|SDEV_LGWAITING)); 2705*2621Sllai1 mutex_exit(&ddv->sdev_lookup_lock); 2706*2621Sllai1 } 2707*2621Sllai1 2708*2621Sllai1 /* 2709*2621Sllai1 * release the contents lock so that 2710*2621Sllai1 * the cache maybe updated by devfsadmd 2711*2621Sllai1 */ 2712*2621Sllai1 rw_exit(&ddv->sdev_contents); 2713*2621Sllai1 mutex_enter(&ddv->sdev_lookup_lock); 2714*2621Sllai1 if (SDEV_IS_READDIR(ddv)) 2715*2621Sllai1 (void) sdev_wait4lookup(ddv, SDEV_READDIR); 2716*2621Sllai1 mutex_exit(&ddv->sdev_lookup_lock); 2717*2621Sllai1 rw_enter(&ddv->sdev_contents, RW_READER); 2718*2621Sllai1 2719*2621Sllai1 sdcmn_err4(("readdir of directory %s by %s\n", 2720*2621Sllai1 ddv->sdev_name, curproc->p_user.u_comm)); 2721*2621Sllai1 while (ddv->sdev_flags & SDEV_BUILD) { 2722*2621Sllai1 if (SDEV_IS_PERSIST(ddv)) { 2723*2621Sllai1 error = sdev_filldir_from_store(ddv, 2724*2621Sllai1 alloc_count, cred); 2725*2621Sllai1 } 2726*2621Sllai1 2727*2621Sllai1 /* 2728*2621Sllai1 * pre-creating the directories 2729*2621Sllai1 * defined in vtab 2730*2621Sllai1 */ 2731*2621Sllai1 if (SDEVTOV(ddv)->v_flag & VROOT) { 2732*2621Sllai1 error = sdev_filldir_dynamic(ddv); 2733*2621Sllai1 } 2734*2621Sllai1 2735*2621Sllai1 if (!error) 2736*2621Sllai1 ddv->sdev_flags &= ~SDEV_BUILD; 2737*2621Sllai1 } 2738*2621Sllai1 } 2739*2621Sllai1 2740*2621Sllai1 get_cache: 2741*2621Sllai1 /* handle "." and ".." */ 2742*2621Sllai1 diroff = 0; 2743*2621Sllai1 if (soff == 0) { 2744*2621Sllai1 /* first time */ 2745*2621Sllai1 this_reclen = DIRENT64_RECLEN(1); 2746*2621Sllai1 if (alloc_count < this_reclen) { 2747*2621Sllai1 error = EINVAL; 2748*2621Sllai1 goto done; 2749*2621Sllai1 } 2750*2621Sllai1 2751*2621Sllai1 dp->d_ino = (ino64_t)ddv->sdev_ino; 2752*2621Sllai1 dp->d_off = (off64_t)1; 2753*2621Sllai1 dp->d_reclen = (ushort_t)this_reclen; 2754*2621Sllai1 2755*2621Sllai1 (void) strncpy(dp->d_name, ".", 2756*2621Sllai1 DIRENT64_NAMELEN(this_reclen)); 2757*2621Sllai1 outcount += dp->d_reclen; 2758*2621Sllai1 dp = nextdp(dp); 2759*2621Sllai1 } 2760*2621Sllai1 2761*2621Sllai1 diroff++; 2762*2621Sllai1 if (soff <= 1) { 2763*2621Sllai1 this_reclen = DIRENT64_RECLEN(2); 2764*2621Sllai1 if (alloc_count < outcount + this_reclen) { 2765*2621Sllai1 error = EINVAL; 2766*2621Sllai1 goto done; 2767*2621Sllai1 } 2768*2621Sllai1 2769*2621Sllai1 dp->d_reclen = (ushort_t)this_reclen; 2770*2621Sllai1 dp->d_ino = (ino64_t)ddv->sdev_dotdot->sdev_ino; 2771*2621Sllai1 dp->d_off = (off64_t)2; 2772*2621Sllai1 2773*2621Sllai1 (void) strncpy(dp->d_name, "..", 2774*2621Sllai1 DIRENT64_NAMELEN(this_reclen)); 2775*2621Sllai1 outcount += dp->d_reclen; 2776*2621Sllai1 2777*2621Sllai1 dp = nextdp(dp); 2778*2621Sllai1 } 2779*2621Sllai1 2780*2621Sllai1 2781*2621Sllai1 /* gets the cache */ 2782*2621Sllai1 diroff++; 2783*2621Sllai1 for (dv = ddv->sdev_dot; dv; dv = dv->sdev_next, diroff++) { 2784*2621Sllai1 sdcmn_err3(("sdev_readdir: diroff %lld soff %lld for '%s' \n", 2785*2621Sllai1 diroff, soff, dv->sdev_name)); 2786*2621Sllai1 2787*2621Sllai1 /* bypassing pre-matured nodes */ 2788*2621Sllai1 if (diroff < soff || (dv->sdev_state != SDEV_READY)) { 2789*2621Sllai1 sdcmn_err3(("sdev_readdir: pre-mature node " 2790*2621Sllai1 "%s\n", dv->sdev_name)); 2791*2621Sllai1 continue; 2792*2621Sllai1 } 2793*2621Sllai1 2794*2621Sllai1 /* don't list stale nodes */ 2795*2621Sllai1 if (dv->sdev_flags & SDEV_STALE) { 2796*2621Sllai1 sdcmn_err4(("sdev_readdir: STALE node " 2797*2621Sllai1 "%s\n", dv->sdev_name)); 2798*2621Sllai1 continue; 2799*2621Sllai1 } 2800*2621Sllai1 2801*2621Sllai1 /* 2802*2621Sllai1 * Check validity of node 2803*2621Sllai1 */ 2804*2621Sllai1 if (vtor) { 2805*2621Sllai1 switch (vtor(dv)) { 2806*2621Sllai1 case SDEV_VTOR_VALID: 2807*2621Sllai1 break; 2808*2621Sllai1 case SDEV_VTOR_INVALID: 2809*2621Sllai1 case SDEV_VTOR_SKIP: 2810*2621Sllai1 continue; 2811*2621Sllai1 default: 2812*2621Sllai1 cmn_err(CE_PANIC, 2813*2621Sllai1 "dev fs: validator failed: %s(%p)\n", 2814*2621Sllai1 dv->sdev_name, (void *)dv); 2815*2621Sllai1 break; 2816*2621Sllai1 /*NOTREACHED*/ 2817*2621Sllai1 } 2818*2621Sllai1 } 2819*2621Sllai1 2820*2621Sllai1 /* 2821*2621Sllai1 * call back into the module for the validity/bookkeeping 2822*2621Sllai1 * of this entry 2823*2621Sllai1 */ 2824*2621Sllai1 if (fn) { 2825*2621Sllai1 error = (*fn)(&(dv->sdev_handle), cred); 2826*2621Sllai1 if (error) { 2827*2621Sllai1 sdcmn_err4(("sdev_readdir: module did not " 2828*2621Sllai1 "validate %s\n", dv->sdev_name)); 2829*2621Sllai1 continue; 2830*2621Sllai1 } 2831*2621Sllai1 } 2832*2621Sllai1 2833*2621Sllai1 namelen = strlen(dv->sdev_name); 2834*2621Sllai1 reclen = DIRENT64_RECLEN(namelen); 2835*2621Sllai1 if (outcount + reclen > alloc_count) { 2836*2621Sllai1 goto full; 2837*2621Sllai1 } 2838*2621Sllai1 dp->d_reclen = (ushort_t)reclen; 2839*2621Sllai1 dp->d_ino = (ino64_t)dv->sdev_ino; 2840*2621Sllai1 dp->d_off = (off64_t)diroff + 1; 2841*2621Sllai1 (void) strncpy(dp->d_name, dv->sdev_name, 2842*2621Sllai1 DIRENT64_NAMELEN(reclen)); 2843*2621Sllai1 outcount += reclen; 2844*2621Sllai1 dp = nextdp(dp); 2845*2621Sllai1 } 2846*2621Sllai1 2847*2621Sllai1 full: 2848*2621Sllai1 sdcmn_err4(("sdev_readdir: moving %lu bytes: " 2849*2621Sllai1 "diroff %lld, soff %lld, dv %p\n", outcount, diroff, soff, 2850*2621Sllai1 (void *)dv)); 2851*2621Sllai1 2852*2621Sllai1 if (outcount) 2853*2621Sllai1 error = uiomove(outbuf, outcount, UIO_READ, uiop); 2854*2621Sllai1 2855*2621Sllai1 if (!error) { 2856*2621Sllai1 uiop->uio_offset = diroff; 2857*2621Sllai1 if (eofp) 2858*2621Sllai1 *eofp = dv ? 0 : 1; 2859*2621Sllai1 } 2860*2621Sllai1 2861*2621Sllai1 2862*2621Sllai1 if (ddv->sdev_attrvp) { 2863*2621Sllai1 gethrestime(&now); 2864*2621Sllai1 attr.va_ctime = now; 2865*2621Sllai1 attr.va_atime = now; 2866*2621Sllai1 attr.va_mask = AT_CTIME|AT_ATIME; 2867*2621Sllai1 2868*2621Sllai1 (void) VOP_SETATTR(ddv->sdev_attrvp, &attr, 0, kcred, NULL); 2869*2621Sllai1 } 2870*2621Sllai1 done: 2871*2621Sllai1 kmem_free(outbuf, alloc_count); 2872*2621Sllai1 return (error); 2873*2621Sllai1 } 2874*2621Sllai1 2875*2621Sllai1 2876*2621Sllai1 static int 2877*2621Sllai1 sdev_modctl_lookup(const char *path, vnode_t **r_vp) 2878*2621Sllai1 { 2879*2621Sllai1 vnode_t *vp; 2880*2621Sllai1 vnode_t *cvp; 2881*2621Sllai1 struct sdev_node *svp; 2882*2621Sllai1 char *nm; 2883*2621Sllai1 struct pathname pn; 2884*2621Sllai1 int error; 2885*2621Sllai1 int persisted = 0; 2886*2621Sllai1 2887*2621Sllai1 if (error = pn_get((char *)path, UIO_SYSSPACE, &pn)) 2888*2621Sllai1 return (error); 2889*2621Sllai1 nm = kmem_alloc(MAXNAMELEN, KM_SLEEP); 2890*2621Sllai1 2891*2621Sllai1 vp = rootdir; 2892*2621Sllai1 VN_HOLD(vp); 2893*2621Sllai1 2894*2621Sllai1 while (pn_pathleft(&pn)) { 2895*2621Sllai1 ASSERT(vp->v_type == VDIR); 2896*2621Sllai1 (void) pn_getcomponent(&pn, nm); 2897*2621Sllai1 error = VOP_LOOKUP(vp, nm, &cvp, NULL, 0, NULL, kcred); 2898*2621Sllai1 VN_RELE(vp); 2899*2621Sllai1 2900*2621Sllai1 if (error) 2901*2621Sllai1 break; 2902*2621Sllai1 2903*2621Sllai1 /* traverse mount points encountered on our journey */ 2904*2621Sllai1 if (vn_ismntpt(cvp) && (error = traverse(&cvp)) != 0) { 2905*2621Sllai1 VN_RELE(cvp); 2906*2621Sllai1 break; 2907*2621Sllai1 } 2908*2621Sllai1 2909*2621Sllai1 /* 2910*2621Sllai1 * Direct the operation to the persisting filesystem 2911*2621Sllai1 * underlying /dev. Bail if we encounter a 2912*2621Sllai1 * non-persistent dev entity here. 2913*2621Sllai1 */ 2914*2621Sllai1 if (cvp->v_vfsp->vfs_fstype == devtype) { 2915*2621Sllai1 2916*2621Sllai1 if ((VTOSDEV(cvp)->sdev_flags & SDEV_PERSIST) == 0) { 2917*2621Sllai1 error = ENOENT; 2918*2621Sllai1 VN_RELE(cvp); 2919*2621Sllai1 break; 2920*2621Sllai1 } 2921*2621Sllai1 2922*2621Sllai1 if (VTOSDEV(cvp) == NULL) { 2923*2621Sllai1 error = ENOENT; 2924*2621Sllai1 VN_RELE(cvp); 2925*2621Sllai1 break; 2926*2621Sllai1 } 2927*2621Sllai1 svp = VTOSDEV(cvp); 2928*2621Sllai1 if ((vp = svp->sdev_attrvp) == NULL) { 2929*2621Sllai1 error = ENOENT; 2930*2621Sllai1 VN_RELE(cvp); 2931*2621Sllai1 break; 2932*2621Sllai1 } 2933*2621Sllai1 persisted = 1; 2934*2621Sllai1 VN_HOLD(vp); 2935*2621Sllai1 VN_RELE(cvp); 2936*2621Sllai1 cvp = vp; 2937*2621Sllai1 } 2938*2621Sllai1 2939*2621Sllai1 vp = cvp; 2940*2621Sllai1 pn_skipslash(&pn); 2941*2621Sllai1 } 2942*2621Sllai1 2943*2621Sllai1 kmem_free(nm, MAXNAMELEN); 2944*2621Sllai1 pn_free(&pn); 2945*2621Sllai1 2946*2621Sllai1 if (error) 2947*2621Sllai1 return (error); 2948*2621Sllai1 2949*2621Sllai1 /* 2950*2621Sllai1 * Only return persisted nodes in the filesystem underlying /dev. 2951*2621Sllai1 */ 2952*2621Sllai1 if (!persisted) { 2953*2621Sllai1 VN_RELE(vp); 2954*2621Sllai1 return (ENOENT); 2955*2621Sllai1 } 2956*2621Sllai1 2957*2621Sllai1 *r_vp = vp; 2958*2621Sllai1 return (0); 2959*2621Sllai1 } 2960*2621Sllai1 2961*2621Sllai1 int 2962*2621Sllai1 sdev_modctl_readdir(const char *dir, char ***dirlistp, 2963*2621Sllai1 int *npathsp, int *npathsp_alloc) 2964*2621Sllai1 { 2965*2621Sllai1 char **pathlist = NULL; 2966*2621Sllai1 char **newlist = NULL; 2967*2621Sllai1 int npaths = 0; 2968*2621Sllai1 int npaths_alloc = 0; 2969*2621Sllai1 dirent64_t *dbuf = NULL; 2970*2621Sllai1 int n; 2971*2621Sllai1 char *s; 2972*2621Sllai1 int error; 2973*2621Sllai1 vnode_t *vp; 2974*2621Sllai1 int eof; 2975*2621Sllai1 struct iovec iov; 2976*2621Sllai1 struct uio uio; 2977*2621Sllai1 struct dirent64 *dp; 2978*2621Sllai1 size_t dlen; 2979*2621Sllai1 size_t dbuflen; 2980*2621Sllai1 int ndirents = 64; 2981*2621Sllai1 char *nm; 2982*2621Sllai1 2983*2621Sllai1 error = sdev_modctl_lookup(dir, &vp); 2984*2621Sllai1 sdcmn_err11(("modctl readdir: %s by %s: %s\n", 2985*2621Sllai1 dir, curproc->p_user.u_comm, 2986*2621Sllai1 (error == 0) ? "ok" : "failed")); 2987*2621Sllai1 if (error) 2988*2621Sllai1 return (error); 2989*2621Sllai1 2990*2621Sllai1 dlen = ndirents * (sizeof (*dbuf)); 2991*2621Sllai1 dbuf = kmem_alloc(dlen, KM_SLEEP); 2992*2621Sllai1 2993*2621Sllai1 uio.uio_iov = &iov; 2994*2621Sllai1 uio.uio_iovcnt = 1; 2995*2621Sllai1 uio.uio_segflg = UIO_SYSSPACE; 2996*2621Sllai1 uio.uio_fmode = 0; 2997*2621Sllai1 uio.uio_extflg = UIO_COPY_CACHED; 2998*2621Sllai1 uio.uio_loffset = 0; 2999*2621Sllai1 uio.uio_llimit = MAXOFFSET_T; 3000*2621Sllai1 3001*2621Sllai1 eof = 0; 3002*2621Sllai1 error = 0; 3003*2621Sllai1 while (!error && !eof) { 3004*2621Sllai1 uio.uio_resid = dlen; 3005*2621Sllai1 iov.iov_base = (char *)dbuf; 3006*2621Sllai1 iov.iov_len = dlen; 3007*2621Sllai1 3008*2621Sllai1 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); 3009*2621Sllai1 error = VOP_READDIR(vp, &uio, kcred, &eof); 3010*2621Sllai1 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); 3011*2621Sllai1 3012*2621Sllai1 dbuflen = dlen - uio.uio_resid; 3013*2621Sllai1 3014*2621Sllai1 if (error || dbuflen == 0) 3015*2621Sllai1 break; 3016*2621Sllai1 3017*2621Sllai1 for (dp = dbuf; ((intptr_t)dp < (intptr_t)dbuf + dbuflen); 3018*2621Sllai1 dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) { 3019*2621Sllai1 3020*2621Sllai1 nm = dp->d_name; 3021*2621Sllai1 3022*2621Sllai1 if (strcmp(nm, ".") == 0 || strcmp(nm, "..") == 0) 3023*2621Sllai1 continue; 3024*2621Sllai1 3025*2621Sllai1 if (npaths == npaths_alloc) { 3026*2621Sllai1 npaths_alloc += 64; 3027*2621Sllai1 newlist = (char **) 3028*2621Sllai1 kmem_zalloc((npaths_alloc + 1) * 3029*2621Sllai1 sizeof (char *), KM_SLEEP); 3030*2621Sllai1 if (pathlist) { 3031*2621Sllai1 bcopy(pathlist, newlist, 3032*2621Sllai1 npaths * sizeof (char *)); 3033*2621Sllai1 kmem_free(pathlist, 3034*2621Sllai1 (npaths + 1) * sizeof (char *)); 3035*2621Sllai1 } 3036*2621Sllai1 pathlist = newlist; 3037*2621Sllai1 } 3038*2621Sllai1 n = strlen(nm) + 1; 3039*2621Sllai1 s = kmem_alloc(n, KM_SLEEP); 3040*2621Sllai1 bcopy(nm, s, n); 3041*2621Sllai1 pathlist[npaths++] = s; 3042*2621Sllai1 sdcmn_err11((" %s/%s\n", dir, s)); 3043*2621Sllai1 } 3044*2621Sllai1 } 3045*2621Sllai1 3046*2621Sllai1 exit: 3047*2621Sllai1 VN_RELE(vp); 3048*2621Sllai1 3049*2621Sllai1 if (dbuf) 3050*2621Sllai1 kmem_free(dbuf, dlen); 3051*2621Sllai1 3052*2621Sllai1 if (error) 3053*2621Sllai1 return (error); 3054*2621Sllai1 3055*2621Sllai1 *dirlistp = pathlist; 3056*2621Sllai1 *npathsp = npaths; 3057*2621Sllai1 *npathsp_alloc = npaths_alloc; 3058*2621Sllai1 3059*2621Sllai1 return (0); 3060*2621Sllai1 } 3061*2621Sllai1 3062*2621Sllai1 void 3063*2621Sllai1 sdev_modctl_readdir_free(char **pathlist, int npaths, int npaths_alloc) 3064*2621Sllai1 { 3065*2621Sllai1 int i, n; 3066*2621Sllai1 3067*2621Sllai1 for (i = 0; i < npaths; i++) { 3068*2621Sllai1 n = strlen(pathlist[i]) + 1; 3069*2621Sllai1 kmem_free(pathlist[i], n); 3070*2621Sllai1 } 3071*2621Sllai1 3072*2621Sllai1 kmem_free(pathlist, (npaths_alloc + 1) * sizeof (char *)); 3073*2621Sllai1 } 3074*2621Sllai1 3075*2621Sllai1 int 3076*2621Sllai1 sdev_modctl_devexists(const char *path) 3077*2621Sllai1 { 3078*2621Sllai1 vnode_t *vp; 3079*2621Sllai1 int error; 3080*2621Sllai1 3081*2621Sllai1 error = sdev_modctl_lookup(path, &vp); 3082*2621Sllai1 sdcmn_err11(("modctl dev exists: %s by %s: %s\n", 3083*2621Sllai1 path, curproc->p_user.u_comm, 3084*2621Sllai1 (error == 0) ? "ok" : "failed")); 3085*2621Sllai1 if (error == 0) 3086*2621Sllai1 VN_RELE(vp); 3087*2621Sllai1 3088*2621Sllai1 return (error); 3089*2621Sllai1 } 3090*2621Sllai1 3091*2621Sllai1 void 3092*2621Sllai1 sdev_update_newnsmap(struct devname_nsmap *map, char *module, char *mapname) 3093*2621Sllai1 { 3094*2621Sllai1 rw_enter(&map->dir_lock, RW_WRITER); 3095*2621Sllai1 if (module) { 3096*2621Sllai1 ASSERT(map->dir_newmodule == NULL); 3097*2621Sllai1 map->dir_newmodule = i_ddi_strdup(module, KM_SLEEP); 3098*2621Sllai1 } 3099*2621Sllai1 if (mapname) { 3100*2621Sllai1 ASSERT(map->dir_newmap == NULL); 3101*2621Sllai1 map->dir_newmap = i_ddi_strdup(mapname, KM_SLEEP); 3102*2621Sllai1 } 3103*2621Sllai1 3104*2621Sllai1 map->dir_invalid = 1; 3105*2621Sllai1 rw_exit(&map->dir_lock); 3106*2621Sllai1 } 3107*2621Sllai1 3108*2621Sllai1 void 3109*2621Sllai1 sdev_replace_nsmap(struct devname_nsmap *map, char *module, char *mapname) 3110*2621Sllai1 { 3111*2621Sllai1 char *old_module = NULL; 3112*2621Sllai1 char *old_map = NULL; 3113*2621Sllai1 3114*2621Sllai1 ASSERT(RW_LOCK_HELD(&map->dir_lock)); 3115*2621Sllai1 if (!rw_tryupgrade(&map->dir_lock)) { 3116*2621Sllai1 rw_exit(&map->dir_lock); 3117*2621Sllai1 rw_enter(&map->dir_lock, RW_WRITER); 3118*2621Sllai1 } 3119*2621Sllai1 3120*2621Sllai1 old_module = map->dir_module; 3121*2621Sllai1 if (module) { 3122*2621Sllai1 if (old_module && strcmp(old_module, module) != 0) { 3123*2621Sllai1 kmem_free(old_module, strlen(old_module) + 1); 3124*2621Sllai1 } 3125*2621Sllai1 map->dir_module = module; 3126*2621Sllai1 map->dir_newmodule = NULL; 3127*2621Sllai1 } 3128*2621Sllai1 3129*2621Sllai1 old_map = map->dir_map; 3130*2621Sllai1 if (mapname) { 3131*2621Sllai1 if (old_map && strcmp(old_map, mapname) != 0) { 3132*2621Sllai1 kmem_free(old_map, strlen(old_map) + 1); 3133*2621Sllai1 } 3134*2621Sllai1 3135*2621Sllai1 map->dir_map = mapname; 3136*2621Sllai1 map->dir_newmap = NULL; 3137*2621Sllai1 } 3138*2621Sllai1 map->dir_maploaded = 0; 3139*2621Sllai1 map->dir_invalid = 0; 3140*2621Sllai1 rw_downgrade(&map->dir_lock); 3141*2621Sllai1 } 3142*2621Sllai1 3143*2621Sllai1 /* 3144*2621Sllai1 * dir_name should have at least one attribute, 3145*2621Sllai1 * dir_module 3146*2621Sllai1 * or dir_map 3147*2621Sllai1 * or both 3148*2621Sllai1 * caller holds the devname_nsmaps_lock 3149*2621Sllai1 */ 3150*2621Sllai1 void 3151*2621Sllai1 sdev_insert_nsmap(char *dir_name, char *dir_module, char *dir_map) 3152*2621Sllai1 { 3153*2621Sllai1 struct devname_nsmap *map; 3154*2621Sllai1 int len = 0; 3155*2621Sllai1 3156*2621Sllai1 ASSERT(dir_name); 3157*2621Sllai1 ASSERT(dir_module || dir_map); 3158*2621Sllai1 ASSERT(MUTEX_HELD(&devname_nsmaps_lock)); 3159*2621Sllai1 3160*2621Sllai1 if (map = sdev_get_nsmap_by_dir(dir_name, 1)) { 3161*2621Sllai1 sdev_update_newnsmap(map, dir_module, dir_map); 3162*2621Sllai1 return; 3163*2621Sllai1 } 3164*2621Sllai1 3165*2621Sllai1 map = (struct devname_nsmap *)kmem_zalloc(sizeof (*map), KM_SLEEP); 3166*2621Sllai1 map->dir_name = i_ddi_strdup(dir_name, KM_SLEEP); 3167*2621Sllai1 if (dir_module) { 3168*2621Sllai1 map->dir_module = i_ddi_strdup(dir_module, KM_SLEEP); 3169*2621Sllai1 } 3170*2621Sllai1 3171*2621Sllai1 if (dir_map) { 3172*2621Sllai1 if (dir_map[0] != '/') { 3173*2621Sllai1 len = strlen(ETC_DEV_DIR) + strlen(dir_map) + 2; 3174*2621Sllai1 map->dir_map = kmem_zalloc(len, KM_SLEEP); 3175*2621Sllai1 (void) snprintf(map->dir_map, len, "%s/%s", ETC_DEV_DIR, 3176*2621Sllai1 dir_map); 3177*2621Sllai1 } else { 3178*2621Sllai1 map->dir_map = i_ddi_strdup(dir_map, KM_SLEEP); 3179*2621Sllai1 } 3180*2621Sllai1 } 3181*2621Sllai1 3182*2621Sllai1 map->dir_ops = NULL; 3183*2621Sllai1 map->dir_maploaded = 0; 3184*2621Sllai1 map->dir_invalid = 0; 3185*2621Sllai1 rw_init(&map->dir_lock, NULL, RW_DEFAULT, NULL); 3186*2621Sllai1 3187*2621Sllai1 map->next = devname_nsmaps; 3188*2621Sllai1 map->prev = NULL; 3189*2621Sllai1 if (devname_nsmaps) { 3190*2621Sllai1 devname_nsmaps->prev = map; 3191*2621Sllai1 } 3192*2621Sllai1 devname_nsmaps = map; 3193*2621Sllai1 } 3194*2621Sllai1 3195*2621Sllai1 struct devname_nsmap * 3196*2621Sllai1 sdev_get_nsmap_by_dir(char *dir_path, int locked) 3197*2621Sllai1 { 3198*2621Sllai1 struct devname_nsmap *map = NULL; 3199*2621Sllai1 3200*2621Sllai1 if (!locked) 3201*2621Sllai1 mutex_enter(&devname_nsmaps_lock); 3202*2621Sllai1 for (map = devname_nsmaps; map; map = map->next) { 3203*2621Sllai1 sdcmn_err6(("sdev_get_nsmap_by_dir: dir %s\n", map->dir_name)); 3204*2621Sllai1 if (strcmp(map->dir_name, dir_path) == 0) { 3205*2621Sllai1 if (!locked) 3206*2621Sllai1 mutex_exit(&devname_nsmaps_lock); 3207*2621Sllai1 return (map); 3208*2621Sllai1 } 3209*2621Sllai1 } 3210*2621Sllai1 if (!locked) 3211*2621Sllai1 mutex_exit(&devname_nsmaps_lock); 3212*2621Sllai1 return (NULL); 3213*2621Sllai1 } 3214*2621Sllai1 3215*2621Sllai1 struct devname_nsmap * 3216*2621Sllai1 sdev_get_nsmap_by_module(char *mod_name) 3217*2621Sllai1 { 3218*2621Sllai1 struct devname_nsmap *map = NULL; 3219*2621Sllai1 3220*2621Sllai1 mutex_enter(&devname_nsmaps_lock); 3221*2621Sllai1 for (map = devname_nsmaps; map; map = map->next) { 3222*2621Sllai1 sdcmn_err7(("sdev_get_nsmap_by_module: module %s\n", 3223*2621Sllai1 map->dir_module)); 3224*2621Sllai1 if (map->dir_module && strcmp(map->dir_module, mod_name) == 0) { 3225*2621Sllai1 mutex_exit(&devname_nsmaps_lock); 3226*2621Sllai1 return (map); 3227*2621Sllai1 } 3228*2621Sllai1 } 3229*2621Sllai1 mutex_exit(&devname_nsmaps_lock); 3230*2621Sllai1 return (NULL); 3231*2621Sllai1 } 3232*2621Sllai1 3233*2621Sllai1 void 3234*2621Sllai1 sdev_invalidate_nsmaps() 3235*2621Sllai1 { 3236*2621Sllai1 struct devname_nsmap *map = NULL; 3237*2621Sllai1 3238*2621Sllai1 ASSERT(MUTEX_HELD(&devname_nsmaps_lock)); 3239*2621Sllai1 3240*2621Sllai1 if (devname_nsmaps == NULL) 3241*2621Sllai1 return; 3242*2621Sllai1 3243*2621Sllai1 for (map = devname_nsmaps; map; map = map->next) { 3244*2621Sllai1 rw_enter(&map->dir_lock, RW_WRITER); 3245*2621Sllai1 map->dir_invalid = 1; 3246*2621Sllai1 rw_exit(&map->dir_lock); 3247*2621Sllai1 } 3248*2621Sllai1 devname_nsmaps_invalidated = 1; 3249*2621Sllai1 } 3250*2621Sllai1 3251*2621Sllai1 3252*2621Sllai1 int 3253*2621Sllai1 sdev_nsmaps_loaded() 3254*2621Sllai1 { 3255*2621Sllai1 int ret = 0; 3256*2621Sllai1 3257*2621Sllai1 mutex_enter(&devname_nsmaps_lock); 3258*2621Sllai1 if (devname_nsmaps_loaded) 3259*2621Sllai1 ret = 1; 3260*2621Sllai1 3261*2621Sllai1 mutex_exit(&devname_nsmaps_lock); 3262*2621Sllai1 return (ret); 3263*2621Sllai1 } 3264*2621Sllai1 3265*2621Sllai1 int 3266*2621Sllai1 sdev_nsmaps_reloaded() 3267*2621Sllai1 { 3268*2621Sllai1 int ret = 0; 3269*2621Sllai1 3270*2621Sllai1 mutex_enter(&devname_nsmaps_lock); 3271*2621Sllai1 if (devname_nsmaps_invalidated) 3272*2621Sllai1 ret = 1; 3273*2621Sllai1 3274*2621Sllai1 mutex_exit(&devname_nsmaps_lock); 3275*2621Sllai1 return (ret); 3276*2621Sllai1 } 3277*2621Sllai1 3278*2621Sllai1 static void 3279*2621Sllai1 sdev_free_nsmap(struct devname_nsmap *map) 3280*2621Sllai1 { 3281*2621Sllai1 ASSERT(map); 3282*2621Sllai1 if (map->dir_name) 3283*2621Sllai1 kmem_free(map->dir_name, strlen(map->dir_name) + 1); 3284*2621Sllai1 if (map->dir_module) 3285*2621Sllai1 kmem_free(map->dir_module, strlen(map->dir_module) + 1); 3286*2621Sllai1 if (map->dir_map) 3287*2621Sllai1 kmem_free(map->dir_map, strlen(map->dir_map) + 1); 3288*2621Sllai1 rw_destroy(&map->dir_lock); 3289*2621Sllai1 kmem_free(map, sizeof (*map)); 3290*2621Sllai1 } 3291*2621Sllai1 3292*2621Sllai1 void 3293*2621Sllai1 sdev_validate_nsmaps() 3294*2621Sllai1 { 3295*2621Sllai1 struct devname_nsmap *map = NULL; 3296*2621Sllai1 struct devname_nsmap *oldmap = NULL; 3297*2621Sllai1 3298*2621Sllai1 ASSERT(MUTEX_HELD(&devname_nsmaps_lock)); 3299*2621Sllai1 map = devname_nsmaps; 3300*2621Sllai1 while (map) { 3301*2621Sllai1 rw_enter(&map->dir_lock, RW_READER); 3302*2621Sllai1 if ((map->dir_invalid == 1) && (map->dir_newmodule == NULL) && 3303*2621Sllai1 (map->dir_newmap == NULL)) { 3304*2621Sllai1 oldmap = map; 3305*2621Sllai1 rw_exit(&map->dir_lock); 3306*2621Sllai1 if (map->prev) 3307*2621Sllai1 map->prev->next = oldmap->next; 3308*2621Sllai1 if (map == devname_nsmaps) 3309*2621Sllai1 devname_nsmaps = oldmap->next; 3310*2621Sllai1 3311*2621Sllai1 map = oldmap->next; 3312*2621Sllai1 if (map) 3313*2621Sllai1 map->prev = oldmap->prev; 3314*2621Sllai1 sdev_free_nsmap(oldmap); 3315*2621Sllai1 oldmap = NULL; 3316*2621Sllai1 } else { 3317*2621Sllai1 rw_exit(&map->dir_lock); 3318*2621Sllai1 map = map->next; 3319*2621Sllai1 } 3320*2621Sllai1 } 3321*2621Sllai1 devname_nsmaps_invalidated = 0; 3322*2621Sllai1 } 3323*2621Sllai1 3324*2621Sllai1 static int 3325*2621Sllai1 sdev_map_is_invalid(struct devname_nsmap *map) 3326*2621Sllai1 { 3327*2621Sllai1 int ret = 0; 3328*2621Sllai1 3329*2621Sllai1 ASSERT(map); 3330*2621Sllai1 rw_enter(&map->dir_lock, RW_READER); 3331*2621Sllai1 if (map->dir_invalid) 3332*2621Sllai1 ret = 1; 3333*2621Sllai1 rw_exit(&map->dir_lock); 3334*2621Sllai1 return (ret); 3335*2621Sllai1 } 3336*2621Sllai1 3337*2621Sllai1 static int 3338*2621Sllai1 sdev_check_map(struct devname_nsmap *map) 3339*2621Sllai1 { 3340*2621Sllai1 struct devname_nsmap *mapp; 3341*2621Sllai1 3342*2621Sllai1 mutex_enter(&devname_nsmaps_lock); 3343*2621Sllai1 if (devname_nsmaps == NULL) { 3344*2621Sllai1 mutex_exit(&devname_nsmaps_lock); 3345*2621Sllai1 return (1); 3346*2621Sllai1 } 3347*2621Sllai1 3348*2621Sllai1 for (mapp = devname_nsmaps; mapp; mapp = mapp->next) { 3349*2621Sllai1 if (mapp == map) { 3350*2621Sllai1 mutex_exit(&devname_nsmaps_lock); 3351*2621Sllai1 return (0); 3352*2621Sllai1 } 3353*2621Sllai1 } 3354*2621Sllai1 3355*2621Sllai1 mutex_exit(&devname_nsmaps_lock); 3356*2621Sllai1 return (1); 3357*2621Sllai1 3358*2621Sllai1 } 3359*2621Sllai1 3360*2621Sllai1 struct devname_nsmap * 3361*2621Sllai1 sdev_get_map(struct sdev_node *dv, int validate) 3362*2621Sllai1 { 3363*2621Sllai1 struct devname_nsmap *map; 3364*2621Sllai1 int error; 3365*2621Sllai1 3366*2621Sllai1 ASSERT(RW_READ_HELD(&dv->sdev_contents)); 3367*2621Sllai1 map = dv->sdev_mapinfo; 3368*2621Sllai1 if (map && sdev_check_map(map)) { 3369*2621Sllai1 if (!rw_tryupgrade(&dv->sdev_contents)) { 3370*2621Sllai1 rw_exit(&dv->sdev_contents); 3371*2621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 3372*2621Sllai1 } 3373*2621Sllai1 dv->sdev_mapinfo = NULL; 3374*2621Sllai1 rw_downgrade(&dv->sdev_contents); 3375*2621Sllai1 return (NULL); 3376*2621Sllai1 } 3377*2621Sllai1 3378*2621Sllai1 if (validate && (!map || (map && sdev_map_is_invalid(map)))) { 3379*2621Sllai1 if (!rw_tryupgrade(&dv->sdev_contents)) { 3380*2621Sllai1 rw_exit(&dv->sdev_contents); 3381*2621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 3382*2621Sllai1 } 3383*2621Sllai1 error = sdev_get_moduleops(dv); 3384*2621Sllai1 if (!error) 3385*2621Sllai1 map = dv->sdev_mapinfo; 3386*2621Sllai1 rw_downgrade(&dv->sdev_contents); 3387*2621Sllai1 } 3388*2621Sllai1 return (map); 3389*2621Sllai1 } 3390*2621Sllai1 3391*2621Sllai1 void 3392*2621Sllai1 sdev_handle_alloc(struct sdev_node *dv) 3393*2621Sllai1 { 3394*2621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 3395*2621Sllai1 dv->sdev_handle.dh_data = dv; 3396*2621Sllai1 rw_exit(&dv->sdev_contents); 3397*2621Sllai1 } 3398*2621Sllai1 3399*2621Sllai1 3400*2621Sllai1 extern int sdev_vnodeops_tbl_size; 3401*2621Sllai1 3402*2621Sllai1 /* 3403*2621Sllai1 * construct a new template with overrides from vtab 3404*2621Sllai1 */ 3405*2621Sllai1 static fs_operation_def_t * 3406*2621Sllai1 sdev_merge_vtab(const fs_operation_def_t tab[]) 3407*2621Sllai1 { 3408*2621Sllai1 fs_operation_def_t *new; 3409*2621Sllai1 const fs_operation_def_t *tab_entry; 3410*2621Sllai1 3411*2621Sllai1 /* make a copy of standard vnode ops table */ 3412*2621Sllai1 new = kmem_alloc(sdev_vnodeops_tbl_size, KM_SLEEP); 3413*2621Sllai1 bcopy((void *)sdev_vnodeops_tbl, new, sdev_vnodeops_tbl_size); 3414*2621Sllai1 3415*2621Sllai1 /* replace the overrides from tab */ 3416*2621Sllai1 for (tab_entry = tab; tab_entry->name != NULL; tab_entry++) { 3417*2621Sllai1 fs_operation_def_t *std_entry = new; 3418*2621Sllai1 while (std_entry->name) { 3419*2621Sllai1 if (strcmp(tab_entry->name, std_entry->name) == 0) { 3420*2621Sllai1 std_entry->func = tab_entry->func; 3421*2621Sllai1 break; 3422*2621Sllai1 } 3423*2621Sllai1 std_entry++; 3424*2621Sllai1 } 3425*2621Sllai1 if (std_entry->name == NULL) 3426*2621Sllai1 cmn_err(CE_NOTE, "sdev_merge_vtab: entry %s unused.", 3427*2621Sllai1 tab_entry->name); 3428*2621Sllai1 } 3429*2621Sllai1 3430*2621Sllai1 return (new); 3431*2621Sllai1 } 3432*2621Sllai1 3433*2621Sllai1 /* free memory allocated by sdev_merge_vtab */ 3434*2621Sllai1 static void 3435*2621Sllai1 sdev_free_vtab(fs_operation_def_t *new) 3436*2621Sllai1 { 3437*2621Sllai1 kmem_free(new, sdev_vnodeops_tbl_size); 3438*2621Sllai1 } 3439*2621Sllai1 3440*2621Sllai1 void 3441*2621Sllai1 devname_get_vnode(devname_handle_t *hdl, vnode_t **vpp) 3442*2621Sllai1 { 3443*2621Sllai1 struct sdev_node *dv = hdl->dh_data; 3444*2621Sllai1 3445*2621Sllai1 ASSERT(dv); 3446*2621Sllai1 3447*2621Sllai1 rw_enter(&dv->sdev_contents, RW_READER); 3448*2621Sllai1 *vpp = SDEVTOV(dv); 3449*2621Sllai1 rw_exit(&dv->sdev_contents); 3450*2621Sllai1 } 3451*2621Sllai1 3452*2621Sllai1 int 3453*2621Sllai1 devname_get_path(devname_handle_t *hdl, char **path) 3454*2621Sllai1 { 3455*2621Sllai1 struct sdev_node *dv = hdl->dh_data; 3456*2621Sllai1 3457*2621Sllai1 ASSERT(dv); 3458*2621Sllai1 3459*2621Sllai1 rw_enter(&dv->sdev_contents, RW_READER); 3460*2621Sllai1 *path = dv->sdev_path; 3461*2621Sllai1 rw_exit(&dv->sdev_contents); 3462*2621Sllai1 return (0); 3463*2621Sllai1 } 3464*2621Sllai1 3465*2621Sllai1 int 3466*2621Sllai1 devname_get_name(devname_handle_t *hdl, char **entry) 3467*2621Sllai1 { 3468*2621Sllai1 struct sdev_node *dv = hdl->dh_data; 3469*2621Sllai1 3470*2621Sllai1 ASSERT(dv); 3471*2621Sllai1 rw_enter(&dv->sdev_contents, RW_READER); 3472*2621Sllai1 *entry = dv->sdev_name; 3473*2621Sllai1 rw_exit(&dv->sdev_contents); 3474*2621Sllai1 return (0); 3475*2621Sllai1 } 3476*2621Sllai1 3477*2621Sllai1 void 3478*2621Sllai1 devname_get_dir_vnode(devname_handle_t *hdl, vnode_t **vpp) 3479*2621Sllai1 { 3480*2621Sllai1 struct sdev_node *dv = hdl->dh_data->sdev_dotdot; 3481*2621Sllai1 3482*2621Sllai1 ASSERT(dv); 3483*2621Sllai1 3484*2621Sllai1 rw_enter(&dv->sdev_contents, RW_READER); 3485*2621Sllai1 *vpp = SDEVTOV(dv); 3486*2621Sllai1 rw_exit(&dv->sdev_contents); 3487*2621Sllai1 } 3488*2621Sllai1 3489*2621Sllai1 int 3490*2621Sllai1 devname_get_dir_path(devname_handle_t *hdl, char **path) 3491*2621Sllai1 { 3492*2621Sllai1 struct sdev_node *dv = hdl->dh_data->sdev_dotdot; 3493*2621Sllai1 3494*2621Sllai1 ASSERT(dv); 3495*2621Sllai1 rw_enter(&dv->sdev_contents, RW_READER); 3496*2621Sllai1 *path = dv->sdev_path; 3497*2621Sllai1 rw_exit(&dv->sdev_contents); 3498*2621Sllai1 return (0); 3499*2621Sllai1 } 3500*2621Sllai1 3501*2621Sllai1 int 3502*2621Sllai1 devname_get_dir_name(devname_handle_t *hdl, char **entry) 3503*2621Sllai1 { 3504*2621Sllai1 struct sdev_node *dv = hdl->dh_data->sdev_dotdot; 3505*2621Sllai1 3506*2621Sllai1 ASSERT(dv); 3507*2621Sllai1 rw_enter(&dv->sdev_contents, RW_READER); 3508*2621Sllai1 *entry = dv->sdev_name; 3509*2621Sllai1 rw_exit(&dv->sdev_contents); 3510*2621Sllai1 return (0); 3511*2621Sllai1 } 3512*2621Sllai1 3513*2621Sllai1 int 3514*2621Sllai1 devname_get_dir_nsmap(devname_handle_t *hdl, struct devname_nsmap **map) 3515*2621Sllai1 { 3516*2621Sllai1 struct sdev_node *dv = hdl->dh_data->sdev_dotdot; 3517*2621Sllai1 3518*2621Sllai1 ASSERT(dv); 3519*2621Sllai1 rw_enter(&dv->sdev_contents, RW_READER); 3520*2621Sllai1 *map = dv->sdev_mapinfo; 3521*2621Sllai1 rw_exit(&dv->sdev_contents); 3522*2621Sllai1 return (0); 3523*2621Sllai1 } 3524*2621Sllai1 3525*2621Sllai1 int 3526*2621Sllai1 devname_get_dir_handle(devname_handle_t *hdl, devname_handle_t **dir_hdl) 3527*2621Sllai1 { 3528*2621Sllai1 struct sdev_node *dv = hdl->dh_data->sdev_dotdot; 3529*2621Sllai1 3530*2621Sllai1 ASSERT(dv); 3531*2621Sllai1 rw_enter(&dv->sdev_contents, RW_READER); 3532*2621Sllai1 *dir_hdl = &(dv->sdev_handle); 3533*2621Sllai1 rw_exit(&dv->sdev_contents); 3534*2621Sllai1 return (0); 3535*2621Sllai1 } 3536*2621Sllai1 3537*2621Sllai1 void 3538*2621Sllai1 devname_set_nodetype(devname_handle_t *hdl, void *args, int spec) 3539*2621Sllai1 { 3540*2621Sllai1 struct sdev_node *dv = hdl->dh_data; 3541*2621Sllai1 3542*2621Sllai1 ASSERT(dv); 3543*2621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 3544*2621Sllai1 hdl->dh_spec = (devname_spec_t)spec; 3545*2621Sllai1 hdl->dh_args = (void *)i_ddi_strdup((char *)args, KM_SLEEP); 3546*2621Sllai1 rw_exit(&dv->sdev_contents); 3547*2621Sllai1 } 3548*2621Sllai1 3549*2621Sllai1 /* 3550*2621Sllai1 * a generic setattr() function 3551*2621Sllai1 * 3552*2621Sllai1 * note: flags only supports AT_UID and AT_GID. 3553*2621Sllai1 * Future enhancements can be done for other types, e.g. AT_MODE 3554*2621Sllai1 */ 3555*2621Sllai1 int 3556*2621Sllai1 devname_setattr_func(struct vnode *vp, struct vattr *vap, int flags, 3557*2621Sllai1 struct cred *cred, int (*callback)(struct sdev_node *, struct vattr *, 3558*2621Sllai1 int), int protocol) 3559*2621Sllai1 { 3560*2621Sllai1 struct sdev_node *dv = VTOSDEV(vp); 3561*2621Sllai1 struct sdev_node *parent = dv->sdev_dotdot; 3562*2621Sllai1 struct vattr *get; 3563*2621Sllai1 uint_t mask = vap->va_mask; 3564*2621Sllai1 int error; 3565*2621Sllai1 3566*2621Sllai1 /* some sanity checks */ 3567*2621Sllai1 if (vap->va_mask & AT_NOSET) 3568*2621Sllai1 return (EINVAL); 3569*2621Sllai1 3570*2621Sllai1 if (vap->va_mask & AT_SIZE) { 3571*2621Sllai1 if (vp->v_type == VDIR) { 3572*2621Sllai1 return (EISDIR); 3573*2621Sllai1 } 3574*2621Sllai1 } 3575*2621Sllai1 3576*2621Sllai1 /* no need to set attribute, but do not fail either */ 3577*2621Sllai1 ASSERT(parent); 3578*2621Sllai1 rw_enter(&parent->sdev_contents, RW_READER); 3579*2621Sllai1 if (dv->sdev_state == SDEV_ZOMBIE) { 3580*2621Sllai1 rw_exit(&parent->sdev_contents); 3581*2621Sllai1 return (0); 3582*2621Sllai1 } 3583*2621Sllai1 3584*2621Sllai1 /* If backing store exists, just set it. */ 3585*2621Sllai1 if (dv->sdev_attrvp) { 3586*2621Sllai1 rw_exit(&parent->sdev_contents); 3587*2621Sllai1 return (VOP_SETATTR(dv->sdev_attrvp, vap, flags, cred, NULL)); 3588*2621Sllai1 } 3589*2621Sllai1 3590*2621Sllai1 /* 3591*2621Sllai1 * Otherwise, for nodes with the persistence attribute, create it. 3592*2621Sllai1 */ 3593*2621Sllai1 ASSERT(dv->sdev_attr); 3594*2621Sllai1 if (SDEV_IS_PERSIST(dv) || 3595*2621Sllai1 ((vap->va_mask & ~AT_TIMES) != 0 && !SDEV_IS_DYNAMIC(dv))) { 3596*2621Sllai1 sdev_vattr_merge(dv, vap); 3597*2621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 3598*2621Sllai1 error = sdev_shadow_node(dv, cred); 3599*2621Sllai1 rw_exit(&dv->sdev_contents); 3600*2621Sllai1 rw_exit(&parent->sdev_contents); 3601*2621Sllai1 3602*2621Sllai1 if (error) 3603*2621Sllai1 return (error); 3604*2621Sllai1 return (VOP_SETATTR(dv->sdev_attrvp, vap, flags, cred, NULL)); 3605*2621Sllai1 } 3606*2621Sllai1 3607*2621Sllai1 3608*2621Sllai1 /* 3609*2621Sllai1 * sdev_attr was allocated in sdev_mknode 3610*2621Sllai1 */ 3611*2621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 3612*2621Sllai1 error = secpolicy_vnode_setattr(cred, vp, vap, dv->sdev_attr, 3613*2621Sllai1 flags, sdev_unlocked_access, dv); 3614*2621Sllai1 if (error) { 3615*2621Sllai1 rw_exit(&dv->sdev_contents); 3616*2621Sllai1 rw_exit(&parent->sdev_contents); 3617*2621Sllai1 return (error); 3618*2621Sllai1 } 3619*2621Sllai1 3620*2621Sllai1 get = dv->sdev_attr; 3621*2621Sllai1 if (mask & AT_MODE) { 3622*2621Sllai1 get->va_mode &= S_IFMT; 3623*2621Sllai1 get->va_mode |= vap->va_mode & ~S_IFMT; 3624*2621Sllai1 } 3625*2621Sllai1 3626*2621Sllai1 if ((mask & AT_UID) || (mask & AT_GID)) { 3627*2621Sllai1 if (mask & AT_UID) 3628*2621Sllai1 get->va_uid = vap->va_uid; 3629*2621Sllai1 if (mask & AT_GID) 3630*2621Sllai1 get->va_gid = vap->va_gid; 3631*2621Sllai1 /* 3632*2621Sllai1 * a callback must be provided if the protocol is set 3633*2621Sllai1 */ 3634*2621Sllai1 if ((protocol & AT_UID) || (protocol & AT_GID)) { 3635*2621Sllai1 ASSERT(callback); 3636*2621Sllai1 error = callback(dv, get, protocol); 3637*2621Sllai1 if (error) { 3638*2621Sllai1 rw_exit(&dv->sdev_contents); 3639*2621Sllai1 rw_exit(&parent->sdev_contents); 3640*2621Sllai1 return (error); 3641*2621Sllai1 } 3642*2621Sllai1 } 3643*2621Sllai1 } 3644*2621Sllai1 3645*2621Sllai1 if (mask & AT_ATIME) 3646*2621Sllai1 get->va_atime = vap->va_atime; 3647*2621Sllai1 if (mask & AT_MTIME) 3648*2621Sllai1 get->va_mtime = vap->va_mtime; 3649*2621Sllai1 if (mask & (AT_MODE | AT_UID | AT_GID | AT_CTIME)) { 3650*2621Sllai1 gethrestime(&get->va_ctime); 3651*2621Sllai1 } 3652*2621Sllai1 3653*2621Sllai1 sdev_vattr_merge(dv, get); 3654*2621Sllai1 rw_exit(&dv->sdev_contents); 3655*2621Sllai1 rw_exit(&parent->sdev_contents); 3656*2621Sllai1 return (0); 3657*2621Sllai1 } 3658