12621Sllai1 /* 22621Sllai1 * CDDL HEADER START 32621Sllai1 * 42621Sllai1 * The contents of this file are subject to the terms of the 52621Sllai1 * Common Development and Distribution License (the "License"). 62621Sllai1 * You may not use this file except in compliance with the License. 72621Sllai1 * 82621Sllai1 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 92621Sllai1 * or http://www.opensolaris.org/os/licensing. 102621Sllai1 * See the License for the specific language governing permissions 112621Sllai1 * and limitations under the License. 122621Sllai1 * 132621Sllai1 * When distributing Covered Code, include this CDDL HEADER in each 142621Sllai1 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 152621Sllai1 * If applicable, add the following below this CDDL HEADER, with the 162621Sllai1 * fields enclosed by brackets "[]" replaced with your own identifying 172621Sllai1 * information: Portions Copyright [yyyy] [name of copyright owner] 182621Sllai1 * 192621Sllai1 * CDDL HEADER END 202621Sllai1 */ 212621Sllai1 /* 222621Sllai1 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 232621Sllai1 * Use is subject to license terms. 242621Sllai1 */ 252621Sllai1 262621Sllai1 #pragma ident "%Z%%M% %I% %E% SMI" 272621Sllai1 282621Sllai1 /* 292621Sllai1 * utility routines for the /dev fs 302621Sllai1 */ 312621Sllai1 322621Sllai1 #include <sys/types.h> 332621Sllai1 #include <sys/param.h> 342621Sllai1 #include <sys/t_lock.h> 352621Sllai1 #include <sys/systm.h> 362621Sllai1 #include <sys/sysmacros.h> 372621Sllai1 #include <sys/user.h> 382621Sllai1 #include <sys/time.h> 392621Sllai1 #include <sys/vfs.h> 402621Sllai1 #include <sys/vnode.h> 412621Sllai1 #include <sys/file.h> 422621Sllai1 #include <sys/fcntl.h> 432621Sllai1 #include <sys/flock.h> 442621Sllai1 #include <sys/kmem.h> 452621Sllai1 #include <sys/uio.h> 462621Sllai1 #include <sys/errno.h> 472621Sllai1 #include <sys/stat.h> 482621Sllai1 #include <sys/cred.h> 492621Sllai1 #include <sys/dirent.h> 502621Sllai1 #include <sys/pathname.h> 512621Sllai1 #include <sys/cmn_err.h> 522621Sllai1 #include <sys/debug.h> 532621Sllai1 #include <sys/mode.h> 542621Sllai1 #include <sys/policy.h> 552621Sllai1 #include <fs/fs_subr.h> 562621Sllai1 #include <sys/mount.h> 572621Sllai1 #include <sys/fs/snode.h> 582621Sllai1 #include <sys/fs/dv_node.h> 592621Sllai1 #include <sys/fs/sdev_impl.h> 602621Sllai1 #include <sys/fs/sdev_node.h> 612621Sllai1 #include <sys/sunndi.h> 622621Sllai1 #include <sys/sunmdi.h> 632621Sllai1 #include <sys/conf.h> 642621Sllai1 #include <sys/proc.h> 652621Sllai1 #include <sys/user.h> 662621Sllai1 #include <sys/modctl.h> 672621Sllai1 682621Sllai1 #ifdef DEBUG 692621Sllai1 int sdev_debug = 0x00000001; 702621Sllai1 int sdev_debug_cache_flags = 0; 712621Sllai1 #endif 722621Sllai1 732621Sllai1 /* 742621Sllai1 * globals 752621Sllai1 */ 762621Sllai1 /* prototype memory vattrs */ 772621Sllai1 vattr_t sdev_vattr_dir = { 782621Sllai1 AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */ 792621Sllai1 VDIR, /* va_type */ 802621Sllai1 SDEV_DIRMODE_DEFAULT, /* va_mode */ 812621Sllai1 SDEV_UID_DEFAULT, /* va_uid */ 822621Sllai1 SDEV_GID_DEFAULT, /* va_gid */ 832621Sllai1 0, /* va_fsid */ 842621Sllai1 0, /* va_nodeid */ 852621Sllai1 0, /* va_nlink */ 862621Sllai1 0, /* va_size */ 872621Sllai1 0, /* va_atime */ 882621Sllai1 0, /* va_mtime */ 892621Sllai1 0, /* va_ctime */ 902621Sllai1 0, /* va_rdev */ 912621Sllai1 0, /* va_blksize */ 922621Sllai1 0, /* va_nblocks */ 932621Sllai1 0 /* va_vcode */ 942621Sllai1 }; 952621Sllai1 962621Sllai1 vattr_t sdev_vattr_lnk = { 972621Sllai1 AT_TYPE|AT_MODE, /* va_mask */ 982621Sllai1 VLNK, /* va_type */ 992621Sllai1 SDEV_LNKMODE_DEFAULT, /* va_mode */ 1002621Sllai1 SDEV_UID_DEFAULT, /* va_uid */ 1012621Sllai1 SDEV_GID_DEFAULT, /* va_gid */ 1022621Sllai1 0, /* va_fsid */ 1032621Sllai1 0, /* va_nodeid */ 1042621Sllai1 0, /* va_nlink */ 1052621Sllai1 0, /* va_size */ 1062621Sllai1 0, /* va_atime */ 1072621Sllai1 0, /* va_mtime */ 1082621Sllai1 0, /* va_ctime */ 1092621Sllai1 0, /* va_rdev */ 1102621Sllai1 0, /* va_blksize */ 1112621Sllai1 0, /* va_nblocks */ 1122621Sllai1 0 /* va_vcode */ 1132621Sllai1 }; 1142621Sllai1 1152621Sllai1 vattr_t sdev_vattr_blk = { 1162621Sllai1 AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */ 1172621Sllai1 VBLK, /* va_type */ 1182621Sllai1 S_IFBLK | SDEV_DEVMODE_DEFAULT, /* va_mode */ 1192621Sllai1 SDEV_UID_DEFAULT, /* va_uid */ 1202621Sllai1 SDEV_GID_DEFAULT, /* va_gid */ 1212621Sllai1 0, /* va_fsid */ 1222621Sllai1 0, /* va_nodeid */ 1232621Sllai1 0, /* va_nlink */ 1242621Sllai1 0, /* va_size */ 1252621Sllai1 0, /* va_atime */ 1262621Sllai1 0, /* va_mtime */ 1272621Sllai1 0, /* va_ctime */ 1282621Sllai1 0, /* va_rdev */ 1292621Sllai1 0, /* va_blksize */ 1302621Sllai1 0, /* va_nblocks */ 1312621Sllai1 0 /* va_vcode */ 1322621Sllai1 }; 1332621Sllai1 1342621Sllai1 vattr_t sdev_vattr_chr = { 1352621Sllai1 AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */ 1362621Sllai1 VCHR, /* va_type */ 1372621Sllai1 S_IFCHR | SDEV_DEVMODE_DEFAULT, /* va_mode */ 1382621Sllai1 SDEV_UID_DEFAULT, /* va_uid */ 1392621Sllai1 SDEV_GID_DEFAULT, /* va_gid */ 1402621Sllai1 0, /* va_fsid */ 1412621Sllai1 0, /* va_nodeid */ 1422621Sllai1 0, /* va_nlink */ 1432621Sllai1 0, /* va_size */ 1442621Sllai1 0, /* va_atime */ 1452621Sllai1 0, /* va_mtime */ 1462621Sllai1 0, /* va_ctime */ 1472621Sllai1 0, /* va_rdev */ 1482621Sllai1 0, /* va_blksize */ 1492621Sllai1 0, /* va_nblocks */ 1502621Sllai1 0 /* va_vcode */ 1512621Sllai1 }; 1522621Sllai1 1532621Sllai1 kmem_cache_t *sdev_node_cache; /* sdev_node cache */ 1542621Sllai1 int devtype; /* fstype */ 1552621Sllai1 1562621Sllai1 struct devname_ops *devname_ns_ops; /* default name service directory ops */ 1572621Sllai1 kmutex_t devname_nsmaps_lock; /* protect devname_nsmaps */ 1582621Sllai1 1592621Sllai1 /* static */ 1602621Sllai1 static struct devname_nsmap *devname_nsmaps = NULL; 1612621Sllai1 /* contents from /etc/dev/devname_master */ 1622621Sllai1 static int devname_nsmaps_invalidated = 0; /* "devfsadm -m" has run */ 1632621Sllai1 1642621Sllai1 static struct vnodeops *sdev_get_vop(struct sdev_node *); 1652621Sllai1 static void sdev_set_no_nocache(struct sdev_node *); 1662621Sllai1 static int sdev_get_moduleops(struct sdev_node *); 1672621Sllai1 static void sdev_handle_alloc(struct sdev_node *); 1682621Sllai1 static fs_operation_def_t *sdev_merge_vtab(const fs_operation_def_t []); 1692621Sllai1 static void sdev_free_vtab(fs_operation_def_t *); 1702621Sllai1 1712621Sllai1 static void 1722621Sllai1 sdev_prof_free(struct sdev_node *dv) 1732621Sllai1 { 1742621Sllai1 ASSERT(!SDEV_IS_GLOBAL(dv)); 1752621Sllai1 if (dv->sdev_prof.dev_name) 1762621Sllai1 nvlist_free(dv->sdev_prof.dev_name); 1772621Sllai1 if (dv->sdev_prof.dev_map) 1782621Sllai1 nvlist_free(dv->sdev_prof.dev_map); 1792621Sllai1 if (dv->sdev_prof.dev_symlink) 1802621Sllai1 nvlist_free(dv->sdev_prof.dev_symlink); 1812621Sllai1 if (dv->sdev_prof.dev_glob_incdir) 1822621Sllai1 nvlist_free(dv->sdev_prof.dev_glob_incdir); 1832621Sllai1 if (dv->sdev_prof.dev_glob_excdir) 1842621Sllai1 nvlist_free(dv->sdev_prof.dev_glob_excdir); 1852621Sllai1 bzero(&dv->sdev_prof, sizeof (dv->sdev_prof)); 1862621Sllai1 } 1872621Sllai1 1882621Sllai1 /* 1892621Sllai1 * sdev_node cache constructor 1902621Sllai1 */ 1912621Sllai1 /*ARGSUSED1*/ 1922621Sllai1 static int 1932621Sllai1 i_sdev_node_ctor(void *buf, void *cfarg, int flag) 1942621Sllai1 { 1952621Sllai1 struct sdev_node *dv = (struct sdev_node *)buf; 1962621Sllai1 struct vnode *vp; 1972621Sllai1 1982621Sllai1 ASSERT(flag == KM_SLEEP); 1992621Sllai1 2002621Sllai1 bzero(buf, sizeof (struct sdev_node)); 2012621Sllai1 rw_init(&dv->sdev_contents, NULL, RW_DEFAULT, NULL); 2022621Sllai1 dv->sdev_vnode = vn_alloc(KM_SLEEP); 2032621Sllai1 vp = SDEVTOV(dv); 2042621Sllai1 vp->v_data = (caddr_t)dv; 2052621Sllai1 return (0); 2062621Sllai1 } 2072621Sllai1 2082621Sllai1 /* sdev_node destructor for kmem cache */ 2092621Sllai1 /*ARGSUSED1*/ 2102621Sllai1 static void 2112621Sllai1 i_sdev_node_dtor(void *buf, void *arg) 2122621Sllai1 { 2132621Sllai1 struct sdev_node *dv = (struct sdev_node *)buf; 2142621Sllai1 struct vnode *vp = SDEVTOV(dv); 2152621Sllai1 2162621Sllai1 rw_destroy(&dv->sdev_contents); 2172621Sllai1 vn_free(vp); 2182621Sllai1 } 2192621Sllai1 2202621Sllai1 /* initialize sdev_node cache */ 2212621Sllai1 void 2222621Sllai1 sdev_node_cache_init() 2232621Sllai1 { 2242621Sllai1 int flags = 0; 2252621Sllai1 2262621Sllai1 #ifdef DEBUG 2272621Sllai1 flags = sdev_debug_cache_flags; 2282621Sllai1 if (flags) 2292621Sllai1 sdcmn_err(("cache debug flags 0x%x\n", flags)); 2302621Sllai1 #endif /* DEBUG */ 2312621Sllai1 2322621Sllai1 ASSERT(sdev_node_cache == NULL); 2332621Sllai1 sdev_node_cache = kmem_cache_create("sdev_node_cache", 2342621Sllai1 sizeof (struct sdev_node), 0, i_sdev_node_ctor, i_sdev_node_dtor, 2352621Sllai1 NULL, NULL, NULL, flags); 2362621Sllai1 } 2372621Sllai1 2382621Sllai1 /* destroy sdev_node cache */ 2392621Sllai1 void 2402621Sllai1 sdev_node_cache_fini() 2412621Sllai1 { 2422621Sllai1 ASSERT(sdev_node_cache != NULL); 2432621Sllai1 kmem_cache_destroy(sdev_node_cache); 2442621Sllai1 sdev_node_cache = NULL; 2452621Sllai1 } 2462621Sllai1 2472621Sllai1 void 2482621Sllai1 sdev_set_nodestate(struct sdev_node *dv, sdev_node_state_t state) 2492621Sllai1 { 2502621Sllai1 ASSERT(dv); 2512621Sllai1 ASSERT(RW_WRITE_HELD(&dv->sdev_contents)); 2522621Sllai1 dv->sdev_state = state; 2532621Sllai1 } 2542621Sllai1 2552621Sllai1 static void 2562621Sllai1 sdev_attrinit(struct sdev_node *dv, vattr_t *vap) 2572621Sllai1 { 2582621Sllai1 timestruc_t now; 2592621Sllai1 2602621Sllai1 ASSERT(vap); 2612621Sllai1 2622621Sllai1 dv->sdev_attr = kmem_zalloc(sizeof (struct vattr), KM_SLEEP); 2632621Sllai1 *dv->sdev_attr = *vap; 2642621Sllai1 2652621Sllai1 dv->sdev_attr->va_mode = MAKEIMODE(vap->va_type, vap->va_mode); 2662621Sllai1 2672621Sllai1 gethrestime(&now); 2682621Sllai1 dv->sdev_attr->va_atime = now; 2692621Sllai1 dv->sdev_attr->va_mtime = now; 2702621Sllai1 dv->sdev_attr->va_ctime = now; 2712621Sllai1 } 2722621Sllai1 2732621Sllai1 /* alloc and initialize a sdev_node */ 2742621Sllai1 int 2752621Sllai1 sdev_nodeinit(struct sdev_node *ddv, char *nm, struct sdev_node **newdv, 2762621Sllai1 vattr_t *vap) 2772621Sllai1 { 2782621Sllai1 struct sdev_node *dv = NULL; 2792621Sllai1 struct vnode *vp; 2802621Sllai1 size_t nmlen, len; 2812621Sllai1 devname_handle_t *dhl; 2822621Sllai1 2832621Sllai1 nmlen = strlen(nm) + 1; 2842621Sllai1 if (nmlen > MAXNAMELEN) { 2852621Sllai1 sdcmn_err9(("sdev_nodeinit: node name %s" 2862621Sllai1 " too long\n", nm)); 2872621Sllai1 *newdv = NULL; 2882621Sllai1 return (ENAMETOOLONG); 2892621Sllai1 } 2902621Sllai1 2912621Sllai1 dv = kmem_cache_alloc(sdev_node_cache, KM_SLEEP); 2922621Sllai1 2932621Sllai1 dv->sdev_name = kmem_alloc(nmlen, KM_SLEEP); 2942621Sllai1 bcopy(nm, dv->sdev_name, nmlen); 2952621Sllai1 dv->sdev_namelen = nmlen - 1; /* '\0' not included */ 2962621Sllai1 len = strlen(ddv->sdev_path) + strlen(nm) + 2; 2972621Sllai1 dv->sdev_path = kmem_alloc(len, KM_SLEEP); 2982621Sllai1 (void) snprintf(dv->sdev_path, len, "%s/%s", ddv->sdev_path, nm); 2992621Sllai1 /* overwritten for VLNK nodes */ 3002621Sllai1 dv->sdev_symlink = NULL; 3012621Sllai1 3022621Sllai1 vp = SDEVTOV(dv); 3032621Sllai1 vn_reinit(vp); 3042621Sllai1 vp->v_vfsp = SDEVTOV(ddv)->v_vfsp; 3052621Sllai1 if (vap) 3062621Sllai1 vp->v_type = vap->va_type; 3072621Sllai1 3082621Sllai1 /* 3092621Sllai1 * initialized to the parent's vnodeops. 3102621Sllai1 * maybe overwriten for a VDIR 3112621Sllai1 */ 3122621Sllai1 vn_setops(vp, vn_getops(SDEVTOV(ddv))); 3132621Sllai1 vn_exists(vp); 3142621Sllai1 3152621Sllai1 dv->sdev_dotdot = NULL; 3162621Sllai1 dv->sdev_dot = NULL; 3172621Sllai1 dv->sdev_next = NULL; 3182621Sllai1 dv->sdev_attrvp = NULL; 3192621Sllai1 if (vap) { 3202621Sllai1 sdev_attrinit(dv, vap); 3212621Sllai1 } else { 3222621Sllai1 dv->sdev_attr = NULL; 3232621Sllai1 } 3242621Sllai1 3252621Sllai1 dv->sdev_ino = sdev_mkino(dv); 3262621Sllai1 dv->sdev_nlink = 0; /* updated on insert */ 3272621Sllai1 dv->sdev_flags = ddv->sdev_flags; /* inherit from the parent first */ 3282621Sllai1 dv->sdev_flags |= SDEV_BUILD; 3292621Sllai1 mutex_init(&dv->sdev_lookup_lock, NULL, MUTEX_DEFAULT, NULL); 3302621Sllai1 cv_init(&dv->sdev_lookup_cv, NULL, CV_DEFAULT, NULL); 3312621Sllai1 if (SDEV_IS_GLOBAL(ddv)) { 3322621Sllai1 dv->sdev_flags |= SDEV_GLOBAL; 3332621Sllai1 dv->sdev_mapinfo = NULL; 3342621Sllai1 dhl = &(dv->sdev_handle); 3352621Sllai1 dhl->dh_data = dv; 3362621Sllai1 dhl->dh_spec = DEVNAME_NS_NONE; 3372621Sllai1 dhl->dh_args = NULL; 3382621Sllai1 sdev_set_no_nocache(dv); 3392621Sllai1 dv->sdev_gdir_gen = 0; 3402621Sllai1 } else { 3412621Sllai1 dv->sdev_flags &= ~SDEV_GLOBAL; 3422621Sllai1 dv->sdev_origin = NULL; /* set later */ 3432621Sllai1 bzero(&dv->sdev_prof, sizeof (dv->sdev_prof)); 3442621Sllai1 dv->sdev_ldir_gen = 0; 3452621Sllai1 dv->sdev_devtree_gen = 0; 3462621Sllai1 } 3472621Sllai1 3482621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 3492621Sllai1 sdev_set_nodestate(dv, SDEV_INIT); 3502621Sllai1 rw_exit(&dv->sdev_contents); 3512621Sllai1 *newdv = dv; 3522621Sllai1 3532621Sllai1 return (0); 3542621Sllai1 } 3552621Sllai1 3562621Sllai1 /* 3572621Sllai1 * transition a sdev_node into SDEV_READY state 3582621Sllai1 */ 3592621Sllai1 int 3602621Sllai1 sdev_nodeready(struct sdev_node *dv, struct vattr *vap, struct vnode *avp, 3612621Sllai1 void *args, struct cred *cred) 3622621Sllai1 { 3632621Sllai1 int error = 0; 3642621Sllai1 struct vnode *vp = SDEVTOV(dv); 3652621Sllai1 vtype_t type; 3662621Sllai1 3672621Sllai1 ASSERT(dv && (dv->sdev_state != SDEV_READY) && vap); 3682621Sllai1 3692621Sllai1 type = vap->va_type; 3702621Sllai1 vp->v_type = type; 3712621Sllai1 vp->v_rdev = vap->va_rdev; 3722621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 3732621Sllai1 if (type == VDIR) { 3742621Sllai1 dv->sdev_nlink = 2; 3752621Sllai1 dv->sdev_flags &= ~SDEV_PERSIST; 3762621Sllai1 dv->sdev_flags &= ~SDEV_DYNAMIC; 3772621Sllai1 vn_setops(vp, sdev_get_vop(dv)); /* from internal vtab */ 3782621Sllai1 error = sdev_get_moduleops(dv); /* from plug-in module */ 3792621Sllai1 ASSERT(dv->sdev_dotdot); 3802621Sllai1 ASSERT(SDEVTOV(dv->sdev_dotdot)->v_type == VDIR); 3812621Sllai1 vp->v_rdev = SDEVTOV(dv->sdev_dotdot)->v_rdev; 3822621Sllai1 } else if (type == VLNK) { 3832621Sllai1 ASSERT(args); 3842621Sllai1 dv->sdev_nlink = 1; 3852621Sllai1 dv->sdev_symlink = i_ddi_strdup((char *)args, KM_SLEEP); 3862621Sllai1 } else { 3872621Sllai1 dv->sdev_nlink = 1; 3882621Sllai1 } 3892621Sllai1 3902621Sllai1 if (!(SDEV_IS_GLOBAL(dv))) { 3912621Sllai1 dv->sdev_origin = (struct sdev_node *)args; 3922621Sllai1 dv->sdev_flags &= ~SDEV_PERSIST; 3932621Sllai1 } 3942621Sllai1 3952621Sllai1 /* 3962621Sllai1 * shadow node is created here OR 3972621Sllai1 * if failed (indicated by dv->sdev_attrvp == NULL), 3982621Sllai1 * created later in sdev_setattr 3992621Sllai1 */ 4002621Sllai1 if (avp) { 4012621Sllai1 dv->sdev_attrvp = avp; 4022621Sllai1 } else { 4032621Sllai1 if (dv->sdev_attr == NULL) 4042621Sllai1 sdev_attrinit(dv, vap); 4052621Sllai1 else 4062621Sllai1 *dv->sdev_attr = *vap; 4072621Sllai1 4082621Sllai1 if ((SDEV_IS_PERSIST(dv) && (dv->sdev_attrvp == NULL)) || 4092621Sllai1 ((SDEVTOV(dv)->v_type == VDIR) && 4102621Sllai1 (dv->sdev_attrvp == NULL))) 4112621Sllai1 error = sdev_shadow_node(dv, cred); 4122621Sllai1 } 4132621Sllai1 4142621Sllai1 /* transition to READY state */ 4152621Sllai1 sdev_set_nodestate(dv, SDEV_READY); 4162621Sllai1 sdev_nc_node_exists(dv); 4172621Sllai1 rw_exit(&dv->sdev_contents); 4182621Sllai1 return (error); 4192621Sllai1 } 4202621Sllai1 4212621Sllai1 /* 4222621Sllai1 * setting ZOMBIE state 4232621Sllai1 */ 4242621Sllai1 static int 4252621Sllai1 sdev_nodezombied(struct sdev_node *dv) 4262621Sllai1 { 4272621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 4282621Sllai1 sdev_set_nodestate(dv, SDEV_ZOMBIE); 4292621Sllai1 rw_exit(&dv->sdev_contents); 4302621Sllai1 return (0); 4312621Sllai1 } 4322621Sllai1 4332621Sllai1 /* 4342621Sllai1 * Build the VROOT sdev_node. 4352621Sllai1 */ 4362621Sllai1 /*ARGSUSED*/ 4372621Sllai1 struct sdev_node * 4382621Sllai1 sdev_mkroot(struct vfs *vfsp, dev_t devdev, struct vnode *mvp, 4392621Sllai1 struct vnode *avp, struct cred *cred) 4402621Sllai1 { 4412621Sllai1 struct sdev_node *dv; 4422621Sllai1 struct vnode *vp; 4432621Sllai1 char devdir[] = "/dev"; 4442621Sllai1 4452621Sllai1 ASSERT(sdev_node_cache != NULL); 4462621Sllai1 ASSERT(avp); 4472621Sllai1 dv = kmem_cache_alloc(sdev_node_cache, KM_SLEEP); 4482621Sllai1 vp = SDEVTOV(dv); 4492621Sllai1 vn_reinit(vp); 4502621Sllai1 vp->v_flag |= VROOT; 4512621Sllai1 vp->v_vfsp = vfsp; 4522621Sllai1 vp->v_type = VDIR; 4532621Sllai1 vp->v_rdev = devdev; 4542621Sllai1 vn_setops(vp, sdev_vnodeops); /* apply the default vnodeops at /dev */ 4552621Sllai1 vn_exists(vp); 4562621Sllai1 4572621Sllai1 if (vfsp->vfs_mntpt) 4582621Sllai1 dv->sdev_name = i_ddi_strdup( 4592621Sllai1 (char *)refstr_value(vfsp->vfs_mntpt), KM_SLEEP); 4602621Sllai1 else 4612621Sllai1 /* vfs_mountdev1 set mount point later */ 4622621Sllai1 dv->sdev_name = i_ddi_strdup("/dev", KM_SLEEP); 4632621Sllai1 dv->sdev_namelen = strlen(dv->sdev_name); /* '\0' not included */ 4642621Sllai1 dv->sdev_path = i_ddi_strdup(devdir, KM_SLEEP); 4652621Sllai1 dv->sdev_ino = SDEV_ROOTINO; 4662621Sllai1 dv->sdev_nlink = 2; /* name + . (no sdev_insert) */ 4672621Sllai1 dv->sdev_dotdot = dv; /* .. == self */ 4682621Sllai1 dv->sdev_attrvp = avp; 4692621Sllai1 dv->sdev_attr = NULL; 4702621Sllai1 mutex_init(&dv->sdev_lookup_lock, NULL, MUTEX_DEFAULT, NULL); 4712621Sllai1 cv_init(&dv->sdev_lookup_cv, NULL, CV_DEFAULT, NULL); 4722621Sllai1 if (strcmp(dv->sdev_name, "/dev") == 0) { 4732621Sllai1 mutex_init(&devname_nsmaps_lock, NULL, MUTEX_DEFAULT, NULL); 4742621Sllai1 dv->sdev_mapinfo = NULL; 4752621Sllai1 dv->sdev_flags = SDEV_BUILD|SDEV_GLOBAL|SDEV_PERSIST; 4762621Sllai1 bzero(&dv->sdev_handle, sizeof (dv->sdev_handle)); 4772621Sllai1 dv->sdev_gdir_gen = 0; 4782621Sllai1 } else { 4792621Sllai1 dv->sdev_flags = SDEV_BUILD; 4802621Sllai1 dv->sdev_flags &= ~SDEV_PERSIST; 4812621Sllai1 bzero(&dv->sdev_prof, sizeof (dv->sdev_prof)); 4822621Sllai1 dv->sdev_ldir_gen = 0; 4832621Sllai1 dv->sdev_devtree_gen = 0; 4842621Sllai1 } 4852621Sllai1 4862621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 4872621Sllai1 sdev_set_nodestate(dv, SDEV_READY); 4882621Sllai1 rw_exit(&dv->sdev_contents); 4892621Sllai1 sdev_nc_node_exists(dv); 4902621Sllai1 return (dv); 4912621Sllai1 } 4922621Sllai1 4932621Sllai1 /* 4942621Sllai1 * 1. load the module 4952621Sllai1 * 2. modload invokes sdev_module_register, which in turn sets 4962621Sllai1 * the dv->sdev_mapinfo->dir_ops 4972621Sllai1 * 4982621Sllai1 * note: locking order: 4992621Sllai1 * dv->sdev_contents -> map->dir_lock 5002621Sllai1 */ 5012621Sllai1 static int 5022621Sllai1 sdev_get_moduleops(struct sdev_node *dv) 5032621Sllai1 { 5042621Sllai1 int error = 0; 5052621Sllai1 struct devname_nsmap *map = NULL; 5062621Sllai1 char *module; 5072621Sllai1 char *path; 5082621Sllai1 int load = 1; 5092621Sllai1 5102621Sllai1 ASSERT(SDEVTOV(dv)->v_type == VDIR); 5112621Sllai1 5122621Sllai1 if (devname_nsmaps == NULL) 5132621Sllai1 return (0); 5142621Sllai1 5152621Sllai1 if (!sdev_nsmaps_loaded() && !sdev_nsmaps_reloaded()) 5162621Sllai1 return (0); 5172621Sllai1 5182621Sllai1 5192621Sllai1 path = dv->sdev_path; 5202621Sllai1 if ((map = sdev_get_nsmap_by_dir(path, 0))) { 5212621Sllai1 rw_enter(&map->dir_lock, RW_READER); 5222621Sllai1 if (map->dir_invalid) { 5232621Sllai1 if (map->dir_module && map->dir_newmodule && 5242621Sllai1 (strcmp(map->dir_module, 5252621Sllai1 map->dir_newmodule) == 0)) { 5262621Sllai1 load = 0; 5272621Sllai1 } 5282621Sllai1 sdev_replace_nsmap(map, map->dir_newmodule, 5292621Sllai1 map->dir_newmap); 5302621Sllai1 } 5312621Sllai1 5322621Sllai1 module = map->dir_module; 5332621Sllai1 if (module && load) { 5342621Sllai1 sdcmn_err6(("sdev_get_moduleops: " 5352621Sllai1 "load module %s", module)); 5362621Sllai1 rw_exit(&map->dir_lock); 5372621Sllai1 error = modload("devname", module); 5382621Sllai1 sdcmn_err6(("sdev_get_moduleops: error %d\n", error)); 5392621Sllai1 if (error < 0) { 5402621Sllai1 return (-1); 5412621Sllai1 } 5422621Sllai1 } else if (module == NULL) { 5432621Sllai1 /* 5442621Sllai1 * loading the module ops for name services 5452621Sllai1 */ 5462621Sllai1 if (devname_ns_ops == NULL) { 5472621Sllai1 sdcmn_err6(( 5482621Sllai1 "sdev_get_moduleops: modload default\n")); 5492621Sllai1 error = modload("devname", DEVNAME_NSCONFIG); 5502621Sllai1 sdcmn_err6(( 5512621Sllai1 "sdev_get_moduleops: error %d\n", error)); 5522621Sllai1 if (error < 0) { 5532621Sllai1 return (-1); 5542621Sllai1 } 5552621Sllai1 } 5562621Sllai1 5572621Sllai1 if (!rw_tryupgrade(&map->dir_lock)) { 5582621Sllai1 rw_exit(&map->dir_lock); 5592621Sllai1 rw_enter(&map->dir_lock, RW_WRITER); 5602621Sllai1 } 5612621Sllai1 ASSERT(devname_ns_ops); 5622621Sllai1 map->dir_ops = devname_ns_ops; 5632621Sllai1 rw_exit(&map->dir_lock); 5642621Sllai1 } 5652621Sllai1 } 5662621Sllai1 5672621Sllai1 dv->sdev_mapinfo = map; 5682621Sllai1 return (0); 5692621Sllai1 } 5702621Sllai1 5712621Sllai1 /* directory dependent vop table */ 5722621Sllai1 struct sdev_vop_table { 5732621Sllai1 char *vt_name; /* subdirectory name */ 5742621Sllai1 const fs_operation_def_t *vt_service; /* vnodeops table */ 5752621Sllai1 struct vnodeops *vt_vops; /* constructed vop */ 5762621Sllai1 struct vnodeops **vt_global_vops; /* global container for vop */ 5772621Sllai1 int (*vt_vtor)(struct sdev_node *); /* validate sdev_node */ 5782621Sllai1 int vt_flags; 5792621Sllai1 }; 5802621Sllai1 5812621Sllai1 /* 5822621Sllai1 * A nice improvement would be to provide a plug-in mechanism 5832621Sllai1 * for this table instead of a const table. 5842621Sllai1 */ 5852621Sllai1 static struct sdev_vop_table vtab[] = 5862621Sllai1 { 5872621Sllai1 { "pts", devpts_vnodeops_tbl, NULL, &devpts_vnodeops, devpts_validate, 5882621Sllai1 SDEV_DYNAMIC | SDEV_VTOR }, 5892621Sllai1 5902621Sllai1 { "zcons", NULL, NULL, NULL, NULL, SDEV_NO_NCACHE }, 5912621Sllai1 5922621Sllai1 { NULL, NULL, NULL, NULL, NULL, 0} 5932621Sllai1 }; 5942621Sllai1 5952621Sllai1 5962621Sllai1 /* 5972621Sllai1 * sets a directory's vnodeops if the directory is in the vtab; 5982621Sllai1 */ 5992621Sllai1 static struct vnodeops * 6002621Sllai1 sdev_get_vop(struct sdev_node *dv) 6012621Sllai1 { 6022621Sllai1 int i; 6032621Sllai1 char *path; 6042621Sllai1 6052621Sllai1 path = dv->sdev_path; 6062621Sllai1 ASSERT(path); 6072621Sllai1 6082621Sllai1 /* gets the relative path to /dev/ */ 6092621Sllai1 path += 5; 6102621Sllai1 6112621Sllai1 /* gets the vtab entry if matches */ 6122621Sllai1 for (i = 0; vtab[i].vt_name; i++) { 6132621Sllai1 if (strcmp(vtab[i].vt_name, path) != 0) 6142621Sllai1 continue; 6152621Sllai1 dv->sdev_flags |= vtab[i].vt_flags; 6162621Sllai1 6172621Sllai1 if (vtab[i].vt_vops) { 6182621Sllai1 if (vtab[i].vt_global_vops) 6192621Sllai1 *(vtab[i].vt_global_vops) = vtab[i].vt_vops; 6202621Sllai1 return (vtab[i].vt_vops); 6212621Sllai1 } 6222621Sllai1 6232621Sllai1 if (vtab[i].vt_service) { 6242621Sllai1 fs_operation_def_t *templ; 6252621Sllai1 templ = sdev_merge_vtab(vtab[i].vt_service); 6262621Sllai1 if (vn_make_ops(vtab[i].vt_name, 6272621Sllai1 (const fs_operation_def_t *)templ, 6282621Sllai1 &vtab[i].vt_vops) != 0) { 6292621Sllai1 cmn_err(CE_PANIC, "%s: malformed vnode ops\n", 6302621Sllai1 vtab[i].vt_name); 6312621Sllai1 /*NOTREACHED*/ 6322621Sllai1 } 6332621Sllai1 if (vtab[i].vt_global_vops) { 6342621Sllai1 *(vtab[i].vt_global_vops) = vtab[i].vt_vops; 6352621Sllai1 } 6362621Sllai1 sdev_free_vtab(templ); 6372621Sllai1 return (vtab[i].vt_vops); 6382621Sllai1 } 6392621Sllai1 return (sdev_vnodeops); 6402621Sllai1 } 6412621Sllai1 6422621Sllai1 /* child inherits the persistence of the parent */ 6432621Sllai1 if (SDEV_IS_PERSIST(dv->sdev_dotdot)) 6442621Sllai1 dv->sdev_flags |= SDEV_PERSIST; 6452621Sllai1 6462621Sllai1 return (sdev_vnodeops); 6472621Sllai1 } 6482621Sllai1 6492621Sllai1 static void 6502621Sllai1 sdev_set_no_nocache(struct sdev_node *dv) 6512621Sllai1 { 6522621Sllai1 int i; 6532621Sllai1 char *path; 6542621Sllai1 6552621Sllai1 ASSERT(dv->sdev_path); 6562621Sllai1 path = dv->sdev_path + strlen("/dev/"); 6572621Sllai1 6582621Sllai1 for (i = 0; vtab[i].vt_name; i++) { 6592621Sllai1 if (strcmp(vtab[i].vt_name, path) == 0) { 6602621Sllai1 if (vtab[i].vt_flags & SDEV_NO_NCACHE) 6612621Sllai1 dv->sdev_flags |= SDEV_NO_NCACHE; 6622621Sllai1 break; 6632621Sllai1 } 6642621Sllai1 } 6652621Sllai1 } 6662621Sllai1 6672621Sllai1 void * 6682621Sllai1 sdev_get_vtor(struct sdev_node *dv) 6692621Sllai1 { 6702621Sllai1 int i; 6712621Sllai1 6722621Sllai1 for (i = 0; vtab[i].vt_name; i++) { 6732621Sllai1 if (strcmp(vtab[i].vt_name, dv->sdev_name) != 0) 6742621Sllai1 continue; 6752621Sllai1 return ((void *)vtab[i].vt_vtor); 6762621Sllai1 } 6772621Sllai1 return (NULL); 6782621Sllai1 } 6792621Sllai1 6802621Sllai1 /* 6812621Sllai1 * Build the base root inode 6822621Sllai1 */ 6832621Sllai1 ino_t 6842621Sllai1 sdev_mkino(struct sdev_node *dv) 6852621Sllai1 { 6862621Sllai1 ino_t ino; 6872621Sllai1 6882621Sllai1 /* 6892621Sllai1 * for now, follow the lead of tmpfs here 6902621Sllai1 * need to someday understand the requirements here 6912621Sllai1 */ 6922621Sllai1 ino = (ino_t)(uint32_t)((uintptr_t)dv >> 3); 6932621Sllai1 ino += SDEV_ROOTINO + 1; 6942621Sllai1 6952621Sllai1 return (ino); 6962621Sllai1 } 6972621Sllai1 6982621Sllai1 static int 6992621Sllai1 sdev_getlink(struct vnode *linkvp, char **link) 7002621Sllai1 { 7012621Sllai1 int err; 7022621Sllai1 char *buf; 7032621Sllai1 struct uio uio = {0}; 7042621Sllai1 struct iovec iov = {0}; 7052621Sllai1 7062621Sllai1 if (linkvp == NULL) 7072621Sllai1 return (ENOENT); 7082621Sllai1 ASSERT(linkvp->v_type == VLNK); 7092621Sllai1 7102621Sllai1 buf = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 7112621Sllai1 iov.iov_base = buf; 7122621Sllai1 iov.iov_len = MAXPATHLEN; 7132621Sllai1 uio.uio_iov = &iov; 7142621Sllai1 uio.uio_iovcnt = 1; 7152621Sllai1 uio.uio_resid = MAXPATHLEN; 7162621Sllai1 uio.uio_segflg = UIO_SYSSPACE; 7172621Sllai1 uio.uio_llimit = MAXOFFSET_T; 7182621Sllai1 7192621Sllai1 err = VOP_READLINK(linkvp, &uio, kcred); 7202621Sllai1 if (err) { 7212621Sllai1 cmn_err(CE_WARN, "readlink %s failed in dev\n", buf); 7222621Sllai1 kmem_free(buf, MAXPATHLEN); 7232621Sllai1 return (ENOENT); 7242621Sllai1 } 7252621Sllai1 7262621Sllai1 /* mission complete */ 7272621Sllai1 *link = i_ddi_strdup(buf, KM_SLEEP); 7282621Sllai1 kmem_free(buf, MAXPATHLEN); 7292621Sllai1 return (0); 7302621Sllai1 } 7312621Sllai1 7322621Sllai1 /* 7332621Sllai1 * A convenient wrapper to get the devfs node vnode for a device 7342621Sllai1 * minor functionality: readlink() of a /dev symlink 7352621Sllai1 * Place the link into dv->sdev_symlink 7362621Sllai1 */ 7372621Sllai1 static int 7382621Sllai1 sdev_follow_link(struct sdev_node *dv) 7392621Sllai1 { 7402621Sllai1 int err; 7412621Sllai1 struct vnode *linkvp; 7422621Sllai1 char *link = NULL; 7432621Sllai1 7442621Sllai1 linkvp = SDEVTOV(dv); 7452621Sllai1 if (linkvp == NULL) 7462621Sllai1 return (ENOENT); 7472621Sllai1 ASSERT(linkvp->v_type == VLNK); 7482621Sllai1 err = sdev_getlink(linkvp, &link); 7492621Sllai1 if (err) { 7502621Sllai1 (void) sdev_nodezombied(dv); 7512621Sllai1 dv->sdev_symlink = NULL; 7522621Sllai1 return (ENOENT); 7532621Sllai1 } 7542621Sllai1 7552621Sllai1 ASSERT(link != NULL); 7562621Sllai1 dv->sdev_symlink = link; 7572621Sllai1 return (0); 7582621Sllai1 } 7592621Sllai1 7602621Sllai1 static int 7612621Sllai1 sdev_node_check(struct sdev_node *dv, struct vattr *nvap, void *nargs) 7622621Sllai1 { 7632621Sllai1 vtype_t otype = SDEVTOV(dv)->v_type; 7642621Sllai1 7652621Sllai1 /* 7662621Sllai1 * existing sdev_node has a different type. 7672621Sllai1 */ 7682621Sllai1 if (otype != nvap->va_type) { 7692621Sllai1 sdcmn_err9(("sdev_node_check: existing node " 7702621Sllai1 " %s type %d does not match new node type %d\n", 7712621Sllai1 dv->sdev_name, otype, nvap->va_type)); 7722621Sllai1 return (EEXIST); 7732621Sllai1 } 7742621Sllai1 7752621Sllai1 /* 7762621Sllai1 * For a symlink, the target should be the same. 7772621Sllai1 */ 7782621Sllai1 if (otype == VLNK) { 7792621Sllai1 ASSERT(nargs != NULL); 7802621Sllai1 ASSERT(dv->sdev_symlink != NULL); 7812621Sllai1 if (strcmp(dv->sdev_symlink, (char *)nargs) != 0) { 7822621Sllai1 sdcmn_err9(("sdev_node_check: existing node " 7832621Sllai1 " %s has different symlink %s as new node " 7842621Sllai1 " %s\n", dv->sdev_name, dv->sdev_symlink, 7852621Sllai1 (char *)nargs)); 7862621Sllai1 return (EEXIST); 7872621Sllai1 } 7882621Sllai1 } 7892621Sllai1 7902621Sllai1 return (0); 7912621Sllai1 } 7922621Sllai1 7932621Sllai1 /* 7942621Sllai1 * sdev_mknode - a wrapper for sdev_nodeinit(), sdev_nodeready() 7952621Sllai1 * 7962621Sllai1 * arguments: 7972621Sllai1 * - ddv (parent) 7982621Sllai1 * - nm (child name) 7992621Sllai1 * - newdv (sdev_node for nm is returned here) 8002621Sllai1 * - vap (vattr for the node to be created, va_type should be set. 8012621Sllai1 * the defaults should be used if unknown) 8022621Sllai1 * - cred 8032621Sllai1 * - args 8042621Sllai1 * . tnm (for VLNK) 8052621Sllai1 * . global sdev_node (for !SDEV_GLOBAL) 8062621Sllai1 * - state: SDEV_INIT, SDEV_READY 8072621Sllai1 * 8082621Sllai1 * only ddv, nm, newddv, vap, cred are required for sdev_mknode(SDEV_INIT) 8092621Sllai1 * 8102621Sllai1 * NOTE: directory contents writers lock needs to be held before 8112621Sllai1 * calling this routine. 8122621Sllai1 */ 8132621Sllai1 int 8142621Sllai1 sdev_mknode(struct sdev_node *ddv, char *nm, struct sdev_node **newdv, 8152621Sllai1 struct vattr *vap, struct vnode *avp, void *args, struct cred *cred, 8162621Sllai1 sdev_node_state_t state) 8172621Sllai1 { 8182621Sllai1 int error = 0; 8192621Sllai1 sdev_node_state_t node_state; 8202621Sllai1 struct sdev_node *dv = NULL; 8212621Sllai1 8222621Sllai1 ASSERT(state != SDEV_ZOMBIE); 8232621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 8242621Sllai1 8252621Sllai1 if (*newdv) { 8262621Sllai1 dv = *newdv; 8272621Sllai1 } else { 8282621Sllai1 /* allocate and initialize a sdev_node */ 8292621Sllai1 if (ddv->sdev_state == SDEV_ZOMBIE) { 8302621Sllai1 sdcmn_err9(("sdev_mknode: parent %s ZOMBIEd\n", 8312621Sllai1 ddv->sdev_path)); 8322621Sllai1 return (ENOENT); 8332621Sllai1 } 8342621Sllai1 8352621Sllai1 error = sdev_nodeinit(ddv, nm, &dv, vap); 8362621Sllai1 if (error != 0) { 8372621Sllai1 sdcmn_err9(("sdev_mknode: error %d," 8382621Sllai1 " name %s can not be initialized\n", 8392621Sllai1 error, nm)); 8402621Sllai1 return (ENOENT); 8412621Sllai1 } 8422621Sllai1 ASSERT(dv); 8432621Sllai1 8442621Sllai1 /* insert into the directory cache */ 8452621Sllai1 error = sdev_cache_update(ddv, &dv, nm, SDEV_CACHE_ADD); 8462621Sllai1 if (error) { 8472621Sllai1 sdcmn_err9(("sdev_mknode: node %s can not" 8482621Sllai1 " be added into directory cache\n", nm)); 8492621Sllai1 return (ENOENT); 8502621Sllai1 } 8512621Sllai1 } 8522621Sllai1 8532621Sllai1 ASSERT(dv); 8542621Sllai1 node_state = dv->sdev_state; 8552621Sllai1 ASSERT(node_state != SDEV_ZOMBIE); 8562621Sllai1 8572621Sllai1 if (state == SDEV_READY) { 8582621Sllai1 switch (node_state) { 8592621Sllai1 case SDEV_INIT: 8602621Sllai1 error = sdev_nodeready(dv, vap, avp, args, cred); 8612621Sllai1 /* 8622621Sllai1 * masking the errors with ENOENT 8632621Sllai1 */ 8642621Sllai1 if (error) { 8652621Sllai1 sdcmn_err9(("sdev_mknode: node %s can NOT" 8662621Sllai1 " be transitioned into READY state, " 8672621Sllai1 "error %d\n", nm, error)); 8682621Sllai1 error = ENOENT; 8692621Sllai1 } 8702621Sllai1 break; 8712621Sllai1 case SDEV_READY: 8722621Sllai1 /* 8732621Sllai1 * Do some sanity checking to make sure 8742621Sllai1 * the existing sdev_node is what has been 8752621Sllai1 * asked for. 8762621Sllai1 */ 8772621Sllai1 error = sdev_node_check(dv, vap, args); 8782621Sllai1 break; 8792621Sllai1 default: 8802621Sllai1 break; 8812621Sllai1 } 8822621Sllai1 } 8832621Sllai1 8842621Sllai1 if (!error) { 8852621Sllai1 *newdv = dv; 8862621Sllai1 ASSERT((*newdv)->sdev_state != SDEV_ZOMBIE); 8872621Sllai1 } else { 8882621Sllai1 SDEV_SIMPLE_RELE(dv); 8892621Sllai1 *newdv = NULL; 8902621Sllai1 } 8912621Sllai1 8922621Sllai1 return (error); 8932621Sllai1 } 8942621Sllai1 8952621Sllai1 /* 8962621Sllai1 * convenient wrapper to change vp's ATIME, CTIME and ATIME 8972621Sllai1 */ 8982621Sllai1 void 8992621Sllai1 sdev_update_timestamps(struct vnode *vp, cred_t *cred, uint_t mask) 9002621Sllai1 { 9012621Sllai1 struct vattr attr; 9022621Sllai1 timestruc_t now; 9032621Sllai1 int err; 9042621Sllai1 9052621Sllai1 ASSERT(vp); 9062621Sllai1 gethrestime(&now); 9072621Sllai1 if (mask & AT_CTIME) 9082621Sllai1 attr.va_ctime = now; 9092621Sllai1 if (mask & AT_MTIME) 9102621Sllai1 attr.va_mtime = now; 9112621Sllai1 if (mask & AT_ATIME) 9122621Sllai1 attr.va_atime = now; 9132621Sllai1 9142621Sllai1 attr.va_mask = (mask & AT_TIMES); 9152621Sllai1 err = VOP_SETATTR(vp, &attr, 0, cred, NULL); 9162621Sllai1 if (err && (err != EROFS)) { 9172621Sllai1 sdcmn_err(("update timestamps error %d\n", err)); 9182621Sllai1 } 9192621Sllai1 } 9202621Sllai1 9212621Sllai1 /* 9222621Sllai1 * the backing store vnode is released here 9232621Sllai1 */ 9242621Sllai1 /*ARGSUSED1*/ 9252621Sllai1 void 9262621Sllai1 sdev_nodedestroy(struct sdev_node *dv, uint_t flags) 9272621Sllai1 { 9282621Sllai1 /* no references */ 9292621Sllai1 ASSERT(dv->sdev_nlink == 0); 9302621Sllai1 9312621Sllai1 if (dv->sdev_attrvp != NULLVP) { 9322621Sllai1 VN_RELE(dv->sdev_attrvp); 9332621Sllai1 /* 9342621Sllai1 * reset the attrvp so that no more 9352621Sllai1 * references can be made on this already 9362621Sllai1 * vn_rele() vnode 9372621Sllai1 */ 9382621Sllai1 dv->sdev_attrvp = NULLVP; 9392621Sllai1 } 9402621Sllai1 9412621Sllai1 if (dv->sdev_attr != NULL) { 9422621Sllai1 kmem_free(dv->sdev_attr, sizeof (struct vattr)); 9432621Sllai1 dv->sdev_attr = NULL; 9442621Sllai1 } 9452621Sllai1 9462621Sllai1 if (dv->sdev_name != NULL) { 9472621Sllai1 kmem_free(dv->sdev_name, dv->sdev_namelen + 1); 9482621Sllai1 dv->sdev_name = NULL; 9492621Sllai1 } 9502621Sllai1 9512621Sllai1 if (dv->sdev_symlink != NULL) { 9522621Sllai1 kmem_free(dv->sdev_symlink, strlen(dv->sdev_symlink) + 1); 9532621Sllai1 dv->sdev_symlink = NULL; 9542621Sllai1 } 9552621Sllai1 9562621Sllai1 if (dv->sdev_path) { 9572621Sllai1 kmem_free(dv->sdev_path, strlen(dv->sdev_path) + 1); 9582621Sllai1 dv->sdev_path = NULL; 9592621Sllai1 } 9602621Sllai1 9612621Sllai1 if (!SDEV_IS_GLOBAL(dv)) 9622621Sllai1 sdev_prof_free(dv); 9632621Sllai1 9642621Sllai1 mutex_destroy(&dv->sdev_lookup_lock); 9652621Sllai1 cv_destroy(&dv->sdev_lookup_cv); 9662621Sllai1 9672621Sllai1 /* return node to initial state as per constructor */ 9682621Sllai1 (void) memset((void *)&dv->sdev_instance_data, 0, 9692621Sllai1 sizeof (dv->sdev_instance_data)); 9702621Sllai1 vn_invalid(SDEVTOV(dv)); 9712621Sllai1 kmem_cache_free(sdev_node_cache, dv); 9722621Sllai1 } 9732621Sllai1 9742621Sllai1 /* 9752621Sllai1 * DIRECTORY CACHE lookup 9762621Sllai1 */ 9772621Sllai1 struct sdev_node * 9782621Sllai1 sdev_findbyname(struct sdev_node *ddv, char *nm) 9792621Sllai1 { 9802621Sllai1 struct sdev_node *dv; 9812621Sllai1 size_t nmlen = strlen(nm); 9822621Sllai1 9832621Sllai1 ASSERT(RW_LOCK_HELD(&ddv->sdev_contents)); 9842621Sllai1 for (dv = ddv->sdev_dot; dv; dv = dv->sdev_next) { 9852621Sllai1 if (dv->sdev_namelen != nmlen) { 9862621Sllai1 continue; 9872621Sllai1 } 9882621Sllai1 9892621Sllai1 /* 9902621Sllai1 * Can't lookup stale nodes 9912621Sllai1 */ 9922621Sllai1 if (dv->sdev_flags & SDEV_STALE) { 9932621Sllai1 sdcmn_err9(( 9942621Sllai1 "sdev_findbyname: skipped stale node: %s\n", 9952621Sllai1 dv->sdev_name)); 9962621Sllai1 continue; 9972621Sllai1 } 9982621Sllai1 9992621Sllai1 if (strcmp(dv->sdev_name, nm) == 0) { 10002621Sllai1 SDEV_HOLD(dv); 10012621Sllai1 return (dv); 10022621Sllai1 } 10032621Sllai1 } 10042621Sllai1 return (NULL); 10052621Sllai1 } 10062621Sllai1 10072621Sllai1 /* 10082621Sllai1 * Inserts a new sdev_node in a parent directory 10092621Sllai1 */ 10102621Sllai1 void 10112621Sllai1 sdev_direnter(struct sdev_node *ddv, struct sdev_node *dv) 10122621Sllai1 { 10132621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 10142621Sllai1 ASSERT(SDEVTOV(ddv)->v_type == VDIR); 10152621Sllai1 ASSERT(ddv->sdev_nlink >= 2); 10162621Sllai1 ASSERT(dv->sdev_nlink == 0); 10172621Sllai1 10182621Sllai1 dv->sdev_dotdot = ddv; 10192621Sllai1 dv->sdev_next = ddv->sdev_dot; 10202621Sllai1 ddv->sdev_dot = dv; 10212621Sllai1 ddv->sdev_nlink++; 10222621Sllai1 } 10232621Sllai1 10242621Sllai1 /* 10252621Sllai1 * The following check is needed because while sdev_nodes are linked 10262621Sllai1 * in SDEV_INIT state, they have their link counts incremented only 10272621Sllai1 * in SDEV_READY state. 10282621Sllai1 */ 10292621Sllai1 static void 10302621Sllai1 decr_link(struct sdev_node *dv) 10312621Sllai1 { 10322621Sllai1 if (dv->sdev_state != SDEV_INIT) 10332621Sllai1 dv->sdev_nlink--; 10342621Sllai1 else 10352621Sllai1 ASSERT(dv->sdev_nlink == 0); 10362621Sllai1 } 10372621Sllai1 10382621Sllai1 /* 10392621Sllai1 * Delete an existing dv from directory cache 10402621Sllai1 * 10412621Sllai1 * In the case of a node is still held by non-zero reference count, 10422621Sllai1 * the node is put into ZOMBIE state. Once the reference count 10432621Sllai1 * reaches "0", the node is unlinked and destroyed, 10442621Sllai1 * in sdev_inactive(). 10452621Sllai1 */ 10462621Sllai1 static int 10472621Sllai1 sdev_dirdelete(struct sdev_node *ddv, struct sdev_node *dv) 10482621Sllai1 { 10492621Sllai1 struct sdev_node *idv; 10502621Sllai1 struct sdev_node *prev = NULL; 10512621Sllai1 struct vnode *vp; 10522621Sllai1 10532621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 10542621Sllai1 10552621Sllai1 vp = SDEVTOV(dv); 10562621Sllai1 mutex_enter(&vp->v_lock); 10572621Sllai1 10582621Sllai1 /* dv is held still */ 10592621Sllai1 if (vp->v_count > 1) { 10602621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 10612621Sllai1 if (dv->sdev_state == SDEV_READY) { 10622621Sllai1 sdcmn_err9(( 10632621Sllai1 "sdev_delete: node %s busy with count %d\n", 10642621Sllai1 dv->sdev_name, vp->v_count)); 10652621Sllai1 dv->sdev_state = SDEV_ZOMBIE; 10662621Sllai1 } 10672621Sllai1 rw_exit(&dv->sdev_contents); 10682621Sllai1 --vp->v_count; 10692621Sllai1 mutex_exit(&vp->v_lock); 10702621Sllai1 return (EBUSY); 10712621Sllai1 } 10722621Sllai1 ASSERT(vp->v_count == 1); 10732621Sllai1 10742621Sllai1 /* unlink from the memory cache */ 10752621Sllai1 ddv->sdev_nlink--; /* .. to above */ 10762621Sllai1 if (vp->v_type == VDIR) { 10772621Sllai1 decr_link(dv); /* . to self */ 10782621Sllai1 } 10792621Sllai1 10802621Sllai1 for (idv = ddv->sdev_dot; idv && idv != dv; 10812621Sllai1 prev = idv, idv = idv->sdev_next) 10822621Sllai1 ; 10832621Sllai1 ASSERT(idv == dv); /* node to be deleted must exist */ 10842621Sllai1 if (prev == NULL) 10852621Sllai1 ddv->sdev_dot = dv->sdev_next; 10862621Sllai1 else 10872621Sllai1 prev->sdev_next = dv->sdev_next; 10882621Sllai1 dv->sdev_next = NULL; 10892621Sllai1 decr_link(dv); /* name, back to zero */ 10902621Sllai1 vp->v_count--; 10912621Sllai1 mutex_exit(&vp->v_lock); 10922621Sllai1 10932621Sllai1 /* destroy the node */ 10942621Sllai1 sdev_nodedestroy(dv, 0); 10952621Sllai1 return (0); 10962621Sllai1 } 10972621Sllai1 10982621Sllai1 /* 10992621Sllai1 * check if the source is in the path of the target 11002621Sllai1 * 11012621Sllai1 * source and target are different 11022621Sllai1 */ 11032621Sllai1 /*ARGSUSED2*/ 11042621Sllai1 static int 11052621Sllai1 sdev_checkpath(struct sdev_node *sdv, struct sdev_node *tdv, struct cred *cred) 11062621Sllai1 { 11072621Sllai1 int error = 0; 11082621Sllai1 struct sdev_node *dotdot, *dir; 11092621Sllai1 11102621Sllai1 dotdot = tdv->sdev_dotdot; 11112621Sllai1 ASSERT(dotdot); 11122621Sllai1 11132621Sllai1 /* fs root */ 11142621Sllai1 if (dotdot == tdv) { 11152621Sllai1 return (0); 11162621Sllai1 } 11172621Sllai1 11182621Sllai1 for (;;) { 11192621Sllai1 /* 11202621Sllai1 * avoid error cases like 11212621Sllai1 * mv a a/b 11222621Sllai1 * mv a a/b/c 11232621Sllai1 * etc. 11242621Sllai1 */ 11252621Sllai1 if (dotdot == sdv) { 11262621Sllai1 error = EINVAL; 11272621Sllai1 break; 11282621Sllai1 } 11292621Sllai1 11302621Sllai1 dir = dotdot; 11312621Sllai1 dotdot = dir->sdev_dotdot; 11322621Sllai1 11332621Sllai1 /* done checking because root is reached */ 11342621Sllai1 if (dir == dotdot) { 11352621Sllai1 break; 11362621Sllai1 } 11372621Sllai1 } 11382621Sllai1 return (error); 11392621Sllai1 } 11402621Sllai1 11412621Sllai1 int 11422621Sllai1 sdev_rnmnode(struct sdev_node *oddv, struct sdev_node *odv, 11432621Sllai1 struct sdev_node *nddv, struct sdev_node **ndvp, char *nnm, 11442621Sllai1 struct cred *cred) 11452621Sllai1 { 11462621Sllai1 int error = 0; 11472621Sllai1 struct vnode *ovp = SDEVTOV(odv); 11482621Sllai1 struct vnode *nvp; 11492621Sllai1 struct vattr vattr; 11502621Sllai1 int doingdir = (ovp->v_type == VDIR); 11512621Sllai1 char *link = NULL; 1152*2729Sllai1 int samedir = (oddv == nddv) ? 1 : 0; 1153*2729Sllai1 int bkstore = 0; 1154*2729Sllai1 int bypass = 0; 1155*2729Sllai1 struct sdev_node *idv = NULL; 1156*2729Sllai1 struct sdev_node *ndv = NULL; 1157*2729Sllai1 timestruc_t now; 1158*2729Sllai1 1159*2729Sllai1 vattr.va_mask = AT_MODE|AT_UID|AT_GID; 1160*2729Sllai1 error = VOP_GETATTR(ovp, &vattr, 0, cred); 1161*2729Sllai1 if (error) 1162*2729Sllai1 return (error); 1163*2729Sllai1 1164*2729Sllai1 if (!samedir) 1165*2729Sllai1 rw_enter(&oddv->sdev_contents, RW_WRITER); 1166*2729Sllai1 rw_enter(&nddv->sdev_contents, RW_WRITER); 1167*2729Sllai1 1168*2729Sllai1 /* 1169*2729Sllai1 * the source may have been deleted by another thread before 1170*2729Sllai1 * we gets here. 1171*2729Sllai1 */ 1172*2729Sllai1 if (odv->sdev_state != SDEV_READY) { 1173*2729Sllai1 error = ENOENT; 1174*2729Sllai1 goto err_out; 1175*2729Sllai1 } 1176*2729Sllai1 1177*2729Sllai1 if (doingdir && (odv == nddv)) { 1178*2729Sllai1 error = EINVAL; 1179*2729Sllai1 goto err_out; 1180*2729Sllai1 } 11812621Sllai1 11822621Sllai1 /* 11832621Sllai1 * If renaming a directory, and the parents are different (".." must be 11842621Sllai1 * changed) then the source dir must not be in the dir hierarchy above 11852621Sllai1 * the target since it would orphan everything below the source dir. 11862621Sllai1 */ 11872621Sllai1 if (doingdir && (oddv != nddv)) { 11882621Sllai1 error = sdev_checkpath(odv, nddv, cred); 11892621Sllai1 if (error) 1190*2729Sllai1 goto err_out; 11912621Sllai1 } 11922621Sllai1 1193*2729Sllai1 /* destination existing */ 11942621Sllai1 if (*ndvp) { 11952621Sllai1 nvp = SDEVTOV(*ndvp); 11962621Sllai1 ASSERT(nvp); 11972621Sllai1 11982621Sllai1 /* handling renaming to itself */ 1199*2729Sllai1 if (odv == *ndvp) { 1200*2729Sllai1 error = 0; 1201*2729Sllai1 goto err_out; 1202*2729Sllai1 } 1203*2729Sllai1 1204*2729Sllai1 if (nvp->v_type == VDIR) { 1205*2729Sllai1 if (!doingdir) { 1206*2729Sllai1 error = EISDIR; 1207*2729Sllai1 goto err_out; 1208*2729Sllai1 } 1209*2729Sllai1 1210*2729Sllai1 if (vn_vfswlock(nvp)) { 1211*2729Sllai1 error = EBUSY; 1212*2729Sllai1 goto err_out; 1213*2729Sllai1 } 1214*2729Sllai1 1215*2729Sllai1 if (vn_mountedvfs(nvp) != NULL) { 1216*2729Sllai1 vn_vfsunlock(nvp); 1217*2729Sllai1 error = EBUSY; 1218*2729Sllai1 goto err_out; 1219*2729Sllai1 } 1220*2729Sllai1 1221*2729Sllai1 /* in case dir1 exists in dir2 and "mv dir1 dir2" */ 1222*2729Sllai1 if ((*ndvp)->sdev_nlink > 2) { 1223*2729Sllai1 vn_vfsunlock(nvp); 1224*2729Sllai1 error = EEXIST; 1225*2729Sllai1 goto err_out; 1226*2729Sllai1 } 1227*2729Sllai1 vn_vfsunlock(nvp); 1228*2729Sllai1 1229*2729Sllai1 (void) sdev_dirdelete(nddv, *ndvp); 1230*2729Sllai1 *ndvp = NULL; 1231*2729Sllai1 error = VOP_RMDIR(nddv->sdev_attrvp, nnm, 1232*2729Sllai1 nddv->sdev_attrvp, cred); 1233*2729Sllai1 if (error) 1234*2729Sllai1 goto err_out; 1235*2729Sllai1 } else { 1236*2729Sllai1 if (doingdir) { 1237*2729Sllai1 error = ENOTDIR; 1238*2729Sllai1 goto err_out; 1239*2729Sllai1 } 1240*2729Sllai1 1241*2729Sllai1 if (SDEV_IS_PERSIST((*ndvp))) { 1242*2729Sllai1 bkstore = 1; 1243*2729Sllai1 } 12442621Sllai1 12452621Sllai1 /* 1246*2729Sllai1 * get rid of the node from the directory cache 1247*2729Sllai1 * note, in case EBUSY is returned, the ZOMBIE 1248*2729Sllai1 * node is taken care in sdev_mknode. 12492621Sllai1 */ 1250*2729Sllai1 (void) sdev_dirdelete(nddv, *ndvp); 1251*2729Sllai1 *ndvp = NULL; 1252*2729Sllai1 if (bkstore) { 1253*2729Sllai1 error = VOP_REMOVE(nddv->sdev_attrvp, 1254*2729Sllai1 nnm, cred); 1255*2729Sllai1 if (error) 1256*2729Sllai1 goto err_out; 12572621Sllai1 } 12582621Sllai1 } 12592621Sllai1 } 12602621Sllai1 12612621Sllai1 /* fix the source for a symlink */ 12622621Sllai1 if (vattr.va_type == VLNK) { 12632621Sllai1 if (odv->sdev_symlink == NULL) { 12642621Sllai1 error = sdev_follow_link(odv); 1265*2729Sllai1 if (error) { 1266*2729Sllai1 error = ENOENT; 1267*2729Sllai1 goto err_out; 1268*2729Sllai1 } 12692621Sllai1 } 12702621Sllai1 ASSERT(odv->sdev_symlink); 12712621Sllai1 link = i_ddi_strdup(odv->sdev_symlink, KM_SLEEP); 12722621Sllai1 } 12732621Sllai1 1274*2729Sllai1 /* 1275*2729Sllai1 * make a fresh node from the source attrs 1276*2729Sllai1 */ 1277*2729Sllai1 ASSERT(RW_WRITE_HELD(&nddv->sdev_contents)); 1278*2729Sllai1 error = sdev_mknode(nddv, nnm, ndvp, &vattr, 1279*2729Sllai1 NULL, (void *)link, cred, SDEV_READY); 12802621Sllai1 12812621Sllai1 if (link) 12822621Sllai1 kmem_free(link, strlen(link) + 1); 12832621Sllai1 1284*2729Sllai1 if (error) 1285*2729Sllai1 goto err_out; 1286*2729Sllai1 ASSERT(*ndvp); 1287*2729Sllai1 ASSERT((*ndvp)->sdev_state == SDEV_READY); 1288*2729Sllai1 1289*2729Sllai1 /* move dir contents */ 1290*2729Sllai1 if (doingdir) { 1291*2729Sllai1 for (idv = odv->sdev_dot; idv; idv = idv->sdev_next) { 1292*2729Sllai1 error = sdev_rnmnode(odv, idv, 1293*2729Sllai1 (struct sdev_node *)(*ndvp), &ndv, 1294*2729Sllai1 idv->sdev_name, cred); 1295*2729Sllai1 1296*2729Sllai1 if (error) 1297*2729Sllai1 goto err_out; 1298*2729Sllai1 ndv = NULL; 1299*2729Sllai1 } 1300*2729Sllai1 1301*2729Sllai1 } 1302*2729Sllai1 1303*2729Sllai1 if ((*ndvp)->sdev_attrvp) { 1304*2729Sllai1 sdev_update_timestamps((*ndvp)->sdev_attrvp, kcred, 1305*2729Sllai1 AT_CTIME|AT_ATIME); 1306*2729Sllai1 } else { 1307*2729Sllai1 ASSERT((*ndvp)->sdev_attr); 1308*2729Sllai1 gethrestime(&now); 1309*2729Sllai1 (*ndvp)->sdev_attr->va_ctime = now; 1310*2729Sllai1 (*ndvp)->sdev_attr->va_atime = now; 1311*2729Sllai1 } 1312*2729Sllai1 1313*2729Sllai1 if (nddv->sdev_attrvp) { 1314*2729Sllai1 sdev_update_timestamps(nddv->sdev_attrvp, kcred, 1315*2729Sllai1 AT_MTIME|AT_ATIME); 1316*2729Sllai1 } else { 1317*2729Sllai1 ASSERT(nddv->sdev_attr); 1318*2729Sllai1 gethrestime(&now); 1319*2729Sllai1 nddv->sdev_attr->va_mtime = now; 1320*2729Sllai1 nddv->sdev_attr->va_atime = now; 1321*2729Sllai1 } 1322*2729Sllai1 rw_exit(&nddv->sdev_contents); 1323*2729Sllai1 if (!samedir) 1324*2729Sllai1 rw_exit(&oddv->sdev_contents); 1325*2729Sllai1 13262621Sllai1 SDEV_RELE(*ndvp); 1327*2729Sllai1 return (error); 1328*2729Sllai1 1329*2729Sllai1 err_out: 1330*2729Sllai1 rw_exit(&nddv->sdev_contents); 1331*2729Sllai1 if (!samedir) 1332*2729Sllai1 rw_exit(&oddv->sdev_contents); 1333*2729Sllai1 return (error); 13342621Sllai1 } 13352621Sllai1 13362621Sllai1 /* 13372621Sllai1 * Merge sdev_node specific information into an attribute structure. 13382621Sllai1 * 13392621Sllai1 * note: sdev_node is not locked here 13402621Sllai1 */ 13412621Sllai1 void 13422621Sllai1 sdev_vattr_merge(struct sdev_node *dv, struct vattr *vap) 13432621Sllai1 { 13442621Sllai1 struct vnode *vp = SDEVTOV(dv); 13452621Sllai1 13462621Sllai1 vap->va_nlink = dv->sdev_nlink; 13472621Sllai1 vap->va_nodeid = dv->sdev_ino; 13482621Sllai1 vap->va_fsid = SDEVTOV(dv->sdev_dotdot)->v_rdev; 13492621Sllai1 vap->va_type = vp->v_type; 13502621Sllai1 13512621Sllai1 if (vp->v_type == VDIR) { 13522621Sllai1 vap->va_rdev = 0; 13532621Sllai1 vap->va_fsid = vp->v_rdev; 13542621Sllai1 } else if (vp->v_type == VLNK) { 13552621Sllai1 vap->va_rdev = 0; 13562621Sllai1 vap->va_mode &= ~S_IFMT; 13572621Sllai1 vap->va_mode |= S_IFLNK; 13582621Sllai1 } else if ((vp->v_type == VCHR) || (vp->v_type == VBLK)) { 13592621Sllai1 vap->va_rdev = vp->v_rdev; 13602621Sllai1 vap->va_mode &= ~S_IFMT; 13612621Sllai1 if (vap->va_type == VCHR) 13622621Sllai1 vap->va_mode |= S_IFCHR; 13632621Sllai1 else 13642621Sllai1 vap->va_mode |= S_IFBLK; 13652621Sllai1 } else { 13662621Sllai1 vap->va_rdev = 0; 13672621Sllai1 } 13682621Sllai1 } 13692621Sllai1 13702621Sllai1 static struct vattr * 13712621Sllai1 sdev_getdefault_attr(enum vtype type) 13722621Sllai1 { 13732621Sllai1 if (type == VDIR) 13742621Sllai1 return (&sdev_vattr_dir); 13752621Sllai1 else if (type == VCHR) 13762621Sllai1 return (&sdev_vattr_chr); 13772621Sllai1 else if (type == VBLK) 13782621Sllai1 return (&sdev_vattr_blk); 13792621Sllai1 else if (type == VLNK) 13802621Sllai1 return (&sdev_vattr_lnk); 13812621Sllai1 else 13822621Sllai1 return (NULL); 13832621Sllai1 } 13842621Sllai1 int 13852621Sllai1 sdev_to_vp(struct sdev_node *dv, struct vnode **vpp) 13862621Sllai1 { 13872621Sllai1 int rv = 0; 13882621Sllai1 struct vnode *vp = SDEVTOV(dv); 13892621Sllai1 13902621Sllai1 switch (vp->v_type) { 13912621Sllai1 case VCHR: 13922621Sllai1 case VBLK: 13932621Sllai1 /* 13942621Sllai1 * If vnode is a device, return special vnode instead 13952621Sllai1 * (though it knows all about -us- via sp->s_realvp) 13962621Sllai1 */ 13972621Sllai1 *vpp = specvp(vp, vp->v_rdev, vp->v_type, kcred); 13982621Sllai1 VN_RELE(vp); 13992621Sllai1 if (*vpp == NULLVP) 14002621Sllai1 rv = ENOSYS; 14012621Sllai1 break; 14022621Sllai1 default: /* most types are returned as is */ 14032621Sllai1 *vpp = vp; 14042621Sllai1 break; 14052621Sllai1 } 14062621Sllai1 return (rv); 14072621Sllai1 } 14082621Sllai1 14092621Sllai1 /* 14102621Sllai1 * loopback into sdev_lookup() 14112621Sllai1 */ 14122621Sllai1 static struct vnode * 14132621Sllai1 devname_find_by_devpath(char *devpath, struct vattr *vattr) 14142621Sllai1 { 14152621Sllai1 int error = 0; 14162621Sllai1 struct vnode *vp; 14172621Sllai1 14182621Sllai1 error = lookupname(devpath, UIO_SYSSPACE, NO_FOLLOW, NULLVPP, &vp); 14192621Sllai1 if (error) { 14202621Sllai1 return (NULL); 14212621Sllai1 } 14222621Sllai1 14232621Sllai1 if (vattr) 14242621Sllai1 (void) VOP_GETATTR(vp, vattr, 0, kcred); 14252621Sllai1 return (vp); 14262621Sllai1 } 14272621Sllai1 14282621Sllai1 /* 14292621Sllai1 * the junction between devname and devfs 14302621Sllai1 */ 14312621Sllai1 static struct vnode * 14322621Sllai1 devname_configure_by_path(char *physpath, struct vattr *vattr) 14332621Sllai1 { 14342621Sllai1 int error = 0; 14352621Sllai1 struct vnode *vp; 14362621Sllai1 1437*2729Sllai1 ASSERT(strncmp(physpath, "/devices/", sizeof ("/devices/") - 1) 14382621Sllai1 == 0); 14392621Sllai1 14402621Sllai1 error = devfs_lookupname(physpath + sizeof ("/devices/") - 1, 14412621Sllai1 NULLVPP, &vp); 14422621Sllai1 if (error != 0) { 14432621Sllai1 if (error == ENODEV) { 14442621Sllai1 cmn_err(CE_CONT, "%s: not found (line %d)\n", 14452621Sllai1 physpath, __LINE__); 14462621Sllai1 } 14472621Sllai1 14482621Sllai1 return (NULL); 14492621Sllai1 } 14502621Sllai1 14512621Sllai1 if (vattr) 14522621Sllai1 (void) VOP_GETATTR(vp, vattr, 0, kcred); 14532621Sllai1 return (vp); 14542621Sllai1 } 14552621Sllai1 14562621Sllai1 /* 14572621Sllai1 * junction between devname and root file system, e.g. ufs 14582621Sllai1 */ 14592621Sllai1 int 14602621Sllai1 devname_backstore_lookup(struct sdev_node *ddv, char *nm, struct vnode **rvp) 14612621Sllai1 { 14622621Sllai1 struct vnode *rdvp = ddv->sdev_attrvp; 14632621Sllai1 int rval = 0; 14642621Sllai1 14652621Sllai1 ASSERT(rdvp); 14662621Sllai1 14672621Sllai1 rval = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, kcred); 14682621Sllai1 return (rval); 14692621Sllai1 } 14702621Sllai1 14712621Sllai1 static int 14722621Sllai1 sdev_filldir_from_store(struct sdev_node *ddv, int dlen, struct cred *cred) 14732621Sllai1 { 14742621Sllai1 struct sdev_node *dv = NULL; 14752621Sllai1 char *nm; 14762621Sllai1 struct vnode *dirvp; 14772621Sllai1 int error; 14782621Sllai1 vnode_t *vp; 14792621Sllai1 int eof; 14802621Sllai1 struct iovec iov; 14812621Sllai1 struct uio uio; 14822621Sllai1 struct dirent64 *dp; 14832621Sllai1 dirent64_t *dbuf; 14842621Sllai1 size_t dbuflen; 14852621Sllai1 struct vattr vattr; 14862621Sllai1 char *link = NULL; 14872621Sllai1 14882621Sllai1 if (ddv->sdev_attrvp == NULL) 14892621Sllai1 return (0); 14902621Sllai1 if (!(ddv->sdev_flags & SDEV_BUILD)) 14912621Sllai1 return (0); 14922621Sllai1 14932621Sllai1 dirvp = ddv->sdev_attrvp; 14942621Sllai1 VN_HOLD(dirvp); 14952621Sllai1 dbuf = kmem_zalloc(dlen, KM_SLEEP); 14962621Sllai1 14972621Sllai1 uio.uio_iov = &iov; 14982621Sllai1 uio.uio_iovcnt = 1; 14992621Sllai1 uio.uio_segflg = UIO_SYSSPACE; 15002621Sllai1 uio.uio_fmode = 0; 15012621Sllai1 uio.uio_extflg = UIO_COPY_CACHED; 15022621Sllai1 uio.uio_loffset = 0; 15032621Sllai1 uio.uio_llimit = MAXOFFSET_T; 15042621Sllai1 15052621Sllai1 eof = 0; 15062621Sllai1 error = 0; 15072621Sllai1 while (!error && !eof) { 15082621Sllai1 uio.uio_resid = dlen; 15092621Sllai1 iov.iov_base = (char *)dbuf; 15102621Sllai1 iov.iov_len = dlen; 15112621Sllai1 (void) VOP_RWLOCK(dirvp, V_WRITELOCK_FALSE, NULL); 15122621Sllai1 error = VOP_READDIR(dirvp, &uio, kcred, &eof); 15132621Sllai1 VOP_RWUNLOCK(dirvp, V_WRITELOCK_FALSE, NULL); 15142621Sllai1 15152621Sllai1 dbuflen = dlen - uio.uio_resid; 15162621Sllai1 if (error || dbuflen == 0) 15172621Sllai1 break; 15182621Sllai1 15192621Sllai1 if (!(ddv->sdev_flags & SDEV_BUILD)) { 15202621Sllai1 error = 0; 15212621Sllai1 break; 15222621Sllai1 } 15232621Sllai1 15242621Sllai1 for (dp = dbuf; ((intptr_t)dp < 15252621Sllai1 (intptr_t)dbuf + dbuflen); 15262621Sllai1 dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) { 15272621Sllai1 nm = dp->d_name; 15282621Sllai1 15292621Sllai1 if (strcmp(nm, ".") == 0 || 15302621Sllai1 strcmp(nm, "..") == 0) 15312621Sllai1 continue; 15322621Sllai1 15332621Sllai1 vp = NULLVP; 15342621Sllai1 dv = sdev_cache_lookup(ddv, nm); 15352621Sllai1 if (dv) { 15362621Sllai1 if (dv->sdev_state != SDEV_ZOMBIE) { 15372621Sllai1 SDEV_SIMPLE_RELE(dv); 15382621Sllai1 } else { 15392621Sllai1 /* 15402621Sllai1 * A ZOMBIE node may not have been 15412621Sllai1 * cleaned up from the backing store, 15422621Sllai1 * bypass this entry in this case, 15432621Sllai1 * and clean it up from the directory 15442621Sllai1 * cache if this is the last call. 15452621Sllai1 */ 15462621Sllai1 (void) sdev_dirdelete(ddv, dv); 15472621Sllai1 } 15482621Sllai1 continue; 15492621Sllai1 } 15502621Sllai1 15512621Sllai1 /* refill the cache if not already */ 15522621Sllai1 error = devname_backstore_lookup(ddv, nm, &vp); 15532621Sllai1 if (error) 15542621Sllai1 continue; 15552621Sllai1 15562621Sllai1 vattr.va_mask = AT_MODE|AT_UID|AT_GID; 15572621Sllai1 error = VOP_GETATTR(vp, &vattr, 0, cred); 15582621Sllai1 if (error) 15592621Sllai1 continue; 15602621Sllai1 15612621Sllai1 if (vattr.va_type == VLNK) { 15622621Sllai1 error = sdev_getlink(vp, &link); 15632621Sllai1 if (error) { 15642621Sllai1 continue; 15652621Sllai1 } 15662621Sllai1 ASSERT(link != NULL); 15672621Sllai1 } 15682621Sllai1 15692621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 15702621Sllai1 rw_exit(&ddv->sdev_contents); 15712621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 15722621Sllai1 } 15732621Sllai1 error = sdev_mknode(ddv, nm, &dv, &vattr, vp, link, 15742621Sllai1 cred, SDEV_READY); 15752621Sllai1 rw_downgrade(&ddv->sdev_contents); 15762621Sllai1 15772621Sllai1 if (link != NULL) { 15782621Sllai1 kmem_free(link, strlen(link) + 1); 15792621Sllai1 link = NULL; 15802621Sllai1 } 15812621Sllai1 15822621Sllai1 if (!error) { 15832621Sllai1 ASSERT(dv); 15842621Sllai1 ASSERT(dv->sdev_state != SDEV_ZOMBIE); 15852621Sllai1 SDEV_SIMPLE_RELE(dv); 15862621Sllai1 } 15872621Sllai1 vp = NULL; 15882621Sllai1 dv = NULL; 15892621Sllai1 } 15902621Sllai1 } 15912621Sllai1 15922621Sllai1 done: 15932621Sllai1 VN_RELE(dirvp); 15942621Sllai1 kmem_free(dbuf, dlen); 15952621Sllai1 15962621Sllai1 return (error); 15972621Sllai1 } 15982621Sllai1 15992621Sllai1 static int 16002621Sllai1 sdev_filldir_dynamic(struct sdev_node *ddv) 16012621Sllai1 { 16022621Sllai1 int error; 16032621Sllai1 int i; 16042621Sllai1 struct vattr *vap; 16052621Sllai1 char *nm = NULL; 16062621Sllai1 struct sdev_node *dv = NULL; 16072621Sllai1 16082621Sllai1 if (!(ddv->sdev_flags & SDEV_BUILD)) { 16092621Sllai1 return (0); 16102621Sllai1 } 16112621Sllai1 16122621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 16132621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 16142621Sllai1 rw_exit(&ddv->sdev_contents); 16152621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 16162621Sllai1 } 16172621Sllai1 16182621Sllai1 vap = sdev_getdefault_attr(VDIR); 16192621Sllai1 for (i = 0; vtab[i].vt_name != NULL; i++) { 16202621Sllai1 nm = vtab[i].vt_name; 16212621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 16222621Sllai1 error = sdev_mknode(ddv, nm, &dv, vap, NULL, 16232621Sllai1 NULL, kcred, SDEV_READY); 16242621Sllai1 if (error) 16252621Sllai1 continue; 16262621Sllai1 ASSERT(dv); 16272621Sllai1 ASSERT(dv->sdev_state != SDEV_ZOMBIE); 16282621Sllai1 SDEV_SIMPLE_RELE(dv); 16292621Sllai1 dv = NULL; 16302621Sllai1 } 16312621Sllai1 rw_downgrade(&ddv->sdev_contents); 16322621Sllai1 return (0); 16332621Sllai1 } 16342621Sllai1 16352621Sllai1 /* 16362621Sllai1 * Creating a backing store entry based on sdev_attr. 16372621Sllai1 * This is called either as part of node creation in a persistent directory 16382621Sllai1 * or from setattr/setsecattr to persist access attributes across reboot. 16392621Sllai1 */ 16402621Sllai1 int 16412621Sllai1 sdev_shadow_node(struct sdev_node *dv, struct cred *cred) 16422621Sllai1 { 16432621Sllai1 int error = 0; 16442621Sllai1 struct vnode *dvp = SDEVTOV(dv->sdev_dotdot); 16452621Sllai1 struct vnode *rdvp = VTOSDEV(dvp)->sdev_attrvp; 16462621Sllai1 struct vattr *vap = dv->sdev_attr; 16472621Sllai1 char *nm = dv->sdev_name; 16482621Sllai1 struct vnode *tmpvp, **rvp = &tmpvp, *rrvp = NULL; 16492621Sllai1 16502621Sllai1 ASSERT(dv && dv->sdev_name && rdvp); 16512621Sllai1 ASSERT(RW_WRITE_HELD(&dv->sdev_contents) && dv->sdev_attrvp == NULL); 16522621Sllai1 16532621Sllai1 lookup: 16542621Sllai1 /* try to find it in the backing store */ 16552621Sllai1 error = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, cred); 16562621Sllai1 if (error == 0) { 16572621Sllai1 if (VOP_REALVP(*rvp, &rrvp) == 0) { 16582621Sllai1 VN_HOLD(rrvp); 16592621Sllai1 VN_RELE(*rvp); 16602621Sllai1 *rvp = rrvp; 16612621Sllai1 } 16622621Sllai1 16632621Sllai1 kmem_free(dv->sdev_attr, sizeof (vattr_t)); 16642621Sllai1 dv->sdev_attr = NULL; 16652621Sllai1 dv->sdev_attrvp = *rvp; 16662621Sllai1 return (0); 16672621Sllai1 } 16682621Sllai1 16692621Sllai1 /* let's try to persist the node */ 16702621Sllai1 gethrestime(&vap->va_atime); 16712621Sllai1 vap->va_mtime = vap->va_atime; 16722621Sllai1 vap->va_ctime = vap->va_atime; 16732621Sllai1 vap->va_mask |= AT_TYPE|AT_MODE; 16742621Sllai1 switch (vap->va_type) { 16752621Sllai1 case VDIR: 16762621Sllai1 error = VOP_MKDIR(rdvp, nm, vap, rvp, cred); 16772621Sllai1 sdcmn_err9(("sdev_shadow_node: mkdir vp %p error %d\n", 16782621Sllai1 (void *)(*rvp), error)); 16792621Sllai1 break; 16802621Sllai1 case VCHR: 16812621Sllai1 case VBLK: 16822621Sllai1 case VREG: 16832621Sllai1 case VDOOR: 16842621Sllai1 error = VOP_CREATE(rdvp, nm, vap, NONEXCL, VREAD|VWRITE, 16852621Sllai1 rvp, cred, 0); 16862621Sllai1 sdcmn_err9(("sdev_shadow_node: create vp %p, error %d\n", 16872621Sllai1 (void *)(*rvp), error)); 16882621Sllai1 if (!error) 16892621Sllai1 VN_RELE(*rvp); 16902621Sllai1 break; 16912621Sllai1 case VLNK: 16922621Sllai1 ASSERT(dv->sdev_symlink); 16932621Sllai1 error = VOP_SYMLINK(rdvp, nm, vap, dv->sdev_symlink, cred); 16942621Sllai1 sdcmn_err9(("sdev_shadow_node: create symlink error %d\n", 16952621Sllai1 error)); 16962621Sllai1 break; 16972621Sllai1 default: 16982621Sllai1 cmn_err(CE_PANIC, "dev: %s: sdev_shadow_node " 16992621Sllai1 "create\n", nm); 17002621Sllai1 /*NOTREACHED*/ 17012621Sllai1 } 17022621Sllai1 17032621Sllai1 /* go back to lookup to factor out spec node and set attrvp */ 17042621Sllai1 if (error == 0) 17052621Sllai1 goto lookup; 17062621Sllai1 17072621Sllai1 return (error); 17082621Sllai1 } 17092621Sllai1 17102621Sllai1 static int 17112621Sllai1 sdev_cache_add(struct sdev_node *ddv, struct sdev_node **dv, char *nm) 17122621Sllai1 { 17132621Sllai1 int error = 0; 17142621Sllai1 struct sdev_node *dup = NULL; 17152621Sllai1 17162621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 17172621Sllai1 if ((dup = sdev_findbyname(ddv, nm)) == NULL) { 17182621Sllai1 sdev_direnter(ddv, *dv); 17192621Sllai1 } else { 17202621Sllai1 if (dup->sdev_state == SDEV_ZOMBIE) { 17212621Sllai1 error = sdev_dirdelete(ddv, dup); 17222621Sllai1 /* 17232621Sllai1 * The ZOMBIE node is still hanging 17242621Sllai1 * around with more than one reference counts. 17252621Sllai1 * Fail the new node creation so that 17262621Sllai1 * the directory cache won't have 17272621Sllai1 * duplicate entries for the same named node 17282621Sllai1 */ 17292621Sllai1 if (error == EBUSY) { 17302621Sllai1 SDEV_SIMPLE_RELE(*dv); 17312621Sllai1 sdev_nodedestroy(*dv, 0); 17322621Sllai1 *dv = NULL; 17332621Sllai1 return (error); 17342621Sllai1 } 17352621Sllai1 sdev_direnter(ddv, *dv); 17362621Sllai1 } else { 17372621Sllai1 ASSERT((*dv)->sdev_state != SDEV_ZOMBIE); 17382621Sllai1 SDEV_SIMPLE_RELE(*dv); 17392621Sllai1 sdev_nodedestroy(*dv, 0); 17402621Sllai1 *dv = dup; 17412621Sllai1 } 17422621Sllai1 } 17432621Sllai1 17442621Sllai1 return (0); 17452621Sllai1 } 17462621Sllai1 17472621Sllai1 static int 17482621Sllai1 sdev_cache_delete(struct sdev_node *ddv, struct sdev_node **dv) 17492621Sllai1 { 17502621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 17512621Sllai1 return (sdev_dirdelete(ddv, *dv)); 17522621Sllai1 } 17532621Sllai1 17542621Sllai1 /* 17552621Sllai1 * update the in-core directory cache 17562621Sllai1 */ 17572621Sllai1 int 17582621Sllai1 sdev_cache_update(struct sdev_node *ddv, struct sdev_node **dv, char *nm, 17592621Sllai1 sdev_cache_ops_t ops) 17602621Sllai1 { 17612621Sllai1 int error = 0; 17622621Sllai1 17632621Sllai1 ASSERT((SDEV_HELD(*dv))); 17642621Sllai1 17652621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 17662621Sllai1 switch (ops) { 17672621Sllai1 case SDEV_CACHE_ADD: 17682621Sllai1 error = sdev_cache_add(ddv, dv, nm); 17692621Sllai1 break; 17702621Sllai1 case SDEV_CACHE_DELETE: 17712621Sllai1 error = sdev_cache_delete(ddv, dv); 17722621Sllai1 break; 17732621Sllai1 default: 17742621Sllai1 break; 17752621Sllai1 } 17762621Sllai1 17772621Sllai1 return (error); 17782621Sllai1 } 17792621Sllai1 17802621Sllai1 /* 17812621Sllai1 * retrive the named entry from the directory cache 17822621Sllai1 */ 17832621Sllai1 struct sdev_node * 17842621Sllai1 sdev_cache_lookup(struct sdev_node *ddv, char *nm) 17852621Sllai1 { 17862621Sllai1 struct sdev_node *dv = NULL; 17872621Sllai1 17882621Sllai1 ASSERT(RW_LOCK_HELD(&ddv->sdev_contents)); 17892621Sllai1 dv = sdev_findbyname(ddv, nm); 17902621Sllai1 17912621Sllai1 return (dv); 17922621Sllai1 } 17932621Sllai1 17942621Sllai1 /* 17952621Sllai1 * Implicit reconfig for nodes constructed by a link generator 17962621Sllai1 * Start devfsadm if needed, or if devfsadm is in progress, 17972621Sllai1 * prepare to block on devfsadm either completing or 17982621Sllai1 * constructing the desired node. As devfsadmd is global 17992621Sllai1 * in scope, constructing all necessary nodes, we only 18002621Sllai1 * need to initiate it once. 18012621Sllai1 */ 18022621Sllai1 static int 18032621Sllai1 sdev_call_devfsadmd(struct sdev_node *ddv, struct sdev_node *dv, char *nm) 18042621Sllai1 { 18052621Sllai1 int error = 0; 18062621Sllai1 18072621Sllai1 if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) { 18082621Sllai1 sdcmn_err6(("lookup: waiting for %s/%s, 0x%x\n", 18092621Sllai1 ddv->sdev_name, nm, devfsadm_state)); 18102621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 18112621Sllai1 SDEV_BLOCK_OTHERS(dv, (SDEV_LOOKUP | SDEV_LGWAITING)); 18122621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 18132621Sllai1 error = 0; 18142621Sllai1 } else if (!DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state)) { 18152621Sllai1 sdcmn_err6(("lookup %s/%s starting devfsadm, 0x%x\n", 18162621Sllai1 ddv->sdev_name, nm, devfsadm_state)); 18172621Sllai1 18182621Sllai1 sdev_devfsadmd_thread(ddv, dv, kcred); 18192621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 18202621Sllai1 SDEV_BLOCK_OTHERS(dv, 18212621Sllai1 (SDEV_LOOKUP | SDEV_LGWAITING)); 18222621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 18232621Sllai1 error = 0; 18242621Sllai1 } else { 18252621Sllai1 error = -1; 18262621Sllai1 } 18272621Sllai1 18282621Sllai1 return (error); 18292621Sllai1 } 18302621Sllai1 18312621Sllai1 static int 18322621Sllai1 sdev_call_modulelookup(struct sdev_node *ddv, struct sdev_node **dvp, char *nm, 18332621Sllai1 int (*fn)(char *, devname_handle_t *, struct cred *), struct cred *cred) 18342621Sllai1 { 18352621Sllai1 struct vnode *rvp = NULL; 18362621Sllai1 int error = 0; 18372621Sllai1 struct vattr *vap; 18382621Sllai1 devname_spec_t spec; 18392621Sllai1 devname_handle_t *hdl; 18402621Sllai1 void *args = NULL; 18412621Sllai1 struct sdev_node *dv = *dvp; 18422621Sllai1 18432621Sllai1 ASSERT(dv && ddv); 18442621Sllai1 hdl = &(dv->sdev_handle); 18452621Sllai1 ASSERT(hdl->dh_data == dv); 18462621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 18472621Sllai1 SDEV_BLOCK_OTHERS(dv, SDEV_LOOKUP); 18482621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 18492621Sllai1 error = (*fn)(nm, hdl, cred); 18502621Sllai1 if (error) { 18512621Sllai1 return (error); 18522621Sllai1 } 18532621Sllai1 18542621Sllai1 spec = hdl->dh_spec; 18552621Sllai1 args = hdl->dh_args; 18562621Sllai1 ASSERT(args); 18572621Sllai1 18582621Sllai1 switch (spec) { 18592621Sllai1 case DEVNAME_NS_PATH: 18602621Sllai1 /* 18612621Sllai1 * symlink of: 18622621Sllai1 * /dev/dir/nm -> /device/... 18632621Sllai1 */ 18642621Sllai1 rvp = devname_configure_by_path((char *)args, NULL); 18652621Sllai1 break; 18662621Sllai1 case DEVNAME_NS_DEV: 18672621Sllai1 /* 18682621Sllai1 * symlink of: 18692621Sllai1 * /dev/dir/nm -> /dev/... 18702621Sllai1 */ 18712621Sllai1 rvp = devname_find_by_devpath((char *)args, NULL); 18722621Sllai1 break; 18732621Sllai1 default: 18742621Sllai1 if (args) 18752621Sllai1 kmem_free((char *)args, strlen(args) + 1); 18762621Sllai1 return (ENOENT); 18772621Sllai1 18782621Sllai1 } 18792621Sllai1 18802621Sllai1 if (rvp == NULL) { 18812621Sllai1 if (args) 18822621Sllai1 kmem_free((char *)args, strlen(args) + 1); 18832621Sllai1 return (ENOENT); 18842621Sllai1 } else { 18852621Sllai1 vap = sdev_getdefault_attr(VLNK); 18862621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 18872621Sllai1 /* 18882621Sllai1 * Could sdev_mknode return a different dv_node 18892621Sllai1 * once the lock is dropped? 18902621Sllai1 */ 18912621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 18922621Sllai1 rw_exit(&ddv->sdev_contents); 18932621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 18942621Sllai1 } 18952621Sllai1 error = sdev_mknode(ddv, nm, &dv, vap, NULL, args, cred, 18962621Sllai1 SDEV_READY); 18972621Sllai1 rw_downgrade(&ddv->sdev_contents); 18982621Sllai1 if (error) { 18992621Sllai1 if (args) 19002621Sllai1 kmem_free((char *)args, strlen(args) + 1); 19012621Sllai1 return (error); 19022621Sllai1 } else { 19032621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 19042621Sllai1 SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP); 19052621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 19062621Sllai1 error = 0; 19072621Sllai1 } 19082621Sllai1 } 19092621Sllai1 19102621Sllai1 if (args) 19112621Sllai1 kmem_free((char *)args, strlen(args) + 1); 19122621Sllai1 19132621Sllai1 *dvp = dv; 19142621Sllai1 return (0); 19152621Sllai1 } 19162621Sllai1 19172621Sllai1 /* 19182621Sllai1 * Support for specialized device naming construction mechanisms 19192621Sllai1 */ 19202621Sllai1 static int 19212621Sllai1 sdev_call_dircallback(struct sdev_node *ddv, struct sdev_node **dvp, char *nm, 19222621Sllai1 int (*callback)(struct sdev_node *, char *, void **, struct cred *, 19232621Sllai1 void *, char *), int flags, struct cred *cred) 19242621Sllai1 { 19252621Sllai1 int rv = 0; 19262621Sllai1 char *physpath = NULL; 19272621Sllai1 struct vnode *rvp = NULL; 19282621Sllai1 struct vattr vattr; 19292621Sllai1 struct vattr *vap; 19302621Sllai1 struct sdev_node *dv = *dvp; 19312621Sllai1 19322621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 19332621Sllai1 SDEV_BLOCK_OTHERS(dv, SDEV_LOOKUP); 19342621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 19352621Sllai1 19362621Sllai1 /* for non-devfsadm devices */ 19372621Sllai1 if (flags & SDEV_PATH) { 19382621Sllai1 physpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 19392621Sllai1 rv = callback(ddv, nm, (void *)&physpath, kcred, NULL, 19402621Sllai1 NULL); 19412621Sllai1 if (rv) { 19422621Sllai1 kmem_free(physpath, MAXPATHLEN); 19432621Sllai1 return (-1); 19442621Sllai1 } 19452621Sllai1 19462621Sllai1 ASSERT(physpath); 19472621Sllai1 rvp = devname_configure_by_path(physpath, NULL); 19482621Sllai1 if (rvp == NULL) { 19492621Sllai1 sdcmn_err3(("devname_configure_by_path: " 19502621Sllai1 "failed for /dev/%s/%s\n", 19512621Sllai1 ddv->sdev_name, nm)); 19522621Sllai1 kmem_free(physpath, MAXPATHLEN); 19532621Sllai1 rv = -1; 19542621Sllai1 } else { 19552621Sllai1 vap = sdev_getdefault_attr(VLNK); 19562621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 19572621Sllai1 19582621Sllai1 /* 19592621Sllai1 * Sdev_mknode may return back a different sdev_node 19602621Sllai1 * that was created by another thread that 19612621Sllai1 * raced to the directroy cache before this thread. 19622621Sllai1 * 19632621Sllai1 * With current directory cache mechanism 19642621Sllai1 * (linked list with the sdev_node name as 19652621Sllai1 * the entity key), this is a way to make sure 19662621Sllai1 * only one entry exists for the same name 19672621Sllai1 * in the same directory. The outcome is 19682621Sllai1 * the winner wins. 19692621Sllai1 */ 19702621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 19712621Sllai1 rw_exit(&ddv->sdev_contents); 19722621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 19732621Sllai1 } 19742621Sllai1 rv = sdev_mknode(ddv, nm, &dv, vap, NULL, 19752621Sllai1 (void *)physpath, cred, SDEV_READY); 19762621Sllai1 rw_downgrade(&ddv->sdev_contents); 19772621Sllai1 kmem_free(physpath, MAXPATHLEN); 19782621Sllai1 if (rv) { 19792621Sllai1 return (rv); 19802621Sllai1 } else { 19812621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 19822621Sllai1 SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP); 19832621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 19842621Sllai1 return (0); 19852621Sllai1 } 19862621Sllai1 } 19872621Sllai1 } else if (flags & SDEV_VNODE) { 19882621Sllai1 /* 19892621Sllai1 * DBNR has its own way to create the device 19902621Sllai1 * and return a backing store vnode in rvp 19912621Sllai1 */ 19922621Sllai1 ASSERT(callback); 19932621Sllai1 rv = callback(ddv, nm, (void *)&rvp, kcred, NULL, NULL); 19942621Sllai1 if (rv || (rvp == NULL)) { 19952621Sllai1 sdcmn_err3(("devname_lookup_func: SDEV_VNODE " 19962621Sllai1 "callback failed \n")); 19972621Sllai1 return (-1); 19982621Sllai1 } 19992621Sllai1 vap = sdev_getdefault_attr(rvp->v_type); 20002621Sllai1 if (vap == NULL) 20012621Sllai1 return (-1); 20022621Sllai1 20032621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 20042621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 20052621Sllai1 rw_exit(&ddv->sdev_contents); 20062621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 20072621Sllai1 } 20082621Sllai1 rv = sdev_mknode(ddv, nm, &dv, vap, rvp, NULL, 20092621Sllai1 cred, SDEV_READY); 20102621Sllai1 rw_downgrade(&ddv->sdev_contents); 20112621Sllai1 if (rv) 20122621Sllai1 return (rv); 20132621Sllai1 20142621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 20152621Sllai1 SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP); 20162621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 20172621Sllai1 return (0); 20182621Sllai1 } else if (flags & SDEV_VATTR) { 20192621Sllai1 /* 20202621Sllai1 * /dev/pts 20212621Sllai1 * 20222621Sllai1 * DBNR has its own way to create the device 20232621Sllai1 * "0" is returned upon success. 20242621Sllai1 * 20252621Sllai1 * callback is responsible to set the basic attributes, 20262621Sllai1 * e.g. va_type/va_uid/va_gid/ 20272621Sllai1 * dev_t if VCHR or VBLK/ 20282621Sllai1 */ 20292621Sllai1 ASSERT(callback); 20302621Sllai1 rv = callback(ddv, nm, (void *)&vattr, kcred, NULL, NULL); 20312621Sllai1 if (rv) { 20322621Sllai1 sdcmn_err3(("devname_lookup_func: SDEV_NONE " 20332621Sllai1 "callback failed \n")); 20342621Sllai1 return (-1); 20352621Sllai1 } 20362621Sllai1 20372621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 20382621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 20392621Sllai1 rw_exit(&ddv->sdev_contents); 20402621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 20412621Sllai1 } 20422621Sllai1 rv = sdev_mknode(ddv, nm, &dv, &vattr, NULL, NULL, 20432621Sllai1 cred, SDEV_READY); 20442621Sllai1 rw_downgrade(&ddv->sdev_contents); 20452621Sllai1 20462621Sllai1 if (rv) 20472621Sllai1 return (rv); 20482621Sllai1 20492621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 20502621Sllai1 SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP); 20512621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 20522621Sllai1 return (0); 20532621Sllai1 } else { 20542621Sllai1 impossible(("lookup: %s/%s by %s not supported (%d)\n", 20552621Sllai1 SDEVTOV(ddv)->v_path, nm, curproc->p_user.u_comm, 20562621Sllai1 __LINE__)); 20572621Sllai1 rv = -1; 20582621Sllai1 } 20592621Sllai1 20602621Sllai1 *dvp = dv; 20612621Sllai1 return (rv); 20622621Sllai1 } 20632621Sllai1 20642621Sllai1 static int 20652621Sllai1 is_devfsadm_thread(char *exec_name) 20662621Sllai1 { 20672621Sllai1 /* 20682621Sllai1 * note: because devfsadmd -> /usr/sbin/devfsadm 20692621Sllai1 * it is safe to use "devfsadm" to capture the lookups 20702621Sllai1 * from devfsadm and its daemon version. 20712621Sllai1 */ 20722621Sllai1 if (strcmp(exec_name, "devfsadm") == 0) 20732621Sllai1 return (1); 20742621Sllai1 return (0); 20752621Sllai1 } 20762621Sllai1 20772621Sllai1 20782621Sllai1 /* 20792621Sllai1 * Lookup Order: 20802621Sllai1 * sdev_node cache; 20812621Sllai1 * backing store (SDEV_PERSIST); 20822621Sllai1 * DBNR: a. dir_ops implemented in the loadable modules; 20832621Sllai1 * b. vnode ops in vtab. 20842621Sllai1 */ 20852621Sllai1 int 20862621Sllai1 devname_lookup_func(struct sdev_node *ddv, char *nm, struct vnode **vpp, 20872621Sllai1 struct cred *cred, int (*callback)(struct sdev_node *, char *, void **, 20882621Sllai1 struct cred *, void *, char *), int flags) 20892621Sllai1 { 20902621Sllai1 int rv = 0, nmlen; 20912621Sllai1 struct vnode *rvp = NULL; 20922621Sllai1 struct sdev_node *dv = NULL; 20932621Sllai1 int retried = 0; 20942621Sllai1 int error = 0; 20952621Sllai1 struct devname_nsmap *map = NULL; 20962621Sllai1 struct devname_ops *dirops = NULL; 20972621Sllai1 int (*fn)(char *, devname_handle_t *, struct cred *) = NULL; 20982621Sllai1 struct vattr vattr; 20992621Sllai1 char *lookup_thread = curproc->p_user.u_comm; 21002621Sllai1 int failed_flags = 0; 21012621Sllai1 int (*vtor)(struct sdev_node *) = NULL; 21022621Sllai1 int state; 21032621Sllai1 int parent_state; 21042621Sllai1 char *link = NULL; 21052621Sllai1 21062621Sllai1 if (SDEVTOV(ddv)->v_type != VDIR) 21072621Sllai1 return (ENOTDIR); 21082621Sllai1 21092621Sllai1 /* 21102621Sllai1 * Empty name or ., return node itself. 21112621Sllai1 */ 21122621Sllai1 nmlen = strlen(nm); 21132621Sllai1 if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) { 21142621Sllai1 *vpp = SDEVTOV(ddv); 21152621Sllai1 VN_HOLD(*vpp); 21162621Sllai1 return (0); 21172621Sllai1 } 21182621Sllai1 21192621Sllai1 /* 21202621Sllai1 * .., return the parent directory 21212621Sllai1 */ 21222621Sllai1 if ((nmlen == 2) && (strcmp(nm, "..") == 0)) { 21232621Sllai1 *vpp = SDEVTOV(ddv->sdev_dotdot); 21242621Sllai1 VN_HOLD(*vpp); 21252621Sllai1 return (0); 21262621Sllai1 } 21272621Sllai1 21282621Sllai1 rw_enter(&ddv->sdev_contents, RW_READER); 21292621Sllai1 if (ddv->sdev_flags & SDEV_VTOR) { 21302621Sllai1 vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv); 21312621Sllai1 ASSERT(vtor); 21322621Sllai1 } 21332621Sllai1 21342621Sllai1 tryagain: 21352621Sllai1 /* 21362621Sllai1 * (a) directory cache lookup: 21372621Sllai1 */ 21382621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 21392621Sllai1 parent_state = ddv->sdev_state; 21402621Sllai1 dv = sdev_cache_lookup(ddv, nm); 21412621Sllai1 if (dv) { 21422621Sllai1 state = dv->sdev_state; 21432621Sllai1 switch (state) { 21442621Sllai1 case SDEV_INIT: 21452621Sllai1 if (is_devfsadm_thread(lookup_thread)) 21462621Sllai1 break; 21472621Sllai1 21482621Sllai1 /* ZOMBIED parent won't allow node creation */ 21492621Sllai1 if (parent_state == SDEV_ZOMBIE) { 21502621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, 21512621Sllai1 retried); 21522621Sllai1 goto nolock_notfound; 21532621Sllai1 } 21542621Sllai1 21552621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 21562621Sllai1 /* compensate the threads started after devfsadm */ 21572621Sllai1 if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) && 21582621Sllai1 !(SDEV_IS_LOOKUP(dv))) 21592621Sllai1 SDEV_BLOCK_OTHERS(dv, 21602621Sllai1 (SDEV_LOOKUP | SDEV_LGWAITING)); 21612621Sllai1 21622621Sllai1 if (SDEV_IS_LOOKUP(dv)) { 21632621Sllai1 failed_flags |= SLF_REBUILT; 21642621Sllai1 rw_exit(&ddv->sdev_contents); 21652621Sllai1 error = sdev_wait4lookup(dv, SDEV_LOOKUP); 21662621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 21672621Sllai1 rw_enter(&ddv->sdev_contents, RW_READER); 21682621Sllai1 21692621Sllai1 if (error != 0) { 21702621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, 21712621Sllai1 retried); 21722621Sllai1 goto nolock_notfound; 21732621Sllai1 } 21742621Sllai1 21752621Sllai1 state = dv->sdev_state; 21762621Sllai1 if (state == SDEV_INIT) { 21772621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, 21782621Sllai1 retried); 21792621Sllai1 goto nolock_notfound; 21802621Sllai1 } else if (state == SDEV_READY) { 21812621Sllai1 goto found; 21822621Sllai1 } else if (state == SDEV_ZOMBIE) { 21832621Sllai1 rw_exit(&ddv->sdev_contents); 21842621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, 21852621Sllai1 retried); 21862621Sllai1 SDEV_RELE(dv); 21872621Sllai1 goto lookup_failed; 21882621Sllai1 } 21892621Sllai1 } else { 21902621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 21912621Sllai1 } 21922621Sllai1 break; 21932621Sllai1 case SDEV_READY: 21942621Sllai1 goto found; 21952621Sllai1 case SDEV_ZOMBIE: 21962621Sllai1 rw_exit(&ddv->sdev_contents); 21972621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 21982621Sllai1 SDEV_RELE(dv); 21992621Sllai1 goto lookup_failed; 22002621Sllai1 default: 22012621Sllai1 rw_exit(&ddv->sdev_contents); 22022621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 22032621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags); 22042621Sllai1 *vpp = NULLVP; 22052621Sllai1 return (ENOENT); 22062621Sllai1 } 22072621Sllai1 } 22082621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 22092621Sllai1 22102621Sllai1 /* 22112621Sllai1 * ZOMBIED parent does not allow new node creation. 22122621Sllai1 * bail out early 22132621Sllai1 */ 22142621Sllai1 if (parent_state == SDEV_ZOMBIE) { 22152621Sllai1 rw_exit(&ddv->sdev_contents); 22162621Sllai1 *vpp = NULL; 22172621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 22182621Sllai1 return (ENOENT); 22192621Sllai1 } 22202621Sllai1 22212621Sllai1 /* 22222621Sllai1 * (b0): backing store lookup 22232621Sllai1 * SDEV_PERSIST is default except: 22242621Sllai1 * 1) pts nodes 22252621Sllai1 * 2) non-chmod'ed local nodes 22262621Sllai1 */ 22272621Sllai1 if (SDEV_IS_PERSIST(ddv)) { 22282621Sllai1 error = devname_backstore_lookup(ddv, nm, &rvp); 22292621Sllai1 22302621Sllai1 if (!error) { 22312621Sllai1 sdcmn_err3(("devname_backstore_lookup: " 22322621Sllai1 "found attrvp %p for %s\n", (void *)rvp, nm)); 22332621Sllai1 22342621Sllai1 vattr.va_mask = AT_MODE|AT_UID|AT_GID; 22352621Sllai1 error = VOP_GETATTR(rvp, &vattr, 0, cred); 22362621Sllai1 if (error) { 22372621Sllai1 rw_exit(&ddv->sdev_contents); 22382621Sllai1 if (dv) 22392621Sllai1 SDEV_RELE(dv); 22402621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 22412621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags); 22422621Sllai1 *vpp = NULLVP; 22432621Sllai1 return (ENOENT); 22442621Sllai1 } 22452621Sllai1 22462621Sllai1 if (vattr.va_type == VLNK) { 22472621Sllai1 error = sdev_getlink(rvp, &link); 22482621Sllai1 if (error) { 22492621Sllai1 rw_exit(&ddv->sdev_contents); 22502621Sllai1 if (dv) 22512621Sllai1 SDEV_RELE(dv); 22522621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, 22532621Sllai1 retried); 22542621Sllai1 sdev_lookup_failed(ddv, nm, 22552621Sllai1 failed_flags); 22562621Sllai1 *vpp = NULLVP; 22572621Sllai1 return (ENOENT); 22582621Sllai1 } 22592621Sllai1 ASSERT(link != NULL); 22602621Sllai1 } 22612621Sllai1 22622621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 22632621Sllai1 rw_exit(&ddv->sdev_contents); 22642621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 22652621Sllai1 } 22662621Sllai1 error = sdev_mknode(ddv, nm, &dv, &vattr, 22672621Sllai1 rvp, link, cred, SDEV_READY); 22682621Sllai1 rw_downgrade(&ddv->sdev_contents); 22692621Sllai1 22702621Sllai1 if (link != NULL) { 22712621Sllai1 kmem_free(link, strlen(link) + 1); 22722621Sllai1 link = NULL; 22732621Sllai1 } 22742621Sllai1 22752621Sllai1 if (error) { 22762621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 22772621Sllai1 rw_exit(&ddv->sdev_contents); 22782621Sllai1 if (dv) 22792621Sllai1 SDEV_RELE(dv); 22802621Sllai1 goto lookup_failed; 22812621Sllai1 } else { 22822621Sllai1 goto found; 22832621Sllai1 } 22842621Sllai1 } else if (retried) { 22852621Sllai1 rw_exit(&ddv->sdev_contents); 22862621Sllai1 sdcmn_err3(("retry of lookup of %s/%s: failed\n", 22872621Sllai1 ddv->sdev_name, nm)); 22882621Sllai1 if (dv) 22892621Sllai1 SDEV_RELE(dv); 22902621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 22912621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags); 22922621Sllai1 *vpp = NULLVP; 22932621Sllai1 return (ENOENT); 22942621Sllai1 } 22952621Sllai1 } 22962621Sllai1 22972621Sllai1 22982621Sllai1 /* first thread that is doing the lookup on this node */ 22992621Sllai1 if (!dv) { 23002621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 23012621Sllai1 rw_exit(&ddv->sdev_contents); 23022621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 23032621Sllai1 } 23042621Sllai1 error = sdev_mknode(ddv, nm, &dv, NULL, NULL, NULL, 23052621Sllai1 cred, SDEV_INIT); 23062621Sllai1 if (!dv) { 23072621Sllai1 rw_exit(&ddv->sdev_contents); 23082621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 23092621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags); 23102621Sllai1 *vpp = NULLVP; 23112621Sllai1 return (ENOENT); 23122621Sllai1 } 23132621Sllai1 rw_downgrade(&ddv->sdev_contents); 23142621Sllai1 } 23152621Sllai1 ASSERT(dv); 23162621Sllai1 ASSERT(SDEV_HELD(dv)); 23172621Sllai1 23182621Sllai1 if (SDEV_IS_NO_NCACHE(dv)) { 23192621Sllai1 failed_flags |= SLF_NO_NCACHE; 23202621Sllai1 } 23212621Sllai1 23222621Sllai1 if (SDEV_IS_GLOBAL(ddv)) { 23232621Sllai1 map = sdev_get_map(ddv, 1); 23242621Sllai1 dirops = map ? map->dir_ops : NULL; 23252621Sllai1 fn = dirops ? dirops->devnops_lookup : NULL; 23262621Sllai1 } 23272621Sllai1 23282621Sllai1 /* 23292621Sllai1 * (b1) invoking devfsadm once per life time for devfsadm nodes 23302621Sllai1 */ 23312621Sllai1 if ((fn == NULL) && !callback) { 23322621Sllai1 23332621Sllai1 if (sdev_reconfig_boot || !i_ddi_io_initialized() || 23342621Sllai1 SDEV_IS_DYNAMIC(ddv) || SDEV_IS_NO_NCACHE(dv) || 23352621Sllai1 ((moddebug & MODDEBUG_FINI_EBUSY) != 0)) { 23362621Sllai1 ASSERT(SDEV_HELD(dv)); 23372621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 23382621Sllai1 goto nolock_notfound; 23392621Sllai1 } 23402621Sllai1 23412621Sllai1 /* 23422621Sllai1 * filter out known non-existent devices recorded 23432621Sllai1 * during initial reconfiguration boot for which 23442621Sllai1 * reconfig should not be done and lookup may 23452621Sllai1 * be short-circuited now. 23462621Sllai1 */ 23472621Sllai1 if (sdev_lookup_filter(ddv, nm)) { 23482621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 23492621Sllai1 goto nolock_notfound; 23502621Sllai1 } 23512621Sllai1 23522621Sllai1 /* bypassing devfsadm internal nodes */ 23532621Sllai1 if (is_devfsadm_thread(lookup_thread)) { 23542621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 23552621Sllai1 goto nolock_notfound; 23562621Sllai1 } 23572621Sllai1 23582621Sllai1 if (sdev_reconfig_disable) { 23592621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 23602621Sllai1 goto nolock_notfound; 23612621Sllai1 } 23622621Sllai1 23632621Sllai1 error = sdev_call_devfsadmd(ddv, dv, nm); 23642621Sllai1 if (error == 0) { 23652621Sllai1 sdcmn_err8(("lookup of %s/%s by %s: reconfig\n", 23662621Sllai1 ddv->sdev_name, nm, curproc->p_user.u_comm)); 23672621Sllai1 if (sdev_reconfig_verbose) { 23682621Sllai1 cmn_err(CE_CONT, 23692621Sllai1 "?lookup of %s/%s by %s: reconfig\n", 23702621Sllai1 ddv->sdev_name, nm, curproc->p_user.u_comm); 23712621Sllai1 } 23722621Sllai1 retried = 1; 23732621Sllai1 failed_flags |= SLF_REBUILT; 23742621Sllai1 ASSERT(dv->sdev_state != SDEV_ZOMBIE); 23752621Sllai1 SDEV_SIMPLE_RELE(dv); 23762621Sllai1 goto tryagain; 23772621Sllai1 } else { 23782621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 23792621Sllai1 goto nolock_notfound; 23802621Sllai1 } 23812621Sllai1 } 23822621Sllai1 23832621Sllai1 /* 23842621Sllai1 * (b2) Directory Based Name Resolution (DBNR): 23852621Sllai1 * ddv - parent 23862621Sllai1 * nm - /dev/(ddv->sdev_name)/nm 23872621Sllai1 * 23882621Sllai1 * note: module vnode ops take precedence than the build-in ones 23892621Sllai1 */ 23902621Sllai1 if (fn) { 23912621Sllai1 error = sdev_call_modulelookup(ddv, &dv, nm, fn, cred); 23922621Sllai1 if (error) { 23932621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 23942621Sllai1 goto notfound; 23952621Sllai1 } else { 23962621Sllai1 goto found; 23972621Sllai1 } 23982621Sllai1 } else if (callback) { 23992621Sllai1 error = sdev_call_dircallback(ddv, &dv, nm, callback, 24002621Sllai1 flags, cred); 24012621Sllai1 if (error == 0) { 24022621Sllai1 goto found; 24032621Sllai1 } else { 24042621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 24052621Sllai1 goto notfound; 24062621Sllai1 } 24072621Sllai1 } 24082621Sllai1 ASSERT(rvp); 24092621Sllai1 24102621Sllai1 found: 24112621Sllai1 ASSERT(!(dv->sdev_flags & SDEV_STALE)); 24122621Sllai1 ASSERT(dv->sdev_state == SDEV_READY); 24132621Sllai1 if (vtor) { 24142621Sllai1 /* 24152621Sllai1 * Check validity of returned node 24162621Sllai1 */ 24172621Sllai1 switch (vtor(dv)) { 24182621Sllai1 case SDEV_VTOR_VALID: 24192621Sllai1 break; 24202621Sllai1 case SDEV_VTOR_INVALID: 24212621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 24222621Sllai1 sdcmn_err7(("lookup: destroy invalid " 24232621Sllai1 "node: %s(%p)\n", dv->sdev_name, (void *)dv)); 24242621Sllai1 goto nolock_notfound; 24252621Sllai1 case SDEV_VTOR_SKIP: 24262621Sllai1 sdcmn_err7(("lookup: node not applicable - " 24272621Sllai1 "skipping: %s(%p)\n", dv->sdev_name, (void *)dv)); 24282621Sllai1 rw_exit(&ddv->sdev_contents); 24292621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 24302621Sllai1 SDEV_RELE(dv); 24312621Sllai1 goto lookup_failed; 24322621Sllai1 default: 24332621Sllai1 cmn_err(CE_PANIC, 24342621Sllai1 "dev fs: validator failed: %s(%p)\n", 24352621Sllai1 dv->sdev_name, (void *)dv); 24362621Sllai1 break; 24372621Sllai1 /*NOTREACHED*/ 24382621Sllai1 } 24392621Sllai1 } 24402621Sllai1 24412621Sllai1 if ((SDEVTOV(dv)->v_type == VDIR) && SDEV_IS_GLOBAL(dv)) { 24422621Sllai1 rw_enter(&dv->sdev_contents, RW_READER); 24432621Sllai1 (void) sdev_get_map(dv, 1); 24442621Sllai1 rw_exit(&dv->sdev_contents); 24452621Sllai1 } 24462621Sllai1 rw_exit(&ddv->sdev_contents); 24472621Sllai1 rv = sdev_to_vp(dv, vpp); 24482621Sllai1 sdcmn_err3(("devname_lookup_func: returning vp %p v_count %d state %d " 24492621Sllai1 "for nm %s, error %d\n", (void *)*vpp, (*vpp)->v_count, 24502621Sllai1 dv->sdev_state, nm, rv)); 24512621Sllai1 return (rv); 24522621Sllai1 24532621Sllai1 notfound: 24542621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 24552621Sllai1 SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP); 24562621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 24572621Sllai1 nolock_notfound: 24582621Sllai1 /* 24592621Sllai1 * Destroy the node that is created for synchronization purposes. 24602621Sllai1 */ 24612621Sllai1 sdcmn_err3(("devname_lookup_func: %s with state %d\n", 24622621Sllai1 nm, dv->sdev_state)); 24632621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 24642621Sllai1 if (dv->sdev_state == SDEV_INIT) { 24652621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 24662621Sllai1 rw_exit(&ddv->sdev_contents); 24672621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 24682621Sllai1 } 24692621Sllai1 24702621Sllai1 /* 24712621Sllai1 * Node state may have changed during the lock 24722621Sllai1 * changes. Re-check. 24732621Sllai1 */ 24742621Sllai1 if (dv->sdev_state == SDEV_INIT) { 24752621Sllai1 (void) sdev_dirdelete(ddv, dv); 24762621Sllai1 rw_exit(&ddv->sdev_contents); 24772621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags); 24782621Sllai1 *vpp = NULL; 24792621Sllai1 return (ENOENT); 24802621Sllai1 } 24812621Sllai1 } 24822621Sllai1 24832621Sllai1 rw_exit(&ddv->sdev_contents); 24842621Sllai1 SDEV_RELE(dv); 24852621Sllai1 24862621Sllai1 lookup_failed: 24872621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags); 24882621Sllai1 *vpp = NULL; 24892621Sllai1 return (ENOENT); 24902621Sllai1 } 24912621Sllai1 24922621Sllai1 /* 24932621Sllai1 * Given a directory node, mark all nodes beneath as 24942621Sllai1 * STALE, i.e. nodes that don't exist as far as new 24952621Sllai1 * consumers are concerned 24962621Sllai1 */ 24972621Sllai1 void 24982621Sllai1 sdev_stale(struct sdev_node *ddv) 24992621Sllai1 { 25002621Sllai1 struct sdev_node *dv; 25012621Sllai1 struct vnode *vp; 25022621Sllai1 25032621Sllai1 ASSERT(SDEVTOV(ddv)->v_type == VDIR); 25042621Sllai1 25052621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 25062621Sllai1 for (dv = ddv->sdev_dot; dv; dv = dv->sdev_next) { 25072621Sllai1 vp = SDEVTOV(dv); 25082621Sllai1 if (vp->v_type == VDIR) 25092621Sllai1 sdev_stale(dv); 25102621Sllai1 25112621Sllai1 sdcmn_err9(("sdev_stale: setting stale %s\n", 25122621Sllai1 dv->sdev_name)); 25132621Sllai1 dv->sdev_flags |= SDEV_STALE; 25142621Sllai1 } 25152621Sllai1 ddv->sdev_flags |= SDEV_BUILD; 25162621Sllai1 rw_exit(&ddv->sdev_contents); 25172621Sllai1 } 25182621Sllai1 25192621Sllai1 /* 25202621Sllai1 * Given a directory node, clean out all the nodes beneath. 25212621Sllai1 * If expr is specified, clean node with names matching expr. 25222621Sllai1 * If SDEV_ENFORCE is specified in flags, busy nodes are made stale, 25232621Sllai1 * so they are excluded from future lookups. 25242621Sllai1 */ 25252621Sllai1 int 25262621Sllai1 sdev_cleandir(struct sdev_node *ddv, char *expr, uint_t flags) 25272621Sllai1 { 25282621Sllai1 int error = 0; 25292621Sllai1 int busy = 0; 25302621Sllai1 struct vnode *vp; 25312621Sllai1 struct sdev_node *dv, *next = NULL; 25322621Sllai1 int bkstore = 0; 25332621Sllai1 int len = 0; 25342621Sllai1 char *bks_name = NULL; 25352621Sllai1 25362621Sllai1 ASSERT(SDEVTOV(ddv)->v_type == VDIR); 25372621Sllai1 25382621Sllai1 /* 25392621Sllai1 * We try our best to destroy all unused sdev_node's 25402621Sllai1 */ 25412621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 25422621Sllai1 for (dv = ddv->sdev_dot; dv; dv = next) { 25432621Sllai1 next = dv->sdev_next; 25442621Sllai1 vp = SDEVTOV(dv); 25452621Sllai1 25462621Sllai1 if (expr && gmatch(dv->sdev_name, expr) == 0) 25472621Sllai1 continue; 25482621Sllai1 25492621Sllai1 if (vp->v_type == VDIR && 25502621Sllai1 sdev_cleandir(dv, NULL, flags) != 0) { 25512621Sllai1 sdcmn_err9(("sdev_cleandir: dir %s busy\n", 25522621Sllai1 dv->sdev_name)); 25532621Sllai1 busy++; 25542621Sllai1 continue; 25552621Sllai1 } 25562621Sllai1 25572621Sllai1 if (vp->v_count > 0 && (flags & SDEV_ENFORCE) == 0) { 25582621Sllai1 sdcmn_err9(("sdev_cleandir: dir %s busy\n", 25592621Sllai1 dv->sdev_name)); 25602621Sllai1 busy++; 25612621Sllai1 continue; 25622621Sllai1 } 25632621Sllai1 25642621Sllai1 /* 25652621Sllai1 * at this point, either dv is not held or SDEV_ENFORCE 25662621Sllai1 * is specified. In either case, dv needs to be deleted 25672621Sllai1 */ 25682621Sllai1 SDEV_HOLD(dv); 25692621Sllai1 25702621Sllai1 bkstore = SDEV_IS_PERSIST(dv) ? 1 : 0; 25712621Sllai1 if (bkstore && (vp->v_type == VDIR)) 25722621Sllai1 bkstore += 1; 25732621Sllai1 25742621Sllai1 if (bkstore) { 25752621Sllai1 len = strlen(dv->sdev_name) + 1; 25762621Sllai1 bks_name = kmem_alloc(len, KM_SLEEP); 25772621Sllai1 bcopy(dv->sdev_name, bks_name, len); 25782621Sllai1 } 25792621Sllai1 25802621Sllai1 error = sdev_dirdelete(ddv, dv); 25812621Sllai1 25822621Sllai1 if (error == EBUSY) { 25832621Sllai1 sdcmn_err9(("sdev_cleandir: dir busy\n")); 25842621Sllai1 busy++; 25852621Sllai1 } 25862621Sllai1 25872621Sllai1 /* take care the backing store clean up */ 25882621Sllai1 if (bkstore && (error == 0)) { 25892621Sllai1 ASSERT(bks_name); 25902621Sllai1 ASSERT(ddv->sdev_attrvp); 25912621Sllai1 25922621Sllai1 if (bkstore == 1) { 25932621Sllai1 error = VOP_REMOVE(ddv->sdev_attrvp, 25942621Sllai1 bks_name, kcred); 25952621Sllai1 } else if (bkstore == 2) { 25962621Sllai1 error = VOP_RMDIR(ddv->sdev_attrvp, 25972621Sllai1 bks_name, ddv->sdev_attrvp, kcred); 25982621Sllai1 } 25992621Sllai1 26002621Sllai1 /* do not propagate the backing store errors */ 26012621Sllai1 if (error) { 26022621Sllai1 sdcmn_err9(("sdev_cleandir: backing store" 26032621Sllai1 "not cleaned\n")); 26042621Sllai1 error = 0; 26052621Sllai1 } 26062621Sllai1 26072621Sllai1 bkstore = 0; 26082621Sllai1 kmem_free(bks_name, len); 26092621Sllai1 bks_name = NULL; 26102621Sllai1 len = 0; 26112621Sllai1 } 26122621Sllai1 } 26132621Sllai1 26142621Sllai1 ddv->sdev_flags |= SDEV_BUILD; 26152621Sllai1 rw_exit(&ddv->sdev_contents); 26162621Sllai1 26172621Sllai1 if (busy) { 26182621Sllai1 error = EBUSY; 26192621Sllai1 } 26202621Sllai1 26212621Sllai1 return (error); 26222621Sllai1 } 26232621Sllai1 26242621Sllai1 /* 26252621Sllai1 * a convenient wrapper for readdir() funcs 26262621Sllai1 */ 26272621Sllai1 size_t 26282621Sllai1 add_dir_entry(dirent64_t *de, char *nm, size_t size, ino_t ino, offset_t off) 26292621Sllai1 { 26302621Sllai1 size_t reclen = DIRENT64_RECLEN(strlen(nm)); 26312621Sllai1 if (reclen > size) 26322621Sllai1 return (0); 26332621Sllai1 26342621Sllai1 de->d_ino = (ino64_t)ino; 26352621Sllai1 de->d_off = (off64_t)off + 1; 26362621Sllai1 de->d_reclen = (ushort_t)reclen; 26372621Sllai1 (void) strncpy(de->d_name, nm, DIRENT64_NAMELEN(reclen)); 26382621Sllai1 return (reclen); 26392621Sllai1 } 26402621Sllai1 26412621Sllai1 /* 26422621Sllai1 * sdev_mount service routines 26432621Sllai1 */ 26442621Sllai1 int 26452621Sllai1 sdev_copyin_mountargs(struct mounta *uap, struct sdev_mountargs *args) 26462621Sllai1 { 26472621Sllai1 int error; 26482621Sllai1 26492621Sllai1 if (uap->datalen != sizeof (*args)) 26502621Sllai1 return (EINVAL); 26512621Sllai1 26522621Sllai1 if (error = copyin(uap->dataptr, args, sizeof (*args))) { 26532621Sllai1 cmn_err(CE_WARN, "sdev_copyin_mountargs: can not" 26542621Sllai1 "get user data. error %d\n", error); 26552621Sllai1 return (EFAULT); 26562621Sllai1 } 26572621Sllai1 26582621Sllai1 return (0); 26592621Sllai1 } 26602621Sllai1 26612621Sllai1 #ifdef nextdp 26622621Sllai1 #undef nextdp 26632621Sllai1 #endif 26642621Sllai1 #define nextdp(dp) ((struct dirent64 *)((char *)(dp) + (dp)->d_reclen)) 26652621Sllai1 26662621Sllai1 /* 26672621Sllai1 * readdir helper func 26682621Sllai1 */ 26692621Sllai1 int 26702621Sllai1 devname_readdir_func(vnode_t *vp, uio_t *uiop, cred_t *cred, int *eofp, 26712621Sllai1 int flags) 26722621Sllai1 { 26732621Sllai1 struct sdev_node *ddv = VTOSDEV(vp); 26742621Sllai1 struct sdev_node *dv; 26752621Sllai1 dirent64_t *dp; 26762621Sllai1 ulong_t outcount = 0; 26772621Sllai1 size_t namelen; 26782621Sllai1 ulong_t alloc_count; 26792621Sllai1 void *outbuf; 26802621Sllai1 struct iovec *iovp; 26812621Sllai1 int error = 0; 26822621Sllai1 size_t reclen; 26832621Sllai1 offset_t diroff; 26842621Sllai1 offset_t soff; 26852621Sllai1 int this_reclen; 26862621Sllai1 struct devname_nsmap *map = NULL; 26872621Sllai1 struct devname_ops *dirops = NULL; 26882621Sllai1 int (*fn)(devname_handle_t *, struct cred *) = NULL; 26892621Sllai1 int (*vtor)(struct sdev_node *) = NULL; 26902621Sllai1 struct vattr attr; 26912621Sllai1 timestruc_t now; 26922621Sllai1 26932621Sllai1 ASSERT(ddv->sdev_attr || ddv->sdev_attrvp); 26942621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 26952621Sllai1 26962621Sllai1 if (uiop->uio_loffset >= MAXOFF_T) { 26972621Sllai1 if (eofp) 26982621Sllai1 *eofp = 1; 26992621Sllai1 return (0); 27002621Sllai1 } 27012621Sllai1 27022621Sllai1 if (uiop->uio_iovcnt != 1) 27032621Sllai1 return (EINVAL); 27042621Sllai1 27052621Sllai1 if (vp->v_type != VDIR) 27062621Sllai1 return (ENOTDIR); 27072621Sllai1 27082621Sllai1 if (ddv->sdev_flags & SDEV_VTOR) { 27092621Sllai1 vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv); 27102621Sllai1 ASSERT(vtor); 27112621Sllai1 } 27122621Sllai1 27132621Sllai1 if (eofp != NULL) 27142621Sllai1 *eofp = 0; 27152621Sllai1 27162621Sllai1 soff = uiop->uio_offset; 27172621Sllai1 iovp = uiop->uio_iov; 27182621Sllai1 alloc_count = iovp->iov_len; 27192621Sllai1 dp = outbuf = kmem_alloc(alloc_count, KM_SLEEP); 27202621Sllai1 outcount = 0; 27212621Sllai1 27222621Sllai1 if (ddv->sdev_state == SDEV_ZOMBIE) 27232621Sllai1 goto get_cache; 27242621Sllai1 27252679Sszhou if (SDEV_IS_GLOBAL(ddv)) { 27262621Sllai1 map = sdev_get_map(ddv, 0); 27272621Sllai1 dirops = map ? map->dir_ops : NULL; 27282621Sllai1 fn = dirops ? dirops->devnops_readdir : NULL; 27292621Sllai1 27302621Sllai1 if (map && map->dir_map) { 27312621Sllai1 /* 27322621Sllai1 * load the name mapping rule database 27332621Sllai1 * through invoking devfsadm and symlink 27342621Sllai1 * all the entries in the map 27352621Sllai1 */ 27362621Sllai1 devname_rdr_result_t rdr_result; 27372621Sllai1 int do_thread = 0; 27382621Sllai1 27392621Sllai1 rw_enter(&map->dir_lock, RW_READER); 27402621Sllai1 do_thread = map->dir_maploaded ? 0 : 1; 27412621Sllai1 rw_exit(&map->dir_lock); 27422621Sllai1 27432621Sllai1 if (do_thread) { 27442621Sllai1 mutex_enter(&ddv->sdev_lookup_lock); 27452621Sllai1 SDEV_BLOCK_OTHERS(ddv, SDEV_READDIR); 27462621Sllai1 mutex_exit(&ddv->sdev_lookup_lock); 27472621Sllai1 27482621Sllai1 sdev_dispatch_to_nsrdr_thread(ddv, 27492621Sllai1 map->dir_map, &rdr_result); 27502621Sllai1 } 27512621Sllai1 } else if ((sdev_boot_state == SDEV_BOOT_STATE_COMPLETE) && 27522621Sllai1 !sdev_reconfig_boot && (flags & SDEV_BROWSE) && 27532621Sllai1 !SDEV_IS_DYNAMIC(ddv) && !SDEV_IS_NO_NCACHE(ddv) && 27542621Sllai1 ((moddebug & MODDEBUG_FINI_EBUSY) == 0) && 27552621Sllai1 !DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state) && 27562621Sllai1 !DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) && 27572621Sllai1 !sdev_reconfig_disable) { 27582621Sllai1 /* 27592621Sllai1 * invoking "devfsadm" to do system device reconfig 27602621Sllai1 */ 27612621Sllai1 mutex_enter(&ddv->sdev_lookup_lock); 27622621Sllai1 SDEV_BLOCK_OTHERS(ddv, 27632621Sllai1 (SDEV_READDIR|SDEV_LGWAITING)); 27642621Sllai1 mutex_exit(&ddv->sdev_lookup_lock); 27652621Sllai1 27662621Sllai1 sdcmn_err8(("readdir of %s by %s: reconfig\n", 27672621Sllai1 ddv->sdev_path, curproc->p_user.u_comm)); 27682621Sllai1 if (sdev_reconfig_verbose) { 27692621Sllai1 cmn_err(CE_CONT, 27702621Sllai1 "?readdir of %s by %s: reconfig\n", 27712621Sllai1 ddv->sdev_path, curproc->p_user.u_comm); 27722621Sllai1 } 27732621Sllai1 27742621Sllai1 sdev_devfsadmd_thread(ddv, NULL, kcred); 27752621Sllai1 } else if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) { 27762621Sllai1 /* 27772621Sllai1 * compensate the "ls" started later than "devfsadm" 27782621Sllai1 */ 27792621Sllai1 mutex_enter(&ddv->sdev_lookup_lock); 27802621Sllai1 SDEV_BLOCK_OTHERS(ddv, (SDEV_READDIR|SDEV_LGWAITING)); 27812621Sllai1 mutex_exit(&ddv->sdev_lookup_lock); 27822621Sllai1 } 27832621Sllai1 27842621Sllai1 /* 27852621Sllai1 * release the contents lock so that 27862621Sllai1 * the cache maybe updated by devfsadmd 27872621Sllai1 */ 27882621Sllai1 rw_exit(&ddv->sdev_contents); 27892621Sllai1 mutex_enter(&ddv->sdev_lookup_lock); 27902621Sllai1 if (SDEV_IS_READDIR(ddv)) 27912621Sllai1 (void) sdev_wait4lookup(ddv, SDEV_READDIR); 27922621Sllai1 mutex_exit(&ddv->sdev_lookup_lock); 27932621Sllai1 rw_enter(&ddv->sdev_contents, RW_READER); 27942621Sllai1 27952621Sllai1 sdcmn_err4(("readdir of directory %s by %s\n", 27962621Sllai1 ddv->sdev_name, curproc->p_user.u_comm)); 27972621Sllai1 while (ddv->sdev_flags & SDEV_BUILD) { 27982621Sllai1 if (SDEV_IS_PERSIST(ddv)) { 27992621Sllai1 error = sdev_filldir_from_store(ddv, 28002621Sllai1 alloc_count, cred); 28012621Sllai1 } 28022621Sllai1 28032621Sllai1 /* 28042621Sllai1 * pre-creating the directories 28052621Sllai1 * defined in vtab 28062621Sllai1 */ 28072621Sllai1 if (SDEVTOV(ddv)->v_flag & VROOT) { 28082621Sllai1 error = sdev_filldir_dynamic(ddv); 28092621Sllai1 } 28102621Sllai1 28112621Sllai1 if (!error) 28122621Sllai1 ddv->sdev_flags &= ~SDEV_BUILD; 28132621Sllai1 } 28142621Sllai1 } 28152621Sllai1 28162621Sllai1 get_cache: 28172621Sllai1 /* handle "." and ".." */ 28182621Sllai1 diroff = 0; 28192621Sllai1 if (soff == 0) { 28202621Sllai1 /* first time */ 28212621Sllai1 this_reclen = DIRENT64_RECLEN(1); 28222621Sllai1 if (alloc_count < this_reclen) { 28232621Sllai1 error = EINVAL; 28242621Sllai1 goto done; 28252621Sllai1 } 28262621Sllai1 28272621Sllai1 dp->d_ino = (ino64_t)ddv->sdev_ino; 28282621Sllai1 dp->d_off = (off64_t)1; 28292621Sllai1 dp->d_reclen = (ushort_t)this_reclen; 28302621Sllai1 28312621Sllai1 (void) strncpy(dp->d_name, ".", 28322621Sllai1 DIRENT64_NAMELEN(this_reclen)); 28332621Sllai1 outcount += dp->d_reclen; 28342621Sllai1 dp = nextdp(dp); 28352621Sllai1 } 28362621Sllai1 28372621Sllai1 diroff++; 28382621Sllai1 if (soff <= 1) { 28392621Sllai1 this_reclen = DIRENT64_RECLEN(2); 28402621Sllai1 if (alloc_count < outcount + this_reclen) { 28412621Sllai1 error = EINVAL; 28422621Sllai1 goto done; 28432621Sllai1 } 28442621Sllai1 28452621Sllai1 dp->d_reclen = (ushort_t)this_reclen; 28462621Sllai1 dp->d_ino = (ino64_t)ddv->sdev_dotdot->sdev_ino; 28472621Sllai1 dp->d_off = (off64_t)2; 28482621Sllai1 28492621Sllai1 (void) strncpy(dp->d_name, "..", 28502621Sllai1 DIRENT64_NAMELEN(this_reclen)); 28512621Sllai1 outcount += dp->d_reclen; 28522621Sllai1 28532621Sllai1 dp = nextdp(dp); 28542621Sllai1 } 28552621Sllai1 28562621Sllai1 28572621Sllai1 /* gets the cache */ 28582621Sllai1 diroff++; 28592621Sllai1 for (dv = ddv->sdev_dot; dv; dv = dv->sdev_next, diroff++) { 28602621Sllai1 sdcmn_err3(("sdev_readdir: diroff %lld soff %lld for '%s' \n", 28612621Sllai1 diroff, soff, dv->sdev_name)); 28622621Sllai1 28632621Sllai1 /* bypassing pre-matured nodes */ 28642621Sllai1 if (diroff < soff || (dv->sdev_state != SDEV_READY)) { 28652621Sllai1 sdcmn_err3(("sdev_readdir: pre-mature node " 28662621Sllai1 "%s\n", dv->sdev_name)); 28672621Sllai1 continue; 28682621Sllai1 } 28692621Sllai1 28702621Sllai1 /* don't list stale nodes */ 28712621Sllai1 if (dv->sdev_flags & SDEV_STALE) { 28722621Sllai1 sdcmn_err4(("sdev_readdir: STALE node " 28732621Sllai1 "%s\n", dv->sdev_name)); 28742621Sllai1 continue; 28752621Sllai1 } 28762621Sllai1 28772621Sllai1 /* 28782621Sllai1 * Check validity of node 28792621Sllai1 */ 28802621Sllai1 if (vtor) { 28812621Sllai1 switch (vtor(dv)) { 28822621Sllai1 case SDEV_VTOR_VALID: 28832621Sllai1 break; 28842621Sllai1 case SDEV_VTOR_INVALID: 28852621Sllai1 case SDEV_VTOR_SKIP: 28862621Sllai1 continue; 28872621Sllai1 default: 28882621Sllai1 cmn_err(CE_PANIC, 28892621Sllai1 "dev fs: validator failed: %s(%p)\n", 28902621Sllai1 dv->sdev_name, (void *)dv); 28912621Sllai1 break; 28922621Sllai1 /*NOTREACHED*/ 28932621Sllai1 } 28942621Sllai1 } 28952621Sllai1 28962621Sllai1 /* 28972621Sllai1 * call back into the module for the validity/bookkeeping 28982621Sllai1 * of this entry 28992621Sllai1 */ 29002621Sllai1 if (fn) { 29012621Sllai1 error = (*fn)(&(dv->sdev_handle), cred); 29022621Sllai1 if (error) { 29032621Sllai1 sdcmn_err4(("sdev_readdir: module did not " 29042621Sllai1 "validate %s\n", dv->sdev_name)); 29052621Sllai1 continue; 29062621Sllai1 } 29072621Sllai1 } 29082621Sllai1 29092621Sllai1 namelen = strlen(dv->sdev_name); 29102621Sllai1 reclen = DIRENT64_RECLEN(namelen); 29112621Sllai1 if (outcount + reclen > alloc_count) { 29122621Sllai1 goto full; 29132621Sllai1 } 29142621Sllai1 dp->d_reclen = (ushort_t)reclen; 29152621Sllai1 dp->d_ino = (ino64_t)dv->sdev_ino; 29162621Sllai1 dp->d_off = (off64_t)diroff + 1; 29172621Sllai1 (void) strncpy(dp->d_name, dv->sdev_name, 29182621Sllai1 DIRENT64_NAMELEN(reclen)); 29192621Sllai1 outcount += reclen; 29202621Sllai1 dp = nextdp(dp); 29212621Sllai1 } 29222621Sllai1 29232621Sllai1 full: 29242621Sllai1 sdcmn_err4(("sdev_readdir: moving %lu bytes: " 29252621Sllai1 "diroff %lld, soff %lld, dv %p\n", outcount, diroff, soff, 29262621Sllai1 (void *)dv)); 29272621Sllai1 29282621Sllai1 if (outcount) 29292621Sllai1 error = uiomove(outbuf, outcount, UIO_READ, uiop); 29302621Sllai1 29312621Sllai1 if (!error) { 29322621Sllai1 uiop->uio_offset = diroff; 29332621Sllai1 if (eofp) 29342621Sllai1 *eofp = dv ? 0 : 1; 29352621Sllai1 } 29362621Sllai1 29372621Sllai1 29382621Sllai1 if (ddv->sdev_attrvp) { 29392621Sllai1 gethrestime(&now); 29402621Sllai1 attr.va_ctime = now; 29412621Sllai1 attr.va_atime = now; 29422621Sllai1 attr.va_mask = AT_CTIME|AT_ATIME; 29432621Sllai1 29442621Sllai1 (void) VOP_SETATTR(ddv->sdev_attrvp, &attr, 0, kcred, NULL); 29452621Sllai1 } 29462621Sllai1 done: 29472621Sllai1 kmem_free(outbuf, alloc_count); 29482621Sllai1 return (error); 29492621Sllai1 } 29502621Sllai1 29512621Sllai1 29522621Sllai1 static int 29532621Sllai1 sdev_modctl_lookup(const char *path, vnode_t **r_vp) 29542621Sllai1 { 29552621Sllai1 vnode_t *vp; 29562621Sllai1 vnode_t *cvp; 29572621Sllai1 struct sdev_node *svp; 29582621Sllai1 char *nm; 29592621Sllai1 struct pathname pn; 29602621Sllai1 int error; 29612621Sllai1 int persisted = 0; 29622621Sllai1 29632621Sllai1 if (error = pn_get((char *)path, UIO_SYSSPACE, &pn)) 29642621Sllai1 return (error); 29652621Sllai1 nm = kmem_alloc(MAXNAMELEN, KM_SLEEP); 29662621Sllai1 29672621Sllai1 vp = rootdir; 29682621Sllai1 VN_HOLD(vp); 29692621Sllai1 29702621Sllai1 while (pn_pathleft(&pn)) { 29712621Sllai1 ASSERT(vp->v_type == VDIR); 29722621Sllai1 (void) pn_getcomponent(&pn, nm); 29732621Sllai1 error = VOP_LOOKUP(vp, nm, &cvp, NULL, 0, NULL, kcred); 29742621Sllai1 VN_RELE(vp); 29752621Sllai1 29762621Sllai1 if (error) 29772621Sllai1 break; 29782621Sllai1 29792621Sllai1 /* traverse mount points encountered on our journey */ 29802621Sllai1 if (vn_ismntpt(cvp) && (error = traverse(&cvp)) != 0) { 29812621Sllai1 VN_RELE(cvp); 29822621Sllai1 break; 29832621Sllai1 } 29842621Sllai1 29852621Sllai1 /* 29862621Sllai1 * Direct the operation to the persisting filesystem 29872621Sllai1 * underlying /dev. Bail if we encounter a 29882621Sllai1 * non-persistent dev entity here. 29892621Sllai1 */ 29902621Sllai1 if (cvp->v_vfsp->vfs_fstype == devtype) { 29912621Sllai1 29922621Sllai1 if ((VTOSDEV(cvp)->sdev_flags & SDEV_PERSIST) == 0) { 29932621Sllai1 error = ENOENT; 29942621Sllai1 VN_RELE(cvp); 29952621Sllai1 break; 29962621Sllai1 } 29972621Sllai1 29982621Sllai1 if (VTOSDEV(cvp) == NULL) { 29992621Sllai1 error = ENOENT; 30002621Sllai1 VN_RELE(cvp); 30012621Sllai1 break; 30022621Sllai1 } 30032621Sllai1 svp = VTOSDEV(cvp); 30042621Sllai1 if ((vp = svp->sdev_attrvp) == NULL) { 30052621Sllai1 error = ENOENT; 30062621Sllai1 VN_RELE(cvp); 30072621Sllai1 break; 30082621Sllai1 } 30092621Sllai1 persisted = 1; 30102621Sllai1 VN_HOLD(vp); 30112621Sllai1 VN_RELE(cvp); 30122621Sllai1 cvp = vp; 30132621Sllai1 } 30142621Sllai1 30152621Sllai1 vp = cvp; 30162621Sllai1 pn_skipslash(&pn); 30172621Sllai1 } 30182621Sllai1 30192621Sllai1 kmem_free(nm, MAXNAMELEN); 30202621Sllai1 pn_free(&pn); 30212621Sllai1 30222621Sllai1 if (error) 30232621Sllai1 return (error); 30242621Sllai1 30252621Sllai1 /* 30262621Sllai1 * Only return persisted nodes in the filesystem underlying /dev. 30272621Sllai1 */ 30282621Sllai1 if (!persisted) { 30292621Sllai1 VN_RELE(vp); 30302621Sllai1 return (ENOENT); 30312621Sllai1 } 30322621Sllai1 30332621Sllai1 *r_vp = vp; 30342621Sllai1 return (0); 30352621Sllai1 } 30362621Sllai1 30372621Sllai1 int 30382621Sllai1 sdev_modctl_readdir(const char *dir, char ***dirlistp, 30392621Sllai1 int *npathsp, int *npathsp_alloc) 30402621Sllai1 { 30412621Sllai1 char **pathlist = NULL; 30422621Sllai1 char **newlist = NULL; 30432621Sllai1 int npaths = 0; 30442621Sllai1 int npaths_alloc = 0; 30452621Sllai1 dirent64_t *dbuf = NULL; 30462621Sllai1 int n; 30472621Sllai1 char *s; 30482621Sllai1 int error; 30492621Sllai1 vnode_t *vp; 30502621Sllai1 int eof; 30512621Sllai1 struct iovec iov; 30522621Sllai1 struct uio uio; 30532621Sllai1 struct dirent64 *dp; 30542621Sllai1 size_t dlen; 30552621Sllai1 size_t dbuflen; 30562621Sllai1 int ndirents = 64; 30572621Sllai1 char *nm; 30582621Sllai1 30592621Sllai1 error = sdev_modctl_lookup(dir, &vp); 30602621Sllai1 sdcmn_err11(("modctl readdir: %s by %s: %s\n", 30612621Sllai1 dir, curproc->p_user.u_comm, 30622621Sllai1 (error == 0) ? "ok" : "failed")); 30632621Sllai1 if (error) 30642621Sllai1 return (error); 30652621Sllai1 30662621Sllai1 dlen = ndirents * (sizeof (*dbuf)); 30672621Sllai1 dbuf = kmem_alloc(dlen, KM_SLEEP); 30682621Sllai1 30692621Sllai1 uio.uio_iov = &iov; 30702621Sllai1 uio.uio_iovcnt = 1; 30712621Sllai1 uio.uio_segflg = UIO_SYSSPACE; 30722621Sllai1 uio.uio_fmode = 0; 30732621Sllai1 uio.uio_extflg = UIO_COPY_CACHED; 30742621Sllai1 uio.uio_loffset = 0; 30752621Sllai1 uio.uio_llimit = MAXOFFSET_T; 30762621Sllai1 30772621Sllai1 eof = 0; 30782621Sllai1 error = 0; 30792621Sllai1 while (!error && !eof) { 30802621Sllai1 uio.uio_resid = dlen; 30812621Sllai1 iov.iov_base = (char *)dbuf; 30822621Sllai1 iov.iov_len = dlen; 30832621Sllai1 30842621Sllai1 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); 30852621Sllai1 error = VOP_READDIR(vp, &uio, kcred, &eof); 30862621Sllai1 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); 30872621Sllai1 30882621Sllai1 dbuflen = dlen - uio.uio_resid; 30892621Sllai1 30902621Sllai1 if (error || dbuflen == 0) 30912621Sllai1 break; 30922621Sllai1 30932621Sllai1 for (dp = dbuf; ((intptr_t)dp < (intptr_t)dbuf + dbuflen); 30942621Sllai1 dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) { 30952621Sllai1 30962621Sllai1 nm = dp->d_name; 30972621Sllai1 30982621Sllai1 if (strcmp(nm, ".") == 0 || strcmp(nm, "..") == 0) 30992621Sllai1 continue; 31002621Sllai1 31012621Sllai1 if (npaths == npaths_alloc) { 31022621Sllai1 npaths_alloc += 64; 31032621Sllai1 newlist = (char **) 31042621Sllai1 kmem_zalloc((npaths_alloc + 1) * 31052621Sllai1 sizeof (char *), KM_SLEEP); 31062621Sllai1 if (pathlist) { 31072621Sllai1 bcopy(pathlist, newlist, 31082621Sllai1 npaths * sizeof (char *)); 31092621Sllai1 kmem_free(pathlist, 31102621Sllai1 (npaths + 1) * sizeof (char *)); 31112621Sllai1 } 31122621Sllai1 pathlist = newlist; 31132621Sllai1 } 31142621Sllai1 n = strlen(nm) + 1; 31152621Sllai1 s = kmem_alloc(n, KM_SLEEP); 31162621Sllai1 bcopy(nm, s, n); 31172621Sllai1 pathlist[npaths++] = s; 31182621Sllai1 sdcmn_err11((" %s/%s\n", dir, s)); 31192621Sllai1 } 31202621Sllai1 } 31212621Sllai1 31222621Sllai1 exit: 31232621Sllai1 VN_RELE(vp); 31242621Sllai1 31252621Sllai1 if (dbuf) 31262621Sllai1 kmem_free(dbuf, dlen); 31272621Sllai1 31282621Sllai1 if (error) 31292621Sllai1 return (error); 31302621Sllai1 31312621Sllai1 *dirlistp = pathlist; 31322621Sllai1 *npathsp = npaths; 31332621Sllai1 *npathsp_alloc = npaths_alloc; 31342621Sllai1 31352621Sllai1 return (0); 31362621Sllai1 } 31372621Sllai1 31382621Sllai1 void 31392621Sllai1 sdev_modctl_readdir_free(char **pathlist, int npaths, int npaths_alloc) 31402621Sllai1 { 31412621Sllai1 int i, n; 31422621Sllai1 31432621Sllai1 for (i = 0; i < npaths; i++) { 31442621Sllai1 n = strlen(pathlist[i]) + 1; 31452621Sllai1 kmem_free(pathlist[i], n); 31462621Sllai1 } 31472621Sllai1 31482621Sllai1 kmem_free(pathlist, (npaths_alloc + 1) * sizeof (char *)); 31492621Sllai1 } 31502621Sllai1 31512621Sllai1 int 31522621Sllai1 sdev_modctl_devexists(const char *path) 31532621Sllai1 { 31542621Sllai1 vnode_t *vp; 31552621Sllai1 int error; 31562621Sllai1 31572621Sllai1 error = sdev_modctl_lookup(path, &vp); 31582621Sllai1 sdcmn_err11(("modctl dev exists: %s by %s: %s\n", 31592621Sllai1 path, curproc->p_user.u_comm, 31602621Sllai1 (error == 0) ? "ok" : "failed")); 31612621Sllai1 if (error == 0) 31622621Sllai1 VN_RELE(vp); 31632621Sllai1 31642621Sllai1 return (error); 31652621Sllai1 } 31662621Sllai1 31672621Sllai1 void 31682621Sllai1 sdev_update_newnsmap(struct devname_nsmap *map, char *module, char *mapname) 31692621Sllai1 { 31702621Sllai1 rw_enter(&map->dir_lock, RW_WRITER); 31712621Sllai1 if (module) { 31722621Sllai1 ASSERT(map->dir_newmodule == NULL); 31732621Sllai1 map->dir_newmodule = i_ddi_strdup(module, KM_SLEEP); 31742621Sllai1 } 31752621Sllai1 if (mapname) { 31762621Sllai1 ASSERT(map->dir_newmap == NULL); 31772621Sllai1 map->dir_newmap = i_ddi_strdup(mapname, KM_SLEEP); 31782621Sllai1 } 31792621Sllai1 31802621Sllai1 map->dir_invalid = 1; 31812621Sllai1 rw_exit(&map->dir_lock); 31822621Sllai1 } 31832621Sllai1 31842621Sllai1 void 31852621Sllai1 sdev_replace_nsmap(struct devname_nsmap *map, char *module, char *mapname) 31862621Sllai1 { 31872621Sllai1 char *old_module = NULL; 31882621Sllai1 char *old_map = NULL; 31892621Sllai1 31902621Sllai1 ASSERT(RW_LOCK_HELD(&map->dir_lock)); 31912621Sllai1 if (!rw_tryupgrade(&map->dir_lock)) { 31922621Sllai1 rw_exit(&map->dir_lock); 31932621Sllai1 rw_enter(&map->dir_lock, RW_WRITER); 31942621Sllai1 } 31952621Sllai1 31962621Sllai1 old_module = map->dir_module; 31972621Sllai1 if (module) { 31982621Sllai1 if (old_module && strcmp(old_module, module) != 0) { 31992621Sllai1 kmem_free(old_module, strlen(old_module) + 1); 32002621Sllai1 } 32012621Sllai1 map->dir_module = module; 32022621Sllai1 map->dir_newmodule = NULL; 32032621Sllai1 } 32042621Sllai1 32052621Sllai1 old_map = map->dir_map; 32062621Sllai1 if (mapname) { 32072621Sllai1 if (old_map && strcmp(old_map, mapname) != 0) { 32082621Sllai1 kmem_free(old_map, strlen(old_map) + 1); 32092621Sllai1 } 32102621Sllai1 32112621Sllai1 map->dir_map = mapname; 32122621Sllai1 map->dir_newmap = NULL; 32132621Sllai1 } 32142621Sllai1 map->dir_maploaded = 0; 32152621Sllai1 map->dir_invalid = 0; 32162621Sllai1 rw_downgrade(&map->dir_lock); 32172621Sllai1 } 32182621Sllai1 32192621Sllai1 /* 32202621Sllai1 * dir_name should have at least one attribute, 32212621Sllai1 * dir_module 32222621Sllai1 * or dir_map 32232621Sllai1 * or both 32242621Sllai1 * caller holds the devname_nsmaps_lock 32252621Sllai1 */ 32262621Sllai1 void 32272621Sllai1 sdev_insert_nsmap(char *dir_name, char *dir_module, char *dir_map) 32282621Sllai1 { 32292621Sllai1 struct devname_nsmap *map; 32302621Sllai1 int len = 0; 32312621Sllai1 32322621Sllai1 ASSERT(dir_name); 32332621Sllai1 ASSERT(dir_module || dir_map); 32342621Sllai1 ASSERT(MUTEX_HELD(&devname_nsmaps_lock)); 32352621Sllai1 32362621Sllai1 if (map = sdev_get_nsmap_by_dir(dir_name, 1)) { 32372621Sllai1 sdev_update_newnsmap(map, dir_module, dir_map); 32382621Sllai1 return; 32392621Sllai1 } 32402621Sllai1 32412621Sllai1 map = (struct devname_nsmap *)kmem_zalloc(sizeof (*map), KM_SLEEP); 32422621Sllai1 map->dir_name = i_ddi_strdup(dir_name, KM_SLEEP); 32432621Sllai1 if (dir_module) { 32442621Sllai1 map->dir_module = i_ddi_strdup(dir_module, KM_SLEEP); 32452621Sllai1 } 32462621Sllai1 32472621Sllai1 if (dir_map) { 32482621Sllai1 if (dir_map[0] != '/') { 32492621Sllai1 len = strlen(ETC_DEV_DIR) + strlen(dir_map) + 2; 32502621Sllai1 map->dir_map = kmem_zalloc(len, KM_SLEEP); 32512621Sllai1 (void) snprintf(map->dir_map, len, "%s/%s", ETC_DEV_DIR, 32522621Sllai1 dir_map); 32532621Sllai1 } else { 32542621Sllai1 map->dir_map = i_ddi_strdup(dir_map, KM_SLEEP); 32552621Sllai1 } 32562621Sllai1 } 32572621Sllai1 32582621Sllai1 map->dir_ops = NULL; 32592621Sllai1 map->dir_maploaded = 0; 32602621Sllai1 map->dir_invalid = 0; 32612621Sllai1 rw_init(&map->dir_lock, NULL, RW_DEFAULT, NULL); 32622621Sllai1 32632621Sllai1 map->next = devname_nsmaps; 32642621Sllai1 map->prev = NULL; 32652621Sllai1 if (devname_nsmaps) { 32662621Sllai1 devname_nsmaps->prev = map; 32672621Sllai1 } 32682621Sllai1 devname_nsmaps = map; 32692621Sllai1 } 32702621Sllai1 32712621Sllai1 struct devname_nsmap * 32722621Sllai1 sdev_get_nsmap_by_dir(char *dir_path, int locked) 32732621Sllai1 { 32742621Sllai1 struct devname_nsmap *map = NULL; 32752621Sllai1 32762621Sllai1 if (!locked) 32772621Sllai1 mutex_enter(&devname_nsmaps_lock); 32782621Sllai1 for (map = devname_nsmaps; map; map = map->next) { 32792621Sllai1 sdcmn_err6(("sdev_get_nsmap_by_dir: dir %s\n", map->dir_name)); 32802621Sllai1 if (strcmp(map->dir_name, dir_path) == 0) { 32812621Sllai1 if (!locked) 32822621Sllai1 mutex_exit(&devname_nsmaps_lock); 32832621Sllai1 return (map); 32842621Sllai1 } 32852621Sllai1 } 32862621Sllai1 if (!locked) 32872621Sllai1 mutex_exit(&devname_nsmaps_lock); 32882621Sllai1 return (NULL); 32892621Sllai1 } 32902621Sllai1 32912621Sllai1 struct devname_nsmap * 32922621Sllai1 sdev_get_nsmap_by_module(char *mod_name) 32932621Sllai1 { 32942621Sllai1 struct devname_nsmap *map = NULL; 32952621Sllai1 32962621Sllai1 mutex_enter(&devname_nsmaps_lock); 32972621Sllai1 for (map = devname_nsmaps; map; map = map->next) { 32982621Sllai1 sdcmn_err7(("sdev_get_nsmap_by_module: module %s\n", 32992621Sllai1 map->dir_module)); 33002621Sllai1 if (map->dir_module && strcmp(map->dir_module, mod_name) == 0) { 33012621Sllai1 mutex_exit(&devname_nsmaps_lock); 33022621Sllai1 return (map); 33032621Sllai1 } 33042621Sllai1 } 33052621Sllai1 mutex_exit(&devname_nsmaps_lock); 33062621Sllai1 return (NULL); 33072621Sllai1 } 33082621Sllai1 33092621Sllai1 void 33102621Sllai1 sdev_invalidate_nsmaps() 33112621Sllai1 { 33122621Sllai1 struct devname_nsmap *map = NULL; 33132621Sllai1 33142621Sllai1 ASSERT(MUTEX_HELD(&devname_nsmaps_lock)); 33152621Sllai1 33162621Sllai1 if (devname_nsmaps == NULL) 33172621Sllai1 return; 33182621Sllai1 33192621Sllai1 for (map = devname_nsmaps; map; map = map->next) { 33202621Sllai1 rw_enter(&map->dir_lock, RW_WRITER); 33212621Sllai1 map->dir_invalid = 1; 33222621Sllai1 rw_exit(&map->dir_lock); 33232621Sllai1 } 33242621Sllai1 devname_nsmaps_invalidated = 1; 33252621Sllai1 } 33262621Sllai1 33272621Sllai1 33282621Sllai1 int 33292621Sllai1 sdev_nsmaps_loaded() 33302621Sllai1 { 33312621Sllai1 int ret = 0; 33322621Sllai1 33332621Sllai1 mutex_enter(&devname_nsmaps_lock); 33342621Sllai1 if (devname_nsmaps_loaded) 33352621Sllai1 ret = 1; 33362621Sllai1 33372621Sllai1 mutex_exit(&devname_nsmaps_lock); 33382621Sllai1 return (ret); 33392621Sllai1 } 33402621Sllai1 33412621Sllai1 int 33422621Sllai1 sdev_nsmaps_reloaded() 33432621Sllai1 { 33442621Sllai1 int ret = 0; 33452621Sllai1 33462621Sllai1 mutex_enter(&devname_nsmaps_lock); 33472621Sllai1 if (devname_nsmaps_invalidated) 33482621Sllai1 ret = 1; 33492621Sllai1 33502621Sllai1 mutex_exit(&devname_nsmaps_lock); 33512621Sllai1 return (ret); 33522621Sllai1 } 33532621Sllai1 33542621Sllai1 static void 33552621Sllai1 sdev_free_nsmap(struct devname_nsmap *map) 33562621Sllai1 { 33572621Sllai1 ASSERT(map); 33582621Sllai1 if (map->dir_name) 33592621Sllai1 kmem_free(map->dir_name, strlen(map->dir_name) + 1); 33602621Sllai1 if (map->dir_module) 33612621Sllai1 kmem_free(map->dir_module, strlen(map->dir_module) + 1); 33622621Sllai1 if (map->dir_map) 33632621Sllai1 kmem_free(map->dir_map, strlen(map->dir_map) + 1); 33642621Sllai1 rw_destroy(&map->dir_lock); 33652621Sllai1 kmem_free(map, sizeof (*map)); 33662621Sllai1 } 33672621Sllai1 33682621Sllai1 void 33692621Sllai1 sdev_validate_nsmaps() 33702621Sllai1 { 33712621Sllai1 struct devname_nsmap *map = NULL; 33722621Sllai1 struct devname_nsmap *oldmap = NULL; 33732621Sllai1 33742621Sllai1 ASSERT(MUTEX_HELD(&devname_nsmaps_lock)); 33752621Sllai1 map = devname_nsmaps; 33762621Sllai1 while (map) { 33772621Sllai1 rw_enter(&map->dir_lock, RW_READER); 33782621Sllai1 if ((map->dir_invalid == 1) && (map->dir_newmodule == NULL) && 33792621Sllai1 (map->dir_newmap == NULL)) { 33802621Sllai1 oldmap = map; 33812621Sllai1 rw_exit(&map->dir_lock); 33822621Sllai1 if (map->prev) 33832621Sllai1 map->prev->next = oldmap->next; 33842621Sllai1 if (map == devname_nsmaps) 33852621Sllai1 devname_nsmaps = oldmap->next; 33862621Sllai1 33872621Sllai1 map = oldmap->next; 33882621Sllai1 if (map) 33892621Sllai1 map->prev = oldmap->prev; 33902621Sllai1 sdev_free_nsmap(oldmap); 33912621Sllai1 oldmap = NULL; 33922621Sllai1 } else { 33932621Sllai1 rw_exit(&map->dir_lock); 33942621Sllai1 map = map->next; 33952621Sllai1 } 33962621Sllai1 } 33972621Sllai1 devname_nsmaps_invalidated = 0; 33982621Sllai1 } 33992621Sllai1 34002621Sllai1 static int 34012621Sllai1 sdev_map_is_invalid(struct devname_nsmap *map) 34022621Sllai1 { 34032621Sllai1 int ret = 0; 34042621Sllai1 34052621Sllai1 ASSERT(map); 34062621Sllai1 rw_enter(&map->dir_lock, RW_READER); 34072621Sllai1 if (map->dir_invalid) 34082621Sllai1 ret = 1; 34092621Sllai1 rw_exit(&map->dir_lock); 34102621Sllai1 return (ret); 34112621Sllai1 } 34122621Sllai1 34132621Sllai1 static int 34142621Sllai1 sdev_check_map(struct devname_nsmap *map) 34152621Sllai1 { 34162621Sllai1 struct devname_nsmap *mapp; 34172621Sllai1 34182621Sllai1 mutex_enter(&devname_nsmaps_lock); 34192621Sllai1 if (devname_nsmaps == NULL) { 34202621Sllai1 mutex_exit(&devname_nsmaps_lock); 34212621Sllai1 return (1); 34222621Sllai1 } 34232621Sllai1 34242621Sllai1 for (mapp = devname_nsmaps; mapp; mapp = mapp->next) { 34252621Sllai1 if (mapp == map) { 34262621Sllai1 mutex_exit(&devname_nsmaps_lock); 34272621Sllai1 return (0); 34282621Sllai1 } 34292621Sllai1 } 34302621Sllai1 34312621Sllai1 mutex_exit(&devname_nsmaps_lock); 34322621Sllai1 return (1); 34332621Sllai1 34342621Sllai1 } 34352621Sllai1 34362621Sllai1 struct devname_nsmap * 34372621Sllai1 sdev_get_map(struct sdev_node *dv, int validate) 34382621Sllai1 { 34392621Sllai1 struct devname_nsmap *map; 34402621Sllai1 int error; 34412621Sllai1 34422621Sllai1 ASSERT(RW_READ_HELD(&dv->sdev_contents)); 34432621Sllai1 map = dv->sdev_mapinfo; 34442621Sllai1 if (map && sdev_check_map(map)) { 34452621Sllai1 if (!rw_tryupgrade(&dv->sdev_contents)) { 34462621Sllai1 rw_exit(&dv->sdev_contents); 34472621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 34482621Sllai1 } 34492621Sllai1 dv->sdev_mapinfo = NULL; 34502621Sllai1 rw_downgrade(&dv->sdev_contents); 34512621Sllai1 return (NULL); 34522621Sllai1 } 34532621Sllai1 34542621Sllai1 if (validate && (!map || (map && sdev_map_is_invalid(map)))) { 34552621Sllai1 if (!rw_tryupgrade(&dv->sdev_contents)) { 34562621Sllai1 rw_exit(&dv->sdev_contents); 34572621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 34582621Sllai1 } 34592621Sllai1 error = sdev_get_moduleops(dv); 34602621Sllai1 if (!error) 34612621Sllai1 map = dv->sdev_mapinfo; 34622621Sllai1 rw_downgrade(&dv->sdev_contents); 34632621Sllai1 } 34642621Sllai1 return (map); 34652621Sllai1 } 34662621Sllai1 34672621Sllai1 void 34682621Sllai1 sdev_handle_alloc(struct sdev_node *dv) 34692621Sllai1 { 34702621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 34712621Sllai1 dv->sdev_handle.dh_data = dv; 34722621Sllai1 rw_exit(&dv->sdev_contents); 34732621Sllai1 } 34742621Sllai1 34752621Sllai1 34762621Sllai1 extern int sdev_vnodeops_tbl_size; 34772621Sllai1 34782621Sllai1 /* 34792621Sllai1 * construct a new template with overrides from vtab 34802621Sllai1 */ 34812621Sllai1 static fs_operation_def_t * 34822621Sllai1 sdev_merge_vtab(const fs_operation_def_t tab[]) 34832621Sllai1 { 34842621Sllai1 fs_operation_def_t *new; 34852621Sllai1 const fs_operation_def_t *tab_entry; 34862621Sllai1 34872621Sllai1 /* make a copy of standard vnode ops table */ 34882621Sllai1 new = kmem_alloc(sdev_vnodeops_tbl_size, KM_SLEEP); 34892621Sllai1 bcopy((void *)sdev_vnodeops_tbl, new, sdev_vnodeops_tbl_size); 34902621Sllai1 34912621Sllai1 /* replace the overrides from tab */ 34922621Sllai1 for (tab_entry = tab; tab_entry->name != NULL; tab_entry++) { 34932621Sllai1 fs_operation_def_t *std_entry = new; 34942621Sllai1 while (std_entry->name) { 34952621Sllai1 if (strcmp(tab_entry->name, std_entry->name) == 0) { 34962621Sllai1 std_entry->func = tab_entry->func; 34972621Sllai1 break; 34982621Sllai1 } 34992621Sllai1 std_entry++; 35002621Sllai1 } 35012621Sllai1 if (std_entry->name == NULL) 35022621Sllai1 cmn_err(CE_NOTE, "sdev_merge_vtab: entry %s unused.", 35032621Sllai1 tab_entry->name); 35042621Sllai1 } 35052621Sllai1 35062621Sllai1 return (new); 35072621Sllai1 } 35082621Sllai1 35092621Sllai1 /* free memory allocated by sdev_merge_vtab */ 35102621Sllai1 static void 35112621Sllai1 sdev_free_vtab(fs_operation_def_t *new) 35122621Sllai1 { 35132621Sllai1 kmem_free(new, sdev_vnodeops_tbl_size); 35142621Sllai1 } 35152621Sllai1 35162621Sllai1 void 35172621Sllai1 devname_get_vnode(devname_handle_t *hdl, vnode_t **vpp) 35182621Sllai1 { 35192621Sllai1 struct sdev_node *dv = hdl->dh_data; 35202621Sllai1 35212621Sllai1 ASSERT(dv); 35222621Sllai1 35232621Sllai1 rw_enter(&dv->sdev_contents, RW_READER); 35242621Sllai1 *vpp = SDEVTOV(dv); 35252621Sllai1 rw_exit(&dv->sdev_contents); 35262621Sllai1 } 35272621Sllai1 35282621Sllai1 int 35292621Sllai1 devname_get_path(devname_handle_t *hdl, char **path) 35302621Sllai1 { 35312621Sllai1 struct sdev_node *dv = hdl->dh_data; 35322621Sllai1 35332621Sllai1 ASSERT(dv); 35342621Sllai1 35352621Sllai1 rw_enter(&dv->sdev_contents, RW_READER); 35362621Sllai1 *path = dv->sdev_path; 35372621Sllai1 rw_exit(&dv->sdev_contents); 35382621Sllai1 return (0); 35392621Sllai1 } 35402621Sllai1 35412621Sllai1 int 35422621Sllai1 devname_get_name(devname_handle_t *hdl, char **entry) 35432621Sllai1 { 35442621Sllai1 struct sdev_node *dv = hdl->dh_data; 35452621Sllai1 35462621Sllai1 ASSERT(dv); 35472621Sllai1 rw_enter(&dv->sdev_contents, RW_READER); 35482621Sllai1 *entry = dv->sdev_name; 35492621Sllai1 rw_exit(&dv->sdev_contents); 35502621Sllai1 return (0); 35512621Sllai1 } 35522621Sllai1 35532621Sllai1 void 35542621Sllai1 devname_get_dir_vnode(devname_handle_t *hdl, vnode_t **vpp) 35552621Sllai1 { 35562621Sllai1 struct sdev_node *dv = hdl->dh_data->sdev_dotdot; 35572621Sllai1 35582621Sllai1 ASSERT(dv); 35592621Sllai1 35602621Sllai1 rw_enter(&dv->sdev_contents, RW_READER); 35612621Sllai1 *vpp = SDEVTOV(dv); 35622621Sllai1 rw_exit(&dv->sdev_contents); 35632621Sllai1 } 35642621Sllai1 35652621Sllai1 int 35662621Sllai1 devname_get_dir_path(devname_handle_t *hdl, char **path) 35672621Sllai1 { 35682621Sllai1 struct sdev_node *dv = hdl->dh_data->sdev_dotdot; 35692621Sllai1 35702621Sllai1 ASSERT(dv); 35712621Sllai1 rw_enter(&dv->sdev_contents, RW_READER); 35722621Sllai1 *path = dv->sdev_path; 35732621Sllai1 rw_exit(&dv->sdev_contents); 35742621Sllai1 return (0); 35752621Sllai1 } 35762621Sllai1 35772621Sllai1 int 35782621Sllai1 devname_get_dir_name(devname_handle_t *hdl, char **entry) 35792621Sllai1 { 35802621Sllai1 struct sdev_node *dv = hdl->dh_data->sdev_dotdot; 35812621Sllai1 35822621Sllai1 ASSERT(dv); 35832621Sllai1 rw_enter(&dv->sdev_contents, RW_READER); 35842621Sllai1 *entry = dv->sdev_name; 35852621Sllai1 rw_exit(&dv->sdev_contents); 35862621Sllai1 return (0); 35872621Sllai1 } 35882621Sllai1 35892621Sllai1 int 35902621Sllai1 devname_get_dir_nsmap(devname_handle_t *hdl, struct devname_nsmap **map) 35912621Sllai1 { 35922621Sllai1 struct sdev_node *dv = hdl->dh_data->sdev_dotdot; 35932621Sllai1 35942621Sllai1 ASSERT(dv); 35952621Sllai1 rw_enter(&dv->sdev_contents, RW_READER); 35962621Sllai1 *map = dv->sdev_mapinfo; 35972621Sllai1 rw_exit(&dv->sdev_contents); 35982621Sllai1 return (0); 35992621Sllai1 } 36002621Sllai1 36012621Sllai1 int 36022621Sllai1 devname_get_dir_handle(devname_handle_t *hdl, devname_handle_t **dir_hdl) 36032621Sllai1 { 36042621Sllai1 struct sdev_node *dv = hdl->dh_data->sdev_dotdot; 36052621Sllai1 36062621Sllai1 ASSERT(dv); 36072621Sllai1 rw_enter(&dv->sdev_contents, RW_READER); 36082621Sllai1 *dir_hdl = &(dv->sdev_handle); 36092621Sllai1 rw_exit(&dv->sdev_contents); 36102621Sllai1 return (0); 36112621Sllai1 } 36122621Sllai1 36132621Sllai1 void 36142621Sllai1 devname_set_nodetype(devname_handle_t *hdl, void *args, int spec) 36152621Sllai1 { 36162621Sllai1 struct sdev_node *dv = hdl->dh_data; 36172621Sllai1 36182621Sllai1 ASSERT(dv); 36192621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 36202621Sllai1 hdl->dh_spec = (devname_spec_t)spec; 36212621Sllai1 hdl->dh_args = (void *)i_ddi_strdup((char *)args, KM_SLEEP); 36222621Sllai1 rw_exit(&dv->sdev_contents); 36232621Sllai1 } 36242621Sllai1 36252621Sllai1 /* 36262621Sllai1 * a generic setattr() function 36272621Sllai1 * 36282621Sllai1 * note: flags only supports AT_UID and AT_GID. 36292621Sllai1 * Future enhancements can be done for other types, e.g. AT_MODE 36302621Sllai1 */ 36312621Sllai1 int 36322621Sllai1 devname_setattr_func(struct vnode *vp, struct vattr *vap, int flags, 36332621Sllai1 struct cred *cred, int (*callback)(struct sdev_node *, struct vattr *, 36342621Sllai1 int), int protocol) 36352621Sllai1 { 36362621Sllai1 struct sdev_node *dv = VTOSDEV(vp); 36372621Sllai1 struct sdev_node *parent = dv->sdev_dotdot; 36382621Sllai1 struct vattr *get; 36392621Sllai1 uint_t mask = vap->va_mask; 36402621Sllai1 int error; 36412621Sllai1 36422621Sllai1 /* some sanity checks */ 36432621Sllai1 if (vap->va_mask & AT_NOSET) 36442621Sllai1 return (EINVAL); 36452621Sllai1 36462621Sllai1 if (vap->va_mask & AT_SIZE) { 36472621Sllai1 if (vp->v_type == VDIR) { 36482621Sllai1 return (EISDIR); 36492621Sllai1 } 36502621Sllai1 } 36512621Sllai1 36522621Sllai1 /* no need to set attribute, but do not fail either */ 36532621Sllai1 ASSERT(parent); 36542621Sllai1 rw_enter(&parent->sdev_contents, RW_READER); 36552621Sllai1 if (dv->sdev_state == SDEV_ZOMBIE) { 36562621Sllai1 rw_exit(&parent->sdev_contents); 36572621Sllai1 return (0); 36582621Sllai1 } 36592621Sllai1 36602621Sllai1 /* If backing store exists, just set it. */ 36612621Sllai1 if (dv->sdev_attrvp) { 36622621Sllai1 rw_exit(&parent->sdev_contents); 36632621Sllai1 return (VOP_SETATTR(dv->sdev_attrvp, vap, flags, cred, NULL)); 36642621Sllai1 } 36652621Sllai1 36662621Sllai1 /* 36672621Sllai1 * Otherwise, for nodes with the persistence attribute, create it. 36682621Sllai1 */ 36692621Sllai1 ASSERT(dv->sdev_attr); 36702621Sllai1 if (SDEV_IS_PERSIST(dv) || 36712621Sllai1 ((vap->va_mask & ~AT_TIMES) != 0 && !SDEV_IS_DYNAMIC(dv))) { 36722621Sllai1 sdev_vattr_merge(dv, vap); 36732621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 36742621Sllai1 error = sdev_shadow_node(dv, cred); 36752621Sllai1 rw_exit(&dv->sdev_contents); 36762621Sllai1 rw_exit(&parent->sdev_contents); 36772621Sllai1 36782621Sllai1 if (error) 36792621Sllai1 return (error); 36802621Sllai1 return (VOP_SETATTR(dv->sdev_attrvp, vap, flags, cred, NULL)); 36812621Sllai1 } 36822621Sllai1 36832621Sllai1 36842621Sllai1 /* 36852621Sllai1 * sdev_attr was allocated in sdev_mknode 36862621Sllai1 */ 36872621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 36882621Sllai1 error = secpolicy_vnode_setattr(cred, vp, vap, dv->sdev_attr, 36892621Sllai1 flags, sdev_unlocked_access, dv); 36902621Sllai1 if (error) { 36912621Sllai1 rw_exit(&dv->sdev_contents); 36922621Sllai1 rw_exit(&parent->sdev_contents); 36932621Sllai1 return (error); 36942621Sllai1 } 36952621Sllai1 36962621Sllai1 get = dv->sdev_attr; 36972621Sllai1 if (mask & AT_MODE) { 36982621Sllai1 get->va_mode &= S_IFMT; 36992621Sllai1 get->va_mode |= vap->va_mode & ~S_IFMT; 37002621Sllai1 } 37012621Sllai1 37022621Sllai1 if ((mask & AT_UID) || (mask & AT_GID)) { 37032621Sllai1 if (mask & AT_UID) 37042621Sllai1 get->va_uid = vap->va_uid; 37052621Sllai1 if (mask & AT_GID) 37062621Sllai1 get->va_gid = vap->va_gid; 37072621Sllai1 /* 37082621Sllai1 * a callback must be provided if the protocol is set 37092621Sllai1 */ 37102621Sllai1 if ((protocol & AT_UID) || (protocol & AT_GID)) { 37112621Sllai1 ASSERT(callback); 37122621Sllai1 error = callback(dv, get, protocol); 37132621Sllai1 if (error) { 37142621Sllai1 rw_exit(&dv->sdev_contents); 37152621Sllai1 rw_exit(&parent->sdev_contents); 37162621Sllai1 return (error); 37172621Sllai1 } 37182621Sllai1 } 37192621Sllai1 } 37202621Sllai1 37212621Sllai1 if (mask & AT_ATIME) 37222621Sllai1 get->va_atime = vap->va_atime; 37232621Sllai1 if (mask & AT_MTIME) 37242621Sllai1 get->va_mtime = vap->va_mtime; 37252621Sllai1 if (mask & (AT_MODE | AT_UID | AT_GID | AT_CTIME)) { 37262621Sllai1 gethrestime(&get->va_ctime); 37272621Sllai1 } 37282621Sllai1 37292621Sllai1 sdev_vattr_merge(dv, get); 37302621Sllai1 rw_exit(&dv->sdev_contents); 37312621Sllai1 rw_exit(&parent->sdev_contents); 37322621Sllai1 return (0); 37332621Sllai1 } 3734