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 /* 2212633Sjohn.levon@sun.com * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 232621Sllai1 */ 242621Sllai1 252621Sllai1 /* 262621Sllai1 * utility routines for the /dev fs 272621Sllai1 */ 282621Sllai1 292621Sllai1 #include <sys/types.h> 302621Sllai1 #include <sys/param.h> 312621Sllai1 #include <sys/t_lock.h> 322621Sllai1 #include <sys/systm.h> 332621Sllai1 #include <sys/sysmacros.h> 342621Sllai1 #include <sys/user.h> 352621Sllai1 #include <sys/time.h> 362621Sllai1 #include <sys/vfs.h> 372621Sllai1 #include <sys/vnode.h> 382621Sllai1 #include <sys/file.h> 392621Sllai1 #include <sys/fcntl.h> 402621Sllai1 #include <sys/flock.h> 412621Sllai1 #include <sys/kmem.h> 422621Sllai1 #include <sys/uio.h> 432621Sllai1 #include <sys/errno.h> 442621Sllai1 #include <sys/stat.h> 452621Sllai1 #include <sys/cred.h> 462621Sllai1 #include <sys/dirent.h> 472621Sllai1 #include <sys/pathname.h> 482621Sllai1 #include <sys/cmn_err.h> 492621Sllai1 #include <sys/debug.h> 502621Sllai1 #include <sys/mode.h> 512621Sllai1 #include <sys/policy.h> 522621Sllai1 #include <fs/fs_subr.h> 532621Sllai1 #include <sys/mount.h> 542621Sllai1 #include <sys/fs/snode.h> 552621Sllai1 #include <sys/fs/dv_node.h> 562621Sllai1 #include <sys/fs/sdev_impl.h> 572621Sllai1 #include <sys/sunndi.h> 582621Sllai1 #include <sys/sunmdi.h> 592621Sllai1 #include <sys/conf.h> 602621Sllai1 #include <sys/proc.h> 612621Sllai1 #include <sys/user.h> 622621Sllai1 #include <sys/modctl.h> 632621Sllai1 642621Sllai1 #ifdef DEBUG 652621Sllai1 int sdev_debug = 0x00000001; 662621Sllai1 int sdev_debug_cache_flags = 0; 672621Sllai1 #endif 682621Sllai1 692621Sllai1 /* 702621Sllai1 * globals 712621Sllai1 */ 722621Sllai1 /* prototype memory vattrs */ 732621Sllai1 vattr_t sdev_vattr_dir = { 742621Sllai1 AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */ 752621Sllai1 VDIR, /* va_type */ 762621Sllai1 SDEV_DIRMODE_DEFAULT, /* va_mode */ 772621Sllai1 SDEV_UID_DEFAULT, /* va_uid */ 782621Sllai1 SDEV_GID_DEFAULT, /* va_gid */ 792621Sllai1 0, /* va_fsid */ 802621Sllai1 0, /* va_nodeid */ 812621Sllai1 0, /* va_nlink */ 822621Sllai1 0, /* va_size */ 832621Sllai1 0, /* va_atime */ 842621Sllai1 0, /* va_mtime */ 852621Sllai1 0, /* va_ctime */ 862621Sllai1 0, /* va_rdev */ 872621Sllai1 0, /* va_blksize */ 882621Sllai1 0, /* va_nblocks */ 892621Sllai1 0 /* va_vcode */ 902621Sllai1 }; 912621Sllai1 922621Sllai1 vattr_t sdev_vattr_lnk = { 932621Sllai1 AT_TYPE|AT_MODE, /* va_mask */ 942621Sllai1 VLNK, /* va_type */ 952621Sllai1 SDEV_LNKMODE_DEFAULT, /* va_mode */ 962621Sllai1 SDEV_UID_DEFAULT, /* va_uid */ 972621Sllai1 SDEV_GID_DEFAULT, /* va_gid */ 982621Sllai1 0, /* va_fsid */ 992621Sllai1 0, /* va_nodeid */ 1002621Sllai1 0, /* va_nlink */ 1012621Sllai1 0, /* va_size */ 1022621Sllai1 0, /* va_atime */ 1032621Sllai1 0, /* va_mtime */ 1042621Sllai1 0, /* va_ctime */ 1052621Sllai1 0, /* va_rdev */ 1062621Sllai1 0, /* va_blksize */ 1072621Sllai1 0, /* va_nblocks */ 1082621Sllai1 0 /* va_vcode */ 1092621Sllai1 }; 1102621Sllai1 1112621Sllai1 vattr_t sdev_vattr_blk = { 1122621Sllai1 AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */ 1132621Sllai1 VBLK, /* va_type */ 1142621Sllai1 S_IFBLK | SDEV_DEVMODE_DEFAULT, /* va_mode */ 1152621Sllai1 SDEV_UID_DEFAULT, /* va_uid */ 1162621Sllai1 SDEV_GID_DEFAULT, /* va_gid */ 1172621Sllai1 0, /* va_fsid */ 1182621Sllai1 0, /* va_nodeid */ 1192621Sllai1 0, /* va_nlink */ 1202621Sllai1 0, /* va_size */ 1212621Sllai1 0, /* va_atime */ 1222621Sllai1 0, /* va_mtime */ 1232621Sllai1 0, /* va_ctime */ 1242621Sllai1 0, /* va_rdev */ 1252621Sllai1 0, /* va_blksize */ 1262621Sllai1 0, /* va_nblocks */ 1272621Sllai1 0 /* va_vcode */ 1282621Sllai1 }; 1292621Sllai1 1302621Sllai1 vattr_t sdev_vattr_chr = { 1312621Sllai1 AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */ 1322621Sllai1 VCHR, /* va_type */ 1332621Sllai1 S_IFCHR | SDEV_DEVMODE_DEFAULT, /* va_mode */ 1342621Sllai1 SDEV_UID_DEFAULT, /* va_uid */ 1352621Sllai1 SDEV_GID_DEFAULT, /* va_gid */ 1362621Sllai1 0, /* va_fsid */ 1372621Sllai1 0, /* va_nodeid */ 1382621Sllai1 0, /* va_nlink */ 1392621Sllai1 0, /* va_size */ 1402621Sllai1 0, /* va_atime */ 1412621Sllai1 0, /* va_mtime */ 1422621Sllai1 0, /* va_ctime */ 1432621Sllai1 0, /* va_rdev */ 1442621Sllai1 0, /* va_blksize */ 1452621Sllai1 0, /* va_nblocks */ 1462621Sllai1 0 /* va_vcode */ 1472621Sllai1 }; 1482621Sllai1 1492621Sllai1 kmem_cache_t *sdev_node_cache; /* sdev_node cache */ 1502621Sllai1 int devtype; /* fstype */ 1512621Sllai1 1522621Sllai1 /* static */ 1532621Sllai1 static struct vnodeops *sdev_get_vop(struct sdev_node *); 15410588SEric.Taylor@Sun.COM static void sdev_set_no_negcache(struct sdev_node *); 1552621Sllai1 static fs_operation_def_t *sdev_merge_vtab(const fs_operation_def_t []); 1562621Sllai1 static void sdev_free_vtab(fs_operation_def_t *); 1572621Sllai1 1582621Sllai1 static void 1592621Sllai1 sdev_prof_free(struct sdev_node *dv) 1602621Sllai1 { 1612621Sllai1 ASSERT(!SDEV_IS_GLOBAL(dv)); 1622621Sllai1 if (dv->sdev_prof.dev_name) 1632621Sllai1 nvlist_free(dv->sdev_prof.dev_name); 1642621Sllai1 if (dv->sdev_prof.dev_map) 1652621Sllai1 nvlist_free(dv->sdev_prof.dev_map); 1662621Sllai1 if (dv->sdev_prof.dev_symlink) 1672621Sllai1 nvlist_free(dv->sdev_prof.dev_symlink); 1682621Sllai1 if (dv->sdev_prof.dev_glob_incdir) 1692621Sllai1 nvlist_free(dv->sdev_prof.dev_glob_incdir); 1702621Sllai1 if (dv->sdev_prof.dev_glob_excdir) 1712621Sllai1 nvlist_free(dv->sdev_prof.dev_glob_excdir); 1722621Sllai1 bzero(&dv->sdev_prof, sizeof (dv->sdev_prof)); 1732621Sllai1 } 1742621Sllai1 1756712Stomee /* sdev_node cache constructor */ 1762621Sllai1 /*ARGSUSED1*/ 1772621Sllai1 static int 1782621Sllai1 i_sdev_node_ctor(void *buf, void *cfarg, int flag) 1792621Sllai1 { 1802621Sllai1 struct sdev_node *dv = (struct sdev_node *)buf; 1812621Sllai1 struct vnode *vp; 1822621Sllai1 1832621Sllai1 bzero(buf, sizeof (struct sdev_node)); 1846712Stomee vp = dv->sdev_vnode = vn_alloc(flag); 1856712Stomee if (vp == NULL) { 1866712Stomee return (-1); 1876712Stomee } 1886712Stomee vp->v_data = dv; 1892621Sllai1 rw_init(&dv->sdev_contents, NULL, RW_DEFAULT, NULL); 1902621Sllai1 return (0); 1912621Sllai1 } 1922621Sllai1 1936712Stomee /* sdev_node cache destructor */ 1942621Sllai1 /*ARGSUSED1*/ 1952621Sllai1 static void 1962621Sllai1 i_sdev_node_dtor(void *buf, void *arg) 1972621Sllai1 { 1982621Sllai1 struct sdev_node *dv = (struct sdev_node *)buf; 1992621Sllai1 struct vnode *vp = SDEVTOV(dv); 2002621Sllai1 2012621Sllai1 rw_destroy(&dv->sdev_contents); 2022621Sllai1 vn_free(vp); 2032621Sllai1 } 2042621Sllai1 2052621Sllai1 /* initialize sdev_node cache */ 2062621Sllai1 void 2072621Sllai1 sdev_node_cache_init() 2082621Sllai1 { 2092621Sllai1 int flags = 0; 2102621Sllai1 2112621Sllai1 #ifdef DEBUG 2122621Sllai1 flags = sdev_debug_cache_flags; 2132621Sllai1 if (flags) 2142621Sllai1 sdcmn_err(("cache debug flags 0x%x\n", flags)); 2152621Sllai1 #endif /* DEBUG */ 2162621Sllai1 2172621Sllai1 ASSERT(sdev_node_cache == NULL); 2182621Sllai1 sdev_node_cache = kmem_cache_create("sdev_node_cache", 2192621Sllai1 sizeof (struct sdev_node), 0, i_sdev_node_ctor, i_sdev_node_dtor, 2202621Sllai1 NULL, NULL, NULL, flags); 2212621Sllai1 } 2222621Sllai1 2232621Sllai1 /* destroy sdev_node cache */ 2242621Sllai1 void 2252621Sllai1 sdev_node_cache_fini() 2262621Sllai1 { 2272621Sllai1 ASSERT(sdev_node_cache != NULL); 2282621Sllai1 kmem_cache_destroy(sdev_node_cache); 2292621Sllai1 sdev_node_cache = NULL; 2302621Sllai1 } 2312621Sllai1 2326260Sjg /* 2336260Sjg * Compare two nodes lexographically to balance avl tree 2346260Sjg */ 2356260Sjg static int 2366260Sjg sdev_compare_nodes(const struct sdev_node *dv1, const struct sdev_node *dv2) 2376260Sjg { 2386260Sjg int rv; 2396260Sjg if ((rv = strcmp(dv1->sdev_name, dv2->sdev_name)) == 0) 2406260Sjg return (0); 2416260Sjg return ((rv < 0) ? -1 : 1); 2426260Sjg } 2436260Sjg 2442621Sllai1 void 2452621Sllai1 sdev_set_nodestate(struct sdev_node *dv, sdev_node_state_t state) 2462621Sllai1 { 2472621Sllai1 ASSERT(dv); 2482621Sllai1 ASSERT(RW_WRITE_HELD(&dv->sdev_contents)); 2492621Sllai1 dv->sdev_state = state; 2502621Sllai1 } 2512621Sllai1 2522621Sllai1 static void 25311279SJerry.Gilliam@Sun.COM sdev_attr_update(struct sdev_node *dv, vattr_t *vap) 2542621Sllai1 { 25511279SJerry.Gilliam@Sun.COM timestruc_t now; 25611279SJerry.Gilliam@Sun.COM struct vattr *attrp; 25711279SJerry.Gilliam@Sun.COM uint_t mask; 25811279SJerry.Gilliam@Sun.COM 25911279SJerry.Gilliam@Sun.COM ASSERT(dv->sdev_attr); 2602621Sllai1 ASSERT(vap); 2612621Sllai1 26211279SJerry.Gilliam@Sun.COM attrp = dv->sdev_attr; 26311279SJerry.Gilliam@Sun.COM mask = vap->va_mask; 26411279SJerry.Gilliam@Sun.COM if (mask & AT_TYPE) 26511279SJerry.Gilliam@Sun.COM attrp->va_type = vap->va_type; 26611279SJerry.Gilliam@Sun.COM if (mask & AT_MODE) 26711279SJerry.Gilliam@Sun.COM attrp->va_mode = vap->va_mode; 26811279SJerry.Gilliam@Sun.COM if (mask & AT_UID) 26911279SJerry.Gilliam@Sun.COM attrp->va_uid = vap->va_uid; 27011279SJerry.Gilliam@Sun.COM if (mask & AT_GID) 27111279SJerry.Gilliam@Sun.COM attrp->va_gid = vap->va_gid; 27211279SJerry.Gilliam@Sun.COM if (mask & AT_RDEV) 27311279SJerry.Gilliam@Sun.COM attrp->va_rdev = vap->va_rdev; 27411279SJerry.Gilliam@Sun.COM 27511279SJerry.Gilliam@Sun.COM gethrestime(&now); 27611279SJerry.Gilliam@Sun.COM attrp->va_atime = (mask & AT_ATIME) ? vap->va_atime : now; 27711279SJerry.Gilliam@Sun.COM attrp->va_mtime = (mask & AT_MTIME) ? vap->va_mtime : now; 27811279SJerry.Gilliam@Sun.COM attrp->va_ctime = (mask & AT_CTIME) ? vap->va_ctime : now; 27911279SJerry.Gilliam@Sun.COM } 28011279SJerry.Gilliam@Sun.COM 28111279SJerry.Gilliam@Sun.COM static void 28211279SJerry.Gilliam@Sun.COM sdev_attr_alloc(struct sdev_node *dv, vattr_t *vap) 28311279SJerry.Gilliam@Sun.COM { 28411279SJerry.Gilliam@Sun.COM ASSERT(dv->sdev_attr == NULL); 28511279SJerry.Gilliam@Sun.COM ASSERT(vap->va_mask & AT_TYPE); 28611279SJerry.Gilliam@Sun.COM ASSERT(vap->va_mask & AT_MODE); 28711279SJerry.Gilliam@Sun.COM 2882621Sllai1 dv->sdev_attr = kmem_zalloc(sizeof (struct vattr), KM_SLEEP); 28911279SJerry.Gilliam@Sun.COM sdev_attr_update(dv, vap); 2902621Sllai1 } 2912621Sllai1 2922621Sllai1 /* alloc and initialize a sdev_node */ 2932621Sllai1 int 2942621Sllai1 sdev_nodeinit(struct sdev_node *ddv, char *nm, struct sdev_node **newdv, 2952621Sllai1 vattr_t *vap) 2962621Sllai1 { 2972621Sllai1 struct sdev_node *dv = NULL; 2982621Sllai1 struct vnode *vp; 2992621Sllai1 size_t nmlen, len; 3002621Sllai1 devname_handle_t *dhl; 3012621Sllai1 3022621Sllai1 nmlen = strlen(nm) + 1; 3032621Sllai1 if (nmlen > MAXNAMELEN) { 3042621Sllai1 sdcmn_err9(("sdev_nodeinit: node name %s" 3052621Sllai1 " too long\n", nm)); 3062621Sllai1 *newdv = NULL; 3072621Sllai1 return (ENAMETOOLONG); 3082621Sllai1 } 3092621Sllai1 3102621Sllai1 dv = kmem_cache_alloc(sdev_node_cache, KM_SLEEP); 3112621Sllai1 3122621Sllai1 dv->sdev_name = kmem_alloc(nmlen, KM_SLEEP); 3132621Sllai1 bcopy(nm, dv->sdev_name, nmlen); 3142621Sllai1 dv->sdev_namelen = nmlen - 1; /* '\0' not included */ 3152621Sllai1 len = strlen(ddv->sdev_path) + strlen(nm) + 2; 3162621Sllai1 dv->sdev_path = kmem_alloc(len, KM_SLEEP); 3172621Sllai1 (void) snprintf(dv->sdev_path, len, "%s/%s", ddv->sdev_path, nm); 3182621Sllai1 /* overwritten for VLNK nodes */ 3192621Sllai1 dv->sdev_symlink = NULL; 3202621Sllai1 3212621Sllai1 vp = SDEVTOV(dv); 3222621Sllai1 vn_reinit(vp); 3232621Sllai1 vp->v_vfsp = SDEVTOV(ddv)->v_vfsp; 3242621Sllai1 if (vap) 3252621Sllai1 vp->v_type = vap->va_type; 3262621Sllai1 3272621Sllai1 /* 3282621Sllai1 * initialized to the parent's vnodeops. 3292621Sllai1 * maybe overwriten for a VDIR 3302621Sllai1 */ 3312621Sllai1 vn_setops(vp, vn_getops(SDEVTOV(ddv))); 3322621Sllai1 vn_exists(vp); 3332621Sllai1 3342621Sllai1 dv->sdev_dotdot = NULL; 3352621Sllai1 dv->sdev_attrvp = NULL; 3362621Sllai1 if (vap) { 33711279SJerry.Gilliam@Sun.COM sdev_attr_alloc(dv, vap); 3382621Sllai1 } else { 3392621Sllai1 dv->sdev_attr = NULL; 3402621Sllai1 } 3412621Sllai1 3422621Sllai1 dv->sdev_ino = sdev_mkino(dv); 3432621Sllai1 dv->sdev_nlink = 0; /* updated on insert */ 3442621Sllai1 dv->sdev_flags = ddv->sdev_flags; /* inherit from the parent first */ 3452621Sllai1 dv->sdev_flags |= SDEV_BUILD; 3462621Sllai1 mutex_init(&dv->sdev_lookup_lock, NULL, MUTEX_DEFAULT, NULL); 3472621Sllai1 cv_init(&dv->sdev_lookup_cv, NULL, CV_DEFAULT, NULL); 3482621Sllai1 if (SDEV_IS_GLOBAL(ddv)) { 3492621Sllai1 dv->sdev_flags |= SDEV_GLOBAL; 3502621Sllai1 dhl = &(dv->sdev_handle); 3512621Sllai1 dhl->dh_data = dv; 3522621Sllai1 dhl->dh_args = NULL; 35310588SEric.Taylor@Sun.COM sdev_set_no_negcache(dv); 3542621Sllai1 dv->sdev_gdir_gen = 0; 3552621Sllai1 } else { 3562621Sllai1 dv->sdev_flags &= ~SDEV_GLOBAL; 3572621Sllai1 dv->sdev_origin = NULL; /* set later */ 3582621Sllai1 bzero(&dv->sdev_prof, sizeof (dv->sdev_prof)); 3592621Sllai1 dv->sdev_ldir_gen = 0; 3602621Sllai1 dv->sdev_devtree_gen = 0; 3612621Sllai1 } 3622621Sllai1 3632621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 3642621Sllai1 sdev_set_nodestate(dv, SDEV_INIT); 3652621Sllai1 rw_exit(&dv->sdev_contents); 3662621Sllai1 *newdv = dv; 3672621Sllai1 3682621Sllai1 return (0); 3692621Sllai1 } 3702621Sllai1 3712621Sllai1 /* 3722621Sllai1 * transition a sdev_node into SDEV_READY state 3732621Sllai1 */ 3742621Sllai1 int 3752621Sllai1 sdev_nodeready(struct sdev_node *dv, struct vattr *vap, struct vnode *avp, 3762621Sllai1 void *args, struct cred *cred) 3772621Sllai1 { 3782621Sllai1 int error = 0; 3792621Sllai1 struct vnode *vp = SDEVTOV(dv); 3802621Sllai1 vtype_t type; 3812621Sllai1 3822621Sllai1 ASSERT(dv && (dv->sdev_state != SDEV_READY) && vap); 3832621Sllai1 3842621Sllai1 type = vap->va_type; 3852621Sllai1 vp->v_type = type; 3862621Sllai1 vp->v_rdev = vap->va_rdev; 3872621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 3882621Sllai1 if (type == VDIR) { 3892621Sllai1 dv->sdev_nlink = 2; 3902621Sllai1 dv->sdev_flags &= ~SDEV_PERSIST; 3912621Sllai1 dv->sdev_flags &= ~SDEV_DYNAMIC; 3922621Sllai1 vn_setops(vp, sdev_get_vop(dv)); /* from internal vtab */ 3932621Sllai1 ASSERT(dv->sdev_dotdot); 3942621Sllai1 ASSERT(SDEVTOV(dv->sdev_dotdot)->v_type == VDIR); 3952621Sllai1 vp->v_rdev = SDEVTOV(dv->sdev_dotdot)->v_rdev; 3966260Sjg avl_create(&dv->sdev_entries, 3976260Sjg (int (*)(const void *, const void *))sdev_compare_nodes, 3986260Sjg sizeof (struct sdev_node), 3996260Sjg offsetof(struct sdev_node, sdev_avllink)); 4002621Sllai1 } else if (type == VLNK) { 4012621Sllai1 ASSERT(args); 4022621Sllai1 dv->sdev_nlink = 1; 4032621Sllai1 dv->sdev_symlink = i_ddi_strdup((char *)args, KM_SLEEP); 4042621Sllai1 } else { 4052621Sllai1 dv->sdev_nlink = 1; 4062621Sllai1 } 4072621Sllai1 4082621Sllai1 if (!(SDEV_IS_GLOBAL(dv))) { 4092621Sllai1 dv->sdev_origin = (struct sdev_node *)args; 4102621Sllai1 dv->sdev_flags &= ~SDEV_PERSIST; 4112621Sllai1 } 4122621Sllai1 4132621Sllai1 /* 4142621Sllai1 * shadow node is created here OR 4152621Sllai1 * if failed (indicated by dv->sdev_attrvp == NULL), 4162621Sllai1 * created later in sdev_setattr 4172621Sllai1 */ 4182621Sllai1 if (avp) { 4192621Sllai1 dv->sdev_attrvp = avp; 4202621Sllai1 } else { 42111279SJerry.Gilliam@Sun.COM if (dv->sdev_attr == NULL) { 42211279SJerry.Gilliam@Sun.COM sdev_attr_alloc(dv, vap); 42311279SJerry.Gilliam@Sun.COM } else { 42411279SJerry.Gilliam@Sun.COM sdev_attr_update(dv, vap); 42511279SJerry.Gilliam@Sun.COM } 4262621Sllai1 42710588SEric.Taylor@Sun.COM if ((dv->sdev_attrvp == NULL) && SDEV_IS_PERSIST(dv)) 4282621Sllai1 error = sdev_shadow_node(dv, cred); 4292621Sllai1 } 4302621Sllai1 4316335Sjg if (error == 0) { 4326335Sjg /* transition to READY state */ 4336335Sjg sdev_set_nodestate(dv, SDEV_READY); 4346335Sjg sdev_nc_node_exists(dv); 4356335Sjg } else { 4366335Sjg sdev_set_nodestate(dv, SDEV_ZOMBIE); 4376335Sjg } 4382621Sllai1 rw_exit(&dv->sdev_contents); 4392621Sllai1 return (error); 4402621Sllai1 } 4412621Sllai1 4422621Sllai1 /* 4432621Sllai1 * setting ZOMBIE state 4442621Sllai1 */ 4452621Sllai1 static int 4462621Sllai1 sdev_nodezombied(struct sdev_node *dv) 4472621Sllai1 { 4482621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 4492621Sllai1 sdev_set_nodestate(dv, SDEV_ZOMBIE); 4502621Sllai1 rw_exit(&dv->sdev_contents); 4512621Sllai1 return (0); 4522621Sllai1 } 4532621Sllai1 4542621Sllai1 /* 4552621Sllai1 * Build the VROOT sdev_node. 4562621Sllai1 */ 4572621Sllai1 /*ARGSUSED*/ 4582621Sllai1 struct sdev_node * 4592621Sllai1 sdev_mkroot(struct vfs *vfsp, dev_t devdev, struct vnode *mvp, 4602621Sllai1 struct vnode *avp, struct cred *cred) 4612621Sllai1 { 4622621Sllai1 struct sdev_node *dv; 4632621Sllai1 struct vnode *vp; 4642621Sllai1 char devdir[] = "/dev"; 4652621Sllai1 4662621Sllai1 ASSERT(sdev_node_cache != NULL); 4672621Sllai1 ASSERT(avp); 4682621Sllai1 dv = kmem_cache_alloc(sdev_node_cache, KM_SLEEP); 4692621Sllai1 vp = SDEVTOV(dv); 4702621Sllai1 vn_reinit(vp); 4712621Sllai1 vp->v_flag |= VROOT; 4722621Sllai1 vp->v_vfsp = vfsp; 4732621Sllai1 vp->v_type = VDIR; 4742621Sllai1 vp->v_rdev = devdev; 4752621Sllai1 vn_setops(vp, sdev_vnodeops); /* apply the default vnodeops at /dev */ 4762621Sllai1 vn_exists(vp); 4772621Sllai1 4782621Sllai1 if (vfsp->vfs_mntpt) 4792621Sllai1 dv->sdev_name = i_ddi_strdup( 4802621Sllai1 (char *)refstr_value(vfsp->vfs_mntpt), KM_SLEEP); 4812621Sllai1 else 4822621Sllai1 /* vfs_mountdev1 set mount point later */ 4832621Sllai1 dv->sdev_name = i_ddi_strdup("/dev", KM_SLEEP); 4842621Sllai1 dv->sdev_namelen = strlen(dv->sdev_name); /* '\0' not included */ 4852621Sllai1 dv->sdev_path = i_ddi_strdup(devdir, KM_SLEEP); 4862621Sllai1 dv->sdev_ino = SDEV_ROOTINO; 4872621Sllai1 dv->sdev_nlink = 2; /* name + . (no sdev_insert) */ 4882621Sllai1 dv->sdev_dotdot = dv; /* .. == self */ 4892621Sllai1 dv->sdev_attrvp = avp; 4902621Sllai1 dv->sdev_attr = NULL; 4912621Sllai1 mutex_init(&dv->sdev_lookup_lock, NULL, MUTEX_DEFAULT, NULL); 4922621Sllai1 cv_init(&dv->sdev_lookup_cv, NULL, CV_DEFAULT, NULL); 4932621Sllai1 if (strcmp(dv->sdev_name, "/dev") == 0) { 4942621Sllai1 dv->sdev_flags = SDEV_BUILD|SDEV_GLOBAL|SDEV_PERSIST; 4952621Sllai1 bzero(&dv->sdev_handle, sizeof (dv->sdev_handle)); 4962621Sllai1 dv->sdev_gdir_gen = 0; 4972621Sllai1 } else { 4982621Sllai1 dv->sdev_flags = SDEV_BUILD; 4992621Sllai1 dv->sdev_flags &= ~SDEV_PERSIST; 5002621Sllai1 bzero(&dv->sdev_prof, sizeof (dv->sdev_prof)); 5012621Sllai1 dv->sdev_ldir_gen = 0; 5022621Sllai1 dv->sdev_devtree_gen = 0; 5032621Sllai1 } 5042621Sllai1 5056260Sjg avl_create(&dv->sdev_entries, 5066260Sjg (int (*)(const void *, const void *))sdev_compare_nodes, 5076260Sjg sizeof (struct sdev_node), 5086260Sjg offsetof(struct sdev_node, sdev_avllink)); 5096260Sjg 5102621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 5112621Sllai1 sdev_set_nodestate(dv, SDEV_READY); 5122621Sllai1 rw_exit(&dv->sdev_contents); 5132621Sllai1 sdev_nc_node_exists(dv); 5142621Sllai1 return (dv); 5152621Sllai1 } 5162621Sllai1 5172621Sllai1 /* directory dependent vop table */ 5182621Sllai1 struct sdev_vop_table { 5192621Sllai1 char *vt_name; /* subdirectory name */ 5202621Sllai1 const fs_operation_def_t *vt_service; /* vnodeops table */ 5212621Sllai1 struct vnodeops *vt_vops; /* constructed vop */ 5222621Sllai1 struct vnodeops **vt_global_vops; /* global container for vop */ 5232621Sllai1 int (*vt_vtor)(struct sdev_node *); /* validate sdev_node */ 5242621Sllai1 int vt_flags; 5252621Sllai1 }; 5262621Sllai1 5272621Sllai1 /* 5282621Sllai1 * A nice improvement would be to provide a plug-in mechanism 5292621Sllai1 * for this table instead of a const table. 5302621Sllai1 */ 5312621Sllai1 static struct sdev_vop_table vtab[] = 5322621Sllai1 { 5332621Sllai1 { "pts", devpts_vnodeops_tbl, NULL, &devpts_vnodeops, devpts_validate, 5342621Sllai1 SDEV_DYNAMIC | SDEV_VTOR }, 5352621Sllai1 5367688SAaron.Zang@Sun.COM { "vt", devvt_vnodeops_tbl, NULL, &devvt_vnodeops, devvt_validate, 5377688SAaron.Zang@Sun.COM SDEV_DYNAMIC | SDEV_VTOR }, 5387688SAaron.Zang@Sun.COM 53910588SEric.Taylor@Sun.COM { "zvol", devzvol_vnodeops_tbl, NULL, &devzvol_vnodeops, 54010588SEric.Taylor@Sun.COM devzvol_validate, SDEV_DYNAMIC | SDEV_VTOR | SDEV_SUBDIR }, 54110588SEric.Taylor@Sun.COM 5422621Sllai1 { "zcons", NULL, NULL, NULL, NULL, SDEV_NO_NCACHE }, 5432621Sllai1 5445895Syz147064 { "net", devnet_vnodeops_tbl, NULL, &devnet_vnodeops, devnet_validate, 5455895Syz147064 SDEV_DYNAMIC | SDEV_VTOR }, 5465895Syz147064 5478023SPhil.Kirk@Sun.COM { "ipnet", devipnet_vnodeops_tbl, NULL, &devipnet_vnodeops, 5488023SPhil.Kirk@Sun.COM devipnet_validate, SDEV_DYNAMIC | SDEV_VTOR | SDEV_NO_NCACHE }, 5498023SPhil.Kirk@Sun.COM 55012633Sjohn.levon@sun.com { "lofi", NULL, NULL, NULL, NULL, SDEV_ZONED }, 55112633Sjohn.levon@sun.com { "rlofi", NULL, NULL, NULL, NULL, SDEV_ZONED }, 55212633Sjohn.levon@sun.com 5532621Sllai1 { NULL, NULL, NULL, NULL, NULL, 0} 5542621Sllai1 }; 5552621Sllai1 55610588SEric.Taylor@Sun.COM struct sdev_vop_table * 55710588SEric.Taylor@Sun.COM sdev_match(struct sdev_node *dv) 55810588SEric.Taylor@Sun.COM { 55910588SEric.Taylor@Sun.COM int vlen; 56010588SEric.Taylor@Sun.COM int i; 56110588SEric.Taylor@Sun.COM 56210588SEric.Taylor@Sun.COM for (i = 0; vtab[i].vt_name; i++) { 56310588SEric.Taylor@Sun.COM if (strcmp(vtab[i].vt_name, dv->sdev_name) == 0) 56410588SEric.Taylor@Sun.COM return (&vtab[i]); 56510588SEric.Taylor@Sun.COM if (vtab[i].vt_flags & SDEV_SUBDIR) { 56610588SEric.Taylor@Sun.COM char *ptr; 56710588SEric.Taylor@Sun.COM 56810588SEric.Taylor@Sun.COM ASSERT(strlen(dv->sdev_path) > 5); 56910588SEric.Taylor@Sun.COM ptr = dv->sdev_path + 5; 57010588SEric.Taylor@Sun.COM vlen = strlen(vtab[i].vt_name); 57110588SEric.Taylor@Sun.COM if ((strncmp(vtab[i].vt_name, ptr, 57210588SEric.Taylor@Sun.COM vlen - 1) == 0) && ptr[vlen] == '/') 57310588SEric.Taylor@Sun.COM return (&vtab[i]); 57410588SEric.Taylor@Sun.COM } 57510588SEric.Taylor@Sun.COM 57610588SEric.Taylor@Sun.COM } 57710588SEric.Taylor@Sun.COM return (NULL); 57810588SEric.Taylor@Sun.COM } 5792621Sllai1 5802621Sllai1 /* 5812621Sllai1 * sets a directory's vnodeops if the directory is in the vtab; 5822621Sllai1 */ 5832621Sllai1 static struct vnodeops * 5842621Sllai1 sdev_get_vop(struct sdev_node *dv) 5852621Sllai1 { 58610588SEric.Taylor@Sun.COM struct sdev_vop_table *vtp; 5872621Sllai1 char *path; 5882621Sllai1 5892621Sllai1 path = dv->sdev_path; 5902621Sllai1 ASSERT(path); 5912621Sllai1 5922621Sllai1 /* gets the relative path to /dev/ */ 5932621Sllai1 path += 5; 5942621Sllai1 59510588SEric.Taylor@Sun.COM /* gets the vtab entry it matches */ 59610588SEric.Taylor@Sun.COM if ((vtp = sdev_match(dv)) != NULL) { 59710588SEric.Taylor@Sun.COM dv->sdev_flags |= vtp->vt_flags; 59810588SEric.Taylor@Sun.COM 59910588SEric.Taylor@Sun.COM if (vtp->vt_vops) { 60010588SEric.Taylor@Sun.COM if (vtp->vt_global_vops) 60110588SEric.Taylor@Sun.COM *(vtp->vt_global_vops) = vtp->vt_vops; 60210588SEric.Taylor@Sun.COM return (vtp->vt_vops); 6032621Sllai1 } 6042621Sllai1 60510588SEric.Taylor@Sun.COM if (vtp->vt_service) { 6062621Sllai1 fs_operation_def_t *templ; 60710588SEric.Taylor@Sun.COM templ = sdev_merge_vtab(vtp->vt_service); 60810588SEric.Taylor@Sun.COM if (vn_make_ops(vtp->vt_name, 6092621Sllai1 (const fs_operation_def_t *)templ, 61010588SEric.Taylor@Sun.COM &vtp->vt_vops) != 0) { 6112621Sllai1 cmn_err(CE_PANIC, "%s: malformed vnode ops\n", 61210588SEric.Taylor@Sun.COM vtp->vt_name); 6132621Sllai1 /*NOTREACHED*/ 6142621Sllai1 } 61510588SEric.Taylor@Sun.COM if (vtp->vt_global_vops) { 61610588SEric.Taylor@Sun.COM *(vtp->vt_global_vops) = vtp->vt_vops; 6172621Sllai1 } 6182621Sllai1 sdev_free_vtab(templ); 61910588SEric.Taylor@Sun.COM return (vtp->vt_vops); 6202621Sllai1 } 6212621Sllai1 return (sdev_vnodeops); 6222621Sllai1 } 6232621Sllai1 6242621Sllai1 /* child inherits the persistence of the parent */ 6252621Sllai1 if (SDEV_IS_PERSIST(dv->sdev_dotdot)) 6262621Sllai1 dv->sdev_flags |= SDEV_PERSIST; 6272621Sllai1 6282621Sllai1 return (sdev_vnodeops); 6292621Sllai1 } 6302621Sllai1 6312621Sllai1 static void 63210588SEric.Taylor@Sun.COM sdev_set_no_negcache(struct sdev_node *dv) 6332621Sllai1 { 6342621Sllai1 int i; 6352621Sllai1 char *path; 6362621Sllai1 6372621Sllai1 ASSERT(dv->sdev_path); 6382621Sllai1 path = dv->sdev_path + strlen("/dev/"); 6392621Sllai1 6402621Sllai1 for (i = 0; vtab[i].vt_name; i++) { 6412621Sllai1 if (strcmp(vtab[i].vt_name, path) == 0) { 6422621Sllai1 if (vtab[i].vt_flags & SDEV_NO_NCACHE) 6432621Sllai1 dv->sdev_flags |= SDEV_NO_NCACHE; 6442621Sllai1 break; 6452621Sllai1 } 6462621Sllai1 } 6472621Sllai1 } 6482621Sllai1 6492621Sllai1 void * 6502621Sllai1 sdev_get_vtor(struct sdev_node *dv) 6512621Sllai1 { 65210588SEric.Taylor@Sun.COM struct sdev_vop_table *vtp; 65310588SEric.Taylor@Sun.COM 65410588SEric.Taylor@Sun.COM vtp = sdev_match(dv); 65510588SEric.Taylor@Sun.COM if (vtp) 65610588SEric.Taylor@Sun.COM return ((void *)vtp->vt_vtor); 65710588SEric.Taylor@Sun.COM else 65810588SEric.Taylor@Sun.COM return (NULL); 6592621Sllai1 } 6602621Sllai1 6612621Sllai1 /* 6622621Sllai1 * Build the base root inode 6632621Sllai1 */ 6642621Sllai1 ino_t 6652621Sllai1 sdev_mkino(struct sdev_node *dv) 6662621Sllai1 { 6672621Sllai1 ino_t ino; 6682621Sllai1 6692621Sllai1 /* 6702621Sllai1 * for now, follow the lead of tmpfs here 6712621Sllai1 * need to someday understand the requirements here 6722621Sllai1 */ 6732621Sllai1 ino = (ino_t)(uint32_t)((uintptr_t)dv >> 3); 6742621Sllai1 ino += SDEV_ROOTINO + 1; 6752621Sllai1 6762621Sllai1 return (ino); 6772621Sllai1 } 6782621Sllai1 67910588SEric.Taylor@Sun.COM int 6802621Sllai1 sdev_getlink(struct vnode *linkvp, char **link) 6812621Sllai1 { 6822621Sllai1 int err; 6832621Sllai1 char *buf; 6842621Sllai1 struct uio uio = {0}; 6852621Sllai1 struct iovec iov = {0}; 6862621Sllai1 6872621Sllai1 if (linkvp == NULL) 6882621Sllai1 return (ENOENT); 6892621Sllai1 ASSERT(linkvp->v_type == VLNK); 6902621Sllai1 6912621Sllai1 buf = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 6922621Sllai1 iov.iov_base = buf; 6932621Sllai1 iov.iov_len = MAXPATHLEN; 6942621Sllai1 uio.uio_iov = &iov; 6952621Sllai1 uio.uio_iovcnt = 1; 6962621Sllai1 uio.uio_resid = MAXPATHLEN; 6972621Sllai1 uio.uio_segflg = UIO_SYSSPACE; 6982621Sllai1 uio.uio_llimit = MAXOFFSET_T; 6992621Sllai1 7005331Samw err = VOP_READLINK(linkvp, &uio, kcred, NULL); 7012621Sllai1 if (err) { 7022621Sllai1 cmn_err(CE_WARN, "readlink %s failed in dev\n", buf); 7032621Sllai1 kmem_free(buf, MAXPATHLEN); 7042621Sllai1 return (ENOENT); 7052621Sllai1 } 7062621Sllai1 7072621Sllai1 /* mission complete */ 7082621Sllai1 *link = i_ddi_strdup(buf, KM_SLEEP); 7092621Sllai1 kmem_free(buf, MAXPATHLEN); 7102621Sllai1 return (0); 7112621Sllai1 } 7122621Sllai1 7132621Sllai1 /* 7142621Sllai1 * A convenient wrapper to get the devfs node vnode for a device 7152621Sllai1 * minor functionality: readlink() of a /dev symlink 7162621Sllai1 * Place the link into dv->sdev_symlink 7172621Sllai1 */ 7182621Sllai1 static int 7192621Sllai1 sdev_follow_link(struct sdev_node *dv) 7202621Sllai1 { 7212621Sllai1 int err; 7222621Sllai1 struct vnode *linkvp; 7232621Sllai1 char *link = NULL; 7242621Sllai1 7252621Sllai1 linkvp = SDEVTOV(dv); 7262621Sllai1 if (linkvp == NULL) 7272621Sllai1 return (ENOENT); 7282621Sllai1 ASSERT(linkvp->v_type == VLNK); 7292621Sllai1 err = sdev_getlink(linkvp, &link); 7302621Sllai1 if (err) { 7312621Sllai1 (void) sdev_nodezombied(dv); 7322621Sllai1 dv->sdev_symlink = NULL; 7332621Sllai1 return (ENOENT); 7342621Sllai1 } 7352621Sllai1 7362621Sllai1 ASSERT(link != NULL); 7372621Sllai1 dv->sdev_symlink = link; 7382621Sllai1 return (0); 7392621Sllai1 } 7402621Sllai1 7412621Sllai1 static int 7422621Sllai1 sdev_node_check(struct sdev_node *dv, struct vattr *nvap, void *nargs) 7432621Sllai1 { 7442621Sllai1 vtype_t otype = SDEVTOV(dv)->v_type; 7452621Sllai1 7462621Sllai1 /* 7472621Sllai1 * existing sdev_node has a different type. 7482621Sllai1 */ 7492621Sllai1 if (otype != nvap->va_type) { 7502621Sllai1 sdcmn_err9(("sdev_node_check: existing node " 7512621Sllai1 " %s type %d does not match new node type %d\n", 7522621Sllai1 dv->sdev_name, otype, nvap->va_type)); 7532621Sllai1 return (EEXIST); 7542621Sllai1 } 7552621Sllai1 7562621Sllai1 /* 7572621Sllai1 * For a symlink, the target should be the same. 7582621Sllai1 */ 7592621Sllai1 if (otype == VLNK) { 7602621Sllai1 ASSERT(nargs != NULL); 7612621Sllai1 ASSERT(dv->sdev_symlink != NULL); 7622621Sllai1 if (strcmp(dv->sdev_symlink, (char *)nargs) != 0) { 7632621Sllai1 sdcmn_err9(("sdev_node_check: existing node " 7642621Sllai1 " %s has different symlink %s as new node " 7652621Sllai1 " %s\n", dv->sdev_name, dv->sdev_symlink, 7662621Sllai1 (char *)nargs)); 7672621Sllai1 return (EEXIST); 7682621Sllai1 } 7692621Sllai1 } 7702621Sllai1 7712621Sllai1 return (0); 7722621Sllai1 } 7732621Sllai1 7742621Sllai1 /* 7752621Sllai1 * sdev_mknode - a wrapper for sdev_nodeinit(), sdev_nodeready() 7762621Sllai1 * 7772621Sllai1 * arguments: 7782621Sllai1 * - ddv (parent) 7792621Sllai1 * - nm (child name) 7802621Sllai1 * - newdv (sdev_node for nm is returned here) 7812621Sllai1 * - vap (vattr for the node to be created, va_type should be set. 7826335Sjg * - avp (attribute vnode) 7832621Sllai1 * the defaults should be used if unknown) 7842621Sllai1 * - cred 7852621Sllai1 * - args 7862621Sllai1 * . tnm (for VLNK) 7872621Sllai1 * . global sdev_node (for !SDEV_GLOBAL) 7882621Sllai1 * - state: SDEV_INIT, SDEV_READY 7892621Sllai1 * 7902621Sllai1 * only ddv, nm, newddv, vap, cred are required for sdev_mknode(SDEV_INIT) 7912621Sllai1 * 7922621Sllai1 * NOTE: directory contents writers lock needs to be held before 7932621Sllai1 * calling this routine. 7942621Sllai1 */ 7952621Sllai1 int 7962621Sllai1 sdev_mknode(struct sdev_node *ddv, char *nm, struct sdev_node **newdv, 7972621Sllai1 struct vattr *vap, struct vnode *avp, void *args, struct cred *cred, 7982621Sllai1 sdev_node_state_t state) 7992621Sllai1 { 8002621Sllai1 int error = 0; 8012621Sllai1 sdev_node_state_t node_state; 8022621Sllai1 struct sdev_node *dv = NULL; 8032621Sllai1 8042621Sllai1 ASSERT(state != SDEV_ZOMBIE); 8052621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 8062621Sllai1 8072621Sllai1 if (*newdv) { 8082621Sllai1 dv = *newdv; 8092621Sllai1 } else { 8102621Sllai1 /* allocate and initialize a sdev_node */ 8112621Sllai1 if (ddv->sdev_state == SDEV_ZOMBIE) { 8122621Sllai1 sdcmn_err9(("sdev_mknode: parent %s ZOMBIEd\n", 8132621Sllai1 ddv->sdev_path)); 8142621Sllai1 return (ENOENT); 8152621Sllai1 } 8162621Sllai1 8172621Sllai1 error = sdev_nodeinit(ddv, nm, &dv, vap); 8182621Sllai1 if (error != 0) { 8192621Sllai1 sdcmn_err9(("sdev_mknode: error %d," 8202621Sllai1 " name %s can not be initialized\n", 8212621Sllai1 error, nm)); 8226335Sjg return (error); 8232621Sllai1 } 8242621Sllai1 ASSERT(dv); 8252621Sllai1 8262621Sllai1 /* insert into the directory cache */ 8272621Sllai1 error = sdev_cache_update(ddv, &dv, nm, SDEV_CACHE_ADD); 8282621Sllai1 if (error) { 8292621Sllai1 sdcmn_err9(("sdev_mknode: node %s can not" 8302621Sllai1 " be added into directory cache\n", nm)); 8312621Sllai1 return (ENOENT); 8322621Sllai1 } 8332621Sllai1 } 8342621Sllai1 8352621Sllai1 ASSERT(dv); 8362621Sllai1 node_state = dv->sdev_state; 8372621Sllai1 ASSERT(node_state != SDEV_ZOMBIE); 8382621Sllai1 8392621Sllai1 if (state == SDEV_READY) { 8402621Sllai1 switch (node_state) { 8412621Sllai1 case SDEV_INIT: 8422621Sllai1 error = sdev_nodeready(dv, vap, avp, args, cred); 8432621Sllai1 if (error) { 8442621Sllai1 sdcmn_err9(("sdev_mknode: node %s can NOT" 8452621Sllai1 " be transitioned into READY state, " 8462621Sllai1 "error %d\n", nm, error)); 8472621Sllai1 } 8482621Sllai1 break; 8492621Sllai1 case SDEV_READY: 8502621Sllai1 /* 8512621Sllai1 * Do some sanity checking to make sure 8522621Sllai1 * the existing sdev_node is what has been 8532621Sllai1 * asked for. 8542621Sllai1 */ 8552621Sllai1 error = sdev_node_check(dv, vap, args); 8562621Sllai1 break; 8572621Sllai1 default: 8582621Sllai1 break; 8592621Sllai1 } 8602621Sllai1 } 8612621Sllai1 8622621Sllai1 if (!error) { 8632621Sllai1 *newdv = dv; 8642621Sllai1 ASSERT((*newdv)->sdev_state != SDEV_ZOMBIE); 8652621Sllai1 } else { 8662621Sllai1 SDEV_SIMPLE_RELE(dv); 8672621Sllai1 *newdv = NULL; 8682621Sllai1 } 8692621Sllai1 8702621Sllai1 return (error); 8712621Sllai1 } 8722621Sllai1 8732621Sllai1 /* 8746335Sjg * convenient wrapper to change vp's ATIME, CTIME and MTIME 8752621Sllai1 */ 8762621Sllai1 void 8772621Sllai1 sdev_update_timestamps(struct vnode *vp, cred_t *cred, uint_t mask) 8782621Sllai1 { 8792621Sllai1 struct vattr attr; 8802621Sllai1 timestruc_t now; 8812621Sllai1 int err; 8822621Sllai1 8832621Sllai1 ASSERT(vp); 8842621Sllai1 gethrestime(&now); 8852621Sllai1 if (mask & AT_CTIME) 8862621Sllai1 attr.va_ctime = now; 8872621Sllai1 if (mask & AT_MTIME) 8882621Sllai1 attr.va_mtime = now; 8892621Sllai1 if (mask & AT_ATIME) 8902621Sllai1 attr.va_atime = now; 8912621Sllai1 8922621Sllai1 attr.va_mask = (mask & AT_TIMES); 8932621Sllai1 err = VOP_SETATTR(vp, &attr, 0, cred, NULL); 8942621Sllai1 if (err && (err != EROFS)) { 8952621Sllai1 sdcmn_err(("update timestamps error %d\n", err)); 8962621Sllai1 } 8972621Sllai1 } 8982621Sllai1 8992621Sllai1 /* 9002621Sllai1 * the backing store vnode is released here 9012621Sllai1 */ 9022621Sllai1 /*ARGSUSED1*/ 9032621Sllai1 void 9042621Sllai1 sdev_nodedestroy(struct sdev_node *dv, uint_t flags) 9052621Sllai1 { 9062621Sllai1 /* no references */ 9072621Sllai1 ASSERT(dv->sdev_nlink == 0); 9082621Sllai1 9092621Sllai1 if (dv->sdev_attrvp != NULLVP) { 9102621Sllai1 VN_RELE(dv->sdev_attrvp); 9112621Sllai1 /* 9122621Sllai1 * reset the attrvp so that no more 9132621Sllai1 * references can be made on this already 9142621Sllai1 * vn_rele() vnode 9152621Sllai1 */ 9162621Sllai1 dv->sdev_attrvp = NULLVP; 9172621Sllai1 } 9182621Sllai1 9192621Sllai1 if (dv->sdev_attr != NULL) { 9202621Sllai1 kmem_free(dv->sdev_attr, sizeof (struct vattr)); 9212621Sllai1 dv->sdev_attr = NULL; 9222621Sllai1 } 9232621Sllai1 9242621Sllai1 if (dv->sdev_name != NULL) { 9252621Sllai1 kmem_free(dv->sdev_name, dv->sdev_namelen + 1); 9262621Sllai1 dv->sdev_name = NULL; 9272621Sllai1 } 9282621Sllai1 9292621Sllai1 if (dv->sdev_symlink != NULL) { 9302621Sllai1 kmem_free(dv->sdev_symlink, strlen(dv->sdev_symlink) + 1); 9312621Sllai1 dv->sdev_symlink = NULL; 9322621Sllai1 } 9332621Sllai1 9342621Sllai1 if (dv->sdev_path) { 9352621Sllai1 kmem_free(dv->sdev_path, strlen(dv->sdev_path) + 1); 9362621Sllai1 dv->sdev_path = NULL; 9372621Sllai1 } 9382621Sllai1 9392621Sllai1 if (!SDEV_IS_GLOBAL(dv)) 9402621Sllai1 sdev_prof_free(dv); 9412621Sllai1 9426260Sjg if (SDEVTOV(dv)->v_type == VDIR) { 9436260Sjg ASSERT(SDEV_FIRST_ENTRY(dv) == NULL); 9446260Sjg avl_destroy(&dv->sdev_entries); 9456260Sjg } 9466260Sjg 9472621Sllai1 mutex_destroy(&dv->sdev_lookup_lock); 9482621Sllai1 cv_destroy(&dv->sdev_lookup_cv); 9492621Sllai1 9502621Sllai1 /* return node to initial state as per constructor */ 9512621Sllai1 (void) memset((void *)&dv->sdev_instance_data, 0, 9522621Sllai1 sizeof (dv->sdev_instance_data)); 9532621Sllai1 vn_invalid(SDEVTOV(dv)); 9542621Sllai1 kmem_cache_free(sdev_node_cache, dv); 9552621Sllai1 } 9562621Sllai1 9572621Sllai1 /* 9582621Sllai1 * DIRECTORY CACHE lookup 9592621Sllai1 */ 9602621Sllai1 struct sdev_node * 9612621Sllai1 sdev_findbyname(struct sdev_node *ddv, char *nm) 9622621Sllai1 { 9632621Sllai1 struct sdev_node *dv; 9646260Sjg struct sdev_node dvtmp; 9656260Sjg avl_index_t where; 9662621Sllai1 9672621Sllai1 ASSERT(RW_LOCK_HELD(&ddv->sdev_contents)); 9686260Sjg 9696260Sjg dvtmp.sdev_name = nm; 9706260Sjg dv = avl_find(&ddv->sdev_entries, &dvtmp, &where); 9716260Sjg if (dv) { 9726260Sjg ASSERT(dv->sdev_dotdot == ddv); 9736260Sjg ASSERT(strcmp(dv->sdev_name, nm) == 0); 9746347Sjg SDEV_HOLD(dv); 9756347Sjg return (dv); 9762621Sllai1 } 9772621Sllai1 return (NULL); 9782621Sllai1 } 9792621Sllai1 9802621Sllai1 /* 9812621Sllai1 * Inserts a new sdev_node in a parent directory 9822621Sllai1 */ 9832621Sllai1 void 9842621Sllai1 sdev_direnter(struct sdev_node *ddv, struct sdev_node *dv) 9852621Sllai1 { 9866260Sjg avl_index_t where; 9876260Sjg 9882621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 9892621Sllai1 ASSERT(SDEVTOV(ddv)->v_type == VDIR); 9902621Sllai1 ASSERT(ddv->sdev_nlink >= 2); 9912621Sllai1 ASSERT(dv->sdev_nlink == 0); 9922621Sllai1 9932621Sllai1 dv->sdev_dotdot = ddv; 9946260Sjg VERIFY(avl_find(&ddv->sdev_entries, dv, &where) == NULL); 9956260Sjg avl_insert(&ddv->sdev_entries, dv, where); 9962621Sllai1 ddv->sdev_nlink++; 9972621Sllai1 } 9982621Sllai1 9992621Sllai1 /* 10002621Sllai1 * The following check is needed because while sdev_nodes are linked 10012621Sllai1 * in SDEV_INIT state, they have their link counts incremented only 10022621Sllai1 * in SDEV_READY state. 10032621Sllai1 */ 10042621Sllai1 static void 10052621Sllai1 decr_link(struct sdev_node *dv) 10062621Sllai1 { 10072621Sllai1 if (dv->sdev_state != SDEV_INIT) 10082621Sllai1 dv->sdev_nlink--; 10092621Sllai1 else 10102621Sllai1 ASSERT(dv->sdev_nlink == 0); 10112621Sllai1 } 10122621Sllai1 10132621Sllai1 /* 10142621Sllai1 * Delete an existing dv from directory cache 10152621Sllai1 * 10162621Sllai1 * In the case of a node is still held by non-zero reference count, 10172621Sllai1 * the node is put into ZOMBIE state. Once the reference count 10182621Sllai1 * reaches "0", the node is unlinked and destroyed, 10192621Sllai1 * in sdev_inactive(). 10202621Sllai1 */ 10212621Sllai1 static int 10222621Sllai1 sdev_dirdelete(struct sdev_node *ddv, struct sdev_node *dv) 10232621Sllai1 { 10242621Sllai1 struct vnode *vp; 10252621Sllai1 10262621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 10272621Sllai1 10282621Sllai1 vp = SDEVTOV(dv); 10292621Sllai1 mutex_enter(&vp->v_lock); 10302621Sllai1 10312621Sllai1 /* dv is held still */ 10322621Sllai1 if (vp->v_count > 1) { 10332621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 10342621Sllai1 if (dv->sdev_state == SDEV_READY) { 10352621Sllai1 sdcmn_err9(( 103610588SEric.Taylor@Sun.COM "sdev_dirdelete: node %s busy with count %d\n", 10372621Sllai1 dv->sdev_name, vp->v_count)); 10382621Sllai1 dv->sdev_state = SDEV_ZOMBIE; 10392621Sllai1 } 10402621Sllai1 rw_exit(&dv->sdev_contents); 10412621Sllai1 --vp->v_count; 10422621Sllai1 mutex_exit(&vp->v_lock); 10432621Sllai1 return (EBUSY); 10442621Sllai1 } 10452621Sllai1 ASSERT(vp->v_count == 1); 10462621Sllai1 10472621Sllai1 /* unlink from the memory cache */ 10482621Sllai1 ddv->sdev_nlink--; /* .. to above */ 10492621Sllai1 if (vp->v_type == VDIR) { 10502621Sllai1 decr_link(dv); /* . to self */ 10512621Sllai1 } 10522621Sllai1 10536260Sjg avl_remove(&ddv->sdev_entries, dv); 10542621Sllai1 decr_link(dv); /* name, back to zero */ 10552621Sllai1 vp->v_count--; 10562621Sllai1 mutex_exit(&vp->v_lock); 10572621Sllai1 10582621Sllai1 /* destroy the node */ 10592621Sllai1 sdev_nodedestroy(dv, 0); 10602621Sllai1 return (0); 10612621Sllai1 } 10622621Sllai1 10632621Sllai1 /* 10642621Sllai1 * check if the source is in the path of the target 10652621Sllai1 * 10662621Sllai1 * source and target are different 10672621Sllai1 */ 10682621Sllai1 /*ARGSUSED2*/ 10692621Sllai1 static int 10702621Sllai1 sdev_checkpath(struct sdev_node *sdv, struct sdev_node *tdv, struct cred *cred) 10712621Sllai1 { 10722621Sllai1 int error = 0; 10732621Sllai1 struct sdev_node *dotdot, *dir; 10742621Sllai1 10752621Sllai1 dotdot = tdv->sdev_dotdot; 10762621Sllai1 ASSERT(dotdot); 10772621Sllai1 10782621Sllai1 /* fs root */ 10792621Sllai1 if (dotdot == tdv) { 10802621Sllai1 return (0); 10812621Sllai1 } 10822621Sllai1 10832621Sllai1 for (;;) { 10842621Sllai1 /* 10852621Sllai1 * avoid error cases like 10862621Sllai1 * mv a a/b 10872621Sllai1 * mv a a/b/c 10882621Sllai1 * etc. 10892621Sllai1 */ 10902621Sllai1 if (dotdot == sdv) { 10912621Sllai1 error = EINVAL; 10922621Sllai1 break; 10932621Sllai1 } 10942621Sllai1 10952621Sllai1 dir = dotdot; 10962621Sllai1 dotdot = dir->sdev_dotdot; 10972621Sllai1 10982621Sllai1 /* done checking because root is reached */ 10992621Sllai1 if (dir == dotdot) { 11002621Sllai1 break; 11012621Sllai1 } 11022621Sllai1 } 11032621Sllai1 return (error); 11042621Sllai1 } 11052621Sllai1 11062621Sllai1 int 11072621Sllai1 sdev_rnmnode(struct sdev_node *oddv, struct sdev_node *odv, 11082621Sllai1 struct sdev_node *nddv, struct sdev_node **ndvp, char *nnm, 11092621Sllai1 struct cred *cred) 11102621Sllai1 { 11112621Sllai1 int error = 0; 11122621Sllai1 struct vnode *ovp = SDEVTOV(odv); 11132621Sllai1 struct vnode *nvp; 11142621Sllai1 struct vattr vattr; 11152621Sllai1 int doingdir = (ovp->v_type == VDIR); 11162621Sllai1 char *link = NULL; 11172729Sllai1 int samedir = (oddv == nddv) ? 1 : 0; 11182729Sllai1 int bkstore = 0; 11192729Sllai1 struct sdev_node *idv = NULL; 11202729Sllai1 struct sdev_node *ndv = NULL; 11212729Sllai1 timestruc_t now; 11222729Sllai1 112311279SJerry.Gilliam@Sun.COM vattr.va_mask = AT_TYPE|AT_MODE|AT_UID|AT_GID; 11245331Samw error = VOP_GETATTR(ovp, &vattr, 0, cred, NULL); 11252729Sllai1 if (error) 11262729Sllai1 return (error); 11272729Sllai1 11282729Sllai1 if (!samedir) 11292729Sllai1 rw_enter(&oddv->sdev_contents, RW_WRITER); 11302729Sllai1 rw_enter(&nddv->sdev_contents, RW_WRITER); 11312729Sllai1 11322729Sllai1 /* 11332729Sllai1 * the source may have been deleted by another thread before 11342729Sllai1 * we gets here. 11352729Sllai1 */ 11362729Sllai1 if (odv->sdev_state != SDEV_READY) { 11372729Sllai1 error = ENOENT; 11382729Sllai1 goto err_out; 11392729Sllai1 } 11402729Sllai1 11412729Sllai1 if (doingdir && (odv == nddv)) { 11422729Sllai1 error = EINVAL; 11432729Sllai1 goto err_out; 11442729Sllai1 } 11452621Sllai1 11462621Sllai1 /* 11472621Sllai1 * If renaming a directory, and the parents are different (".." must be 11482621Sllai1 * changed) then the source dir must not be in the dir hierarchy above 11492621Sllai1 * the target since it would orphan everything below the source dir. 11502621Sllai1 */ 11512621Sllai1 if (doingdir && (oddv != nddv)) { 11522621Sllai1 error = sdev_checkpath(odv, nddv, cred); 11532621Sllai1 if (error) 11542729Sllai1 goto err_out; 11552621Sllai1 } 11562621Sllai1 11572729Sllai1 /* destination existing */ 11582621Sllai1 if (*ndvp) { 11592621Sllai1 nvp = SDEVTOV(*ndvp); 11602621Sllai1 ASSERT(nvp); 11612621Sllai1 11622621Sllai1 /* handling renaming to itself */ 11632729Sllai1 if (odv == *ndvp) { 11642729Sllai1 error = 0; 11652729Sllai1 goto err_out; 11662729Sllai1 } 11672729Sllai1 11682729Sllai1 if (nvp->v_type == VDIR) { 11692729Sllai1 if (!doingdir) { 11702729Sllai1 error = EISDIR; 11712729Sllai1 goto err_out; 11722729Sllai1 } 11732729Sllai1 11742729Sllai1 if (vn_vfswlock(nvp)) { 11752729Sllai1 error = EBUSY; 11762729Sllai1 goto err_out; 11772729Sllai1 } 11782729Sllai1 11792729Sllai1 if (vn_mountedvfs(nvp) != NULL) { 11802729Sllai1 vn_vfsunlock(nvp); 11812729Sllai1 error = EBUSY; 11822729Sllai1 goto err_out; 11832729Sllai1 } 11842729Sllai1 11852729Sllai1 /* in case dir1 exists in dir2 and "mv dir1 dir2" */ 11862729Sllai1 if ((*ndvp)->sdev_nlink > 2) { 11872729Sllai1 vn_vfsunlock(nvp); 11882729Sllai1 error = EEXIST; 11892729Sllai1 goto err_out; 11902729Sllai1 } 11912729Sllai1 vn_vfsunlock(nvp); 11922729Sllai1 11932729Sllai1 (void) sdev_dirdelete(nddv, *ndvp); 11942729Sllai1 *ndvp = NULL; 11956335Sjg ASSERT(nddv->sdev_attrvp); 11962729Sllai1 error = VOP_RMDIR(nddv->sdev_attrvp, nnm, 11976065Scth nddv->sdev_attrvp, cred, NULL, 0); 11982729Sllai1 if (error) 11992729Sllai1 goto err_out; 12002729Sllai1 } else { 12012729Sllai1 if (doingdir) { 12022729Sllai1 error = ENOTDIR; 12032729Sllai1 goto err_out; 12042729Sllai1 } 12052729Sllai1 12062729Sllai1 if (SDEV_IS_PERSIST((*ndvp))) { 12072729Sllai1 bkstore = 1; 12082729Sllai1 } 12092621Sllai1 12102621Sllai1 /* 12112729Sllai1 * get rid of the node from the directory cache 12122729Sllai1 * note, in case EBUSY is returned, the ZOMBIE 12132729Sllai1 * node is taken care in sdev_mknode. 12142621Sllai1 */ 12152729Sllai1 (void) sdev_dirdelete(nddv, *ndvp); 12162729Sllai1 *ndvp = NULL; 12172729Sllai1 if (bkstore) { 12186335Sjg ASSERT(nddv->sdev_attrvp); 12192729Sllai1 error = VOP_REMOVE(nddv->sdev_attrvp, 12205331Samw nnm, cred, NULL, 0); 12212729Sllai1 if (error) 12226065Scth goto err_out; 12232621Sllai1 } 12242621Sllai1 } 12252621Sllai1 } 12262621Sllai1 12272621Sllai1 /* fix the source for a symlink */ 12282621Sllai1 if (vattr.va_type == VLNK) { 12292621Sllai1 if (odv->sdev_symlink == NULL) { 12302621Sllai1 error = sdev_follow_link(odv); 12312729Sllai1 if (error) { 12322729Sllai1 error = ENOENT; 12332729Sllai1 goto err_out; 12342729Sllai1 } 12352621Sllai1 } 12362621Sllai1 ASSERT(odv->sdev_symlink); 12372621Sllai1 link = i_ddi_strdup(odv->sdev_symlink, KM_SLEEP); 12382621Sllai1 } 12392621Sllai1 12402729Sllai1 /* 12412729Sllai1 * make a fresh node from the source attrs 12422729Sllai1 */ 12432729Sllai1 ASSERT(RW_WRITE_HELD(&nddv->sdev_contents)); 12442729Sllai1 error = sdev_mknode(nddv, nnm, ndvp, &vattr, 12452729Sllai1 NULL, (void *)link, cred, SDEV_READY); 12462621Sllai1 12472621Sllai1 if (link) 12482621Sllai1 kmem_free(link, strlen(link) + 1); 12492621Sllai1 12502729Sllai1 if (error) 12512729Sllai1 goto err_out; 12522729Sllai1 ASSERT(*ndvp); 12532729Sllai1 ASSERT((*ndvp)->sdev_state == SDEV_READY); 12542729Sllai1 12552729Sllai1 /* move dir contents */ 12562729Sllai1 if (doingdir) { 12576260Sjg for (idv = SDEV_FIRST_ENTRY(odv); idv; 12586260Sjg idv = SDEV_NEXT_ENTRY(odv, idv)) { 12592729Sllai1 error = sdev_rnmnode(odv, idv, 12602729Sllai1 (struct sdev_node *)(*ndvp), &ndv, 12612729Sllai1 idv->sdev_name, cred); 12622729Sllai1 if (error) 12632729Sllai1 goto err_out; 12642729Sllai1 ndv = NULL; 12652729Sllai1 } 12662729Sllai1 } 12672729Sllai1 12682729Sllai1 if ((*ndvp)->sdev_attrvp) { 12692729Sllai1 sdev_update_timestamps((*ndvp)->sdev_attrvp, kcred, 12702729Sllai1 AT_CTIME|AT_ATIME); 12712729Sllai1 } else { 12722729Sllai1 ASSERT((*ndvp)->sdev_attr); 12732729Sllai1 gethrestime(&now); 12742729Sllai1 (*ndvp)->sdev_attr->va_ctime = now; 12752729Sllai1 (*ndvp)->sdev_attr->va_atime = now; 12762729Sllai1 } 12772729Sllai1 12782729Sllai1 if (nddv->sdev_attrvp) { 12792729Sllai1 sdev_update_timestamps(nddv->sdev_attrvp, kcred, 12802729Sllai1 AT_MTIME|AT_ATIME); 12812729Sllai1 } else { 12822729Sllai1 ASSERT(nddv->sdev_attr); 12832729Sllai1 gethrestime(&now); 12842729Sllai1 nddv->sdev_attr->va_mtime = now; 12852729Sllai1 nddv->sdev_attr->va_atime = now; 12862729Sllai1 } 12872729Sllai1 rw_exit(&nddv->sdev_contents); 12882729Sllai1 if (!samedir) 12892729Sllai1 rw_exit(&oddv->sdev_contents); 12902729Sllai1 12912621Sllai1 SDEV_RELE(*ndvp); 12922729Sllai1 return (error); 12932729Sllai1 12942729Sllai1 err_out: 12952729Sllai1 rw_exit(&nddv->sdev_contents); 12962729Sllai1 if (!samedir) 12972729Sllai1 rw_exit(&oddv->sdev_contents); 12982729Sllai1 return (error); 12992621Sllai1 } 13002621Sllai1 13012621Sllai1 /* 13022621Sllai1 * Merge sdev_node specific information into an attribute structure. 13032621Sllai1 * 13042621Sllai1 * note: sdev_node is not locked here 13052621Sllai1 */ 13062621Sllai1 void 13072621Sllai1 sdev_vattr_merge(struct sdev_node *dv, struct vattr *vap) 13082621Sllai1 { 13092621Sllai1 struct vnode *vp = SDEVTOV(dv); 13102621Sllai1 13112621Sllai1 vap->va_nlink = dv->sdev_nlink; 13122621Sllai1 vap->va_nodeid = dv->sdev_ino; 13132621Sllai1 vap->va_fsid = SDEVTOV(dv->sdev_dotdot)->v_rdev; 13142621Sllai1 vap->va_type = vp->v_type; 13152621Sllai1 13162621Sllai1 if (vp->v_type == VDIR) { 13172621Sllai1 vap->va_rdev = 0; 13182621Sllai1 vap->va_fsid = vp->v_rdev; 13192621Sllai1 } else if (vp->v_type == VLNK) { 13202621Sllai1 vap->va_rdev = 0; 13212621Sllai1 vap->va_mode &= ~S_IFMT; 13222621Sllai1 vap->va_mode |= S_IFLNK; 13232621Sllai1 } else if ((vp->v_type == VCHR) || (vp->v_type == VBLK)) { 13242621Sllai1 vap->va_rdev = vp->v_rdev; 13252621Sllai1 vap->va_mode &= ~S_IFMT; 13262621Sllai1 if (vap->va_type == VCHR) 13272621Sllai1 vap->va_mode |= S_IFCHR; 13282621Sllai1 else 13292621Sllai1 vap->va_mode |= S_IFBLK; 13302621Sllai1 } else { 13312621Sllai1 vap->va_rdev = 0; 13322621Sllai1 } 13332621Sllai1 } 13342621Sllai1 133510588SEric.Taylor@Sun.COM struct vattr * 13362621Sllai1 sdev_getdefault_attr(enum vtype type) 13372621Sllai1 { 13382621Sllai1 if (type == VDIR) 13392621Sllai1 return (&sdev_vattr_dir); 13402621Sllai1 else if (type == VCHR) 13412621Sllai1 return (&sdev_vattr_chr); 13422621Sllai1 else if (type == VBLK) 13432621Sllai1 return (&sdev_vattr_blk); 13442621Sllai1 else if (type == VLNK) 13452621Sllai1 return (&sdev_vattr_lnk); 13462621Sllai1 else 13472621Sllai1 return (NULL); 13482621Sllai1 } 13492621Sllai1 int 13502621Sllai1 sdev_to_vp(struct sdev_node *dv, struct vnode **vpp) 13512621Sllai1 { 13522621Sllai1 int rv = 0; 13532621Sllai1 struct vnode *vp = SDEVTOV(dv); 13542621Sllai1 13552621Sllai1 switch (vp->v_type) { 13562621Sllai1 case VCHR: 13572621Sllai1 case VBLK: 13582621Sllai1 /* 13592621Sllai1 * If vnode is a device, return special vnode instead 13602621Sllai1 * (though it knows all about -us- via sp->s_realvp) 13612621Sllai1 */ 13622621Sllai1 *vpp = specvp(vp, vp->v_rdev, vp->v_type, kcred); 13632621Sllai1 VN_RELE(vp); 13642621Sllai1 if (*vpp == NULLVP) 13652621Sllai1 rv = ENOSYS; 13662621Sllai1 break; 13672621Sllai1 default: /* most types are returned as is */ 13682621Sllai1 *vpp = vp; 13692621Sllai1 break; 13702621Sllai1 } 13712621Sllai1 return (rv); 13722621Sllai1 } 13732621Sllai1 13742621Sllai1 /* 13752621Sllai1 * junction between devname and root file system, e.g. ufs 13762621Sllai1 */ 13772621Sllai1 int 13782621Sllai1 devname_backstore_lookup(struct sdev_node *ddv, char *nm, struct vnode **rvp) 13792621Sllai1 { 13802621Sllai1 struct vnode *rdvp = ddv->sdev_attrvp; 13812621Sllai1 int rval = 0; 13822621Sllai1 13832621Sllai1 ASSERT(rdvp); 13842621Sllai1 13855331Samw rval = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, kcred, NULL, NULL, 13865331Samw NULL); 13872621Sllai1 return (rval); 13882621Sllai1 } 13892621Sllai1 13902621Sllai1 static int 13912621Sllai1 sdev_filldir_from_store(struct sdev_node *ddv, int dlen, struct cred *cred) 13922621Sllai1 { 13932621Sllai1 struct sdev_node *dv = NULL; 13942621Sllai1 char *nm; 13952621Sllai1 struct vnode *dirvp; 13962621Sllai1 int error; 13972621Sllai1 vnode_t *vp; 13982621Sllai1 int eof; 13992621Sllai1 struct iovec iov; 14002621Sllai1 struct uio uio; 14012621Sllai1 struct dirent64 *dp; 14022621Sllai1 dirent64_t *dbuf; 14032621Sllai1 size_t dbuflen; 14042621Sllai1 struct vattr vattr; 14052621Sllai1 char *link = NULL; 14062621Sllai1 14072621Sllai1 if (ddv->sdev_attrvp == NULL) 14082621Sllai1 return (0); 14092621Sllai1 if (!(ddv->sdev_flags & SDEV_BUILD)) 14102621Sllai1 return (0); 14112621Sllai1 14122621Sllai1 dirvp = ddv->sdev_attrvp; 14132621Sllai1 VN_HOLD(dirvp); 14142621Sllai1 dbuf = kmem_zalloc(dlen, KM_SLEEP); 14152621Sllai1 14162621Sllai1 uio.uio_iov = &iov; 14172621Sllai1 uio.uio_iovcnt = 1; 14182621Sllai1 uio.uio_segflg = UIO_SYSSPACE; 14192621Sllai1 uio.uio_fmode = 0; 14202621Sllai1 uio.uio_extflg = UIO_COPY_CACHED; 14212621Sllai1 uio.uio_loffset = 0; 14222621Sllai1 uio.uio_llimit = MAXOFFSET_T; 14232621Sllai1 14242621Sllai1 eof = 0; 14252621Sllai1 error = 0; 14262621Sllai1 while (!error && !eof) { 14272621Sllai1 uio.uio_resid = dlen; 14282621Sllai1 iov.iov_base = (char *)dbuf; 14292621Sllai1 iov.iov_len = dlen; 14302621Sllai1 (void) VOP_RWLOCK(dirvp, V_WRITELOCK_FALSE, NULL); 14315331Samw error = VOP_READDIR(dirvp, &uio, kcred, &eof, NULL, 0); 14322621Sllai1 VOP_RWUNLOCK(dirvp, V_WRITELOCK_FALSE, NULL); 14332621Sllai1 14342621Sllai1 dbuflen = dlen - uio.uio_resid; 14352621Sllai1 if (error || dbuflen == 0) 14362621Sllai1 break; 14372621Sllai1 143810588SEric.Taylor@Sun.COM if (!(ddv->sdev_flags & SDEV_BUILD)) 14392621Sllai1 break; 14402621Sllai1 14412621Sllai1 for (dp = dbuf; ((intptr_t)dp < 14422621Sllai1 (intptr_t)dbuf + dbuflen); 14432621Sllai1 dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) { 14442621Sllai1 nm = dp->d_name; 14452621Sllai1 14462621Sllai1 if (strcmp(nm, ".") == 0 || 14472621Sllai1 strcmp(nm, "..") == 0) 14482621Sllai1 continue; 14492621Sllai1 14502621Sllai1 vp = NULLVP; 14512621Sllai1 dv = sdev_cache_lookup(ddv, nm); 14522621Sllai1 if (dv) { 14532621Sllai1 if (dv->sdev_state != SDEV_ZOMBIE) { 14542621Sllai1 SDEV_SIMPLE_RELE(dv); 14552621Sllai1 } else { 14562621Sllai1 /* 14572621Sllai1 * A ZOMBIE node may not have been 14582621Sllai1 * cleaned up from the backing store, 14592621Sllai1 * bypass this entry in this case, 14602621Sllai1 * and clean it up from the directory 14612621Sllai1 * cache if this is the last call. 14622621Sllai1 */ 14632621Sllai1 (void) sdev_dirdelete(ddv, dv); 14642621Sllai1 } 14652621Sllai1 continue; 14662621Sllai1 } 14672621Sllai1 14682621Sllai1 /* refill the cache if not already */ 14692621Sllai1 error = devname_backstore_lookup(ddv, nm, &vp); 14702621Sllai1 if (error) 14712621Sllai1 continue; 14722621Sllai1 147311279SJerry.Gilliam@Sun.COM vattr.va_mask = AT_TYPE|AT_MODE|AT_UID|AT_GID; 14745331Samw error = VOP_GETATTR(vp, &vattr, 0, cred, NULL); 14752621Sllai1 if (error) 14762621Sllai1 continue; 14772621Sllai1 14782621Sllai1 if (vattr.va_type == VLNK) { 14792621Sllai1 error = sdev_getlink(vp, &link); 14802621Sllai1 if (error) { 14812621Sllai1 continue; 14822621Sllai1 } 14832621Sllai1 ASSERT(link != NULL); 14842621Sllai1 } 14852621Sllai1 14862621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 14872621Sllai1 rw_exit(&ddv->sdev_contents); 14882621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 14892621Sllai1 } 14902621Sllai1 error = sdev_mknode(ddv, nm, &dv, &vattr, vp, link, 14912621Sllai1 cred, SDEV_READY); 14922621Sllai1 rw_downgrade(&ddv->sdev_contents); 14932621Sllai1 14942621Sllai1 if (link != NULL) { 14952621Sllai1 kmem_free(link, strlen(link) + 1); 14962621Sllai1 link = NULL; 14972621Sllai1 } 14982621Sllai1 14992621Sllai1 if (!error) { 15002621Sllai1 ASSERT(dv); 15012621Sllai1 ASSERT(dv->sdev_state != SDEV_ZOMBIE); 15022621Sllai1 SDEV_SIMPLE_RELE(dv); 15032621Sllai1 } 15042621Sllai1 vp = NULL; 15052621Sllai1 dv = NULL; 15062621Sllai1 } 15072621Sllai1 } 15082621Sllai1 15092621Sllai1 done: 15102621Sllai1 VN_RELE(dirvp); 15112621Sllai1 kmem_free(dbuf, dlen); 15122621Sllai1 15132621Sllai1 return (error); 15142621Sllai1 } 15152621Sllai1 15163843Sjg void 15172621Sllai1 sdev_filldir_dynamic(struct sdev_node *ddv) 15182621Sllai1 { 15192621Sllai1 int error; 15202621Sllai1 int i; 152111279SJerry.Gilliam@Sun.COM struct vattr vattr; 152211279SJerry.Gilliam@Sun.COM struct vattr *vap = &vattr; 15232621Sllai1 char *nm = NULL; 15242621Sllai1 struct sdev_node *dv = NULL; 15252621Sllai1 15263843Sjg ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 15273843Sjg ASSERT((ddv->sdev_flags & SDEV_BUILD)); 15282621Sllai1 152911279SJerry.Gilliam@Sun.COM *vap = *sdev_getdefault_attr(VDIR); /* note structure copy here */ 153010588SEric.Taylor@Sun.COM gethrestime(&vap->va_atime); 153110588SEric.Taylor@Sun.COM vap->va_mtime = vap->va_atime; 153210588SEric.Taylor@Sun.COM vap->va_ctime = vap->va_atime; 15332621Sllai1 for (i = 0; vtab[i].vt_name != NULL; i++) { 15342621Sllai1 nm = vtab[i].vt_name; 15352621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 15363843Sjg dv = NULL; 15372621Sllai1 error = sdev_mknode(ddv, nm, &dv, vap, NULL, 15382621Sllai1 NULL, kcred, SDEV_READY); 15393843Sjg if (error) { 15403843Sjg cmn_err(CE_WARN, "%s/%s: error %d\n", 15413843Sjg ddv->sdev_name, nm, error); 15423843Sjg } else { 15433843Sjg ASSERT(dv); 15443843Sjg ASSERT(dv->sdev_state != SDEV_ZOMBIE); 15453843Sjg SDEV_SIMPLE_RELE(dv); 15463843Sjg } 15472621Sllai1 } 15482621Sllai1 } 15492621Sllai1 15502621Sllai1 /* 15512621Sllai1 * Creating a backing store entry based on sdev_attr. 15522621Sllai1 * This is called either as part of node creation in a persistent directory 15532621Sllai1 * or from setattr/setsecattr to persist access attributes across reboot. 15542621Sllai1 */ 15552621Sllai1 int 15562621Sllai1 sdev_shadow_node(struct sdev_node *dv, struct cred *cred) 15572621Sllai1 { 15582621Sllai1 int error = 0; 15592621Sllai1 struct vnode *dvp = SDEVTOV(dv->sdev_dotdot); 15602621Sllai1 struct vnode *rdvp = VTOSDEV(dvp)->sdev_attrvp; 15612621Sllai1 struct vattr *vap = dv->sdev_attr; 15622621Sllai1 char *nm = dv->sdev_name; 15632621Sllai1 struct vnode *tmpvp, **rvp = &tmpvp, *rrvp = NULL; 15642621Sllai1 15652621Sllai1 ASSERT(dv && dv->sdev_name && rdvp); 15662621Sllai1 ASSERT(RW_WRITE_HELD(&dv->sdev_contents) && dv->sdev_attrvp == NULL); 15672621Sllai1 15682621Sllai1 lookup: 15692621Sllai1 /* try to find it in the backing store */ 15705331Samw error = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, cred, NULL, NULL, 15715331Samw NULL); 15722621Sllai1 if (error == 0) { 15735331Samw if (VOP_REALVP(*rvp, &rrvp, NULL) == 0) { 15742621Sllai1 VN_HOLD(rrvp); 15752621Sllai1 VN_RELE(*rvp); 15762621Sllai1 *rvp = rrvp; 15772621Sllai1 } 15782621Sllai1 15792621Sllai1 kmem_free(dv->sdev_attr, sizeof (vattr_t)); 15802621Sllai1 dv->sdev_attr = NULL; 15812621Sllai1 dv->sdev_attrvp = *rvp; 15822621Sllai1 return (0); 15832621Sllai1 } 15842621Sllai1 15852621Sllai1 /* let's try to persist the node */ 15862621Sllai1 gethrestime(&vap->va_atime); 15872621Sllai1 vap->va_mtime = vap->va_atime; 15882621Sllai1 vap->va_ctime = vap->va_atime; 15892621Sllai1 vap->va_mask |= AT_TYPE|AT_MODE; 15902621Sllai1 switch (vap->va_type) { 15912621Sllai1 case VDIR: 15925331Samw error = VOP_MKDIR(rdvp, nm, vap, rvp, cred, NULL, 0, NULL); 15932621Sllai1 sdcmn_err9(("sdev_shadow_node: mkdir vp %p error %d\n", 15942621Sllai1 (void *)(*rvp), error)); 15952621Sllai1 break; 15962621Sllai1 case VCHR: 15972621Sllai1 case VBLK: 15982621Sllai1 case VREG: 15992621Sllai1 case VDOOR: 16002621Sllai1 error = VOP_CREATE(rdvp, nm, vap, NONEXCL, VREAD|VWRITE, 16015331Samw rvp, cred, 0, NULL, NULL); 16022621Sllai1 sdcmn_err9(("sdev_shadow_node: create vp %p, error %d\n", 16032621Sllai1 (void *)(*rvp), error)); 16042621Sllai1 if (!error) 16052621Sllai1 VN_RELE(*rvp); 16062621Sllai1 break; 16072621Sllai1 case VLNK: 16082621Sllai1 ASSERT(dv->sdev_symlink); 16095331Samw error = VOP_SYMLINK(rdvp, nm, vap, dv->sdev_symlink, cred, 16105331Samw NULL, 0); 16112621Sllai1 sdcmn_err9(("sdev_shadow_node: create symlink error %d\n", 16122621Sllai1 error)); 16132621Sllai1 break; 16142621Sllai1 default: 16152621Sllai1 cmn_err(CE_PANIC, "dev: %s: sdev_shadow_node " 16162621Sllai1 "create\n", nm); 16172621Sllai1 /*NOTREACHED*/ 16182621Sllai1 } 16192621Sllai1 16202621Sllai1 /* go back to lookup to factor out spec node and set attrvp */ 16212621Sllai1 if (error == 0) 16222621Sllai1 goto lookup; 16232621Sllai1 16246335Sjg sdcmn_err(("cannot persist %s - error %d\n", dv->sdev_path, error)); 16252621Sllai1 return (error); 16262621Sllai1 } 16272621Sllai1 16282621Sllai1 static int 16292621Sllai1 sdev_cache_add(struct sdev_node *ddv, struct sdev_node **dv, char *nm) 16302621Sllai1 { 16312621Sllai1 int error = 0; 16322621Sllai1 struct sdev_node *dup = NULL; 16332621Sllai1 16342621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 16352621Sllai1 if ((dup = sdev_findbyname(ddv, nm)) == NULL) { 16362621Sllai1 sdev_direnter(ddv, *dv); 16372621Sllai1 } else { 16382621Sllai1 if (dup->sdev_state == SDEV_ZOMBIE) { 16392621Sllai1 error = sdev_dirdelete(ddv, dup); 16402621Sllai1 /* 16412621Sllai1 * The ZOMBIE node is still hanging 16422621Sllai1 * around with more than one reference counts. 16432621Sllai1 * Fail the new node creation so that 16442621Sllai1 * the directory cache won't have 16452621Sllai1 * duplicate entries for the same named node 16462621Sllai1 */ 16472621Sllai1 if (error == EBUSY) { 16482621Sllai1 SDEV_SIMPLE_RELE(*dv); 16492621Sllai1 sdev_nodedestroy(*dv, 0); 16502621Sllai1 *dv = NULL; 16512621Sllai1 return (error); 16522621Sllai1 } 16532621Sllai1 sdev_direnter(ddv, *dv); 16542621Sllai1 } else { 16552621Sllai1 ASSERT((*dv)->sdev_state != SDEV_ZOMBIE); 16562621Sllai1 SDEV_SIMPLE_RELE(*dv); 16572621Sllai1 sdev_nodedestroy(*dv, 0); 16582621Sllai1 *dv = dup; 16592621Sllai1 } 16602621Sllai1 } 16612621Sllai1 16622621Sllai1 return (0); 16632621Sllai1 } 16642621Sllai1 16652621Sllai1 static int 16662621Sllai1 sdev_cache_delete(struct sdev_node *ddv, struct sdev_node **dv) 16672621Sllai1 { 16682621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 16692621Sllai1 return (sdev_dirdelete(ddv, *dv)); 16702621Sllai1 } 16712621Sllai1 16722621Sllai1 /* 16732621Sllai1 * update the in-core directory cache 16742621Sllai1 */ 16752621Sllai1 int 16762621Sllai1 sdev_cache_update(struct sdev_node *ddv, struct sdev_node **dv, char *nm, 16772621Sllai1 sdev_cache_ops_t ops) 16782621Sllai1 { 16792621Sllai1 int error = 0; 16802621Sllai1 16812621Sllai1 ASSERT((SDEV_HELD(*dv))); 16822621Sllai1 16832621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 16842621Sllai1 switch (ops) { 16852621Sllai1 case SDEV_CACHE_ADD: 16862621Sllai1 error = sdev_cache_add(ddv, dv, nm); 16872621Sllai1 break; 16882621Sllai1 case SDEV_CACHE_DELETE: 16892621Sllai1 error = sdev_cache_delete(ddv, dv); 16902621Sllai1 break; 16912621Sllai1 default: 16922621Sllai1 break; 16932621Sllai1 } 16942621Sllai1 16952621Sllai1 return (error); 16962621Sllai1 } 16972621Sllai1 16982621Sllai1 /* 16995331Samw * retrieve the named entry from the directory cache 17002621Sllai1 */ 17012621Sllai1 struct sdev_node * 17022621Sllai1 sdev_cache_lookup(struct sdev_node *ddv, char *nm) 17032621Sllai1 { 17042621Sllai1 struct sdev_node *dv = NULL; 17052621Sllai1 17062621Sllai1 ASSERT(RW_LOCK_HELD(&ddv->sdev_contents)); 17072621Sllai1 dv = sdev_findbyname(ddv, nm); 17082621Sllai1 17092621Sllai1 return (dv); 17102621Sllai1 } 17112621Sllai1 17122621Sllai1 /* 17132621Sllai1 * Implicit reconfig for nodes constructed by a link generator 17142621Sllai1 * Start devfsadm if needed, or if devfsadm is in progress, 17152621Sllai1 * prepare to block on devfsadm either completing or 17162621Sllai1 * constructing the desired node. As devfsadmd is global 17172621Sllai1 * in scope, constructing all necessary nodes, we only 17182621Sllai1 * need to initiate it once. 17192621Sllai1 */ 17202621Sllai1 static int 17212621Sllai1 sdev_call_devfsadmd(struct sdev_node *ddv, struct sdev_node *dv, char *nm) 17222621Sllai1 { 17232621Sllai1 int error = 0; 17242621Sllai1 17252621Sllai1 if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) { 17262621Sllai1 sdcmn_err6(("lookup: waiting for %s/%s, 0x%x\n", 17272621Sllai1 ddv->sdev_name, nm, devfsadm_state)); 17282621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 17292621Sllai1 SDEV_BLOCK_OTHERS(dv, (SDEV_LOOKUP | SDEV_LGWAITING)); 17302621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 17312621Sllai1 error = 0; 17322621Sllai1 } else if (!DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state)) { 17332621Sllai1 sdcmn_err6(("lookup %s/%s starting devfsadm, 0x%x\n", 17346065Scth ddv->sdev_name, nm, devfsadm_state)); 17352621Sllai1 17362621Sllai1 sdev_devfsadmd_thread(ddv, dv, kcred); 17372621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 17382621Sllai1 SDEV_BLOCK_OTHERS(dv, 17392621Sllai1 (SDEV_LOOKUP | SDEV_LGWAITING)); 17402621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 17412621Sllai1 error = 0; 17422621Sllai1 } else { 17432621Sllai1 error = -1; 17442621Sllai1 } 17452621Sllai1 17462621Sllai1 return (error); 17472621Sllai1 } 17482621Sllai1 17492621Sllai1 /* 17502621Sllai1 * Support for specialized device naming construction mechanisms 17512621Sllai1 */ 17522621Sllai1 static int 17532621Sllai1 sdev_call_dircallback(struct sdev_node *ddv, struct sdev_node **dvp, char *nm, 17542621Sllai1 int (*callback)(struct sdev_node *, char *, void **, struct cred *, 17552621Sllai1 void *, char *), int flags, struct cred *cred) 17562621Sllai1 { 17572621Sllai1 int rv = 0; 17582621Sllai1 char *physpath = NULL; 17592621Sllai1 struct vattr vattr; 176011279SJerry.Gilliam@Sun.COM struct vattr *vap = &vattr; 176110588SEric.Taylor@Sun.COM struct sdev_node *dv = NULL; 176210588SEric.Taylor@Sun.COM 176310588SEric.Taylor@Sun.COM ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 176410588SEric.Taylor@Sun.COM if (flags & SDEV_VLINK) { 17657688SAaron.Zang@Sun.COM physpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 17667688SAaron.Zang@Sun.COM rv = callback(ddv, nm, (void *)&physpath, kcred, NULL, 17677688SAaron.Zang@Sun.COM NULL); 17687688SAaron.Zang@Sun.COM if (rv) { 17697688SAaron.Zang@Sun.COM kmem_free(physpath, MAXPATHLEN); 17707688SAaron.Zang@Sun.COM return (-1); 17717688SAaron.Zang@Sun.COM } 17727688SAaron.Zang@Sun.COM 177311279SJerry.Gilliam@Sun.COM *vap = *sdev_getdefault_attr(VLNK); /* structure copy */ 17747688SAaron.Zang@Sun.COM vap->va_size = strlen(physpath); 177510588SEric.Taylor@Sun.COM gethrestime(&vap->va_atime); 177610588SEric.Taylor@Sun.COM vap->va_mtime = vap->va_atime; 177710588SEric.Taylor@Sun.COM vap->va_ctime = vap->va_atime; 177810588SEric.Taylor@Sun.COM 17797688SAaron.Zang@Sun.COM rv = sdev_mknode(ddv, nm, &dv, vap, NULL, 17807688SAaron.Zang@Sun.COM (void *)physpath, cred, SDEV_READY); 17817688SAaron.Zang@Sun.COM kmem_free(physpath, MAXPATHLEN); 17827688SAaron.Zang@Sun.COM if (rv) 17837688SAaron.Zang@Sun.COM return (rv); 17842621Sllai1 } else if (flags & SDEV_VATTR) { 17852621Sllai1 /* 17862621Sllai1 * /dev/pts 17872621Sllai1 * 17882621Sllai1 * callback is responsible to set the basic attributes, 17892621Sllai1 * e.g. va_type/va_uid/va_gid/ 17902621Sllai1 * dev_t if VCHR or VBLK/ 17912621Sllai1 */ 17922621Sllai1 ASSERT(callback); 17932621Sllai1 rv = callback(ddv, nm, (void *)&vattr, kcred, NULL, NULL); 17942621Sllai1 if (rv) { 17952621Sllai1 sdcmn_err3(("devname_lookup_func: SDEV_NONE " 17962621Sllai1 "callback failed \n")); 17972621Sllai1 return (-1); 17982621Sllai1 } 17992621Sllai1 18002621Sllai1 rv = sdev_mknode(ddv, nm, &dv, &vattr, NULL, NULL, 18012621Sllai1 cred, SDEV_READY); 18022621Sllai1 18032621Sllai1 if (rv) 18042621Sllai1 return (rv); 18052621Sllai1 18062621Sllai1 } else { 18072621Sllai1 impossible(("lookup: %s/%s by %s not supported (%d)\n", 18082621Sllai1 SDEVTOV(ddv)->v_path, nm, curproc->p_user.u_comm, 18092621Sllai1 __LINE__)); 18102621Sllai1 rv = -1; 18112621Sllai1 } 18122621Sllai1 18132621Sllai1 *dvp = dv; 18142621Sllai1 return (rv); 18152621Sllai1 } 18162621Sllai1 18172621Sllai1 static int 18182621Sllai1 is_devfsadm_thread(char *exec_name) 18192621Sllai1 { 18202621Sllai1 /* 18212621Sllai1 * note: because devfsadmd -> /usr/sbin/devfsadm 18222621Sllai1 * it is safe to use "devfsadm" to capture the lookups 18232621Sllai1 * from devfsadm and its daemon version. 18242621Sllai1 */ 18252621Sllai1 if (strcmp(exec_name, "devfsadm") == 0) 18262621Sllai1 return (1); 18272621Sllai1 return (0); 18282621Sllai1 } 18292621Sllai1 18302621Sllai1 /* 18312621Sllai1 * Lookup Order: 18322621Sllai1 * sdev_node cache; 18332621Sllai1 * backing store (SDEV_PERSIST); 18342621Sllai1 * DBNR: a. dir_ops implemented in the loadable modules; 18352621Sllai1 * b. vnode ops in vtab. 18362621Sllai1 */ 18372621Sllai1 int 18382621Sllai1 devname_lookup_func(struct sdev_node *ddv, char *nm, struct vnode **vpp, 18392621Sllai1 struct cred *cred, int (*callback)(struct sdev_node *, char *, void **, 18402621Sllai1 struct cred *, void *, char *), int flags) 18412621Sllai1 { 18422621Sllai1 int rv = 0, nmlen; 18432621Sllai1 struct vnode *rvp = NULL; 18442621Sllai1 struct sdev_node *dv = NULL; 18452621Sllai1 int retried = 0; 18462621Sllai1 int error = 0; 18472621Sllai1 struct vattr vattr; 18482621Sllai1 char *lookup_thread = curproc->p_user.u_comm; 18492621Sllai1 int failed_flags = 0; 18502621Sllai1 int (*vtor)(struct sdev_node *) = NULL; 18512621Sllai1 int state; 18522621Sllai1 int parent_state; 18532621Sllai1 char *link = NULL; 18542621Sllai1 18552621Sllai1 if (SDEVTOV(ddv)->v_type != VDIR) 18562621Sllai1 return (ENOTDIR); 18572621Sllai1 18582621Sllai1 /* 18592621Sllai1 * Empty name or ., return node itself. 18602621Sllai1 */ 18612621Sllai1 nmlen = strlen(nm); 18622621Sllai1 if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) { 18632621Sllai1 *vpp = SDEVTOV(ddv); 18642621Sllai1 VN_HOLD(*vpp); 18652621Sllai1 return (0); 18662621Sllai1 } 18672621Sllai1 18682621Sllai1 /* 18692621Sllai1 * .., return the parent directory 18702621Sllai1 */ 18712621Sllai1 if ((nmlen == 2) && (strcmp(nm, "..") == 0)) { 18722621Sllai1 *vpp = SDEVTOV(ddv->sdev_dotdot); 18732621Sllai1 VN_HOLD(*vpp); 18742621Sllai1 return (0); 18752621Sllai1 } 18762621Sllai1 18772621Sllai1 rw_enter(&ddv->sdev_contents, RW_READER); 18782621Sllai1 if (ddv->sdev_flags & SDEV_VTOR) { 18792621Sllai1 vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv); 18802621Sllai1 ASSERT(vtor); 18812621Sllai1 } 18822621Sllai1 18832621Sllai1 tryagain: 18842621Sllai1 /* 18852621Sllai1 * (a) directory cache lookup: 18862621Sllai1 */ 18872621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 18882621Sllai1 parent_state = ddv->sdev_state; 18892621Sllai1 dv = sdev_cache_lookup(ddv, nm); 18902621Sllai1 if (dv) { 18912621Sllai1 state = dv->sdev_state; 18922621Sllai1 switch (state) { 18932621Sllai1 case SDEV_INIT: 18942621Sllai1 if (is_devfsadm_thread(lookup_thread)) 18952621Sllai1 break; 18962621Sllai1 18972621Sllai1 /* ZOMBIED parent won't allow node creation */ 18982621Sllai1 if (parent_state == SDEV_ZOMBIE) { 18992621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, 19002621Sllai1 retried); 19012621Sllai1 goto nolock_notfound; 19022621Sllai1 } 19032621Sllai1 19042621Sllai1 mutex_enter(&dv->sdev_lookup_lock); 19052621Sllai1 /* compensate the threads started after devfsadm */ 19062621Sllai1 if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) && 19072621Sllai1 !(SDEV_IS_LOOKUP(dv))) 19082621Sllai1 SDEV_BLOCK_OTHERS(dv, 19092621Sllai1 (SDEV_LOOKUP | SDEV_LGWAITING)); 19102621Sllai1 19112621Sllai1 if (SDEV_IS_LOOKUP(dv)) { 19122621Sllai1 failed_flags |= SLF_REBUILT; 19132621Sllai1 rw_exit(&ddv->sdev_contents); 19142621Sllai1 error = sdev_wait4lookup(dv, SDEV_LOOKUP); 19152621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 19162621Sllai1 rw_enter(&ddv->sdev_contents, RW_READER); 19172621Sllai1 19182621Sllai1 if (error != 0) { 19192621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, 19202621Sllai1 retried); 19212621Sllai1 goto nolock_notfound; 19222621Sllai1 } 19232621Sllai1 19242621Sllai1 state = dv->sdev_state; 19252621Sllai1 if (state == SDEV_INIT) { 19262621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, 19272621Sllai1 retried); 19282621Sllai1 goto nolock_notfound; 19292621Sllai1 } else if (state == SDEV_READY) { 19302621Sllai1 goto found; 19312621Sllai1 } else if (state == SDEV_ZOMBIE) { 19322621Sllai1 rw_exit(&ddv->sdev_contents); 19332621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, 19342621Sllai1 retried); 19352621Sllai1 SDEV_RELE(dv); 19362621Sllai1 goto lookup_failed; 19372621Sllai1 } 19382621Sllai1 } else { 19392621Sllai1 mutex_exit(&dv->sdev_lookup_lock); 19402621Sllai1 } 19412621Sllai1 break; 19422621Sllai1 case SDEV_READY: 19432621Sllai1 goto found; 19442621Sllai1 case SDEV_ZOMBIE: 19452621Sllai1 rw_exit(&ddv->sdev_contents); 19462621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 19472621Sllai1 SDEV_RELE(dv); 19482621Sllai1 goto lookup_failed; 19492621Sllai1 default: 19502621Sllai1 rw_exit(&ddv->sdev_contents); 19512621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 19522621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags); 19532621Sllai1 *vpp = NULLVP; 19542621Sllai1 return (ENOENT); 19552621Sllai1 } 19562621Sllai1 } 19572621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 19582621Sllai1 19592621Sllai1 /* 19602621Sllai1 * ZOMBIED parent does not allow new node creation. 19612621Sllai1 * bail out early 19622621Sllai1 */ 19632621Sllai1 if (parent_state == SDEV_ZOMBIE) { 19642621Sllai1 rw_exit(&ddv->sdev_contents); 196510588SEric.Taylor@Sun.COM *vpp = NULLVP; 19662621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 19672621Sllai1 return (ENOENT); 19682621Sllai1 } 19692621Sllai1 19702621Sllai1 /* 19712621Sllai1 * (b0): backing store lookup 19722621Sllai1 * SDEV_PERSIST is default except: 19732621Sllai1 * 1) pts nodes 19742621Sllai1 * 2) non-chmod'ed local nodes 197510588SEric.Taylor@Sun.COM * 3) zvol nodes 19762621Sllai1 */ 19772621Sllai1 if (SDEV_IS_PERSIST(ddv)) { 19782621Sllai1 error = devname_backstore_lookup(ddv, nm, &rvp); 19792621Sllai1 19802621Sllai1 if (!error) { 19812621Sllai1 198211279SJerry.Gilliam@Sun.COM vattr.va_mask = AT_TYPE|AT_MODE|AT_UID|AT_GID; 19835331Samw error = VOP_GETATTR(rvp, &vattr, 0, cred, NULL); 19842621Sllai1 if (error) { 19852621Sllai1 rw_exit(&ddv->sdev_contents); 19862621Sllai1 if (dv) 19872621Sllai1 SDEV_RELE(dv); 19882621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 19892621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags); 19902621Sllai1 *vpp = NULLVP; 19912621Sllai1 return (ENOENT); 19922621Sllai1 } 19932621Sllai1 19942621Sllai1 if (vattr.va_type == VLNK) { 19952621Sllai1 error = sdev_getlink(rvp, &link); 19962621Sllai1 if (error) { 19972621Sllai1 rw_exit(&ddv->sdev_contents); 19982621Sllai1 if (dv) 19992621Sllai1 SDEV_RELE(dv); 20002621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, 20012621Sllai1 retried); 20022621Sllai1 sdev_lookup_failed(ddv, nm, 20032621Sllai1 failed_flags); 20042621Sllai1 *vpp = NULLVP; 20052621Sllai1 return (ENOENT); 20062621Sllai1 } 20072621Sllai1 ASSERT(link != NULL); 20082621Sllai1 } 20092621Sllai1 20102621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 20112621Sllai1 rw_exit(&ddv->sdev_contents); 20122621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 20132621Sllai1 } 20142621Sllai1 error = sdev_mknode(ddv, nm, &dv, &vattr, 20152621Sllai1 rvp, link, cred, SDEV_READY); 20162621Sllai1 rw_downgrade(&ddv->sdev_contents); 20172621Sllai1 20182621Sllai1 if (link != NULL) { 20192621Sllai1 kmem_free(link, strlen(link) + 1); 20202621Sllai1 link = NULL; 20212621Sllai1 } 20222621Sllai1 20232621Sllai1 if (error) { 20242621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 20252621Sllai1 rw_exit(&ddv->sdev_contents); 20262621Sllai1 if (dv) 20272621Sllai1 SDEV_RELE(dv); 20282621Sllai1 goto lookup_failed; 20292621Sllai1 } else { 20302621Sllai1 goto found; 20312621Sllai1 } 20322621Sllai1 } else if (retried) { 20332621Sllai1 rw_exit(&ddv->sdev_contents); 20342621Sllai1 sdcmn_err3(("retry of lookup of %s/%s: failed\n", 20352621Sllai1 ddv->sdev_name, nm)); 20362621Sllai1 if (dv) 20372621Sllai1 SDEV_RELE(dv); 20382621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 20392621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags); 20402621Sllai1 *vpp = NULLVP; 20412621Sllai1 return (ENOENT); 20422621Sllai1 } 20432621Sllai1 } 20442621Sllai1 20458023SPhil.Kirk@Sun.COM lookup_create_node: 20462621Sllai1 /* first thread that is doing the lookup on this node */ 204710588SEric.Taylor@Sun.COM if (callback) { 204810588SEric.Taylor@Sun.COM ASSERT(dv == NULL); 204910588SEric.Taylor@Sun.COM if (!rw_tryupgrade(&ddv->sdev_contents)) { 205010588SEric.Taylor@Sun.COM rw_exit(&ddv->sdev_contents); 205110588SEric.Taylor@Sun.COM rw_enter(&ddv->sdev_contents, RW_WRITER); 205210588SEric.Taylor@Sun.COM } 205310588SEric.Taylor@Sun.COM error = sdev_call_dircallback(ddv, &dv, nm, callback, 205410588SEric.Taylor@Sun.COM flags, cred); 205510588SEric.Taylor@Sun.COM rw_downgrade(&ddv->sdev_contents); 205610588SEric.Taylor@Sun.COM if (error == 0) { 205710588SEric.Taylor@Sun.COM goto found; 205810588SEric.Taylor@Sun.COM } else { 205910588SEric.Taylor@Sun.COM SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 206010588SEric.Taylor@Sun.COM rw_exit(&ddv->sdev_contents); 206110588SEric.Taylor@Sun.COM goto lookup_failed; 206210588SEric.Taylor@Sun.COM } 206310588SEric.Taylor@Sun.COM } 20642621Sllai1 if (!dv) { 20652621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 20662621Sllai1 rw_exit(&ddv->sdev_contents); 20672621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 20682621Sllai1 } 20692621Sllai1 error = sdev_mknode(ddv, nm, &dv, NULL, NULL, NULL, 20702621Sllai1 cred, SDEV_INIT); 20712621Sllai1 if (!dv) { 20722621Sllai1 rw_exit(&ddv->sdev_contents); 20732621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 20742621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags); 20752621Sllai1 *vpp = NULLVP; 20762621Sllai1 return (ENOENT); 20772621Sllai1 } 20782621Sllai1 rw_downgrade(&ddv->sdev_contents); 20792621Sllai1 } 20802621Sllai1 20812621Sllai1 /* 20822621Sllai1 * (b1) invoking devfsadm once per life time for devfsadm nodes 20832621Sllai1 */ 208410588SEric.Taylor@Sun.COM ASSERT(SDEV_HELD(dv)); 208510588SEric.Taylor@Sun.COM 208610588SEric.Taylor@Sun.COM if (SDEV_IS_NO_NCACHE(dv)) 208710588SEric.Taylor@Sun.COM failed_flags |= SLF_NO_NCACHE; 208810588SEric.Taylor@Sun.COM if (sdev_reconfig_boot || !i_ddi_io_initialized() || 208910588SEric.Taylor@Sun.COM SDEV_IS_DYNAMIC(ddv) || SDEV_IS_NO_NCACHE(dv) || 209010588SEric.Taylor@Sun.COM ((moddebug & MODDEBUG_FINI_EBUSY) != 0)) { 209110588SEric.Taylor@Sun.COM ASSERT(SDEV_HELD(dv)); 209210588SEric.Taylor@Sun.COM SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 209310588SEric.Taylor@Sun.COM goto nolock_notfound; 20942621Sllai1 } 20952621Sllai1 20962621Sllai1 /* 209710588SEric.Taylor@Sun.COM * filter out known non-existent devices recorded 209810588SEric.Taylor@Sun.COM * during initial reconfiguration boot for which 209910588SEric.Taylor@Sun.COM * reconfig should not be done and lookup may 210010588SEric.Taylor@Sun.COM * be short-circuited now. 21012621Sllai1 */ 210210588SEric.Taylor@Sun.COM if (sdev_lookup_filter(ddv, nm)) { 210310588SEric.Taylor@Sun.COM SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 210410588SEric.Taylor@Sun.COM goto nolock_notfound; 210510588SEric.Taylor@Sun.COM } 210610588SEric.Taylor@Sun.COM 210710588SEric.Taylor@Sun.COM /* bypassing devfsadm internal nodes */ 210810588SEric.Taylor@Sun.COM if (is_devfsadm_thread(lookup_thread)) { 210910588SEric.Taylor@Sun.COM SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 211010588SEric.Taylor@Sun.COM goto nolock_notfound; 211110588SEric.Taylor@Sun.COM } 211210588SEric.Taylor@Sun.COM 211310588SEric.Taylor@Sun.COM if (sdev_reconfig_disable) { 211410588SEric.Taylor@Sun.COM SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 211510588SEric.Taylor@Sun.COM goto nolock_notfound; 211610588SEric.Taylor@Sun.COM } 211710588SEric.Taylor@Sun.COM 211810588SEric.Taylor@Sun.COM error = sdev_call_devfsadmd(ddv, dv, nm); 211910588SEric.Taylor@Sun.COM if (error == 0) { 212010588SEric.Taylor@Sun.COM sdcmn_err8(("lookup of %s/%s by %s: reconfig\n", 212110588SEric.Taylor@Sun.COM ddv->sdev_name, nm, curproc->p_user.u_comm)); 212210588SEric.Taylor@Sun.COM if (sdev_reconfig_verbose) { 212310588SEric.Taylor@Sun.COM cmn_err(CE_CONT, 212410588SEric.Taylor@Sun.COM "?lookup of %s/%s by %s: reconfig\n", 212510588SEric.Taylor@Sun.COM ddv->sdev_name, nm, curproc->p_user.u_comm); 21262621Sllai1 } 212710588SEric.Taylor@Sun.COM retried = 1; 212810588SEric.Taylor@Sun.COM failed_flags |= SLF_REBUILT; 212910588SEric.Taylor@Sun.COM ASSERT(dv->sdev_state != SDEV_ZOMBIE); 213010588SEric.Taylor@Sun.COM SDEV_SIMPLE_RELE(dv); 213110588SEric.Taylor@Sun.COM goto tryagain; 213210588SEric.Taylor@Sun.COM } else { 213310588SEric.Taylor@Sun.COM SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 213410588SEric.Taylor@Sun.COM goto nolock_notfound; 21352621Sllai1 } 21362621Sllai1 21372621Sllai1 found: 21382621Sllai1 ASSERT(!(dv->sdev_flags & SDEV_STALE)); 21392621Sllai1 ASSERT(dv->sdev_state == SDEV_READY); 21402621Sllai1 if (vtor) { 21412621Sllai1 /* 21422621Sllai1 * Check validity of returned node 21432621Sllai1 */ 21442621Sllai1 switch (vtor(dv)) { 21452621Sllai1 case SDEV_VTOR_VALID: 21462621Sllai1 break; 21478023SPhil.Kirk@Sun.COM case SDEV_VTOR_STALE: 21488023SPhil.Kirk@Sun.COM /* 21498023SPhil.Kirk@Sun.COM * The name exists, but the cache entry is 21508023SPhil.Kirk@Sun.COM * stale and needs to be re-created. 21518023SPhil.Kirk@Sun.COM */ 21528023SPhil.Kirk@Sun.COM ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 21538023SPhil.Kirk@Sun.COM if (rw_tryupgrade(&ddv->sdev_contents) == 0) { 21548023SPhil.Kirk@Sun.COM rw_exit(&ddv->sdev_contents); 21558023SPhil.Kirk@Sun.COM rw_enter(&ddv->sdev_contents, RW_WRITER); 21568023SPhil.Kirk@Sun.COM } 21578023SPhil.Kirk@Sun.COM error = sdev_cache_update(ddv, &dv, nm, 21588023SPhil.Kirk@Sun.COM SDEV_CACHE_DELETE); 21598023SPhil.Kirk@Sun.COM rw_downgrade(&ddv->sdev_contents); 21608023SPhil.Kirk@Sun.COM if (error == 0) { 21618023SPhil.Kirk@Sun.COM dv = NULL; 21628023SPhil.Kirk@Sun.COM goto lookup_create_node; 21638023SPhil.Kirk@Sun.COM } 21648023SPhil.Kirk@Sun.COM /* FALLTHRU */ 21652621Sllai1 case SDEV_VTOR_INVALID: 21662621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 21672621Sllai1 sdcmn_err7(("lookup: destroy invalid " 21682621Sllai1 "node: %s(%p)\n", dv->sdev_name, (void *)dv)); 21692621Sllai1 goto nolock_notfound; 21702621Sllai1 case SDEV_VTOR_SKIP: 21712621Sllai1 sdcmn_err7(("lookup: node not applicable - " 21722621Sllai1 "skipping: %s(%p)\n", dv->sdev_name, (void *)dv)); 21732621Sllai1 rw_exit(&ddv->sdev_contents); 21742621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 21752621Sllai1 SDEV_RELE(dv); 21762621Sllai1 goto lookup_failed; 21772621Sllai1 default: 21782621Sllai1 cmn_err(CE_PANIC, 21792621Sllai1 "dev fs: validator failed: %s(%p)\n", 21802621Sllai1 dv->sdev_name, (void *)dv); 21812621Sllai1 break; 21822621Sllai1 } 21832621Sllai1 } 21842621Sllai1 21852621Sllai1 rw_exit(&ddv->sdev_contents); 21862621Sllai1 rv = sdev_to_vp(dv, vpp); 21872621Sllai1 sdcmn_err3(("devname_lookup_func: returning vp %p v_count %d state %d " 21882621Sllai1 "for nm %s, error %d\n", (void *)*vpp, (*vpp)->v_count, 21892621Sllai1 dv->sdev_state, nm, rv)); 21902621Sllai1 return (rv); 21912621Sllai1 21922621Sllai1 nolock_notfound: 21932621Sllai1 /* 21942621Sllai1 * Destroy the node that is created for synchronization purposes. 21952621Sllai1 */ 21962621Sllai1 sdcmn_err3(("devname_lookup_func: %s with state %d\n", 21972621Sllai1 nm, dv->sdev_state)); 21982621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 21992621Sllai1 if (dv->sdev_state == SDEV_INIT) { 22002621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 22012621Sllai1 rw_exit(&ddv->sdev_contents); 22022621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 22032621Sllai1 } 22042621Sllai1 22052621Sllai1 /* 22062621Sllai1 * Node state may have changed during the lock 22072621Sllai1 * changes. Re-check. 22082621Sllai1 */ 22092621Sllai1 if (dv->sdev_state == SDEV_INIT) { 22102621Sllai1 (void) sdev_dirdelete(ddv, dv); 22112621Sllai1 rw_exit(&ddv->sdev_contents); 22122621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags); 22132621Sllai1 *vpp = NULL; 22142621Sllai1 return (ENOENT); 22152621Sllai1 } 22162621Sllai1 } 22172621Sllai1 22182621Sllai1 rw_exit(&ddv->sdev_contents); 22192621Sllai1 SDEV_RELE(dv); 22202621Sllai1 22212621Sllai1 lookup_failed: 22222621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags); 22232621Sllai1 *vpp = NULL; 22242621Sllai1 return (ENOENT); 22252621Sllai1 } 22262621Sllai1 22272621Sllai1 /* 22282621Sllai1 * Given a directory node, mark all nodes beneath as 22292621Sllai1 * STALE, i.e. nodes that don't exist as far as new 22306347Sjg * consumers are concerned. Remove them from the 22316347Sjg * list of directory entries so that no lookup or 22326347Sjg * directory traversal will find them. The node 22336347Sjg * not deallocated so existing holds are not affected. 22342621Sllai1 */ 22352621Sllai1 void 22362621Sllai1 sdev_stale(struct sdev_node *ddv) 22372621Sllai1 { 22382621Sllai1 struct sdev_node *dv; 22392621Sllai1 struct vnode *vp; 22402621Sllai1 22412621Sllai1 ASSERT(SDEVTOV(ddv)->v_type == VDIR); 22422621Sllai1 22432621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 22446260Sjg for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = SDEV_NEXT_ENTRY(ddv, dv)) { 22452621Sllai1 vp = SDEVTOV(dv); 22462621Sllai1 if (vp->v_type == VDIR) 22472621Sllai1 sdev_stale(dv); 22482621Sllai1 22492621Sllai1 sdcmn_err9(("sdev_stale: setting stale %s\n", 22506347Sjg dv->sdev_path)); 22512621Sllai1 dv->sdev_flags |= SDEV_STALE; 22526347Sjg avl_remove(&ddv->sdev_entries, dv); 22532621Sllai1 } 22542621Sllai1 ddv->sdev_flags |= SDEV_BUILD; 22552621Sllai1 rw_exit(&ddv->sdev_contents); 22562621Sllai1 } 22572621Sllai1 22582621Sllai1 /* 22592621Sllai1 * Given a directory node, clean out all the nodes beneath. 22602621Sllai1 * If expr is specified, clean node with names matching expr. 22612621Sllai1 * If SDEV_ENFORCE is specified in flags, busy nodes are made stale, 22622621Sllai1 * so they are excluded from future lookups. 22632621Sllai1 */ 22642621Sllai1 int 22652621Sllai1 sdev_cleandir(struct sdev_node *ddv, char *expr, uint_t flags) 22662621Sllai1 { 22672621Sllai1 int error = 0; 22682621Sllai1 int busy = 0; 22692621Sllai1 struct vnode *vp; 22702621Sllai1 struct sdev_node *dv, *next = NULL; 22712621Sllai1 int bkstore = 0; 22722621Sllai1 int len = 0; 22732621Sllai1 char *bks_name = NULL; 22742621Sllai1 22752621Sllai1 ASSERT(SDEVTOV(ddv)->v_type == VDIR); 22762621Sllai1 22772621Sllai1 /* 22782621Sllai1 * We try our best to destroy all unused sdev_node's 22792621Sllai1 */ 22802621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 22816260Sjg for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) { 22826260Sjg next = SDEV_NEXT_ENTRY(ddv, dv); 22832621Sllai1 vp = SDEVTOV(dv); 22842621Sllai1 22852621Sllai1 if (expr && gmatch(dv->sdev_name, expr) == 0) 22862621Sllai1 continue; 22872621Sllai1 22882621Sllai1 if (vp->v_type == VDIR && 22892621Sllai1 sdev_cleandir(dv, NULL, flags) != 0) { 22902621Sllai1 sdcmn_err9(("sdev_cleandir: dir %s busy\n", 22912621Sllai1 dv->sdev_name)); 22922621Sllai1 busy++; 22932621Sllai1 continue; 22942621Sllai1 } 22952621Sllai1 22962621Sllai1 if (vp->v_count > 0 && (flags & SDEV_ENFORCE) == 0) { 22972621Sllai1 sdcmn_err9(("sdev_cleandir: dir %s busy\n", 22982621Sllai1 dv->sdev_name)); 22992621Sllai1 busy++; 23002621Sllai1 continue; 23012621Sllai1 } 23022621Sllai1 23032621Sllai1 /* 23042621Sllai1 * at this point, either dv is not held or SDEV_ENFORCE 23052621Sllai1 * is specified. In either case, dv needs to be deleted 23062621Sllai1 */ 23072621Sllai1 SDEV_HOLD(dv); 23082621Sllai1 23092621Sllai1 bkstore = SDEV_IS_PERSIST(dv) ? 1 : 0; 23102621Sllai1 if (bkstore && (vp->v_type == VDIR)) 23112621Sllai1 bkstore += 1; 23122621Sllai1 23132621Sllai1 if (bkstore) { 23142621Sllai1 len = strlen(dv->sdev_name) + 1; 23152621Sllai1 bks_name = kmem_alloc(len, KM_SLEEP); 23162621Sllai1 bcopy(dv->sdev_name, bks_name, len); 23172621Sllai1 } 23182621Sllai1 23192621Sllai1 error = sdev_dirdelete(ddv, dv); 23202621Sllai1 23212621Sllai1 if (error == EBUSY) { 23222621Sllai1 sdcmn_err9(("sdev_cleandir: dir busy\n")); 23232621Sllai1 busy++; 23242621Sllai1 } 23252621Sllai1 23262621Sllai1 /* take care the backing store clean up */ 23272621Sllai1 if (bkstore && (error == 0)) { 23282621Sllai1 ASSERT(bks_name); 23292621Sllai1 ASSERT(ddv->sdev_attrvp); 23302621Sllai1 23312621Sllai1 if (bkstore == 1) { 23322621Sllai1 error = VOP_REMOVE(ddv->sdev_attrvp, 23335331Samw bks_name, kcred, NULL, 0); 23342621Sllai1 } else if (bkstore == 2) { 23352621Sllai1 error = VOP_RMDIR(ddv->sdev_attrvp, 23365331Samw bks_name, ddv->sdev_attrvp, kcred, NULL, 0); 23372621Sllai1 } 23382621Sllai1 23392621Sllai1 /* do not propagate the backing store errors */ 23402621Sllai1 if (error) { 23412621Sllai1 sdcmn_err9(("sdev_cleandir: backing store" 23422621Sllai1 "not cleaned\n")); 23432621Sllai1 error = 0; 23442621Sllai1 } 23452621Sllai1 23462621Sllai1 bkstore = 0; 23472621Sllai1 kmem_free(bks_name, len); 23482621Sllai1 bks_name = NULL; 23492621Sllai1 len = 0; 23502621Sllai1 } 23512621Sllai1 } 23522621Sllai1 23532621Sllai1 ddv->sdev_flags |= SDEV_BUILD; 23542621Sllai1 rw_exit(&ddv->sdev_contents); 23552621Sllai1 23562621Sllai1 if (busy) { 23572621Sllai1 error = EBUSY; 23582621Sllai1 } 23592621Sllai1 23602621Sllai1 return (error); 23612621Sllai1 } 23622621Sllai1 23632621Sllai1 /* 23642621Sllai1 * a convenient wrapper for readdir() funcs 23652621Sllai1 */ 23662621Sllai1 size_t 23672621Sllai1 add_dir_entry(dirent64_t *de, char *nm, size_t size, ino_t ino, offset_t off) 23682621Sllai1 { 23692621Sllai1 size_t reclen = DIRENT64_RECLEN(strlen(nm)); 23702621Sllai1 if (reclen > size) 23712621Sllai1 return (0); 23722621Sllai1 23732621Sllai1 de->d_ino = (ino64_t)ino; 23742621Sllai1 de->d_off = (off64_t)off + 1; 23752621Sllai1 de->d_reclen = (ushort_t)reclen; 23762621Sllai1 (void) strncpy(de->d_name, nm, DIRENT64_NAMELEN(reclen)); 23772621Sllai1 return (reclen); 23782621Sllai1 } 23792621Sllai1 23802621Sllai1 /* 23812621Sllai1 * sdev_mount service routines 23822621Sllai1 */ 23832621Sllai1 int 23842621Sllai1 sdev_copyin_mountargs(struct mounta *uap, struct sdev_mountargs *args) 23852621Sllai1 { 23862621Sllai1 int error; 23872621Sllai1 23882621Sllai1 if (uap->datalen != sizeof (*args)) 23892621Sllai1 return (EINVAL); 23902621Sllai1 23912621Sllai1 if (error = copyin(uap->dataptr, args, sizeof (*args))) { 23922621Sllai1 cmn_err(CE_WARN, "sdev_copyin_mountargs: can not" 23932621Sllai1 "get user data. error %d\n", error); 23942621Sllai1 return (EFAULT); 23952621Sllai1 } 23962621Sllai1 23972621Sllai1 return (0); 23982621Sllai1 } 23992621Sllai1 24002621Sllai1 #ifdef nextdp 24012621Sllai1 #undef nextdp 24022621Sllai1 #endif 24033133Sjg #define nextdp(dp) ((struct dirent64 *) \ 24043133Sjg (intptr_t)((char *)(dp) + (dp)->d_reclen)) 24052621Sllai1 24062621Sllai1 /* 24072621Sllai1 * readdir helper func 24082621Sllai1 */ 24092621Sllai1 int 24102621Sllai1 devname_readdir_func(vnode_t *vp, uio_t *uiop, cred_t *cred, int *eofp, 24112621Sllai1 int flags) 24122621Sllai1 { 24132621Sllai1 struct sdev_node *ddv = VTOSDEV(vp); 24142621Sllai1 struct sdev_node *dv; 24152621Sllai1 dirent64_t *dp; 24162621Sllai1 ulong_t outcount = 0; 24172621Sllai1 size_t namelen; 24182621Sllai1 ulong_t alloc_count; 24192621Sllai1 void *outbuf; 24202621Sllai1 struct iovec *iovp; 24212621Sllai1 int error = 0; 24222621Sllai1 size_t reclen; 24232621Sllai1 offset_t diroff; 24242621Sllai1 offset_t soff; 24252621Sllai1 int this_reclen; 24262621Sllai1 int (*vtor)(struct sdev_node *) = NULL; 24272621Sllai1 struct vattr attr; 24282621Sllai1 timestruc_t now; 24292621Sllai1 24302621Sllai1 ASSERT(ddv->sdev_attr || ddv->sdev_attrvp); 24312621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 24322621Sllai1 24332621Sllai1 if (uiop->uio_loffset >= MAXOFF_T) { 24342621Sllai1 if (eofp) 24352621Sllai1 *eofp = 1; 24362621Sllai1 return (0); 24372621Sllai1 } 24382621Sllai1 24392621Sllai1 if (uiop->uio_iovcnt != 1) 24402621Sllai1 return (EINVAL); 24412621Sllai1 24422621Sllai1 if (vp->v_type != VDIR) 24432621Sllai1 return (ENOTDIR); 24442621Sllai1 24452621Sllai1 if (ddv->sdev_flags & SDEV_VTOR) { 24462621Sllai1 vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv); 24472621Sllai1 ASSERT(vtor); 24482621Sllai1 } 24492621Sllai1 24502621Sllai1 if (eofp != NULL) 24512621Sllai1 *eofp = 0; 24522621Sllai1 24533133Sjg soff = uiop->uio_loffset; 24542621Sllai1 iovp = uiop->uio_iov; 24552621Sllai1 alloc_count = iovp->iov_len; 24562621Sllai1 dp = outbuf = kmem_alloc(alloc_count, KM_SLEEP); 24572621Sllai1 outcount = 0; 24582621Sllai1 24592621Sllai1 if (ddv->sdev_state == SDEV_ZOMBIE) 24602621Sllai1 goto get_cache; 24612621Sllai1 24622679Sszhou if (SDEV_IS_GLOBAL(ddv)) { 246310097SEric.Taylor@Sun.COM 246410097SEric.Taylor@Sun.COM if ((sdev_boot_state == SDEV_BOOT_STATE_COMPLETE) && 24652621Sllai1 !sdev_reconfig_boot && (flags & SDEV_BROWSE) && 24662621Sllai1 !SDEV_IS_DYNAMIC(ddv) && !SDEV_IS_NO_NCACHE(ddv) && 24672621Sllai1 ((moddebug & MODDEBUG_FINI_EBUSY) == 0) && 24682621Sllai1 !DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state) && 24692621Sllai1 !DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) && 24702621Sllai1 !sdev_reconfig_disable) { 24712621Sllai1 /* 24722621Sllai1 * invoking "devfsadm" to do system device reconfig 24732621Sllai1 */ 24742621Sllai1 mutex_enter(&ddv->sdev_lookup_lock); 24752621Sllai1 SDEV_BLOCK_OTHERS(ddv, 24762621Sllai1 (SDEV_READDIR|SDEV_LGWAITING)); 24772621Sllai1 mutex_exit(&ddv->sdev_lookup_lock); 24782621Sllai1 24792621Sllai1 sdcmn_err8(("readdir of %s by %s: reconfig\n", 24802621Sllai1 ddv->sdev_path, curproc->p_user.u_comm)); 24812621Sllai1 if (sdev_reconfig_verbose) { 24822621Sllai1 cmn_err(CE_CONT, 24832621Sllai1 "?readdir of %s by %s: reconfig\n", 24842621Sllai1 ddv->sdev_path, curproc->p_user.u_comm); 24852621Sllai1 } 24862621Sllai1 24872621Sllai1 sdev_devfsadmd_thread(ddv, NULL, kcred); 24882621Sllai1 } else if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) { 24892621Sllai1 /* 24902621Sllai1 * compensate the "ls" started later than "devfsadm" 24912621Sllai1 */ 24922621Sllai1 mutex_enter(&ddv->sdev_lookup_lock); 24932621Sllai1 SDEV_BLOCK_OTHERS(ddv, (SDEV_READDIR|SDEV_LGWAITING)); 24942621Sllai1 mutex_exit(&ddv->sdev_lookup_lock); 24952621Sllai1 } 24962621Sllai1 24972621Sllai1 /* 24982621Sllai1 * release the contents lock so that 24993843Sjg * the cache may be updated by devfsadmd 25002621Sllai1 */ 25012621Sllai1 rw_exit(&ddv->sdev_contents); 25022621Sllai1 mutex_enter(&ddv->sdev_lookup_lock); 25032621Sllai1 if (SDEV_IS_READDIR(ddv)) 25042621Sllai1 (void) sdev_wait4lookup(ddv, SDEV_READDIR); 25052621Sllai1 mutex_exit(&ddv->sdev_lookup_lock); 25062621Sllai1 rw_enter(&ddv->sdev_contents, RW_READER); 25072621Sllai1 25082621Sllai1 sdcmn_err4(("readdir of directory %s by %s\n", 25092621Sllai1 ddv->sdev_name, curproc->p_user.u_comm)); 25103843Sjg if (ddv->sdev_flags & SDEV_BUILD) { 25112621Sllai1 if (SDEV_IS_PERSIST(ddv)) { 25122621Sllai1 error = sdev_filldir_from_store(ddv, 25132621Sllai1 alloc_count, cred); 25142621Sllai1 } 25153843Sjg ddv->sdev_flags &= ~SDEV_BUILD; 25162621Sllai1 } 25172621Sllai1 } 25182621Sllai1 25192621Sllai1 get_cache: 25202621Sllai1 /* handle "." and ".." */ 25212621Sllai1 diroff = 0; 25222621Sllai1 if (soff == 0) { 25232621Sllai1 /* first time */ 25242621Sllai1 this_reclen = DIRENT64_RECLEN(1); 25252621Sllai1 if (alloc_count < this_reclen) { 25262621Sllai1 error = EINVAL; 25272621Sllai1 goto done; 25282621Sllai1 } 25292621Sllai1 25302621Sllai1 dp->d_ino = (ino64_t)ddv->sdev_ino; 25312621Sllai1 dp->d_off = (off64_t)1; 25322621Sllai1 dp->d_reclen = (ushort_t)this_reclen; 25332621Sllai1 25342621Sllai1 (void) strncpy(dp->d_name, ".", 25352621Sllai1 DIRENT64_NAMELEN(this_reclen)); 25362621Sllai1 outcount += dp->d_reclen; 25372621Sllai1 dp = nextdp(dp); 25382621Sllai1 } 25392621Sllai1 25402621Sllai1 diroff++; 25412621Sllai1 if (soff <= 1) { 25422621Sllai1 this_reclen = DIRENT64_RECLEN(2); 25432621Sllai1 if (alloc_count < outcount + this_reclen) { 25442621Sllai1 error = EINVAL; 25452621Sllai1 goto done; 25462621Sllai1 } 25472621Sllai1 25482621Sllai1 dp->d_reclen = (ushort_t)this_reclen; 25492621Sllai1 dp->d_ino = (ino64_t)ddv->sdev_dotdot->sdev_ino; 25502621Sllai1 dp->d_off = (off64_t)2; 25512621Sllai1 25522621Sllai1 (void) strncpy(dp->d_name, "..", 25532621Sllai1 DIRENT64_NAMELEN(this_reclen)); 25542621Sllai1 outcount += dp->d_reclen; 25552621Sllai1 25562621Sllai1 dp = nextdp(dp); 25572621Sllai1 } 25582621Sllai1 25592621Sllai1 25602621Sllai1 /* gets the cache */ 25612621Sllai1 diroff++; 25626260Sjg for (dv = SDEV_FIRST_ENTRY(ddv); dv; 25636260Sjg dv = SDEV_NEXT_ENTRY(ddv, dv), diroff++) { 25642621Sllai1 sdcmn_err3(("sdev_readdir: diroff %lld soff %lld for '%s' \n", 25652621Sllai1 diroff, soff, dv->sdev_name)); 25662621Sllai1 25672621Sllai1 /* bypassing pre-matured nodes */ 25682621Sllai1 if (diroff < soff || (dv->sdev_state != SDEV_READY)) { 25692621Sllai1 sdcmn_err3(("sdev_readdir: pre-mature node " 257010588SEric.Taylor@Sun.COM "%s %d\n", dv->sdev_name, dv->sdev_state)); 25712621Sllai1 continue; 25722621Sllai1 } 25732621Sllai1 25742621Sllai1 /* 25752621Sllai1 * Check validity of node 2576*12717SJerry.Gilliam@Sun.COM * Drop invalid and nodes to be skipped. 2577*12717SJerry.Gilliam@Sun.COM * A node the validator indicates as stale needs 2578*12717SJerry.Gilliam@Sun.COM * to be returned as presumably the node name itself 2579*12717SJerry.Gilliam@Sun.COM * is valid and the node data itself will be refreshed 2580*12717SJerry.Gilliam@Sun.COM * on lookup. An application performing a readdir then 2581*12717SJerry.Gilliam@Sun.COM * stat on each entry should thus always see consistent 2582*12717SJerry.Gilliam@Sun.COM * data. In any case, it is not possible to synchronize 2583*12717SJerry.Gilliam@Sun.COM * with dynamic kernel state, and any view we return can 2584*12717SJerry.Gilliam@Sun.COM * never be anything more than a snapshot at a point in time. 25852621Sllai1 */ 25862621Sllai1 if (vtor) { 25872621Sllai1 switch (vtor(dv)) { 25882621Sllai1 case SDEV_VTOR_VALID: 25892621Sllai1 break; 25902621Sllai1 case SDEV_VTOR_INVALID: 25912621Sllai1 case SDEV_VTOR_SKIP: 25922621Sllai1 continue; 2593*12717SJerry.Gilliam@Sun.COM case SDEV_VTOR_STALE: 2594*12717SJerry.Gilliam@Sun.COM sdcmn_err3(("sdev_readir: %s stale\n", 2595*12717SJerry.Gilliam@Sun.COM dv->sdev_name)); 2596*12717SJerry.Gilliam@Sun.COM break; 25972621Sllai1 default: 25982621Sllai1 cmn_err(CE_PANIC, 25992621Sllai1 "dev fs: validator failed: %s(%p)\n", 26002621Sllai1 dv->sdev_name, (void *)dv); 26012621Sllai1 break; 26022621Sllai1 /*NOTREACHED*/ 26032621Sllai1 } 26042621Sllai1 } 26052621Sllai1 26062621Sllai1 namelen = strlen(dv->sdev_name); 26072621Sllai1 reclen = DIRENT64_RECLEN(namelen); 26082621Sllai1 if (outcount + reclen > alloc_count) { 26092621Sllai1 goto full; 26102621Sllai1 } 26112621Sllai1 dp->d_reclen = (ushort_t)reclen; 26122621Sllai1 dp->d_ino = (ino64_t)dv->sdev_ino; 26132621Sllai1 dp->d_off = (off64_t)diroff + 1; 26142621Sllai1 (void) strncpy(dp->d_name, dv->sdev_name, 26152621Sllai1 DIRENT64_NAMELEN(reclen)); 26162621Sllai1 outcount += reclen; 26172621Sllai1 dp = nextdp(dp); 26182621Sllai1 } 26192621Sllai1 26202621Sllai1 full: 26212621Sllai1 sdcmn_err4(("sdev_readdir: moving %lu bytes: " 26222621Sllai1 "diroff %lld, soff %lld, dv %p\n", outcount, diroff, soff, 26232621Sllai1 (void *)dv)); 26242621Sllai1 26252621Sllai1 if (outcount) 26262621Sllai1 error = uiomove(outbuf, outcount, UIO_READ, uiop); 26272621Sllai1 26282621Sllai1 if (!error) { 26293133Sjg uiop->uio_loffset = diroff; 26302621Sllai1 if (eofp) 26312621Sllai1 *eofp = dv ? 0 : 1; 26322621Sllai1 } 26332621Sllai1 26342621Sllai1 26352621Sllai1 if (ddv->sdev_attrvp) { 26362621Sllai1 gethrestime(&now); 26372621Sllai1 attr.va_ctime = now; 26382621Sllai1 attr.va_atime = now; 26392621Sllai1 attr.va_mask = AT_CTIME|AT_ATIME; 26402621Sllai1 26412621Sllai1 (void) VOP_SETATTR(ddv->sdev_attrvp, &attr, 0, kcred, NULL); 26422621Sllai1 } 26432621Sllai1 done: 26442621Sllai1 kmem_free(outbuf, alloc_count); 26452621Sllai1 return (error); 26462621Sllai1 } 26472621Sllai1 26482621Sllai1 static int 26492621Sllai1 sdev_modctl_lookup(const char *path, vnode_t **r_vp) 26502621Sllai1 { 26512621Sllai1 vnode_t *vp; 26522621Sllai1 vnode_t *cvp; 26532621Sllai1 struct sdev_node *svp; 26542621Sllai1 char *nm; 26552621Sllai1 struct pathname pn; 26562621Sllai1 int error; 26572621Sllai1 int persisted = 0; 26582621Sllai1 26597988SJerry.Gilliam@Sun.COM ASSERT(INGLOBALZONE(curproc)); 26607988SJerry.Gilliam@Sun.COM 26612621Sllai1 if (error = pn_get((char *)path, UIO_SYSSPACE, &pn)) 26622621Sllai1 return (error); 26632621Sllai1 nm = kmem_alloc(MAXNAMELEN, KM_SLEEP); 26642621Sllai1 26652621Sllai1 vp = rootdir; 26662621Sllai1 VN_HOLD(vp); 26672621Sllai1 26682621Sllai1 while (pn_pathleft(&pn)) { 26697988SJerry.Gilliam@Sun.COM ASSERT(vp->v_type == VDIR || vp->v_type == VLNK); 26702621Sllai1 (void) pn_getcomponent(&pn, nm); 26717988SJerry.Gilliam@Sun.COM 26727988SJerry.Gilliam@Sun.COM /* 26737988SJerry.Gilliam@Sun.COM * Deal with the .. special case where we may be 26747988SJerry.Gilliam@Sun.COM * traversing up across a mount point, to the 26757988SJerry.Gilliam@Sun.COM * root of this filesystem or global root. 26767988SJerry.Gilliam@Sun.COM */ 26777988SJerry.Gilliam@Sun.COM if (nm[0] == '.' && nm[1] == '.' && nm[2] == 0) { 26787988SJerry.Gilliam@Sun.COM checkforroot: 26797988SJerry.Gilliam@Sun.COM if (VN_CMP(vp, rootdir)) { 26807988SJerry.Gilliam@Sun.COM nm[1] = 0; 26817988SJerry.Gilliam@Sun.COM } else if (vp->v_flag & VROOT) { 26827988SJerry.Gilliam@Sun.COM vfs_t *vfsp; 26837988SJerry.Gilliam@Sun.COM cvp = vp; 26847988SJerry.Gilliam@Sun.COM vfsp = cvp->v_vfsp; 26857988SJerry.Gilliam@Sun.COM vfs_rlock_wait(vfsp); 26867988SJerry.Gilliam@Sun.COM vp = cvp->v_vfsp->vfs_vnodecovered; 26877988SJerry.Gilliam@Sun.COM if (vp == NULL || 26887988SJerry.Gilliam@Sun.COM (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) { 26897988SJerry.Gilliam@Sun.COM vfs_unlock(vfsp); 26907988SJerry.Gilliam@Sun.COM VN_RELE(cvp); 26917988SJerry.Gilliam@Sun.COM error = EIO; 26927988SJerry.Gilliam@Sun.COM break; 26937988SJerry.Gilliam@Sun.COM } 26947988SJerry.Gilliam@Sun.COM VN_HOLD(vp); 26957988SJerry.Gilliam@Sun.COM vfs_unlock(vfsp); 26967988SJerry.Gilliam@Sun.COM VN_RELE(cvp); 26977988SJerry.Gilliam@Sun.COM cvp = NULL; 26987988SJerry.Gilliam@Sun.COM goto checkforroot; 26997988SJerry.Gilliam@Sun.COM } 27007988SJerry.Gilliam@Sun.COM } 27017988SJerry.Gilliam@Sun.COM 27025331Samw error = VOP_LOOKUP(vp, nm, &cvp, NULL, 0, NULL, kcred, NULL, 27035331Samw NULL, NULL); 27047988SJerry.Gilliam@Sun.COM if (error) { 27057988SJerry.Gilliam@Sun.COM VN_RELE(vp); 27062621Sllai1 break; 27077988SJerry.Gilliam@Sun.COM } 27082621Sllai1 27092621Sllai1 /* traverse mount points encountered on our journey */ 27102621Sllai1 if (vn_ismntpt(cvp) && (error = traverse(&cvp)) != 0) { 27117988SJerry.Gilliam@Sun.COM VN_RELE(vp); 27122621Sllai1 VN_RELE(cvp); 27132621Sllai1 break; 27142621Sllai1 } 27152621Sllai1 27162621Sllai1 /* 27177988SJerry.Gilliam@Sun.COM * symbolic link, can be either relative and absolute 27187988SJerry.Gilliam@Sun.COM */ 27197988SJerry.Gilliam@Sun.COM if ((cvp->v_type == VLNK) && pn_pathleft(&pn)) { 27207988SJerry.Gilliam@Sun.COM struct pathname linkpath; 27217988SJerry.Gilliam@Sun.COM pn_alloc(&linkpath); 27227988SJerry.Gilliam@Sun.COM if (error = pn_getsymlink(cvp, &linkpath, kcred)) { 27237988SJerry.Gilliam@Sun.COM pn_free(&linkpath); 27247988SJerry.Gilliam@Sun.COM break; 27257988SJerry.Gilliam@Sun.COM } 27267988SJerry.Gilliam@Sun.COM if (pn_pathleft(&linkpath) == 0) 27277988SJerry.Gilliam@Sun.COM (void) pn_set(&linkpath, "."); 27287988SJerry.Gilliam@Sun.COM error = pn_insert(&pn, &linkpath, strlen(nm)); 27297988SJerry.Gilliam@Sun.COM pn_free(&linkpath); 27307988SJerry.Gilliam@Sun.COM if (pn.pn_pathlen == 0) { 27317988SJerry.Gilliam@Sun.COM VN_RELE(vp); 27327988SJerry.Gilliam@Sun.COM return (ENOENT); 27337988SJerry.Gilliam@Sun.COM } 27347988SJerry.Gilliam@Sun.COM if (pn.pn_path[0] == '/') { 27357988SJerry.Gilliam@Sun.COM pn_skipslash(&pn); 27367988SJerry.Gilliam@Sun.COM VN_RELE(vp); 27377988SJerry.Gilliam@Sun.COM VN_RELE(cvp); 27387988SJerry.Gilliam@Sun.COM vp = rootdir; 27397988SJerry.Gilliam@Sun.COM VN_HOLD(vp); 27407988SJerry.Gilliam@Sun.COM } else { 27417988SJerry.Gilliam@Sun.COM VN_RELE(cvp); 27427988SJerry.Gilliam@Sun.COM } 27437988SJerry.Gilliam@Sun.COM continue; 27447988SJerry.Gilliam@Sun.COM } 27457988SJerry.Gilliam@Sun.COM 27467988SJerry.Gilliam@Sun.COM VN_RELE(vp); 27477988SJerry.Gilliam@Sun.COM 27487988SJerry.Gilliam@Sun.COM /* 27492621Sllai1 * Direct the operation to the persisting filesystem 27502621Sllai1 * underlying /dev. Bail if we encounter a 27512621Sllai1 * non-persistent dev entity here. 27522621Sllai1 */ 27532621Sllai1 if (cvp->v_vfsp->vfs_fstype == devtype) { 27542621Sllai1 27552621Sllai1 if ((VTOSDEV(cvp)->sdev_flags & SDEV_PERSIST) == 0) { 27562621Sllai1 error = ENOENT; 27572621Sllai1 VN_RELE(cvp); 27582621Sllai1 break; 27592621Sllai1 } 27602621Sllai1 27612621Sllai1 if (VTOSDEV(cvp) == NULL) { 27622621Sllai1 error = ENOENT; 27632621Sllai1 VN_RELE(cvp); 27642621Sllai1 break; 27652621Sllai1 } 27662621Sllai1 svp = VTOSDEV(cvp); 27672621Sllai1 if ((vp = svp->sdev_attrvp) == NULL) { 27682621Sllai1 error = ENOENT; 27692621Sllai1 VN_RELE(cvp); 27702621Sllai1 break; 27712621Sllai1 } 27722621Sllai1 persisted = 1; 27732621Sllai1 VN_HOLD(vp); 27742621Sllai1 VN_RELE(cvp); 27752621Sllai1 cvp = vp; 27762621Sllai1 } 27772621Sllai1 27782621Sllai1 vp = cvp; 27792621Sllai1 pn_skipslash(&pn); 27802621Sllai1 } 27812621Sllai1 27822621Sllai1 kmem_free(nm, MAXNAMELEN); 27832621Sllai1 pn_free(&pn); 27842621Sllai1 27852621Sllai1 if (error) 27862621Sllai1 return (error); 27872621Sllai1 27882621Sllai1 /* 27892621Sllai1 * Only return persisted nodes in the filesystem underlying /dev. 27902621Sllai1 */ 27912621Sllai1 if (!persisted) { 27922621Sllai1 VN_RELE(vp); 27932621Sllai1 return (ENOENT); 27942621Sllai1 } 27952621Sllai1 27962621Sllai1 *r_vp = vp; 27972621Sllai1 return (0); 27982621Sllai1 } 27992621Sllai1 28002621Sllai1 int 28012621Sllai1 sdev_modctl_readdir(const char *dir, char ***dirlistp, 28026065Scth int *npathsp, int *npathsp_alloc, int checking_empty) 28032621Sllai1 { 28042621Sllai1 char **pathlist = NULL; 28052621Sllai1 char **newlist = NULL; 28062621Sllai1 int npaths = 0; 28072621Sllai1 int npaths_alloc = 0; 28082621Sllai1 dirent64_t *dbuf = NULL; 28092621Sllai1 int n; 28102621Sllai1 char *s; 28112621Sllai1 int error; 28122621Sllai1 vnode_t *vp; 28132621Sllai1 int eof; 28142621Sllai1 struct iovec iov; 28152621Sllai1 struct uio uio; 28162621Sllai1 struct dirent64 *dp; 28172621Sllai1 size_t dlen; 28182621Sllai1 size_t dbuflen; 28192621Sllai1 int ndirents = 64; 28202621Sllai1 char *nm; 28212621Sllai1 28222621Sllai1 error = sdev_modctl_lookup(dir, &vp); 28232621Sllai1 sdcmn_err11(("modctl readdir: %s by %s: %s\n", 28242621Sllai1 dir, curproc->p_user.u_comm, 28252621Sllai1 (error == 0) ? "ok" : "failed")); 28262621Sllai1 if (error) 28272621Sllai1 return (error); 28282621Sllai1 28292621Sllai1 dlen = ndirents * (sizeof (*dbuf)); 28302621Sllai1 dbuf = kmem_alloc(dlen, KM_SLEEP); 28312621Sllai1 28322621Sllai1 uio.uio_iov = &iov; 28332621Sllai1 uio.uio_iovcnt = 1; 28342621Sllai1 uio.uio_segflg = UIO_SYSSPACE; 28352621Sllai1 uio.uio_fmode = 0; 28362621Sllai1 uio.uio_extflg = UIO_COPY_CACHED; 28372621Sllai1 uio.uio_loffset = 0; 28382621Sllai1 uio.uio_llimit = MAXOFFSET_T; 28392621Sllai1 28402621Sllai1 eof = 0; 28412621Sllai1 error = 0; 28422621Sllai1 while (!error && !eof) { 28432621Sllai1 uio.uio_resid = dlen; 28442621Sllai1 iov.iov_base = (char *)dbuf; 28452621Sllai1 iov.iov_len = dlen; 28462621Sllai1 28472621Sllai1 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); 28485331Samw error = VOP_READDIR(vp, &uio, kcred, &eof, NULL, 0); 28492621Sllai1 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); 28502621Sllai1 28512621Sllai1 dbuflen = dlen - uio.uio_resid; 28522621Sllai1 28532621Sllai1 if (error || dbuflen == 0) 28542621Sllai1 break; 28552621Sllai1 28562621Sllai1 for (dp = dbuf; ((intptr_t)dp < (intptr_t)dbuf + dbuflen); 28576065Scth dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) { 28582621Sllai1 28592621Sllai1 nm = dp->d_name; 28602621Sllai1 28612621Sllai1 if (strcmp(nm, ".") == 0 || strcmp(nm, "..") == 0) 28622621Sllai1 continue; 28632621Sllai1 if (npaths == npaths_alloc) { 28642621Sllai1 npaths_alloc += 64; 28652621Sllai1 newlist = (char **) 28662621Sllai1 kmem_zalloc((npaths_alloc + 1) * 28676065Scth sizeof (char *), KM_SLEEP); 28682621Sllai1 if (pathlist) { 28692621Sllai1 bcopy(pathlist, newlist, 28702621Sllai1 npaths * sizeof (char *)); 28712621Sllai1 kmem_free(pathlist, 28722621Sllai1 (npaths + 1) * sizeof (char *)); 28732621Sllai1 } 28742621Sllai1 pathlist = newlist; 28752621Sllai1 } 28762621Sllai1 n = strlen(nm) + 1; 28772621Sllai1 s = kmem_alloc(n, KM_SLEEP); 28782621Sllai1 bcopy(nm, s, n); 28792621Sllai1 pathlist[npaths++] = s; 28802621Sllai1 sdcmn_err11((" %s/%s\n", dir, s)); 28816065Scth 28826065Scth /* if checking empty, one entry is as good as many */ 28836065Scth if (checking_empty) { 28846065Scth eof = 1; 28856065Scth break; 28866065Scth } 28872621Sllai1 } 28882621Sllai1 } 28892621Sllai1 28902621Sllai1 exit: 28912621Sllai1 VN_RELE(vp); 28922621Sllai1 28932621Sllai1 if (dbuf) 28942621Sllai1 kmem_free(dbuf, dlen); 28952621Sllai1 28962621Sllai1 if (error) 28972621Sllai1 return (error); 28982621Sllai1 28992621Sllai1 *dirlistp = pathlist; 29002621Sllai1 *npathsp = npaths; 29012621Sllai1 *npathsp_alloc = npaths_alloc; 29022621Sllai1 29032621Sllai1 return (0); 29042621Sllai1 } 29052621Sllai1 29062621Sllai1 void 29072621Sllai1 sdev_modctl_readdir_free(char **pathlist, int npaths, int npaths_alloc) 29082621Sllai1 { 29092621Sllai1 int i, n; 29102621Sllai1 29112621Sllai1 for (i = 0; i < npaths; i++) { 29122621Sllai1 n = strlen(pathlist[i]) + 1; 29132621Sllai1 kmem_free(pathlist[i], n); 29142621Sllai1 } 29152621Sllai1 29162621Sllai1 kmem_free(pathlist, (npaths_alloc + 1) * sizeof (char *)); 29172621Sllai1 } 29182621Sllai1 29192621Sllai1 int 29202621Sllai1 sdev_modctl_devexists(const char *path) 29212621Sllai1 { 29222621Sllai1 vnode_t *vp; 29232621Sllai1 int error; 29242621Sllai1 29252621Sllai1 error = sdev_modctl_lookup(path, &vp); 29262621Sllai1 sdcmn_err11(("modctl dev exists: %s by %s: %s\n", 29272621Sllai1 path, curproc->p_user.u_comm, 29282621Sllai1 (error == 0) ? "ok" : "failed")); 29292621Sllai1 if (error == 0) 29302621Sllai1 VN_RELE(vp); 29312621Sllai1 29322621Sllai1 return (error); 29332621Sllai1 } 29342621Sllai1 29352621Sllai1 extern int sdev_vnodeops_tbl_size; 29362621Sllai1 29372621Sllai1 /* 29382621Sllai1 * construct a new template with overrides from vtab 29392621Sllai1 */ 29402621Sllai1 static fs_operation_def_t * 29412621Sllai1 sdev_merge_vtab(const fs_operation_def_t tab[]) 29422621Sllai1 { 29432621Sllai1 fs_operation_def_t *new; 29442621Sllai1 const fs_operation_def_t *tab_entry; 29452621Sllai1 29462621Sllai1 /* make a copy of standard vnode ops table */ 29472621Sllai1 new = kmem_alloc(sdev_vnodeops_tbl_size, KM_SLEEP); 29482621Sllai1 bcopy((void *)sdev_vnodeops_tbl, new, sdev_vnodeops_tbl_size); 29492621Sllai1 29502621Sllai1 /* replace the overrides from tab */ 29512621Sllai1 for (tab_entry = tab; tab_entry->name != NULL; tab_entry++) { 29522621Sllai1 fs_operation_def_t *std_entry = new; 29532621Sllai1 while (std_entry->name) { 29542621Sllai1 if (strcmp(tab_entry->name, std_entry->name) == 0) { 29552621Sllai1 std_entry->func = tab_entry->func; 29562621Sllai1 break; 29572621Sllai1 } 29582621Sllai1 std_entry++; 29592621Sllai1 } 29602621Sllai1 if (std_entry->name == NULL) 29612621Sllai1 cmn_err(CE_NOTE, "sdev_merge_vtab: entry %s unused.", 29622621Sllai1 tab_entry->name); 29632621Sllai1 } 29642621Sllai1 29652621Sllai1 return (new); 29662621Sllai1 } 29672621Sllai1 29682621Sllai1 /* free memory allocated by sdev_merge_vtab */ 29692621Sllai1 static void 29702621Sllai1 sdev_free_vtab(fs_operation_def_t *new) 29712621Sllai1 { 29722621Sllai1 kmem_free(new, sdev_vnodeops_tbl_size); 29732621Sllai1 } 29742621Sllai1 29752621Sllai1 /* 29762621Sllai1 * a generic setattr() function 29772621Sllai1 * 29782621Sllai1 * note: flags only supports AT_UID and AT_GID. 29792621Sllai1 * Future enhancements can be done for other types, e.g. AT_MODE 29802621Sllai1 */ 29812621Sllai1 int 29822621Sllai1 devname_setattr_func(struct vnode *vp, struct vattr *vap, int flags, 29832621Sllai1 struct cred *cred, int (*callback)(struct sdev_node *, struct vattr *, 29842621Sllai1 int), int protocol) 29852621Sllai1 { 29862621Sllai1 struct sdev_node *dv = VTOSDEV(vp); 29872621Sllai1 struct sdev_node *parent = dv->sdev_dotdot; 29882621Sllai1 struct vattr *get; 29892621Sllai1 uint_t mask = vap->va_mask; 29902621Sllai1 int error; 29912621Sllai1 29922621Sllai1 /* some sanity checks */ 29932621Sllai1 if (vap->va_mask & AT_NOSET) 29942621Sllai1 return (EINVAL); 29952621Sllai1 29962621Sllai1 if (vap->va_mask & AT_SIZE) { 29972621Sllai1 if (vp->v_type == VDIR) { 29982621Sllai1 return (EISDIR); 29992621Sllai1 } 30002621Sllai1 } 30012621Sllai1 30022621Sllai1 /* no need to set attribute, but do not fail either */ 30032621Sllai1 ASSERT(parent); 30042621Sllai1 rw_enter(&parent->sdev_contents, RW_READER); 30052621Sllai1 if (dv->sdev_state == SDEV_ZOMBIE) { 30062621Sllai1 rw_exit(&parent->sdev_contents); 30072621Sllai1 return (0); 30082621Sllai1 } 30092621Sllai1 30102621Sllai1 /* If backing store exists, just set it. */ 30112621Sllai1 if (dv->sdev_attrvp) { 30122621Sllai1 rw_exit(&parent->sdev_contents); 30132621Sllai1 return (VOP_SETATTR(dv->sdev_attrvp, vap, flags, cred, NULL)); 30142621Sllai1 } 30152621Sllai1 30162621Sllai1 /* 30172621Sllai1 * Otherwise, for nodes with the persistence attribute, create it. 30182621Sllai1 */ 30192621Sllai1 ASSERT(dv->sdev_attr); 30202621Sllai1 if (SDEV_IS_PERSIST(dv) || 30212621Sllai1 ((vap->va_mask & ~AT_TIMES) != 0 && !SDEV_IS_DYNAMIC(dv))) { 30222621Sllai1 sdev_vattr_merge(dv, vap); 30232621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 30242621Sllai1 error = sdev_shadow_node(dv, cred); 30252621Sllai1 rw_exit(&dv->sdev_contents); 30262621Sllai1 rw_exit(&parent->sdev_contents); 30272621Sllai1 30282621Sllai1 if (error) 30292621Sllai1 return (error); 30302621Sllai1 return (VOP_SETATTR(dv->sdev_attrvp, vap, flags, cred, NULL)); 30312621Sllai1 } 30322621Sllai1 30332621Sllai1 30342621Sllai1 /* 30352621Sllai1 * sdev_attr was allocated in sdev_mknode 30362621Sllai1 */ 30372621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 30385331Samw error = secpolicy_vnode_setattr(cred, vp, vap, 30395331Samw dv->sdev_attr, flags, sdev_unlocked_access, dv); 30402621Sllai1 if (error) { 30412621Sllai1 rw_exit(&dv->sdev_contents); 30422621Sllai1 rw_exit(&parent->sdev_contents); 30432621Sllai1 return (error); 30442621Sllai1 } 30452621Sllai1 30462621Sllai1 get = dv->sdev_attr; 30472621Sllai1 if (mask & AT_MODE) { 30482621Sllai1 get->va_mode &= S_IFMT; 30492621Sllai1 get->va_mode |= vap->va_mode & ~S_IFMT; 30502621Sllai1 } 30512621Sllai1 30522621Sllai1 if ((mask & AT_UID) || (mask & AT_GID)) { 30532621Sllai1 if (mask & AT_UID) 30542621Sllai1 get->va_uid = vap->va_uid; 30552621Sllai1 if (mask & AT_GID) 30562621Sllai1 get->va_gid = vap->va_gid; 30572621Sllai1 /* 30582621Sllai1 * a callback must be provided if the protocol is set 30592621Sllai1 */ 30602621Sllai1 if ((protocol & AT_UID) || (protocol & AT_GID)) { 30612621Sllai1 ASSERT(callback); 30622621Sllai1 error = callback(dv, get, protocol); 30632621Sllai1 if (error) { 30642621Sllai1 rw_exit(&dv->sdev_contents); 30652621Sllai1 rw_exit(&parent->sdev_contents); 30662621Sllai1 return (error); 30672621Sllai1 } 30682621Sllai1 } 30692621Sllai1 } 30702621Sllai1 30712621Sllai1 if (mask & AT_ATIME) 30722621Sllai1 get->va_atime = vap->va_atime; 30732621Sllai1 if (mask & AT_MTIME) 30742621Sllai1 get->va_mtime = vap->va_mtime; 30752621Sllai1 if (mask & (AT_MODE | AT_UID | AT_GID | AT_CTIME)) { 30762621Sllai1 gethrestime(&get->va_ctime); 30772621Sllai1 } 30782621Sllai1 30792621Sllai1 sdev_vattr_merge(dv, get); 30802621Sllai1 rw_exit(&dv->sdev_contents); 30812621Sllai1 rw_exit(&parent->sdev_contents); 30822621Sllai1 return (0); 30832621Sllai1 } 30845895Syz147064 30855895Syz147064 /* 30865895Syz147064 * a generic inactive() function 30875895Syz147064 */ 308810097SEric.Taylor@Sun.COM /*ARGSUSED*/ 30895895Syz147064 void 30905895Syz147064 devname_inactive_func(struct vnode *vp, struct cred *cred, 30915895Syz147064 void (*callback)(struct vnode *)) 30925895Syz147064 { 30935895Syz147064 int clean; 30945895Syz147064 struct sdev_node *dv = VTOSDEV(vp); 30955895Syz147064 struct sdev_node *ddv = dv->sdev_dotdot; 30965895Syz147064 int state; 30975895Syz147064 30985895Syz147064 rw_enter(&ddv->sdev_contents, RW_WRITER); 30995895Syz147064 state = dv->sdev_state; 31005895Syz147064 31015895Syz147064 mutex_enter(&vp->v_lock); 31025895Syz147064 ASSERT(vp->v_count >= 1); 31035895Syz147064 31045895Syz147064 if (vp->v_count == 1 && callback != NULL) 31055895Syz147064 callback(vp); 31065895Syz147064 31075895Syz147064 clean = (vp->v_count == 1) && (state == SDEV_ZOMBIE); 31085895Syz147064 31095895Syz147064 /* 31105895Syz147064 * last ref count on the ZOMBIE node is released. 31115895Syz147064 * clean up the sdev_node, and 31125895Syz147064 * release the hold on the backing store node so that 31135895Syz147064 * the ZOMBIE backing stores also cleaned out. 31145895Syz147064 */ 31155895Syz147064 if (clean) { 31165895Syz147064 ASSERT(ddv); 31175895Syz147064 31185895Syz147064 ddv->sdev_nlink--; 31195895Syz147064 if (vp->v_type == VDIR) { 31205895Syz147064 dv->sdev_nlink--; 31215895Syz147064 } 31226347Sjg if ((dv->sdev_flags & SDEV_STALE) == 0) 31236347Sjg avl_remove(&ddv->sdev_entries, dv); 31245895Syz147064 dv->sdev_nlink--; 31255895Syz147064 --vp->v_count; 31265895Syz147064 mutex_exit(&vp->v_lock); 31275895Syz147064 sdev_nodedestroy(dv, 0); 31285895Syz147064 } else { 31295895Syz147064 --vp->v_count; 31305895Syz147064 mutex_exit(&vp->v_lock); 31315895Syz147064 } 31325895Syz147064 rw_exit(&ddv->sdev_contents); 31335895Syz147064 } 3134