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
sdev_prof_free(struct sdev_node * dv)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
i_sdev_node_ctor(void * buf,void * cfarg,int flag)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
i_sdev_node_dtor(void * buf,void * arg)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
sdev_node_cache_init()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
sdev_node_cache_fini()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
sdev_compare_nodes(const struct sdev_node * dv1,const struct sdev_node * dv2)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
sdev_set_nodestate(struct sdev_node * dv,sdev_node_state_t state)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
sdev_attr_update(struct sdev_node * dv,vattr_t * vap)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
sdev_attr_alloc(struct sdev_node * dv,vattr_t * vap)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
sdev_nodeinit(struct sdev_node * ddv,char * nm,struct sdev_node ** newdv,vattr_t * vap)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
sdev_nodeready(struct sdev_node * dv,struct vattr * vap,struct vnode * avp,void * args,struct cred * cred)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
sdev_nodezombied(struct sdev_node * dv)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 *
sdev_mkroot(struct vfs * vfsp,dev_t devdev,struct vnode * mvp,struct vnode * avp,struct cred * cred)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
550*12911Sjohn.levon@sun.com /*
551*12911Sjohn.levon@sun.com * SDEV_DYNAMIC: prevent calling out to devfsadm, since only the
552*12911Sjohn.levon@sun.com * lofi driver controls child nodes.
553*12911Sjohn.levon@sun.com *
554*12911Sjohn.levon@sun.com * SDEV_PERSIST: ensure devfsadm knows to clean up any persisted
555*12911Sjohn.levon@sun.com * stale nodes (e.g. from devfsadm -R).
556*12911Sjohn.levon@sun.com *
557*12911Sjohn.levon@sun.com * In addition, devfsadm knows not to attempt a rmdir: a zone
558*12911Sjohn.levon@sun.com * may hold a reference, which would zombify the node,
559*12911Sjohn.levon@sun.com * preventing a mkdir.
560*12911Sjohn.levon@sun.com */
561*12911Sjohn.levon@sun.com
562*12911Sjohn.levon@sun.com { "lofi", NULL, NULL, NULL, NULL,
563*12911Sjohn.levon@sun.com SDEV_ZONED | SDEV_DYNAMIC | SDEV_PERSIST },
564*12911Sjohn.levon@sun.com { "rlofi", NULL, NULL, NULL, NULL,
565*12911Sjohn.levon@sun.com SDEV_ZONED | SDEV_DYNAMIC | SDEV_PERSIST },
56612633Sjohn.levon@sun.com
5672621Sllai1 { NULL, NULL, NULL, NULL, NULL, 0}
5682621Sllai1 };
5692621Sllai1
57010588SEric.Taylor@Sun.COM struct sdev_vop_table *
sdev_match(struct sdev_node * dv)57110588SEric.Taylor@Sun.COM sdev_match(struct sdev_node *dv)
57210588SEric.Taylor@Sun.COM {
57310588SEric.Taylor@Sun.COM int vlen;
57410588SEric.Taylor@Sun.COM int i;
57510588SEric.Taylor@Sun.COM
57610588SEric.Taylor@Sun.COM for (i = 0; vtab[i].vt_name; i++) {
57710588SEric.Taylor@Sun.COM if (strcmp(vtab[i].vt_name, dv->sdev_name) == 0)
57810588SEric.Taylor@Sun.COM return (&vtab[i]);
57910588SEric.Taylor@Sun.COM if (vtab[i].vt_flags & SDEV_SUBDIR) {
58010588SEric.Taylor@Sun.COM char *ptr;
58110588SEric.Taylor@Sun.COM
58210588SEric.Taylor@Sun.COM ASSERT(strlen(dv->sdev_path) > 5);
58310588SEric.Taylor@Sun.COM ptr = dv->sdev_path + 5;
58410588SEric.Taylor@Sun.COM vlen = strlen(vtab[i].vt_name);
58510588SEric.Taylor@Sun.COM if ((strncmp(vtab[i].vt_name, ptr,
58610588SEric.Taylor@Sun.COM vlen - 1) == 0) && ptr[vlen] == '/')
58710588SEric.Taylor@Sun.COM return (&vtab[i]);
58810588SEric.Taylor@Sun.COM }
58910588SEric.Taylor@Sun.COM
59010588SEric.Taylor@Sun.COM }
59110588SEric.Taylor@Sun.COM return (NULL);
59210588SEric.Taylor@Sun.COM }
5932621Sllai1
5942621Sllai1 /*
5952621Sllai1 * sets a directory's vnodeops if the directory is in the vtab;
5962621Sllai1 */
5972621Sllai1 static struct vnodeops *
sdev_get_vop(struct sdev_node * dv)5982621Sllai1 sdev_get_vop(struct sdev_node *dv)
5992621Sllai1 {
60010588SEric.Taylor@Sun.COM struct sdev_vop_table *vtp;
6012621Sllai1 char *path;
6022621Sllai1
6032621Sllai1 path = dv->sdev_path;
6042621Sllai1 ASSERT(path);
6052621Sllai1
6062621Sllai1 /* gets the relative path to /dev/ */
6072621Sllai1 path += 5;
6082621Sllai1
60910588SEric.Taylor@Sun.COM /* gets the vtab entry it matches */
61010588SEric.Taylor@Sun.COM if ((vtp = sdev_match(dv)) != NULL) {
61110588SEric.Taylor@Sun.COM dv->sdev_flags |= vtp->vt_flags;
61210588SEric.Taylor@Sun.COM
61310588SEric.Taylor@Sun.COM if (vtp->vt_vops) {
61410588SEric.Taylor@Sun.COM if (vtp->vt_global_vops)
61510588SEric.Taylor@Sun.COM *(vtp->vt_global_vops) = vtp->vt_vops;
61610588SEric.Taylor@Sun.COM return (vtp->vt_vops);
6172621Sllai1 }
6182621Sllai1
61910588SEric.Taylor@Sun.COM if (vtp->vt_service) {
6202621Sllai1 fs_operation_def_t *templ;
62110588SEric.Taylor@Sun.COM templ = sdev_merge_vtab(vtp->vt_service);
62210588SEric.Taylor@Sun.COM if (vn_make_ops(vtp->vt_name,
6232621Sllai1 (const fs_operation_def_t *)templ,
62410588SEric.Taylor@Sun.COM &vtp->vt_vops) != 0) {
6252621Sllai1 cmn_err(CE_PANIC, "%s: malformed vnode ops\n",
62610588SEric.Taylor@Sun.COM vtp->vt_name);
6272621Sllai1 /*NOTREACHED*/
6282621Sllai1 }
62910588SEric.Taylor@Sun.COM if (vtp->vt_global_vops) {
63010588SEric.Taylor@Sun.COM *(vtp->vt_global_vops) = vtp->vt_vops;
6312621Sllai1 }
6322621Sllai1 sdev_free_vtab(templ);
63310588SEric.Taylor@Sun.COM return (vtp->vt_vops);
6342621Sllai1 }
6352621Sllai1 return (sdev_vnodeops);
6362621Sllai1 }
6372621Sllai1
6382621Sllai1 /* child inherits the persistence of the parent */
6392621Sllai1 if (SDEV_IS_PERSIST(dv->sdev_dotdot))
6402621Sllai1 dv->sdev_flags |= SDEV_PERSIST;
6412621Sllai1
6422621Sllai1 return (sdev_vnodeops);
6432621Sllai1 }
6442621Sllai1
6452621Sllai1 static void
sdev_set_no_negcache(struct sdev_node * dv)64610588SEric.Taylor@Sun.COM sdev_set_no_negcache(struct sdev_node *dv)
6472621Sllai1 {
6482621Sllai1 int i;
6492621Sllai1 char *path;
6502621Sllai1
6512621Sllai1 ASSERT(dv->sdev_path);
6522621Sllai1 path = dv->sdev_path + strlen("/dev/");
6532621Sllai1
6542621Sllai1 for (i = 0; vtab[i].vt_name; i++) {
6552621Sllai1 if (strcmp(vtab[i].vt_name, path) == 0) {
6562621Sllai1 if (vtab[i].vt_flags & SDEV_NO_NCACHE)
6572621Sllai1 dv->sdev_flags |= SDEV_NO_NCACHE;
6582621Sllai1 break;
6592621Sllai1 }
6602621Sllai1 }
6612621Sllai1 }
6622621Sllai1
6632621Sllai1 void *
sdev_get_vtor(struct sdev_node * dv)6642621Sllai1 sdev_get_vtor(struct sdev_node *dv)
6652621Sllai1 {
66610588SEric.Taylor@Sun.COM struct sdev_vop_table *vtp;
66710588SEric.Taylor@Sun.COM
66810588SEric.Taylor@Sun.COM vtp = sdev_match(dv);
66910588SEric.Taylor@Sun.COM if (vtp)
67010588SEric.Taylor@Sun.COM return ((void *)vtp->vt_vtor);
67110588SEric.Taylor@Sun.COM else
67210588SEric.Taylor@Sun.COM return (NULL);
6732621Sllai1 }
6742621Sllai1
6752621Sllai1 /*
6762621Sllai1 * Build the base root inode
6772621Sllai1 */
6782621Sllai1 ino_t
sdev_mkino(struct sdev_node * dv)6792621Sllai1 sdev_mkino(struct sdev_node *dv)
6802621Sllai1 {
6812621Sllai1 ino_t ino;
6822621Sllai1
6832621Sllai1 /*
6842621Sllai1 * for now, follow the lead of tmpfs here
6852621Sllai1 * need to someday understand the requirements here
6862621Sllai1 */
6872621Sllai1 ino = (ino_t)(uint32_t)((uintptr_t)dv >> 3);
6882621Sllai1 ino += SDEV_ROOTINO + 1;
6892621Sllai1
6902621Sllai1 return (ino);
6912621Sllai1 }
6922621Sllai1
69310588SEric.Taylor@Sun.COM int
sdev_getlink(struct vnode * linkvp,char ** link)6942621Sllai1 sdev_getlink(struct vnode *linkvp, char **link)
6952621Sllai1 {
6962621Sllai1 int err;
6972621Sllai1 char *buf;
6982621Sllai1 struct uio uio = {0};
6992621Sllai1 struct iovec iov = {0};
7002621Sllai1
7012621Sllai1 if (linkvp == NULL)
7022621Sllai1 return (ENOENT);
7032621Sllai1 ASSERT(linkvp->v_type == VLNK);
7042621Sllai1
7052621Sllai1 buf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
7062621Sllai1 iov.iov_base = buf;
7072621Sllai1 iov.iov_len = MAXPATHLEN;
7082621Sllai1 uio.uio_iov = &iov;
7092621Sllai1 uio.uio_iovcnt = 1;
7102621Sllai1 uio.uio_resid = MAXPATHLEN;
7112621Sllai1 uio.uio_segflg = UIO_SYSSPACE;
7122621Sllai1 uio.uio_llimit = MAXOFFSET_T;
7132621Sllai1
7145331Samw err = VOP_READLINK(linkvp, &uio, kcred, NULL);
7152621Sllai1 if (err) {
7162621Sllai1 cmn_err(CE_WARN, "readlink %s failed in dev\n", buf);
7172621Sllai1 kmem_free(buf, MAXPATHLEN);
7182621Sllai1 return (ENOENT);
7192621Sllai1 }
7202621Sllai1
7212621Sllai1 /* mission complete */
7222621Sllai1 *link = i_ddi_strdup(buf, KM_SLEEP);
7232621Sllai1 kmem_free(buf, MAXPATHLEN);
7242621Sllai1 return (0);
7252621Sllai1 }
7262621Sllai1
7272621Sllai1 /*
7282621Sllai1 * A convenient wrapper to get the devfs node vnode for a device
7292621Sllai1 * minor functionality: readlink() of a /dev symlink
7302621Sllai1 * Place the link into dv->sdev_symlink
7312621Sllai1 */
7322621Sllai1 static int
sdev_follow_link(struct sdev_node * dv)7332621Sllai1 sdev_follow_link(struct sdev_node *dv)
7342621Sllai1 {
7352621Sllai1 int err;
7362621Sllai1 struct vnode *linkvp;
7372621Sllai1 char *link = NULL;
7382621Sllai1
7392621Sllai1 linkvp = SDEVTOV(dv);
7402621Sllai1 if (linkvp == NULL)
7412621Sllai1 return (ENOENT);
7422621Sllai1 ASSERT(linkvp->v_type == VLNK);
7432621Sllai1 err = sdev_getlink(linkvp, &link);
7442621Sllai1 if (err) {
7452621Sllai1 (void) sdev_nodezombied(dv);
7462621Sllai1 dv->sdev_symlink = NULL;
7472621Sllai1 return (ENOENT);
7482621Sllai1 }
7492621Sllai1
7502621Sllai1 ASSERT(link != NULL);
7512621Sllai1 dv->sdev_symlink = link;
7522621Sllai1 return (0);
7532621Sllai1 }
7542621Sllai1
7552621Sllai1 static int
sdev_node_check(struct sdev_node * dv,struct vattr * nvap,void * nargs)7562621Sllai1 sdev_node_check(struct sdev_node *dv, struct vattr *nvap, void *nargs)
7572621Sllai1 {
7582621Sllai1 vtype_t otype = SDEVTOV(dv)->v_type;
7592621Sllai1
7602621Sllai1 /*
7612621Sllai1 * existing sdev_node has a different type.
7622621Sllai1 */
7632621Sllai1 if (otype != nvap->va_type) {
7642621Sllai1 sdcmn_err9(("sdev_node_check: existing node "
7652621Sllai1 " %s type %d does not match new node type %d\n",
7662621Sllai1 dv->sdev_name, otype, nvap->va_type));
7672621Sllai1 return (EEXIST);
7682621Sllai1 }
7692621Sllai1
7702621Sllai1 /*
7712621Sllai1 * For a symlink, the target should be the same.
7722621Sllai1 */
7732621Sllai1 if (otype == VLNK) {
7742621Sllai1 ASSERT(nargs != NULL);
7752621Sllai1 ASSERT(dv->sdev_symlink != NULL);
7762621Sllai1 if (strcmp(dv->sdev_symlink, (char *)nargs) != 0) {
7772621Sllai1 sdcmn_err9(("sdev_node_check: existing node "
7782621Sllai1 " %s has different symlink %s as new node "
7792621Sllai1 " %s\n", dv->sdev_name, dv->sdev_symlink,
7802621Sllai1 (char *)nargs));
7812621Sllai1 return (EEXIST);
7822621Sllai1 }
7832621Sllai1 }
7842621Sllai1
7852621Sllai1 return (0);
7862621Sllai1 }
7872621Sllai1
7882621Sllai1 /*
7892621Sllai1 * sdev_mknode - a wrapper for sdev_nodeinit(), sdev_nodeready()
7902621Sllai1 *
7912621Sllai1 * arguments:
7922621Sllai1 * - ddv (parent)
7932621Sllai1 * - nm (child name)
7942621Sllai1 * - newdv (sdev_node for nm is returned here)
7952621Sllai1 * - vap (vattr for the node to be created, va_type should be set.
7966335Sjg * - avp (attribute vnode)
7972621Sllai1 * the defaults should be used if unknown)
7982621Sllai1 * - cred
7992621Sllai1 * - args
8002621Sllai1 * . tnm (for VLNK)
8012621Sllai1 * . global sdev_node (for !SDEV_GLOBAL)
8022621Sllai1 * - state: SDEV_INIT, SDEV_READY
8032621Sllai1 *
8042621Sllai1 * only ddv, nm, newddv, vap, cred are required for sdev_mknode(SDEV_INIT)
8052621Sllai1 *
8062621Sllai1 * NOTE: directory contents writers lock needs to be held before
8072621Sllai1 * calling this routine.
8082621Sllai1 */
8092621Sllai1 int
sdev_mknode(struct sdev_node * ddv,char * nm,struct sdev_node ** newdv,struct vattr * vap,struct vnode * avp,void * args,struct cred * cred,sdev_node_state_t state)8102621Sllai1 sdev_mknode(struct sdev_node *ddv, char *nm, struct sdev_node **newdv,
8112621Sllai1 struct vattr *vap, struct vnode *avp, void *args, struct cred *cred,
8122621Sllai1 sdev_node_state_t state)
8132621Sllai1 {
8142621Sllai1 int error = 0;
8152621Sllai1 sdev_node_state_t node_state;
8162621Sllai1 struct sdev_node *dv = NULL;
8172621Sllai1
8182621Sllai1 ASSERT(state != SDEV_ZOMBIE);
8192621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
8202621Sllai1
8212621Sllai1 if (*newdv) {
8222621Sllai1 dv = *newdv;
8232621Sllai1 } else {
8242621Sllai1 /* allocate and initialize a sdev_node */
8252621Sllai1 if (ddv->sdev_state == SDEV_ZOMBIE) {
8262621Sllai1 sdcmn_err9(("sdev_mknode: parent %s ZOMBIEd\n",
8272621Sllai1 ddv->sdev_path));
8282621Sllai1 return (ENOENT);
8292621Sllai1 }
8302621Sllai1
8312621Sllai1 error = sdev_nodeinit(ddv, nm, &dv, vap);
8322621Sllai1 if (error != 0) {
8332621Sllai1 sdcmn_err9(("sdev_mknode: error %d,"
8342621Sllai1 " name %s can not be initialized\n",
8352621Sllai1 error, nm));
8366335Sjg return (error);
8372621Sllai1 }
8382621Sllai1 ASSERT(dv);
8392621Sllai1
8402621Sllai1 /* insert into the directory cache */
8412621Sllai1 error = sdev_cache_update(ddv, &dv, nm, SDEV_CACHE_ADD);
8422621Sllai1 if (error) {
8432621Sllai1 sdcmn_err9(("sdev_mknode: node %s can not"
8442621Sllai1 " be added into directory cache\n", nm));
8452621Sllai1 return (ENOENT);
8462621Sllai1 }
8472621Sllai1 }
8482621Sllai1
8492621Sllai1 ASSERT(dv);
8502621Sllai1 node_state = dv->sdev_state;
8512621Sllai1 ASSERT(node_state != SDEV_ZOMBIE);
8522621Sllai1
8532621Sllai1 if (state == SDEV_READY) {
8542621Sllai1 switch (node_state) {
8552621Sllai1 case SDEV_INIT:
8562621Sllai1 error = sdev_nodeready(dv, vap, avp, args, cred);
8572621Sllai1 if (error) {
8582621Sllai1 sdcmn_err9(("sdev_mknode: node %s can NOT"
8592621Sllai1 " be transitioned into READY state, "
8602621Sllai1 "error %d\n", nm, error));
8612621Sllai1 }
8622621Sllai1 break;
8632621Sllai1 case SDEV_READY:
8642621Sllai1 /*
8652621Sllai1 * Do some sanity checking to make sure
8662621Sllai1 * the existing sdev_node is what has been
8672621Sllai1 * asked for.
8682621Sllai1 */
8692621Sllai1 error = sdev_node_check(dv, vap, args);
8702621Sllai1 break;
8712621Sllai1 default:
8722621Sllai1 break;
8732621Sllai1 }
8742621Sllai1 }
8752621Sllai1
8762621Sllai1 if (!error) {
8772621Sllai1 *newdv = dv;
8782621Sllai1 ASSERT((*newdv)->sdev_state != SDEV_ZOMBIE);
8792621Sllai1 } else {
8802621Sllai1 SDEV_SIMPLE_RELE(dv);
8812621Sllai1 *newdv = NULL;
8822621Sllai1 }
8832621Sllai1
8842621Sllai1 return (error);
8852621Sllai1 }
8862621Sllai1
8872621Sllai1 /*
8886335Sjg * convenient wrapper to change vp's ATIME, CTIME and MTIME
8892621Sllai1 */
8902621Sllai1 void
sdev_update_timestamps(struct vnode * vp,cred_t * cred,uint_t mask)8912621Sllai1 sdev_update_timestamps(struct vnode *vp, cred_t *cred, uint_t mask)
8922621Sllai1 {
8932621Sllai1 struct vattr attr;
8942621Sllai1 timestruc_t now;
8952621Sllai1 int err;
8962621Sllai1
8972621Sllai1 ASSERT(vp);
8982621Sllai1 gethrestime(&now);
8992621Sllai1 if (mask & AT_CTIME)
9002621Sllai1 attr.va_ctime = now;
9012621Sllai1 if (mask & AT_MTIME)
9022621Sllai1 attr.va_mtime = now;
9032621Sllai1 if (mask & AT_ATIME)
9042621Sllai1 attr.va_atime = now;
9052621Sllai1
9062621Sllai1 attr.va_mask = (mask & AT_TIMES);
9072621Sllai1 err = VOP_SETATTR(vp, &attr, 0, cred, NULL);
9082621Sllai1 if (err && (err != EROFS)) {
9092621Sllai1 sdcmn_err(("update timestamps error %d\n", err));
9102621Sllai1 }
9112621Sllai1 }
9122621Sllai1
9132621Sllai1 /*
9142621Sllai1 * the backing store vnode is released here
9152621Sllai1 */
9162621Sllai1 /*ARGSUSED1*/
9172621Sllai1 void
sdev_nodedestroy(struct sdev_node * dv,uint_t flags)9182621Sllai1 sdev_nodedestroy(struct sdev_node *dv, uint_t flags)
9192621Sllai1 {
9202621Sllai1 /* no references */
9212621Sllai1 ASSERT(dv->sdev_nlink == 0);
9222621Sllai1
9232621Sllai1 if (dv->sdev_attrvp != NULLVP) {
9242621Sllai1 VN_RELE(dv->sdev_attrvp);
9252621Sllai1 /*
9262621Sllai1 * reset the attrvp so that no more
9272621Sllai1 * references can be made on this already
9282621Sllai1 * vn_rele() vnode
9292621Sllai1 */
9302621Sllai1 dv->sdev_attrvp = NULLVP;
9312621Sllai1 }
9322621Sllai1
9332621Sllai1 if (dv->sdev_attr != NULL) {
9342621Sllai1 kmem_free(dv->sdev_attr, sizeof (struct vattr));
9352621Sllai1 dv->sdev_attr = NULL;
9362621Sllai1 }
9372621Sllai1
9382621Sllai1 if (dv->sdev_name != NULL) {
9392621Sllai1 kmem_free(dv->sdev_name, dv->sdev_namelen + 1);
9402621Sllai1 dv->sdev_name = NULL;
9412621Sllai1 }
9422621Sllai1
9432621Sllai1 if (dv->sdev_symlink != NULL) {
9442621Sllai1 kmem_free(dv->sdev_symlink, strlen(dv->sdev_symlink) + 1);
9452621Sllai1 dv->sdev_symlink = NULL;
9462621Sllai1 }
9472621Sllai1
9482621Sllai1 if (dv->sdev_path) {
9492621Sllai1 kmem_free(dv->sdev_path, strlen(dv->sdev_path) + 1);
9502621Sllai1 dv->sdev_path = NULL;
9512621Sllai1 }
9522621Sllai1
9532621Sllai1 if (!SDEV_IS_GLOBAL(dv))
9542621Sllai1 sdev_prof_free(dv);
9552621Sllai1
9566260Sjg if (SDEVTOV(dv)->v_type == VDIR) {
9576260Sjg ASSERT(SDEV_FIRST_ENTRY(dv) == NULL);
9586260Sjg avl_destroy(&dv->sdev_entries);
9596260Sjg }
9606260Sjg
9612621Sllai1 mutex_destroy(&dv->sdev_lookup_lock);
9622621Sllai1 cv_destroy(&dv->sdev_lookup_cv);
9632621Sllai1
9642621Sllai1 /* return node to initial state as per constructor */
9652621Sllai1 (void) memset((void *)&dv->sdev_instance_data, 0,
9662621Sllai1 sizeof (dv->sdev_instance_data));
9672621Sllai1 vn_invalid(SDEVTOV(dv));
9682621Sllai1 kmem_cache_free(sdev_node_cache, dv);
9692621Sllai1 }
9702621Sllai1
9712621Sllai1 /*
9722621Sllai1 * DIRECTORY CACHE lookup
9732621Sllai1 */
9742621Sllai1 struct sdev_node *
sdev_findbyname(struct sdev_node * ddv,char * nm)9752621Sllai1 sdev_findbyname(struct sdev_node *ddv, char *nm)
9762621Sllai1 {
9772621Sllai1 struct sdev_node *dv;
9786260Sjg struct sdev_node dvtmp;
9796260Sjg avl_index_t where;
9802621Sllai1
9812621Sllai1 ASSERT(RW_LOCK_HELD(&ddv->sdev_contents));
9826260Sjg
9836260Sjg dvtmp.sdev_name = nm;
9846260Sjg dv = avl_find(&ddv->sdev_entries, &dvtmp, &where);
9856260Sjg if (dv) {
9866260Sjg ASSERT(dv->sdev_dotdot == ddv);
9876260Sjg ASSERT(strcmp(dv->sdev_name, nm) == 0);
9886347Sjg SDEV_HOLD(dv);
9896347Sjg return (dv);
9902621Sllai1 }
9912621Sllai1 return (NULL);
9922621Sllai1 }
9932621Sllai1
9942621Sllai1 /*
9952621Sllai1 * Inserts a new sdev_node in a parent directory
9962621Sllai1 */
9972621Sllai1 void
sdev_direnter(struct sdev_node * ddv,struct sdev_node * dv)9982621Sllai1 sdev_direnter(struct sdev_node *ddv, struct sdev_node *dv)
9992621Sllai1 {
10006260Sjg avl_index_t where;
10016260Sjg
10022621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
10032621Sllai1 ASSERT(SDEVTOV(ddv)->v_type == VDIR);
10042621Sllai1 ASSERT(ddv->sdev_nlink >= 2);
10052621Sllai1 ASSERT(dv->sdev_nlink == 0);
10062621Sllai1
10072621Sllai1 dv->sdev_dotdot = ddv;
10086260Sjg VERIFY(avl_find(&ddv->sdev_entries, dv, &where) == NULL);
10096260Sjg avl_insert(&ddv->sdev_entries, dv, where);
10102621Sllai1 ddv->sdev_nlink++;
10112621Sllai1 }
10122621Sllai1
10132621Sllai1 /*
10142621Sllai1 * The following check is needed because while sdev_nodes are linked
10152621Sllai1 * in SDEV_INIT state, they have their link counts incremented only
10162621Sllai1 * in SDEV_READY state.
10172621Sllai1 */
10182621Sllai1 static void
decr_link(struct sdev_node * dv)10192621Sllai1 decr_link(struct sdev_node *dv)
10202621Sllai1 {
10212621Sllai1 if (dv->sdev_state != SDEV_INIT)
10222621Sllai1 dv->sdev_nlink--;
10232621Sllai1 else
10242621Sllai1 ASSERT(dv->sdev_nlink == 0);
10252621Sllai1 }
10262621Sllai1
10272621Sllai1 /*
10282621Sllai1 * Delete an existing dv from directory cache
10292621Sllai1 *
10302621Sllai1 * In the case of a node is still held by non-zero reference count,
10312621Sllai1 * the node is put into ZOMBIE state. Once the reference count
10322621Sllai1 * reaches "0", the node is unlinked and destroyed,
10332621Sllai1 * in sdev_inactive().
10342621Sllai1 */
10352621Sllai1 static int
sdev_dirdelete(struct sdev_node * ddv,struct sdev_node * dv)10362621Sllai1 sdev_dirdelete(struct sdev_node *ddv, struct sdev_node *dv)
10372621Sllai1 {
10382621Sllai1 struct vnode *vp;
10392621Sllai1
10402621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
10412621Sllai1
10422621Sllai1 vp = SDEVTOV(dv);
10432621Sllai1 mutex_enter(&vp->v_lock);
10442621Sllai1
10452621Sllai1 /* dv is held still */
10462621Sllai1 if (vp->v_count > 1) {
10472621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER);
10482621Sllai1 if (dv->sdev_state == SDEV_READY) {
10492621Sllai1 sdcmn_err9((
105010588SEric.Taylor@Sun.COM "sdev_dirdelete: node %s busy with count %d\n",
10512621Sllai1 dv->sdev_name, vp->v_count));
10522621Sllai1 dv->sdev_state = SDEV_ZOMBIE;
10532621Sllai1 }
10542621Sllai1 rw_exit(&dv->sdev_contents);
10552621Sllai1 --vp->v_count;
10562621Sllai1 mutex_exit(&vp->v_lock);
10572621Sllai1 return (EBUSY);
10582621Sllai1 }
10592621Sllai1 ASSERT(vp->v_count == 1);
10602621Sllai1
10612621Sllai1 /* unlink from the memory cache */
10622621Sllai1 ddv->sdev_nlink--; /* .. to above */
10632621Sllai1 if (vp->v_type == VDIR) {
10642621Sllai1 decr_link(dv); /* . to self */
10652621Sllai1 }
10662621Sllai1
10676260Sjg avl_remove(&ddv->sdev_entries, dv);
10682621Sllai1 decr_link(dv); /* name, back to zero */
10692621Sllai1 vp->v_count--;
10702621Sllai1 mutex_exit(&vp->v_lock);
10712621Sllai1
10722621Sllai1 /* destroy the node */
10732621Sllai1 sdev_nodedestroy(dv, 0);
10742621Sllai1 return (0);
10752621Sllai1 }
10762621Sllai1
10772621Sllai1 /*
10782621Sllai1 * check if the source is in the path of the target
10792621Sllai1 *
10802621Sllai1 * source and target are different
10812621Sllai1 */
10822621Sllai1 /*ARGSUSED2*/
10832621Sllai1 static int
sdev_checkpath(struct sdev_node * sdv,struct sdev_node * tdv,struct cred * cred)10842621Sllai1 sdev_checkpath(struct sdev_node *sdv, struct sdev_node *tdv, struct cred *cred)
10852621Sllai1 {
10862621Sllai1 int error = 0;
10872621Sllai1 struct sdev_node *dotdot, *dir;
10882621Sllai1
10892621Sllai1 dotdot = tdv->sdev_dotdot;
10902621Sllai1 ASSERT(dotdot);
10912621Sllai1
10922621Sllai1 /* fs root */
10932621Sllai1 if (dotdot == tdv) {
10942621Sllai1 return (0);
10952621Sllai1 }
10962621Sllai1
10972621Sllai1 for (;;) {
10982621Sllai1 /*
10992621Sllai1 * avoid error cases like
11002621Sllai1 * mv a a/b
11012621Sllai1 * mv a a/b/c
11022621Sllai1 * etc.
11032621Sllai1 */
11042621Sllai1 if (dotdot == sdv) {
11052621Sllai1 error = EINVAL;
11062621Sllai1 break;
11072621Sllai1 }
11082621Sllai1
11092621Sllai1 dir = dotdot;
11102621Sllai1 dotdot = dir->sdev_dotdot;
11112621Sllai1
11122621Sllai1 /* done checking because root is reached */
11132621Sllai1 if (dir == dotdot) {
11142621Sllai1 break;
11152621Sllai1 }
11162621Sllai1 }
11172621Sllai1 return (error);
11182621Sllai1 }
11192621Sllai1
11202621Sllai1 int
sdev_rnmnode(struct sdev_node * oddv,struct sdev_node * odv,struct sdev_node * nddv,struct sdev_node ** ndvp,char * nnm,struct cred * cred)11212621Sllai1 sdev_rnmnode(struct sdev_node *oddv, struct sdev_node *odv,
11222621Sllai1 struct sdev_node *nddv, struct sdev_node **ndvp, char *nnm,
11232621Sllai1 struct cred *cred)
11242621Sllai1 {
11252621Sllai1 int error = 0;
11262621Sllai1 struct vnode *ovp = SDEVTOV(odv);
11272621Sllai1 struct vnode *nvp;
11282621Sllai1 struct vattr vattr;
11292621Sllai1 int doingdir = (ovp->v_type == VDIR);
11302621Sllai1 char *link = NULL;
11312729Sllai1 int samedir = (oddv == nddv) ? 1 : 0;
11322729Sllai1 int bkstore = 0;
11332729Sllai1 struct sdev_node *idv = NULL;
11342729Sllai1 struct sdev_node *ndv = NULL;
11352729Sllai1 timestruc_t now;
11362729Sllai1
113711279SJerry.Gilliam@Sun.COM vattr.va_mask = AT_TYPE|AT_MODE|AT_UID|AT_GID;
11385331Samw error = VOP_GETATTR(ovp, &vattr, 0, cred, NULL);
11392729Sllai1 if (error)
11402729Sllai1 return (error);
11412729Sllai1
11422729Sllai1 if (!samedir)
11432729Sllai1 rw_enter(&oddv->sdev_contents, RW_WRITER);
11442729Sllai1 rw_enter(&nddv->sdev_contents, RW_WRITER);
11452729Sllai1
11462729Sllai1 /*
11472729Sllai1 * the source may have been deleted by another thread before
11482729Sllai1 * we gets here.
11492729Sllai1 */
11502729Sllai1 if (odv->sdev_state != SDEV_READY) {
11512729Sllai1 error = ENOENT;
11522729Sllai1 goto err_out;
11532729Sllai1 }
11542729Sllai1
11552729Sllai1 if (doingdir && (odv == nddv)) {
11562729Sllai1 error = EINVAL;
11572729Sllai1 goto err_out;
11582729Sllai1 }
11592621Sllai1
11602621Sllai1 /*
11612621Sllai1 * If renaming a directory, and the parents are different (".." must be
11622621Sllai1 * changed) then the source dir must not be in the dir hierarchy above
11632621Sllai1 * the target since it would orphan everything below the source dir.
11642621Sllai1 */
11652621Sllai1 if (doingdir && (oddv != nddv)) {
11662621Sllai1 error = sdev_checkpath(odv, nddv, cred);
11672621Sllai1 if (error)
11682729Sllai1 goto err_out;
11692621Sllai1 }
11702621Sllai1
11712729Sllai1 /* destination existing */
11722621Sllai1 if (*ndvp) {
11732621Sllai1 nvp = SDEVTOV(*ndvp);
11742621Sllai1 ASSERT(nvp);
11752621Sllai1
11762621Sllai1 /* handling renaming to itself */
11772729Sllai1 if (odv == *ndvp) {
11782729Sllai1 error = 0;
11792729Sllai1 goto err_out;
11802729Sllai1 }
11812729Sllai1
11822729Sllai1 if (nvp->v_type == VDIR) {
11832729Sllai1 if (!doingdir) {
11842729Sllai1 error = EISDIR;
11852729Sllai1 goto err_out;
11862729Sllai1 }
11872729Sllai1
11882729Sllai1 if (vn_vfswlock(nvp)) {
11892729Sllai1 error = EBUSY;
11902729Sllai1 goto err_out;
11912729Sllai1 }
11922729Sllai1
11932729Sllai1 if (vn_mountedvfs(nvp) != NULL) {
11942729Sllai1 vn_vfsunlock(nvp);
11952729Sllai1 error = EBUSY;
11962729Sllai1 goto err_out;
11972729Sllai1 }
11982729Sllai1
11992729Sllai1 /* in case dir1 exists in dir2 and "mv dir1 dir2" */
12002729Sllai1 if ((*ndvp)->sdev_nlink > 2) {
12012729Sllai1 vn_vfsunlock(nvp);
12022729Sllai1 error = EEXIST;
12032729Sllai1 goto err_out;
12042729Sllai1 }
12052729Sllai1 vn_vfsunlock(nvp);
12062729Sllai1
12072729Sllai1 (void) sdev_dirdelete(nddv, *ndvp);
12082729Sllai1 *ndvp = NULL;
12096335Sjg ASSERT(nddv->sdev_attrvp);
12102729Sllai1 error = VOP_RMDIR(nddv->sdev_attrvp, nnm,
12116065Scth nddv->sdev_attrvp, cred, NULL, 0);
12122729Sllai1 if (error)
12132729Sllai1 goto err_out;
12142729Sllai1 } else {
12152729Sllai1 if (doingdir) {
12162729Sllai1 error = ENOTDIR;
12172729Sllai1 goto err_out;
12182729Sllai1 }
12192729Sllai1
12202729Sllai1 if (SDEV_IS_PERSIST((*ndvp))) {
12212729Sllai1 bkstore = 1;
12222729Sllai1 }
12232621Sllai1
12242621Sllai1 /*
12252729Sllai1 * get rid of the node from the directory cache
12262729Sllai1 * note, in case EBUSY is returned, the ZOMBIE
12272729Sllai1 * node is taken care in sdev_mknode.
12282621Sllai1 */
12292729Sllai1 (void) sdev_dirdelete(nddv, *ndvp);
12302729Sllai1 *ndvp = NULL;
12312729Sllai1 if (bkstore) {
12326335Sjg ASSERT(nddv->sdev_attrvp);
12332729Sllai1 error = VOP_REMOVE(nddv->sdev_attrvp,
12345331Samw nnm, cred, NULL, 0);
12352729Sllai1 if (error)
12366065Scth goto err_out;
12372621Sllai1 }
12382621Sllai1 }
12392621Sllai1 }
12402621Sllai1
12412621Sllai1 /* fix the source for a symlink */
12422621Sllai1 if (vattr.va_type == VLNK) {
12432621Sllai1 if (odv->sdev_symlink == NULL) {
12442621Sllai1 error = sdev_follow_link(odv);
12452729Sllai1 if (error) {
12462729Sllai1 error = ENOENT;
12472729Sllai1 goto err_out;
12482729Sllai1 }
12492621Sllai1 }
12502621Sllai1 ASSERT(odv->sdev_symlink);
12512621Sllai1 link = i_ddi_strdup(odv->sdev_symlink, KM_SLEEP);
12522621Sllai1 }
12532621Sllai1
12542729Sllai1 /*
12552729Sllai1 * make a fresh node from the source attrs
12562729Sllai1 */
12572729Sllai1 ASSERT(RW_WRITE_HELD(&nddv->sdev_contents));
12582729Sllai1 error = sdev_mknode(nddv, nnm, ndvp, &vattr,
12592729Sllai1 NULL, (void *)link, cred, SDEV_READY);
12602621Sllai1
12612621Sllai1 if (link)
12622621Sllai1 kmem_free(link, strlen(link) + 1);
12632621Sllai1
12642729Sllai1 if (error)
12652729Sllai1 goto err_out;
12662729Sllai1 ASSERT(*ndvp);
12672729Sllai1 ASSERT((*ndvp)->sdev_state == SDEV_READY);
12682729Sllai1
12692729Sllai1 /* move dir contents */
12702729Sllai1 if (doingdir) {
12716260Sjg for (idv = SDEV_FIRST_ENTRY(odv); idv;
12726260Sjg idv = SDEV_NEXT_ENTRY(odv, idv)) {
12732729Sllai1 error = sdev_rnmnode(odv, idv,
12742729Sllai1 (struct sdev_node *)(*ndvp), &ndv,
12752729Sllai1 idv->sdev_name, cred);
12762729Sllai1 if (error)
12772729Sllai1 goto err_out;
12782729Sllai1 ndv = NULL;
12792729Sllai1 }
12802729Sllai1 }
12812729Sllai1
12822729Sllai1 if ((*ndvp)->sdev_attrvp) {
12832729Sllai1 sdev_update_timestamps((*ndvp)->sdev_attrvp, kcred,
12842729Sllai1 AT_CTIME|AT_ATIME);
12852729Sllai1 } else {
12862729Sllai1 ASSERT((*ndvp)->sdev_attr);
12872729Sllai1 gethrestime(&now);
12882729Sllai1 (*ndvp)->sdev_attr->va_ctime = now;
12892729Sllai1 (*ndvp)->sdev_attr->va_atime = now;
12902729Sllai1 }
12912729Sllai1
12922729Sllai1 if (nddv->sdev_attrvp) {
12932729Sllai1 sdev_update_timestamps(nddv->sdev_attrvp, kcred,
12942729Sllai1 AT_MTIME|AT_ATIME);
12952729Sllai1 } else {
12962729Sllai1 ASSERT(nddv->sdev_attr);
12972729Sllai1 gethrestime(&now);
12982729Sllai1 nddv->sdev_attr->va_mtime = now;
12992729Sllai1 nddv->sdev_attr->va_atime = now;
13002729Sllai1 }
13012729Sllai1 rw_exit(&nddv->sdev_contents);
13022729Sllai1 if (!samedir)
13032729Sllai1 rw_exit(&oddv->sdev_contents);
13042729Sllai1
13052621Sllai1 SDEV_RELE(*ndvp);
13062729Sllai1 return (error);
13072729Sllai1
13082729Sllai1 err_out:
13092729Sllai1 rw_exit(&nddv->sdev_contents);
13102729Sllai1 if (!samedir)
13112729Sllai1 rw_exit(&oddv->sdev_contents);
13122729Sllai1 return (error);
13132621Sllai1 }
13142621Sllai1
13152621Sllai1 /*
13162621Sllai1 * Merge sdev_node specific information into an attribute structure.
13172621Sllai1 *
13182621Sllai1 * note: sdev_node is not locked here
13192621Sllai1 */
13202621Sllai1 void
sdev_vattr_merge(struct sdev_node * dv,struct vattr * vap)13212621Sllai1 sdev_vattr_merge(struct sdev_node *dv, struct vattr *vap)
13222621Sllai1 {
13232621Sllai1 struct vnode *vp = SDEVTOV(dv);
13242621Sllai1
13252621Sllai1 vap->va_nlink = dv->sdev_nlink;
13262621Sllai1 vap->va_nodeid = dv->sdev_ino;
13272621Sllai1 vap->va_fsid = SDEVTOV(dv->sdev_dotdot)->v_rdev;
13282621Sllai1 vap->va_type = vp->v_type;
13292621Sllai1
13302621Sllai1 if (vp->v_type == VDIR) {
13312621Sllai1 vap->va_rdev = 0;
13322621Sllai1 vap->va_fsid = vp->v_rdev;
13332621Sllai1 } else if (vp->v_type == VLNK) {
13342621Sllai1 vap->va_rdev = 0;
13352621Sllai1 vap->va_mode &= ~S_IFMT;
13362621Sllai1 vap->va_mode |= S_IFLNK;
13372621Sllai1 } else if ((vp->v_type == VCHR) || (vp->v_type == VBLK)) {
13382621Sllai1 vap->va_rdev = vp->v_rdev;
13392621Sllai1 vap->va_mode &= ~S_IFMT;
13402621Sllai1 if (vap->va_type == VCHR)
13412621Sllai1 vap->va_mode |= S_IFCHR;
13422621Sllai1 else
13432621Sllai1 vap->va_mode |= S_IFBLK;
13442621Sllai1 } else {
13452621Sllai1 vap->va_rdev = 0;
13462621Sllai1 }
13472621Sllai1 }
13482621Sllai1
134910588SEric.Taylor@Sun.COM struct vattr *
sdev_getdefault_attr(enum vtype type)13502621Sllai1 sdev_getdefault_attr(enum vtype type)
13512621Sllai1 {
13522621Sllai1 if (type == VDIR)
13532621Sllai1 return (&sdev_vattr_dir);
13542621Sllai1 else if (type == VCHR)
13552621Sllai1 return (&sdev_vattr_chr);
13562621Sllai1 else if (type == VBLK)
13572621Sllai1 return (&sdev_vattr_blk);
13582621Sllai1 else if (type == VLNK)
13592621Sllai1 return (&sdev_vattr_lnk);
13602621Sllai1 else
13612621Sllai1 return (NULL);
13622621Sllai1 }
13632621Sllai1 int
sdev_to_vp(struct sdev_node * dv,struct vnode ** vpp)13642621Sllai1 sdev_to_vp(struct sdev_node *dv, struct vnode **vpp)
13652621Sllai1 {
13662621Sllai1 int rv = 0;
13672621Sllai1 struct vnode *vp = SDEVTOV(dv);
13682621Sllai1
13692621Sllai1 switch (vp->v_type) {
13702621Sllai1 case VCHR:
13712621Sllai1 case VBLK:
13722621Sllai1 /*
13732621Sllai1 * If vnode is a device, return special vnode instead
13742621Sllai1 * (though it knows all about -us- via sp->s_realvp)
13752621Sllai1 */
13762621Sllai1 *vpp = specvp(vp, vp->v_rdev, vp->v_type, kcred);
13772621Sllai1 VN_RELE(vp);
13782621Sllai1 if (*vpp == NULLVP)
13792621Sllai1 rv = ENOSYS;
13802621Sllai1 break;
13812621Sllai1 default: /* most types are returned as is */
13822621Sllai1 *vpp = vp;
13832621Sllai1 break;
13842621Sllai1 }
13852621Sllai1 return (rv);
13862621Sllai1 }
13872621Sllai1
13882621Sllai1 /*
13892621Sllai1 * junction between devname and root file system, e.g. ufs
13902621Sllai1 */
13912621Sllai1 int
devname_backstore_lookup(struct sdev_node * ddv,char * nm,struct vnode ** rvp)13922621Sllai1 devname_backstore_lookup(struct sdev_node *ddv, char *nm, struct vnode **rvp)
13932621Sllai1 {
13942621Sllai1 struct vnode *rdvp = ddv->sdev_attrvp;
13952621Sllai1 int rval = 0;
13962621Sllai1
13972621Sllai1 ASSERT(rdvp);
13982621Sllai1
13995331Samw rval = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, kcred, NULL, NULL,
14005331Samw NULL);
14012621Sllai1 return (rval);
14022621Sllai1 }
14032621Sllai1
14042621Sllai1 static int
sdev_filldir_from_store(struct sdev_node * ddv,int dlen,struct cred * cred)14052621Sllai1 sdev_filldir_from_store(struct sdev_node *ddv, int dlen, struct cred *cred)
14062621Sllai1 {
14072621Sllai1 struct sdev_node *dv = NULL;
14082621Sllai1 char *nm;
14092621Sllai1 struct vnode *dirvp;
14102621Sllai1 int error;
14112621Sllai1 vnode_t *vp;
14122621Sllai1 int eof;
14132621Sllai1 struct iovec iov;
14142621Sllai1 struct uio uio;
14152621Sllai1 struct dirent64 *dp;
14162621Sllai1 dirent64_t *dbuf;
14172621Sllai1 size_t dbuflen;
14182621Sllai1 struct vattr vattr;
14192621Sllai1 char *link = NULL;
14202621Sllai1
14212621Sllai1 if (ddv->sdev_attrvp == NULL)
14222621Sllai1 return (0);
14232621Sllai1 if (!(ddv->sdev_flags & SDEV_BUILD))
14242621Sllai1 return (0);
14252621Sllai1
14262621Sllai1 dirvp = ddv->sdev_attrvp;
14272621Sllai1 VN_HOLD(dirvp);
14282621Sllai1 dbuf = kmem_zalloc(dlen, KM_SLEEP);
14292621Sllai1
14302621Sllai1 uio.uio_iov = &iov;
14312621Sllai1 uio.uio_iovcnt = 1;
14322621Sllai1 uio.uio_segflg = UIO_SYSSPACE;
14332621Sllai1 uio.uio_fmode = 0;
14342621Sllai1 uio.uio_extflg = UIO_COPY_CACHED;
14352621Sllai1 uio.uio_loffset = 0;
14362621Sllai1 uio.uio_llimit = MAXOFFSET_T;
14372621Sllai1
14382621Sllai1 eof = 0;
14392621Sllai1 error = 0;
14402621Sllai1 while (!error && !eof) {
14412621Sllai1 uio.uio_resid = dlen;
14422621Sllai1 iov.iov_base = (char *)dbuf;
14432621Sllai1 iov.iov_len = dlen;
14442621Sllai1 (void) VOP_RWLOCK(dirvp, V_WRITELOCK_FALSE, NULL);
14455331Samw error = VOP_READDIR(dirvp, &uio, kcred, &eof, NULL, 0);
14462621Sllai1 VOP_RWUNLOCK(dirvp, V_WRITELOCK_FALSE, NULL);
14472621Sllai1
14482621Sllai1 dbuflen = dlen - uio.uio_resid;
14492621Sllai1 if (error || dbuflen == 0)
14502621Sllai1 break;
14512621Sllai1
145210588SEric.Taylor@Sun.COM if (!(ddv->sdev_flags & SDEV_BUILD))
14532621Sllai1 break;
14542621Sllai1
14552621Sllai1 for (dp = dbuf; ((intptr_t)dp <
14562621Sllai1 (intptr_t)dbuf + dbuflen);
14572621Sllai1 dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
14582621Sllai1 nm = dp->d_name;
14592621Sllai1
14602621Sllai1 if (strcmp(nm, ".") == 0 ||
14612621Sllai1 strcmp(nm, "..") == 0)
14622621Sllai1 continue;
14632621Sllai1
14642621Sllai1 vp = NULLVP;
14652621Sllai1 dv = sdev_cache_lookup(ddv, nm);
14662621Sllai1 if (dv) {
14672621Sllai1 if (dv->sdev_state != SDEV_ZOMBIE) {
14682621Sllai1 SDEV_SIMPLE_RELE(dv);
14692621Sllai1 } else {
14702621Sllai1 /*
14712621Sllai1 * A ZOMBIE node may not have been
14722621Sllai1 * cleaned up from the backing store,
14732621Sllai1 * bypass this entry in this case,
14742621Sllai1 * and clean it up from the directory
14752621Sllai1 * cache if this is the last call.
14762621Sllai1 */
14772621Sllai1 (void) sdev_dirdelete(ddv, dv);
14782621Sllai1 }
14792621Sllai1 continue;
14802621Sllai1 }
14812621Sllai1
14822621Sllai1 /* refill the cache if not already */
14832621Sllai1 error = devname_backstore_lookup(ddv, nm, &vp);
14842621Sllai1 if (error)
14852621Sllai1 continue;
14862621Sllai1
148711279SJerry.Gilliam@Sun.COM vattr.va_mask = AT_TYPE|AT_MODE|AT_UID|AT_GID;
14885331Samw error = VOP_GETATTR(vp, &vattr, 0, cred, NULL);
14892621Sllai1 if (error)
14902621Sllai1 continue;
14912621Sllai1
14922621Sllai1 if (vattr.va_type == VLNK) {
14932621Sllai1 error = sdev_getlink(vp, &link);
14942621Sllai1 if (error) {
14952621Sllai1 continue;
14962621Sllai1 }
14972621Sllai1 ASSERT(link != NULL);
14982621Sllai1 }
14992621Sllai1
15002621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) {
15012621Sllai1 rw_exit(&ddv->sdev_contents);
15022621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER);
15032621Sllai1 }
15042621Sllai1 error = sdev_mknode(ddv, nm, &dv, &vattr, vp, link,
15052621Sllai1 cred, SDEV_READY);
15062621Sllai1 rw_downgrade(&ddv->sdev_contents);
15072621Sllai1
15082621Sllai1 if (link != NULL) {
15092621Sllai1 kmem_free(link, strlen(link) + 1);
15102621Sllai1 link = NULL;
15112621Sllai1 }
15122621Sllai1
15132621Sllai1 if (!error) {
15142621Sllai1 ASSERT(dv);
15152621Sllai1 ASSERT(dv->sdev_state != SDEV_ZOMBIE);
15162621Sllai1 SDEV_SIMPLE_RELE(dv);
15172621Sllai1 }
15182621Sllai1 vp = NULL;
15192621Sllai1 dv = NULL;
15202621Sllai1 }
15212621Sllai1 }
15222621Sllai1
15232621Sllai1 done:
15242621Sllai1 VN_RELE(dirvp);
15252621Sllai1 kmem_free(dbuf, dlen);
15262621Sllai1
15272621Sllai1 return (error);
15282621Sllai1 }
15292621Sllai1
15303843Sjg void
sdev_filldir_dynamic(struct sdev_node * ddv)15312621Sllai1 sdev_filldir_dynamic(struct sdev_node *ddv)
15322621Sllai1 {
15332621Sllai1 int error;
15342621Sllai1 int i;
153511279SJerry.Gilliam@Sun.COM struct vattr vattr;
153611279SJerry.Gilliam@Sun.COM struct vattr *vap = &vattr;
15372621Sllai1 char *nm = NULL;
15382621Sllai1 struct sdev_node *dv = NULL;
15392621Sllai1
15403843Sjg ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
15413843Sjg ASSERT((ddv->sdev_flags & SDEV_BUILD));
15422621Sllai1
154311279SJerry.Gilliam@Sun.COM *vap = *sdev_getdefault_attr(VDIR); /* note structure copy here */
154410588SEric.Taylor@Sun.COM gethrestime(&vap->va_atime);
154510588SEric.Taylor@Sun.COM vap->va_mtime = vap->va_atime;
154610588SEric.Taylor@Sun.COM vap->va_ctime = vap->va_atime;
15472621Sllai1 for (i = 0; vtab[i].vt_name != NULL; i++) {
1548*12911Sjohn.levon@sun.com /*
1549*12911Sjohn.levon@sun.com * This early, we may be in a read-only /dev
1550*12911Sjohn.levon@sun.com * environment: leave the creation of any nodes we'd
1551*12911Sjohn.levon@sun.com * attempt to persist to devfsadm.
1552*12911Sjohn.levon@sun.com */
1553*12911Sjohn.levon@sun.com if (vtab[i].vt_flags & SDEV_PERSIST)
1554*12911Sjohn.levon@sun.com continue;
15552621Sllai1 nm = vtab[i].vt_name;
15562621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
15573843Sjg dv = NULL;
15582621Sllai1 error = sdev_mknode(ddv, nm, &dv, vap, NULL,
15592621Sllai1 NULL, kcred, SDEV_READY);
15603843Sjg if (error) {
15613843Sjg cmn_err(CE_WARN, "%s/%s: error %d\n",
15623843Sjg ddv->sdev_name, nm, error);
15633843Sjg } else {
15643843Sjg ASSERT(dv);
15653843Sjg ASSERT(dv->sdev_state != SDEV_ZOMBIE);
15663843Sjg SDEV_SIMPLE_RELE(dv);
15673843Sjg }
15682621Sllai1 }
15692621Sllai1 }
15702621Sllai1
15712621Sllai1 /*
15722621Sllai1 * Creating a backing store entry based on sdev_attr.
15732621Sllai1 * This is called either as part of node creation in a persistent directory
15742621Sllai1 * or from setattr/setsecattr to persist access attributes across reboot.
15752621Sllai1 */
15762621Sllai1 int
sdev_shadow_node(struct sdev_node * dv,struct cred * cred)15772621Sllai1 sdev_shadow_node(struct sdev_node *dv, struct cred *cred)
15782621Sllai1 {
15792621Sllai1 int error = 0;
15802621Sllai1 struct vnode *dvp = SDEVTOV(dv->sdev_dotdot);
15812621Sllai1 struct vnode *rdvp = VTOSDEV(dvp)->sdev_attrvp;
15822621Sllai1 struct vattr *vap = dv->sdev_attr;
15832621Sllai1 char *nm = dv->sdev_name;
15842621Sllai1 struct vnode *tmpvp, **rvp = &tmpvp, *rrvp = NULL;
15852621Sllai1
15862621Sllai1 ASSERT(dv && dv->sdev_name && rdvp);
15872621Sllai1 ASSERT(RW_WRITE_HELD(&dv->sdev_contents) && dv->sdev_attrvp == NULL);
15882621Sllai1
15892621Sllai1 lookup:
15902621Sllai1 /* try to find it in the backing store */
15915331Samw error = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, cred, NULL, NULL,
15925331Samw NULL);
15932621Sllai1 if (error == 0) {
15945331Samw if (VOP_REALVP(*rvp, &rrvp, NULL) == 0) {
15952621Sllai1 VN_HOLD(rrvp);
15962621Sllai1 VN_RELE(*rvp);
15972621Sllai1 *rvp = rrvp;
15982621Sllai1 }
15992621Sllai1
16002621Sllai1 kmem_free(dv->sdev_attr, sizeof (vattr_t));
16012621Sllai1 dv->sdev_attr = NULL;
16022621Sllai1 dv->sdev_attrvp = *rvp;
16032621Sllai1 return (0);
16042621Sllai1 }
16052621Sllai1
16062621Sllai1 /* let's try to persist the node */
16072621Sllai1 gethrestime(&vap->va_atime);
16082621Sllai1 vap->va_mtime = vap->va_atime;
16092621Sllai1 vap->va_ctime = vap->va_atime;
16102621Sllai1 vap->va_mask |= AT_TYPE|AT_MODE;
16112621Sllai1 switch (vap->va_type) {
16122621Sllai1 case VDIR:
16135331Samw error = VOP_MKDIR(rdvp, nm, vap, rvp, cred, NULL, 0, NULL);
16142621Sllai1 sdcmn_err9(("sdev_shadow_node: mkdir vp %p error %d\n",
16152621Sllai1 (void *)(*rvp), error));
16162621Sllai1 break;
16172621Sllai1 case VCHR:
16182621Sllai1 case VBLK:
16192621Sllai1 case VREG:
16202621Sllai1 case VDOOR:
16212621Sllai1 error = VOP_CREATE(rdvp, nm, vap, NONEXCL, VREAD|VWRITE,
16225331Samw rvp, cred, 0, NULL, NULL);
16232621Sllai1 sdcmn_err9(("sdev_shadow_node: create vp %p, error %d\n",
16242621Sllai1 (void *)(*rvp), error));
16252621Sllai1 if (!error)
16262621Sllai1 VN_RELE(*rvp);
16272621Sllai1 break;
16282621Sllai1 case VLNK:
16292621Sllai1 ASSERT(dv->sdev_symlink);
16305331Samw error = VOP_SYMLINK(rdvp, nm, vap, dv->sdev_symlink, cred,
16315331Samw NULL, 0);
16322621Sllai1 sdcmn_err9(("sdev_shadow_node: create symlink error %d\n",
16332621Sllai1 error));
16342621Sllai1 break;
16352621Sllai1 default:
16362621Sllai1 cmn_err(CE_PANIC, "dev: %s: sdev_shadow_node "
16372621Sllai1 "create\n", nm);
16382621Sllai1 /*NOTREACHED*/
16392621Sllai1 }
16402621Sllai1
16412621Sllai1 /* go back to lookup to factor out spec node and set attrvp */
16422621Sllai1 if (error == 0)
16432621Sllai1 goto lookup;
16442621Sllai1
16456335Sjg sdcmn_err(("cannot persist %s - error %d\n", dv->sdev_path, error));
16462621Sllai1 return (error);
16472621Sllai1 }
16482621Sllai1
16492621Sllai1 static int
sdev_cache_add(struct sdev_node * ddv,struct sdev_node ** dv,char * nm)16502621Sllai1 sdev_cache_add(struct sdev_node *ddv, struct sdev_node **dv, char *nm)
16512621Sllai1 {
16522621Sllai1 int error = 0;
16532621Sllai1 struct sdev_node *dup = NULL;
16542621Sllai1
16552621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
16562621Sllai1 if ((dup = sdev_findbyname(ddv, nm)) == NULL) {
16572621Sllai1 sdev_direnter(ddv, *dv);
16582621Sllai1 } else {
16592621Sllai1 if (dup->sdev_state == SDEV_ZOMBIE) {
16602621Sllai1 error = sdev_dirdelete(ddv, dup);
16612621Sllai1 /*
16622621Sllai1 * The ZOMBIE node is still hanging
16632621Sllai1 * around with more than one reference counts.
16642621Sllai1 * Fail the new node creation so that
16652621Sllai1 * the directory cache won't have
16662621Sllai1 * duplicate entries for the same named node
16672621Sllai1 */
16682621Sllai1 if (error == EBUSY) {
16692621Sllai1 SDEV_SIMPLE_RELE(*dv);
16702621Sllai1 sdev_nodedestroy(*dv, 0);
16712621Sllai1 *dv = NULL;
16722621Sllai1 return (error);
16732621Sllai1 }
16742621Sllai1 sdev_direnter(ddv, *dv);
16752621Sllai1 } else {
16762621Sllai1 ASSERT((*dv)->sdev_state != SDEV_ZOMBIE);
16772621Sllai1 SDEV_SIMPLE_RELE(*dv);
16782621Sllai1 sdev_nodedestroy(*dv, 0);
16792621Sllai1 *dv = dup;
16802621Sllai1 }
16812621Sllai1 }
16822621Sllai1
16832621Sllai1 return (0);
16842621Sllai1 }
16852621Sllai1
16862621Sllai1 static int
sdev_cache_delete(struct sdev_node * ddv,struct sdev_node ** dv)16872621Sllai1 sdev_cache_delete(struct sdev_node *ddv, struct sdev_node **dv)
16882621Sllai1 {
16892621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
16902621Sllai1 return (sdev_dirdelete(ddv, *dv));
16912621Sllai1 }
16922621Sllai1
16932621Sllai1 /*
16942621Sllai1 * update the in-core directory cache
16952621Sllai1 */
16962621Sllai1 int
sdev_cache_update(struct sdev_node * ddv,struct sdev_node ** dv,char * nm,sdev_cache_ops_t ops)16972621Sllai1 sdev_cache_update(struct sdev_node *ddv, struct sdev_node **dv, char *nm,
16982621Sllai1 sdev_cache_ops_t ops)
16992621Sllai1 {
17002621Sllai1 int error = 0;
17012621Sllai1
17022621Sllai1 ASSERT((SDEV_HELD(*dv)));
17032621Sllai1
17042621Sllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
17052621Sllai1 switch (ops) {
17062621Sllai1 case SDEV_CACHE_ADD:
17072621Sllai1 error = sdev_cache_add(ddv, dv, nm);
17082621Sllai1 break;
17092621Sllai1 case SDEV_CACHE_DELETE:
17102621Sllai1 error = sdev_cache_delete(ddv, dv);
17112621Sllai1 break;
17122621Sllai1 default:
17132621Sllai1 break;
17142621Sllai1 }
17152621Sllai1
17162621Sllai1 return (error);
17172621Sllai1 }
17182621Sllai1
17192621Sllai1 /*
17205331Samw * retrieve the named entry from the directory cache
17212621Sllai1 */
17222621Sllai1 struct sdev_node *
sdev_cache_lookup(struct sdev_node * ddv,char * nm)17232621Sllai1 sdev_cache_lookup(struct sdev_node *ddv, char *nm)
17242621Sllai1 {
17252621Sllai1 struct sdev_node *dv = NULL;
17262621Sllai1
17272621Sllai1 ASSERT(RW_LOCK_HELD(&ddv->sdev_contents));
17282621Sllai1 dv = sdev_findbyname(ddv, nm);
17292621Sllai1
17302621Sllai1 return (dv);
17312621Sllai1 }
17322621Sllai1
17332621Sllai1 /*
17342621Sllai1 * Implicit reconfig for nodes constructed by a link generator
17352621Sllai1 * Start devfsadm if needed, or if devfsadm is in progress,
17362621Sllai1 * prepare to block on devfsadm either completing or
17372621Sllai1 * constructing the desired node. As devfsadmd is global
17382621Sllai1 * in scope, constructing all necessary nodes, we only
17392621Sllai1 * need to initiate it once.
17402621Sllai1 */
17412621Sllai1 static int
sdev_call_devfsadmd(struct sdev_node * ddv,struct sdev_node * dv,char * nm)17422621Sllai1 sdev_call_devfsadmd(struct sdev_node *ddv, struct sdev_node *dv, char *nm)
17432621Sllai1 {
17442621Sllai1 int error = 0;
17452621Sllai1
17462621Sllai1 if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) {
17472621Sllai1 sdcmn_err6(("lookup: waiting for %s/%s, 0x%x\n",
17482621Sllai1 ddv->sdev_name, nm, devfsadm_state));
17492621Sllai1 mutex_enter(&dv->sdev_lookup_lock);
17502621Sllai1 SDEV_BLOCK_OTHERS(dv, (SDEV_LOOKUP | SDEV_LGWAITING));
17512621Sllai1 mutex_exit(&dv->sdev_lookup_lock);
17522621Sllai1 error = 0;
17532621Sllai1 } else if (!DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state)) {
17542621Sllai1 sdcmn_err6(("lookup %s/%s starting devfsadm, 0x%x\n",
17556065Scth ddv->sdev_name, nm, devfsadm_state));
17562621Sllai1
17572621Sllai1 sdev_devfsadmd_thread(ddv, dv, kcred);
17582621Sllai1 mutex_enter(&dv->sdev_lookup_lock);
17592621Sllai1 SDEV_BLOCK_OTHERS(dv,
17602621Sllai1 (SDEV_LOOKUP | SDEV_LGWAITING));
17612621Sllai1 mutex_exit(&dv->sdev_lookup_lock);
17622621Sllai1 error = 0;
17632621Sllai1 } else {
17642621Sllai1 error = -1;
17652621Sllai1 }
17662621Sllai1
17672621Sllai1 return (error);
17682621Sllai1 }
17692621Sllai1
17702621Sllai1 /*
17712621Sllai1 * Support for specialized device naming construction mechanisms
17722621Sllai1 */
17732621Sllai1 static int
sdev_call_dircallback(struct sdev_node * ddv,struct sdev_node ** dvp,char * nm,int (* callback)(struct sdev_node *,char *,void **,struct cred *,void *,char *),int flags,struct cred * cred)17742621Sllai1 sdev_call_dircallback(struct sdev_node *ddv, struct sdev_node **dvp, char *nm,
17752621Sllai1 int (*callback)(struct sdev_node *, char *, void **, struct cred *,
17762621Sllai1 void *, char *), int flags, struct cred *cred)
17772621Sllai1 {
17782621Sllai1 int rv = 0;
17792621Sllai1 char *physpath = NULL;
17802621Sllai1 struct vattr vattr;
178111279SJerry.Gilliam@Sun.COM struct vattr *vap = &vattr;
178210588SEric.Taylor@Sun.COM struct sdev_node *dv = NULL;
178310588SEric.Taylor@Sun.COM
178410588SEric.Taylor@Sun.COM ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
178510588SEric.Taylor@Sun.COM if (flags & SDEV_VLINK) {
17867688SAaron.Zang@Sun.COM physpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
17877688SAaron.Zang@Sun.COM rv = callback(ddv, nm, (void *)&physpath, kcred, NULL,
17887688SAaron.Zang@Sun.COM NULL);
17897688SAaron.Zang@Sun.COM if (rv) {
17907688SAaron.Zang@Sun.COM kmem_free(physpath, MAXPATHLEN);
17917688SAaron.Zang@Sun.COM return (-1);
17927688SAaron.Zang@Sun.COM }
17937688SAaron.Zang@Sun.COM
179411279SJerry.Gilliam@Sun.COM *vap = *sdev_getdefault_attr(VLNK); /* structure copy */
17957688SAaron.Zang@Sun.COM vap->va_size = strlen(physpath);
179610588SEric.Taylor@Sun.COM gethrestime(&vap->va_atime);
179710588SEric.Taylor@Sun.COM vap->va_mtime = vap->va_atime;
179810588SEric.Taylor@Sun.COM vap->va_ctime = vap->va_atime;
179910588SEric.Taylor@Sun.COM
18007688SAaron.Zang@Sun.COM rv = sdev_mknode(ddv, nm, &dv, vap, NULL,
18017688SAaron.Zang@Sun.COM (void *)physpath, cred, SDEV_READY);
18027688SAaron.Zang@Sun.COM kmem_free(physpath, MAXPATHLEN);
18037688SAaron.Zang@Sun.COM if (rv)
18047688SAaron.Zang@Sun.COM return (rv);
18052621Sllai1 } else if (flags & SDEV_VATTR) {
18062621Sllai1 /*
18072621Sllai1 * /dev/pts
18082621Sllai1 *
18092621Sllai1 * callback is responsible to set the basic attributes,
18102621Sllai1 * e.g. va_type/va_uid/va_gid/
18112621Sllai1 * dev_t if VCHR or VBLK/
18122621Sllai1 */
18132621Sllai1 ASSERT(callback);
18142621Sllai1 rv = callback(ddv, nm, (void *)&vattr, kcred, NULL, NULL);
18152621Sllai1 if (rv) {
18162621Sllai1 sdcmn_err3(("devname_lookup_func: SDEV_NONE "
18172621Sllai1 "callback failed \n"));
18182621Sllai1 return (-1);
18192621Sllai1 }
18202621Sllai1
18212621Sllai1 rv = sdev_mknode(ddv, nm, &dv, &vattr, NULL, NULL,
18222621Sllai1 cred, SDEV_READY);
18232621Sllai1
18242621Sllai1 if (rv)
18252621Sllai1 return (rv);
18262621Sllai1
18272621Sllai1 } else {
18282621Sllai1 impossible(("lookup: %s/%s by %s not supported (%d)\n",
18292621Sllai1 SDEVTOV(ddv)->v_path, nm, curproc->p_user.u_comm,
18302621Sllai1 __LINE__));
18312621Sllai1 rv = -1;
18322621Sllai1 }
18332621Sllai1
18342621Sllai1 *dvp = dv;
18352621Sllai1 return (rv);
18362621Sllai1 }
18372621Sllai1
18382621Sllai1 static int
is_devfsadm_thread(char * exec_name)18392621Sllai1 is_devfsadm_thread(char *exec_name)
18402621Sllai1 {
18412621Sllai1 /*
18422621Sllai1 * note: because devfsadmd -> /usr/sbin/devfsadm
18432621Sllai1 * it is safe to use "devfsadm" to capture the lookups
18442621Sllai1 * from devfsadm and its daemon version.
18452621Sllai1 */
18462621Sllai1 if (strcmp(exec_name, "devfsadm") == 0)
18472621Sllai1 return (1);
18482621Sllai1 return (0);
18492621Sllai1 }
18502621Sllai1
18512621Sllai1 /*
18522621Sllai1 * Lookup Order:
18532621Sllai1 * sdev_node cache;
18542621Sllai1 * backing store (SDEV_PERSIST);
18552621Sllai1 * DBNR: a. dir_ops implemented in the loadable modules;
18562621Sllai1 * b. vnode ops in vtab.
18572621Sllai1 */
18582621Sllai1 int
devname_lookup_func(struct sdev_node * ddv,char * nm,struct vnode ** vpp,struct cred * cred,int (* callback)(struct sdev_node *,char *,void **,struct cred *,void *,char *),int flags)18592621Sllai1 devname_lookup_func(struct sdev_node *ddv, char *nm, struct vnode **vpp,
18602621Sllai1 struct cred *cred, int (*callback)(struct sdev_node *, char *, void **,
18612621Sllai1 struct cred *, void *, char *), int flags)
18622621Sllai1 {
18632621Sllai1 int rv = 0, nmlen;
18642621Sllai1 struct vnode *rvp = NULL;
18652621Sllai1 struct sdev_node *dv = NULL;
18662621Sllai1 int retried = 0;
18672621Sllai1 int error = 0;
18682621Sllai1 struct vattr vattr;
18692621Sllai1 char *lookup_thread = curproc->p_user.u_comm;
18702621Sllai1 int failed_flags = 0;
18712621Sllai1 int (*vtor)(struct sdev_node *) = NULL;
18722621Sllai1 int state;
18732621Sllai1 int parent_state;
18742621Sllai1 char *link = NULL;
18752621Sllai1
18762621Sllai1 if (SDEVTOV(ddv)->v_type != VDIR)
18772621Sllai1 return (ENOTDIR);
18782621Sllai1
18792621Sllai1 /*
18802621Sllai1 * Empty name or ., return node itself.
18812621Sllai1 */
18822621Sllai1 nmlen = strlen(nm);
18832621Sllai1 if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
18842621Sllai1 *vpp = SDEVTOV(ddv);
18852621Sllai1 VN_HOLD(*vpp);
18862621Sllai1 return (0);
18872621Sllai1 }
18882621Sllai1
18892621Sllai1 /*
18902621Sllai1 * .., return the parent directory
18912621Sllai1 */
18922621Sllai1 if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
18932621Sllai1 *vpp = SDEVTOV(ddv->sdev_dotdot);
18942621Sllai1 VN_HOLD(*vpp);
18952621Sllai1 return (0);
18962621Sllai1 }
18972621Sllai1
18982621Sllai1 rw_enter(&ddv->sdev_contents, RW_READER);
18992621Sllai1 if (ddv->sdev_flags & SDEV_VTOR) {
19002621Sllai1 vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv);
19012621Sllai1 ASSERT(vtor);
19022621Sllai1 }
19032621Sllai1
19042621Sllai1 tryagain:
19052621Sllai1 /*
19062621Sllai1 * (a) directory cache lookup:
19072621Sllai1 */
19082621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents));
19092621Sllai1 parent_state = ddv->sdev_state;
19102621Sllai1 dv = sdev_cache_lookup(ddv, nm);
19112621Sllai1 if (dv) {
19122621Sllai1 state = dv->sdev_state;
19132621Sllai1 switch (state) {
19142621Sllai1 case SDEV_INIT:
19152621Sllai1 if (is_devfsadm_thread(lookup_thread))
19162621Sllai1 break;
19172621Sllai1
19182621Sllai1 /* ZOMBIED parent won't allow node creation */
19192621Sllai1 if (parent_state == SDEV_ZOMBIE) {
19202621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm,
19212621Sllai1 retried);
19222621Sllai1 goto nolock_notfound;
19232621Sllai1 }
19242621Sllai1
19252621Sllai1 mutex_enter(&dv->sdev_lookup_lock);
19262621Sllai1 /* compensate the threads started after devfsadm */
19272621Sllai1 if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) &&
19282621Sllai1 !(SDEV_IS_LOOKUP(dv)))
19292621Sllai1 SDEV_BLOCK_OTHERS(dv,
19302621Sllai1 (SDEV_LOOKUP | SDEV_LGWAITING));
19312621Sllai1
19322621Sllai1 if (SDEV_IS_LOOKUP(dv)) {
19332621Sllai1 failed_flags |= SLF_REBUILT;
19342621Sllai1 rw_exit(&ddv->sdev_contents);
19352621Sllai1 error = sdev_wait4lookup(dv, SDEV_LOOKUP);
19362621Sllai1 mutex_exit(&dv->sdev_lookup_lock);
19372621Sllai1 rw_enter(&ddv->sdev_contents, RW_READER);
19382621Sllai1
19392621Sllai1 if (error != 0) {
19402621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm,
19412621Sllai1 retried);
19422621Sllai1 goto nolock_notfound;
19432621Sllai1 }
19442621Sllai1
19452621Sllai1 state = dv->sdev_state;
19462621Sllai1 if (state == SDEV_INIT) {
19472621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm,
19482621Sllai1 retried);
19492621Sllai1 goto nolock_notfound;
19502621Sllai1 } else if (state == SDEV_READY) {
19512621Sllai1 goto found;
19522621Sllai1 } else if (state == SDEV_ZOMBIE) {
19532621Sllai1 rw_exit(&ddv->sdev_contents);
19542621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm,
19552621Sllai1 retried);
19562621Sllai1 SDEV_RELE(dv);
19572621Sllai1 goto lookup_failed;
19582621Sllai1 }
19592621Sllai1 } else {
19602621Sllai1 mutex_exit(&dv->sdev_lookup_lock);
19612621Sllai1 }
19622621Sllai1 break;
19632621Sllai1 case SDEV_READY:
19642621Sllai1 goto found;
19652621Sllai1 case SDEV_ZOMBIE:
19662621Sllai1 rw_exit(&ddv->sdev_contents);
19672621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
19682621Sllai1 SDEV_RELE(dv);
19692621Sllai1 goto lookup_failed;
19702621Sllai1 default:
19712621Sllai1 rw_exit(&ddv->sdev_contents);
19722621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
19732621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags);
19742621Sllai1 *vpp = NULLVP;
19752621Sllai1 return (ENOENT);
19762621Sllai1 }
19772621Sllai1 }
19782621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents));
19792621Sllai1
19802621Sllai1 /*
19812621Sllai1 * ZOMBIED parent does not allow new node creation.
19822621Sllai1 * bail out early
19832621Sllai1 */
19842621Sllai1 if (parent_state == SDEV_ZOMBIE) {
19852621Sllai1 rw_exit(&ddv->sdev_contents);
198610588SEric.Taylor@Sun.COM *vpp = NULLVP;
19872621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
19882621Sllai1 return (ENOENT);
19892621Sllai1 }
19902621Sllai1
19912621Sllai1 /*
19922621Sllai1 * (b0): backing store lookup
19932621Sllai1 * SDEV_PERSIST is default except:
19942621Sllai1 * 1) pts nodes
19952621Sllai1 * 2) non-chmod'ed local nodes
199610588SEric.Taylor@Sun.COM * 3) zvol nodes
19972621Sllai1 */
19982621Sllai1 if (SDEV_IS_PERSIST(ddv)) {
19992621Sllai1 error = devname_backstore_lookup(ddv, nm, &rvp);
20002621Sllai1
20012621Sllai1 if (!error) {
20022621Sllai1
200311279SJerry.Gilliam@Sun.COM vattr.va_mask = AT_TYPE|AT_MODE|AT_UID|AT_GID;
20045331Samw error = VOP_GETATTR(rvp, &vattr, 0, cred, NULL);
20052621Sllai1 if (error) {
20062621Sllai1 rw_exit(&ddv->sdev_contents);
20072621Sllai1 if (dv)
20082621Sllai1 SDEV_RELE(dv);
20092621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
20102621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags);
20112621Sllai1 *vpp = NULLVP;
20122621Sllai1 return (ENOENT);
20132621Sllai1 }
20142621Sllai1
20152621Sllai1 if (vattr.va_type == VLNK) {
20162621Sllai1 error = sdev_getlink(rvp, &link);
20172621Sllai1 if (error) {
20182621Sllai1 rw_exit(&ddv->sdev_contents);
20192621Sllai1 if (dv)
20202621Sllai1 SDEV_RELE(dv);
20212621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm,
20222621Sllai1 retried);
20232621Sllai1 sdev_lookup_failed(ddv, nm,
20242621Sllai1 failed_flags);
20252621Sllai1 *vpp = NULLVP;
20262621Sllai1 return (ENOENT);
20272621Sllai1 }
20282621Sllai1 ASSERT(link != NULL);
20292621Sllai1 }
20302621Sllai1
20312621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) {
20322621Sllai1 rw_exit(&ddv->sdev_contents);
20332621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER);
20342621Sllai1 }
20352621Sllai1 error = sdev_mknode(ddv, nm, &dv, &vattr,
20362621Sllai1 rvp, link, cred, SDEV_READY);
20372621Sllai1 rw_downgrade(&ddv->sdev_contents);
20382621Sllai1
20392621Sllai1 if (link != NULL) {
20402621Sllai1 kmem_free(link, strlen(link) + 1);
20412621Sllai1 link = NULL;
20422621Sllai1 }
20432621Sllai1
20442621Sllai1 if (error) {
20452621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
20462621Sllai1 rw_exit(&ddv->sdev_contents);
20472621Sllai1 if (dv)
20482621Sllai1 SDEV_RELE(dv);
20492621Sllai1 goto lookup_failed;
20502621Sllai1 } else {
20512621Sllai1 goto found;
20522621Sllai1 }
20532621Sllai1 } else if (retried) {
20542621Sllai1 rw_exit(&ddv->sdev_contents);
20552621Sllai1 sdcmn_err3(("retry of lookup of %s/%s: failed\n",
20562621Sllai1 ddv->sdev_name, nm));
20572621Sllai1 if (dv)
20582621Sllai1 SDEV_RELE(dv);
20592621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
20602621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags);
20612621Sllai1 *vpp = NULLVP;
20622621Sllai1 return (ENOENT);
20632621Sllai1 }
20642621Sllai1 }
20652621Sllai1
20668023SPhil.Kirk@Sun.COM lookup_create_node:
20672621Sllai1 /* first thread that is doing the lookup on this node */
206810588SEric.Taylor@Sun.COM if (callback) {
206910588SEric.Taylor@Sun.COM ASSERT(dv == NULL);
207010588SEric.Taylor@Sun.COM if (!rw_tryupgrade(&ddv->sdev_contents)) {
207110588SEric.Taylor@Sun.COM rw_exit(&ddv->sdev_contents);
207210588SEric.Taylor@Sun.COM rw_enter(&ddv->sdev_contents, RW_WRITER);
207310588SEric.Taylor@Sun.COM }
207410588SEric.Taylor@Sun.COM error = sdev_call_dircallback(ddv, &dv, nm, callback,
207510588SEric.Taylor@Sun.COM flags, cred);
207610588SEric.Taylor@Sun.COM rw_downgrade(&ddv->sdev_contents);
207710588SEric.Taylor@Sun.COM if (error == 0) {
207810588SEric.Taylor@Sun.COM goto found;
207910588SEric.Taylor@Sun.COM } else {
208010588SEric.Taylor@Sun.COM SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
208110588SEric.Taylor@Sun.COM rw_exit(&ddv->sdev_contents);
208210588SEric.Taylor@Sun.COM goto lookup_failed;
208310588SEric.Taylor@Sun.COM }
208410588SEric.Taylor@Sun.COM }
20852621Sllai1 if (!dv) {
20862621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) {
20872621Sllai1 rw_exit(&ddv->sdev_contents);
20882621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER);
20892621Sllai1 }
20902621Sllai1 error = sdev_mknode(ddv, nm, &dv, NULL, NULL, NULL,
20912621Sllai1 cred, SDEV_INIT);
20922621Sllai1 if (!dv) {
20932621Sllai1 rw_exit(&ddv->sdev_contents);
20942621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
20952621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags);
20962621Sllai1 *vpp = NULLVP;
20972621Sllai1 return (ENOENT);
20982621Sllai1 }
20992621Sllai1 rw_downgrade(&ddv->sdev_contents);
21002621Sllai1 }
21012621Sllai1
21022621Sllai1 /*
21032621Sllai1 * (b1) invoking devfsadm once per life time for devfsadm nodes
21042621Sllai1 */
210510588SEric.Taylor@Sun.COM ASSERT(SDEV_HELD(dv));
210610588SEric.Taylor@Sun.COM
210710588SEric.Taylor@Sun.COM if (SDEV_IS_NO_NCACHE(dv))
210810588SEric.Taylor@Sun.COM failed_flags |= SLF_NO_NCACHE;
210910588SEric.Taylor@Sun.COM if (sdev_reconfig_boot || !i_ddi_io_initialized() ||
211010588SEric.Taylor@Sun.COM SDEV_IS_DYNAMIC(ddv) || SDEV_IS_NO_NCACHE(dv) ||
211110588SEric.Taylor@Sun.COM ((moddebug & MODDEBUG_FINI_EBUSY) != 0)) {
211210588SEric.Taylor@Sun.COM ASSERT(SDEV_HELD(dv));
211310588SEric.Taylor@Sun.COM SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
211410588SEric.Taylor@Sun.COM goto nolock_notfound;
21152621Sllai1 }
21162621Sllai1
21172621Sllai1 /*
211810588SEric.Taylor@Sun.COM * filter out known non-existent devices recorded
211910588SEric.Taylor@Sun.COM * during initial reconfiguration boot for which
212010588SEric.Taylor@Sun.COM * reconfig should not be done and lookup may
212110588SEric.Taylor@Sun.COM * be short-circuited now.
21222621Sllai1 */
212310588SEric.Taylor@Sun.COM if (sdev_lookup_filter(ddv, nm)) {
212410588SEric.Taylor@Sun.COM SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
212510588SEric.Taylor@Sun.COM goto nolock_notfound;
212610588SEric.Taylor@Sun.COM }
212710588SEric.Taylor@Sun.COM
212810588SEric.Taylor@Sun.COM /* bypassing devfsadm internal nodes */
212910588SEric.Taylor@Sun.COM if (is_devfsadm_thread(lookup_thread)) {
213010588SEric.Taylor@Sun.COM SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
213110588SEric.Taylor@Sun.COM goto nolock_notfound;
213210588SEric.Taylor@Sun.COM }
213310588SEric.Taylor@Sun.COM
213410588SEric.Taylor@Sun.COM if (sdev_reconfig_disable) {
213510588SEric.Taylor@Sun.COM SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
213610588SEric.Taylor@Sun.COM goto nolock_notfound;
213710588SEric.Taylor@Sun.COM }
213810588SEric.Taylor@Sun.COM
213910588SEric.Taylor@Sun.COM error = sdev_call_devfsadmd(ddv, dv, nm);
214010588SEric.Taylor@Sun.COM if (error == 0) {
214110588SEric.Taylor@Sun.COM sdcmn_err8(("lookup of %s/%s by %s: reconfig\n",
214210588SEric.Taylor@Sun.COM ddv->sdev_name, nm, curproc->p_user.u_comm));
214310588SEric.Taylor@Sun.COM if (sdev_reconfig_verbose) {
214410588SEric.Taylor@Sun.COM cmn_err(CE_CONT,
214510588SEric.Taylor@Sun.COM "?lookup of %s/%s by %s: reconfig\n",
214610588SEric.Taylor@Sun.COM ddv->sdev_name, nm, curproc->p_user.u_comm);
21472621Sllai1 }
214810588SEric.Taylor@Sun.COM retried = 1;
214910588SEric.Taylor@Sun.COM failed_flags |= SLF_REBUILT;
215010588SEric.Taylor@Sun.COM ASSERT(dv->sdev_state != SDEV_ZOMBIE);
215110588SEric.Taylor@Sun.COM SDEV_SIMPLE_RELE(dv);
215210588SEric.Taylor@Sun.COM goto tryagain;
215310588SEric.Taylor@Sun.COM } else {
215410588SEric.Taylor@Sun.COM SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
215510588SEric.Taylor@Sun.COM goto nolock_notfound;
21562621Sllai1 }
21572621Sllai1
21582621Sllai1 found:
21592621Sllai1 ASSERT(!(dv->sdev_flags & SDEV_STALE));
21602621Sllai1 ASSERT(dv->sdev_state == SDEV_READY);
21612621Sllai1 if (vtor) {
21622621Sllai1 /*
21632621Sllai1 * Check validity of returned node
21642621Sllai1 */
21652621Sllai1 switch (vtor(dv)) {
21662621Sllai1 case SDEV_VTOR_VALID:
21672621Sllai1 break;
21688023SPhil.Kirk@Sun.COM case SDEV_VTOR_STALE:
21698023SPhil.Kirk@Sun.COM /*
21708023SPhil.Kirk@Sun.COM * The name exists, but the cache entry is
21718023SPhil.Kirk@Sun.COM * stale and needs to be re-created.
21728023SPhil.Kirk@Sun.COM */
21738023SPhil.Kirk@Sun.COM ASSERT(RW_READ_HELD(&ddv->sdev_contents));
21748023SPhil.Kirk@Sun.COM if (rw_tryupgrade(&ddv->sdev_contents) == 0) {
21758023SPhil.Kirk@Sun.COM rw_exit(&ddv->sdev_contents);
21768023SPhil.Kirk@Sun.COM rw_enter(&ddv->sdev_contents, RW_WRITER);
21778023SPhil.Kirk@Sun.COM }
21788023SPhil.Kirk@Sun.COM error = sdev_cache_update(ddv, &dv, nm,
21798023SPhil.Kirk@Sun.COM SDEV_CACHE_DELETE);
21808023SPhil.Kirk@Sun.COM rw_downgrade(&ddv->sdev_contents);
21818023SPhil.Kirk@Sun.COM if (error == 0) {
21828023SPhil.Kirk@Sun.COM dv = NULL;
21838023SPhil.Kirk@Sun.COM goto lookup_create_node;
21848023SPhil.Kirk@Sun.COM }
21858023SPhil.Kirk@Sun.COM /* FALLTHRU */
21862621Sllai1 case SDEV_VTOR_INVALID:
21872621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
21882621Sllai1 sdcmn_err7(("lookup: destroy invalid "
21892621Sllai1 "node: %s(%p)\n", dv->sdev_name, (void *)dv));
21902621Sllai1 goto nolock_notfound;
21912621Sllai1 case SDEV_VTOR_SKIP:
21922621Sllai1 sdcmn_err7(("lookup: node not applicable - "
21932621Sllai1 "skipping: %s(%p)\n", dv->sdev_name, (void *)dv));
21942621Sllai1 rw_exit(&ddv->sdev_contents);
21952621Sllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
21962621Sllai1 SDEV_RELE(dv);
21972621Sllai1 goto lookup_failed;
21982621Sllai1 default:
21992621Sllai1 cmn_err(CE_PANIC,
22002621Sllai1 "dev fs: validator failed: %s(%p)\n",
22012621Sllai1 dv->sdev_name, (void *)dv);
22022621Sllai1 break;
22032621Sllai1 }
22042621Sllai1 }
22052621Sllai1
22062621Sllai1 rw_exit(&ddv->sdev_contents);
22072621Sllai1 rv = sdev_to_vp(dv, vpp);
22082621Sllai1 sdcmn_err3(("devname_lookup_func: returning vp %p v_count %d state %d "
22092621Sllai1 "for nm %s, error %d\n", (void *)*vpp, (*vpp)->v_count,
22102621Sllai1 dv->sdev_state, nm, rv));
22112621Sllai1 return (rv);
22122621Sllai1
22132621Sllai1 nolock_notfound:
22142621Sllai1 /*
22152621Sllai1 * Destroy the node that is created for synchronization purposes.
22162621Sllai1 */
22172621Sllai1 sdcmn_err3(("devname_lookup_func: %s with state %d\n",
22182621Sllai1 nm, dv->sdev_state));
22192621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents));
22202621Sllai1 if (dv->sdev_state == SDEV_INIT) {
22212621Sllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) {
22222621Sllai1 rw_exit(&ddv->sdev_contents);
22232621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER);
22242621Sllai1 }
22252621Sllai1
22262621Sllai1 /*
22272621Sllai1 * Node state may have changed during the lock
22282621Sllai1 * changes. Re-check.
22292621Sllai1 */
22302621Sllai1 if (dv->sdev_state == SDEV_INIT) {
22312621Sllai1 (void) sdev_dirdelete(ddv, dv);
22322621Sllai1 rw_exit(&ddv->sdev_contents);
22332621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags);
22342621Sllai1 *vpp = NULL;
22352621Sllai1 return (ENOENT);
22362621Sllai1 }
22372621Sllai1 }
22382621Sllai1
22392621Sllai1 rw_exit(&ddv->sdev_contents);
22402621Sllai1 SDEV_RELE(dv);
22412621Sllai1
22422621Sllai1 lookup_failed:
22432621Sllai1 sdev_lookup_failed(ddv, nm, failed_flags);
22442621Sllai1 *vpp = NULL;
22452621Sllai1 return (ENOENT);
22462621Sllai1 }
22472621Sllai1
22482621Sllai1 /*
22492621Sllai1 * Given a directory node, mark all nodes beneath as
22502621Sllai1 * STALE, i.e. nodes that don't exist as far as new
22516347Sjg * consumers are concerned. Remove them from the
22526347Sjg * list of directory entries so that no lookup or
22536347Sjg * directory traversal will find them. The node
22546347Sjg * not deallocated so existing holds are not affected.
22552621Sllai1 */
22562621Sllai1 void
sdev_stale(struct sdev_node * ddv)22572621Sllai1 sdev_stale(struct sdev_node *ddv)
22582621Sllai1 {
22592621Sllai1 struct sdev_node *dv;
22602621Sllai1 struct vnode *vp;
22612621Sllai1
22622621Sllai1 ASSERT(SDEVTOV(ddv)->v_type == VDIR);
22632621Sllai1
22642621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER);
22656260Sjg for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = SDEV_NEXT_ENTRY(ddv, dv)) {
22662621Sllai1 vp = SDEVTOV(dv);
22672621Sllai1 if (vp->v_type == VDIR)
22682621Sllai1 sdev_stale(dv);
22692621Sllai1
22702621Sllai1 sdcmn_err9(("sdev_stale: setting stale %s\n",
22716347Sjg dv->sdev_path));
22722621Sllai1 dv->sdev_flags |= SDEV_STALE;
22736347Sjg avl_remove(&ddv->sdev_entries, dv);
22742621Sllai1 }
22752621Sllai1 ddv->sdev_flags |= SDEV_BUILD;
22762621Sllai1 rw_exit(&ddv->sdev_contents);
22772621Sllai1 }
22782621Sllai1
22792621Sllai1 /*
22802621Sllai1 * Given a directory node, clean out all the nodes beneath.
22812621Sllai1 * If expr is specified, clean node with names matching expr.
22822621Sllai1 * If SDEV_ENFORCE is specified in flags, busy nodes are made stale,
22832621Sllai1 * so they are excluded from future lookups.
22842621Sllai1 */
22852621Sllai1 int
sdev_cleandir(struct sdev_node * ddv,char * expr,uint_t flags)22862621Sllai1 sdev_cleandir(struct sdev_node *ddv, char *expr, uint_t flags)
22872621Sllai1 {
22882621Sllai1 int error = 0;
22892621Sllai1 int busy = 0;
22902621Sllai1 struct vnode *vp;
22912621Sllai1 struct sdev_node *dv, *next = NULL;
22922621Sllai1 int bkstore = 0;
22932621Sllai1 int len = 0;
22942621Sllai1 char *bks_name = NULL;
22952621Sllai1
22962621Sllai1 ASSERT(SDEVTOV(ddv)->v_type == VDIR);
22972621Sllai1
22982621Sllai1 /*
22992621Sllai1 * We try our best to destroy all unused sdev_node's
23002621Sllai1 */
23012621Sllai1 rw_enter(&ddv->sdev_contents, RW_WRITER);
23026260Sjg for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) {
23036260Sjg next = SDEV_NEXT_ENTRY(ddv, dv);
23042621Sllai1 vp = SDEVTOV(dv);
23052621Sllai1
23062621Sllai1 if (expr && gmatch(dv->sdev_name, expr) == 0)
23072621Sllai1 continue;
23082621Sllai1
23092621Sllai1 if (vp->v_type == VDIR &&
23102621Sllai1 sdev_cleandir(dv, NULL, flags) != 0) {
23112621Sllai1 sdcmn_err9(("sdev_cleandir: dir %s busy\n",
23122621Sllai1 dv->sdev_name));
23132621Sllai1 busy++;
23142621Sllai1 continue;
23152621Sllai1 }
23162621Sllai1
23172621Sllai1 if (vp->v_count > 0 && (flags & SDEV_ENFORCE) == 0) {
23182621Sllai1 sdcmn_err9(("sdev_cleandir: dir %s busy\n",
23192621Sllai1 dv->sdev_name));
23202621Sllai1 busy++;
23212621Sllai1 continue;
23222621Sllai1 }
23232621Sllai1
23242621Sllai1 /*
23252621Sllai1 * at this point, either dv is not held or SDEV_ENFORCE
23262621Sllai1 * is specified. In either case, dv needs to be deleted
23272621Sllai1 */
23282621Sllai1 SDEV_HOLD(dv);
23292621Sllai1
23302621Sllai1 bkstore = SDEV_IS_PERSIST(dv) ? 1 : 0;
23312621Sllai1 if (bkstore && (vp->v_type == VDIR))
23322621Sllai1 bkstore += 1;
23332621Sllai1
23342621Sllai1 if (bkstore) {
23352621Sllai1 len = strlen(dv->sdev_name) + 1;
23362621Sllai1 bks_name = kmem_alloc(len, KM_SLEEP);
23372621Sllai1 bcopy(dv->sdev_name, bks_name, len);
23382621Sllai1 }
23392621Sllai1
23402621Sllai1 error = sdev_dirdelete(ddv, dv);
23412621Sllai1
23422621Sllai1 if (error == EBUSY) {
23432621Sllai1 sdcmn_err9(("sdev_cleandir: dir busy\n"));
23442621Sllai1 busy++;
23452621Sllai1 }
23462621Sllai1
23472621Sllai1 /* take care the backing store clean up */
23482621Sllai1 if (bkstore && (error == 0)) {
23492621Sllai1 ASSERT(bks_name);
23502621Sllai1 ASSERT(ddv->sdev_attrvp);
23512621Sllai1
23522621Sllai1 if (bkstore == 1) {
23532621Sllai1 error = VOP_REMOVE(ddv->sdev_attrvp,
23545331Samw bks_name, kcred, NULL, 0);
23552621Sllai1 } else if (bkstore == 2) {
23562621Sllai1 error = VOP_RMDIR(ddv->sdev_attrvp,
23575331Samw bks_name, ddv->sdev_attrvp, kcred, NULL, 0);
23582621Sllai1 }
23592621Sllai1
23602621Sllai1 /* do not propagate the backing store errors */
23612621Sllai1 if (error) {
23622621Sllai1 sdcmn_err9(("sdev_cleandir: backing store"
23632621Sllai1 "not cleaned\n"));
23642621Sllai1 error = 0;
23652621Sllai1 }
23662621Sllai1
23672621Sllai1 bkstore = 0;
23682621Sllai1 kmem_free(bks_name, len);
23692621Sllai1 bks_name = NULL;
23702621Sllai1 len = 0;
23712621Sllai1 }
23722621Sllai1 }
23732621Sllai1
23742621Sllai1 ddv->sdev_flags |= SDEV_BUILD;
23752621Sllai1 rw_exit(&ddv->sdev_contents);
23762621Sllai1
23772621Sllai1 if (busy) {
23782621Sllai1 error = EBUSY;
23792621Sllai1 }
23802621Sllai1
23812621Sllai1 return (error);
23822621Sllai1 }
23832621Sllai1
23842621Sllai1 /*
23852621Sllai1 * a convenient wrapper for readdir() funcs
23862621Sllai1 */
23872621Sllai1 size_t
add_dir_entry(dirent64_t * de,char * nm,size_t size,ino_t ino,offset_t off)23882621Sllai1 add_dir_entry(dirent64_t *de, char *nm, size_t size, ino_t ino, offset_t off)
23892621Sllai1 {
23902621Sllai1 size_t reclen = DIRENT64_RECLEN(strlen(nm));
23912621Sllai1 if (reclen > size)
23922621Sllai1 return (0);
23932621Sllai1
23942621Sllai1 de->d_ino = (ino64_t)ino;
23952621Sllai1 de->d_off = (off64_t)off + 1;
23962621Sllai1 de->d_reclen = (ushort_t)reclen;
23972621Sllai1 (void) strncpy(de->d_name, nm, DIRENT64_NAMELEN(reclen));
23982621Sllai1 return (reclen);
23992621Sllai1 }
24002621Sllai1
24012621Sllai1 /*
24022621Sllai1 * sdev_mount service routines
24032621Sllai1 */
24042621Sllai1 int
sdev_copyin_mountargs(struct mounta * uap,struct sdev_mountargs * args)24052621Sllai1 sdev_copyin_mountargs(struct mounta *uap, struct sdev_mountargs *args)
24062621Sllai1 {
24072621Sllai1 int error;
24082621Sllai1
24092621Sllai1 if (uap->datalen != sizeof (*args))
24102621Sllai1 return (EINVAL);
24112621Sllai1
24122621Sllai1 if (error = copyin(uap->dataptr, args, sizeof (*args))) {
24132621Sllai1 cmn_err(CE_WARN, "sdev_copyin_mountargs: can not"
24142621Sllai1 "get user data. error %d\n", error);
24152621Sllai1 return (EFAULT);
24162621Sllai1 }
24172621Sllai1
24182621Sllai1 return (0);
24192621Sllai1 }
24202621Sllai1
24212621Sllai1 #ifdef nextdp
24222621Sllai1 #undef nextdp
24232621Sllai1 #endif
24243133Sjg #define nextdp(dp) ((struct dirent64 *) \
24253133Sjg (intptr_t)((char *)(dp) + (dp)->d_reclen))
24262621Sllai1
24272621Sllai1 /*
24282621Sllai1 * readdir helper func
24292621Sllai1 */
24302621Sllai1 int
devname_readdir_func(vnode_t * vp,uio_t * uiop,cred_t * cred,int * eofp,int flags)24312621Sllai1 devname_readdir_func(vnode_t *vp, uio_t *uiop, cred_t *cred, int *eofp,
24322621Sllai1 int flags)
24332621Sllai1 {
24342621Sllai1 struct sdev_node *ddv = VTOSDEV(vp);
24352621Sllai1 struct sdev_node *dv;
24362621Sllai1 dirent64_t *dp;
24372621Sllai1 ulong_t outcount = 0;
24382621Sllai1 size_t namelen;
24392621Sllai1 ulong_t alloc_count;
24402621Sllai1 void *outbuf;
24412621Sllai1 struct iovec *iovp;
24422621Sllai1 int error = 0;
24432621Sllai1 size_t reclen;
24442621Sllai1 offset_t diroff;
24452621Sllai1 offset_t soff;
24462621Sllai1 int this_reclen;
24472621Sllai1 int (*vtor)(struct sdev_node *) = NULL;
24482621Sllai1 struct vattr attr;
24492621Sllai1 timestruc_t now;
24502621Sllai1
24512621Sllai1 ASSERT(ddv->sdev_attr || ddv->sdev_attrvp);
24522621Sllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents));
24532621Sllai1
24542621Sllai1 if (uiop->uio_loffset >= MAXOFF_T) {
24552621Sllai1 if (eofp)
24562621Sllai1 *eofp = 1;
24572621Sllai1 return (0);
24582621Sllai1 }
24592621Sllai1
24602621Sllai1 if (uiop->uio_iovcnt != 1)
24612621Sllai1 return (EINVAL);
24622621Sllai1
24632621Sllai1 if (vp->v_type != VDIR)
24642621Sllai1 return (ENOTDIR);
24652621Sllai1
24662621Sllai1 if (ddv->sdev_flags & SDEV_VTOR) {
24672621Sllai1 vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv);
24682621Sllai1 ASSERT(vtor);
24692621Sllai1 }
24702621Sllai1
24712621Sllai1 if (eofp != NULL)
24722621Sllai1 *eofp = 0;
24732621Sllai1
24743133Sjg soff = uiop->uio_loffset;
24752621Sllai1 iovp = uiop->uio_iov;
24762621Sllai1 alloc_count = iovp->iov_len;
24772621Sllai1 dp = outbuf = kmem_alloc(alloc_count, KM_SLEEP);
24782621Sllai1 outcount = 0;
24792621Sllai1
24802621Sllai1 if (ddv->sdev_state == SDEV_ZOMBIE)
24812621Sllai1 goto get_cache;
24822621Sllai1
24832679Sszhou if (SDEV_IS_GLOBAL(ddv)) {
248410097SEric.Taylor@Sun.COM
248510097SEric.Taylor@Sun.COM if ((sdev_boot_state == SDEV_BOOT_STATE_COMPLETE) &&
24862621Sllai1 !sdev_reconfig_boot && (flags & SDEV_BROWSE) &&
24872621Sllai1 !SDEV_IS_DYNAMIC(ddv) && !SDEV_IS_NO_NCACHE(ddv) &&
24882621Sllai1 ((moddebug & MODDEBUG_FINI_EBUSY) == 0) &&
24892621Sllai1 !DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state) &&
24902621Sllai1 !DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) &&
24912621Sllai1 !sdev_reconfig_disable) {
24922621Sllai1 /*
24932621Sllai1 * invoking "devfsadm" to do system device reconfig
24942621Sllai1 */
24952621Sllai1 mutex_enter(&ddv->sdev_lookup_lock);
24962621Sllai1 SDEV_BLOCK_OTHERS(ddv,
24972621Sllai1 (SDEV_READDIR|SDEV_LGWAITING));
24982621Sllai1 mutex_exit(&ddv->sdev_lookup_lock);
24992621Sllai1
25002621Sllai1 sdcmn_err8(("readdir of %s by %s: reconfig\n",
25012621Sllai1 ddv->sdev_path, curproc->p_user.u_comm));
25022621Sllai1 if (sdev_reconfig_verbose) {
25032621Sllai1 cmn_err(CE_CONT,
25042621Sllai1 "?readdir of %s by %s: reconfig\n",
25052621Sllai1 ddv->sdev_path, curproc->p_user.u_comm);
25062621Sllai1 }
25072621Sllai1
25082621Sllai1 sdev_devfsadmd_thread(ddv, NULL, kcred);
25092621Sllai1 } else if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) {
25102621Sllai1 /*
25112621Sllai1 * compensate the "ls" started later than "devfsadm"
25122621Sllai1 */
25132621Sllai1 mutex_enter(&ddv->sdev_lookup_lock);
25142621Sllai1 SDEV_BLOCK_OTHERS(ddv, (SDEV_READDIR|SDEV_LGWAITING));
25152621Sllai1 mutex_exit(&ddv->sdev_lookup_lock);
25162621Sllai1 }
25172621Sllai1
25182621Sllai1 /*
25192621Sllai1 * release the contents lock so that
25203843Sjg * the cache may be updated by devfsadmd
25212621Sllai1 */
25222621Sllai1 rw_exit(&ddv->sdev_contents);
25232621Sllai1 mutex_enter(&ddv->sdev_lookup_lock);
25242621Sllai1 if (SDEV_IS_READDIR(ddv))
25252621Sllai1 (void) sdev_wait4lookup(ddv, SDEV_READDIR);
25262621Sllai1 mutex_exit(&ddv->sdev_lookup_lock);
25272621Sllai1 rw_enter(&ddv->sdev_contents, RW_READER);
25282621Sllai1
25292621Sllai1 sdcmn_err4(("readdir of directory %s by %s\n",
25302621Sllai1 ddv->sdev_name, curproc->p_user.u_comm));
25313843Sjg if (ddv->sdev_flags & SDEV_BUILD) {
25322621Sllai1 if (SDEV_IS_PERSIST(ddv)) {
25332621Sllai1 error = sdev_filldir_from_store(ddv,
25342621Sllai1 alloc_count, cred);
25352621Sllai1 }
25363843Sjg ddv->sdev_flags &= ~SDEV_BUILD;
25372621Sllai1 }
25382621Sllai1 }
25392621Sllai1
25402621Sllai1 get_cache:
25412621Sllai1 /* handle "." and ".." */
25422621Sllai1 diroff = 0;
25432621Sllai1 if (soff == 0) {
25442621Sllai1 /* first time */
25452621Sllai1 this_reclen = DIRENT64_RECLEN(1);
25462621Sllai1 if (alloc_count < this_reclen) {
25472621Sllai1 error = EINVAL;
25482621Sllai1 goto done;
25492621Sllai1 }
25502621Sllai1
25512621Sllai1 dp->d_ino = (ino64_t)ddv->sdev_ino;
25522621Sllai1 dp->d_off = (off64_t)1;
25532621Sllai1 dp->d_reclen = (ushort_t)this_reclen;
25542621Sllai1
25552621Sllai1 (void) strncpy(dp->d_name, ".",
25562621Sllai1 DIRENT64_NAMELEN(this_reclen));
25572621Sllai1 outcount += dp->d_reclen;
25582621Sllai1 dp = nextdp(dp);
25592621Sllai1 }
25602621Sllai1
25612621Sllai1 diroff++;
25622621Sllai1 if (soff <= 1) {
25632621Sllai1 this_reclen = DIRENT64_RECLEN(2);
25642621Sllai1 if (alloc_count < outcount + this_reclen) {
25652621Sllai1 error = EINVAL;
25662621Sllai1 goto done;
25672621Sllai1 }
25682621Sllai1
25692621Sllai1 dp->d_reclen = (ushort_t)this_reclen;
25702621Sllai1 dp->d_ino = (ino64_t)ddv->sdev_dotdot->sdev_ino;
25712621Sllai1 dp->d_off = (off64_t)2;
25722621Sllai1
25732621Sllai1 (void) strncpy(dp->d_name, "..",
25742621Sllai1 DIRENT64_NAMELEN(this_reclen));
25752621Sllai1 outcount += dp->d_reclen;
25762621Sllai1
25772621Sllai1 dp = nextdp(dp);
25782621Sllai1 }
25792621Sllai1
25802621Sllai1
25812621Sllai1 /* gets the cache */
25822621Sllai1 diroff++;
25836260Sjg for (dv = SDEV_FIRST_ENTRY(ddv); dv;
25846260Sjg dv = SDEV_NEXT_ENTRY(ddv, dv), diroff++) {
25852621Sllai1 sdcmn_err3(("sdev_readdir: diroff %lld soff %lld for '%s' \n",
25862621Sllai1 diroff, soff, dv->sdev_name));
25872621Sllai1
25882621Sllai1 /* bypassing pre-matured nodes */
25892621Sllai1 if (diroff < soff || (dv->sdev_state != SDEV_READY)) {
25902621Sllai1 sdcmn_err3(("sdev_readdir: pre-mature node "
259110588SEric.Taylor@Sun.COM "%s %d\n", dv->sdev_name, dv->sdev_state));
25922621Sllai1 continue;
25932621Sllai1 }
25942621Sllai1
25952621Sllai1 /*
25962621Sllai1 * Check validity of node
259712717SJerry.Gilliam@Sun.COM * Drop invalid and nodes to be skipped.
259812717SJerry.Gilliam@Sun.COM * A node the validator indicates as stale needs
259912717SJerry.Gilliam@Sun.COM * to be returned as presumably the node name itself
260012717SJerry.Gilliam@Sun.COM * is valid and the node data itself will be refreshed
260112717SJerry.Gilliam@Sun.COM * on lookup. An application performing a readdir then
260212717SJerry.Gilliam@Sun.COM * stat on each entry should thus always see consistent
260312717SJerry.Gilliam@Sun.COM * data. In any case, it is not possible to synchronize
260412717SJerry.Gilliam@Sun.COM * with dynamic kernel state, and any view we return can
260512717SJerry.Gilliam@Sun.COM * never be anything more than a snapshot at a point in time.
26062621Sllai1 */
26072621Sllai1 if (vtor) {
26082621Sllai1 switch (vtor(dv)) {
26092621Sllai1 case SDEV_VTOR_VALID:
26102621Sllai1 break;
26112621Sllai1 case SDEV_VTOR_INVALID:
26122621Sllai1 case SDEV_VTOR_SKIP:
26132621Sllai1 continue;
261412717SJerry.Gilliam@Sun.COM case SDEV_VTOR_STALE:
261512717SJerry.Gilliam@Sun.COM sdcmn_err3(("sdev_readir: %s stale\n",
261612717SJerry.Gilliam@Sun.COM dv->sdev_name));
261712717SJerry.Gilliam@Sun.COM break;
26182621Sllai1 default:
26192621Sllai1 cmn_err(CE_PANIC,
26202621Sllai1 "dev fs: validator failed: %s(%p)\n",
26212621Sllai1 dv->sdev_name, (void *)dv);
26222621Sllai1 break;
26232621Sllai1 /*NOTREACHED*/
26242621Sllai1 }
26252621Sllai1 }
26262621Sllai1
26272621Sllai1 namelen = strlen(dv->sdev_name);
26282621Sllai1 reclen = DIRENT64_RECLEN(namelen);
26292621Sllai1 if (outcount + reclen > alloc_count) {
26302621Sllai1 goto full;
26312621Sllai1 }
26322621Sllai1 dp->d_reclen = (ushort_t)reclen;
26332621Sllai1 dp->d_ino = (ino64_t)dv->sdev_ino;
26342621Sllai1 dp->d_off = (off64_t)diroff + 1;
26352621Sllai1 (void) strncpy(dp->d_name, dv->sdev_name,
26362621Sllai1 DIRENT64_NAMELEN(reclen));
26372621Sllai1 outcount += reclen;
26382621Sllai1 dp = nextdp(dp);
26392621Sllai1 }
26402621Sllai1
26412621Sllai1 full:
26422621Sllai1 sdcmn_err4(("sdev_readdir: moving %lu bytes: "
26432621Sllai1 "diroff %lld, soff %lld, dv %p\n", outcount, diroff, soff,
26442621Sllai1 (void *)dv));
26452621Sllai1
26462621Sllai1 if (outcount)
26472621Sllai1 error = uiomove(outbuf, outcount, UIO_READ, uiop);
26482621Sllai1
26492621Sllai1 if (!error) {
26503133Sjg uiop->uio_loffset = diroff;
26512621Sllai1 if (eofp)
26522621Sllai1 *eofp = dv ? 0 : 1;
26532621Sllai1 }
26542621Sllai1
26552621Sllai1
26562621Sllai1 if (ddv->sdev_attrvp) {
26572621Sllai1 gethrestime(&now);
26582621Sllai1 attr.va_ctime = now;
26592621Sllai1 attr.va_atime = now;
26602621Sllai1 attr.va_mask = AT_CTIME|AT_ATIME;
26612621Sllai1
26622621Sllai1 (void) VOP_SETATTR(ddv->sdev_attrvp, &attr, 0, kcred, NULL);
26632621Sllai1 }
26642621Sllai1 done:
26652621Sllai1 kmem_free(outbuf, alloc_count);
26662621Sllai1 return (error);
26672621Sllai1 }
26682621Sllai1
26692621Sllai1 static int
sdev_modctl_lookup(const char * path,vnode_t ** r_vp)26702621Sllai1 sdev_modctl_lookup(const char *path, vnode_t **r_vp)
26712621Sllai1 {
26722621Sllai1 vnode_t *vp;
26732621Sllai1 vnode_t *cvp;
26742621Sllai1 struct sdev_node *svp;
26752621Sllai1 char *nm;
26762621Sllai1 struct pathname pn;
26772621Sllai1 int error;
26782621Sllai1 int persisted = 0;
26792621Sllai1
26807988SJerry.Gilliam@Sun.COM ASSERT(INGLOBALZONE(curproc));
26817988SJerry.Gilliam@Sun.COM
26822621Sllai1 if (error = pn_get((char *)path, UIO_SYSSPACE, &pn))
26832621Sllai1 return (error);
26842621Sllai1 nm = kmem_alloc(MAXNAMELEN, KM_SLEEP);
26852621Sllai1
26862621Sllai1 vp = rootdir;
26872621Sllai1 VN_HOLD(vp);
26882621Sllai1
26892621Sllai1 while (pn_pathleft(&pn)) {
26907988SJerry.Gilliam@Sun.COM ASSERT(vp->v_type == VDIR || vp->v_type == VLNK);
26912621Sllai1 (void) pn_getcomponent(&pn, nm);
26927988SJerry.Gilliam@Sun.COM
26937988SJerry.Gilliam@Sun.COM /*
26947988SJerry.Gilliam@Sun.COM * Deal with the .. special case where we may be
26957988SJerry.Gilliam@Sun.COM * traversing up across a mount point, to the
26967988SJerry.Gilliam@Sun.COM * root of this filesystem or global root.
26977988SJerry.Gilliam@Sun.COM */
26987988SJerry.Gilliam@Sun.COM if (nm[0] == '.' && nm[1] == '.' && nm[2] == 0) {
26997988SJerry.Gilliam@Sun.COM checkforroot:
27007988SJerry.Gilliam@Sun.COM if (VN_CMP(vp, rootdir)) {
27017988SJerry.Gilliam@Sun.COM nm[1] = 0;
27027988SJerry.Gilliam@Sun.COM } else if (vp->v_flag & VROOT) {
27037988SJerry.Gilliam@Sun.COM vfs_t *vfsp;
27047988SJerry.Gilliam@Sun.COM cvp = vp;
27057988SJerry.Gilliam@Sun.COM vfsp = cvp->v_vfsp;
27067988SJerry.Gilliam@Sun.COM vfs_rlock_wait(vfsp);
27077988SJerry.Gilliam@Sun.COM vp = cvp->v_vfsp->vfs_vnodecovered;
27087988SJerry.Gilliam@Sun.COM if (vp == NULL ||
27097988SJerry.Gilliam@Sun.COM (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) {
27107988SJerry.Gilliam@Sun.COM vfs_unlock(vfsp);
27117988SJerry.Gilliam@Sun.COM VN_RELE(cvp);
27127988SJerry.Gilliam@Sun.COM error = EIO;
27137988SJerry.Gilliam@Sun.COM break;
27147988SJerry.Gilliam@Sun.COM }
27157988SJerry.Gilliam@Sun.COM VN_HOLD(vp);
27167988SJerry.Gilliam@Sun.COM vfs_unlock(vfsp);
27177988SJerry.Gilliam@Sun.COM VN_RELE(cvp);
27187988SJerry.Gilliam@Sun.COM cvp = NULL;
27197988SJerry.Gilliam@Sun.COM goto checkforroot;
27207988SJerry.Gilliam@Sun.COM }
27217988SJerry.Gilliam@Sun.COM }
27227988SJerry.Gilliam@Sun.COM
27235331Samw error = VOP_LOOKUP(vp, nm, &cvp, NULL, 0, NULL, kcred, NULL,
27245331Samw NULL, NULL);
27257988SJerry.Gilliam@Sun.COM if (error) {
27267988SJerry.Gilliam@Sun.COM VN_RELE(vp);
27272621Sllai1 break;
27287988SJerry.Gilliam@Sun.COM }
27292621Sllai1
27302621Sllai1 /* traverse mount points encountered on our journey */
27312621Sllai1 if (vn_ismntpt(cvp) && (error = traverse(&cvp)) != 0) {
27327988SJerry.Gilliam@Sun.COM VN_RELE(vp);
27332621Sllai1 VN_RELE(cvp);
27342621Sllai1 break;
27352621Sllai1 }
27362621Sllai1
27372621Sllai1 /*
27387988SJerry.Gilliam@Sun.COM * symbolic link, can be either relative and absolute
27397988SJerry.Gilliam@Sun.COM */
27407988SJerry.Gilliam@Sun.COM if ((cvp->v_type == VLNK) && pn_pathleft(&pn)) {
27417988SJerry.Gilliam@Sun.COM struct pathname linkpath;
27427988SJerry.Gilliam@Sun.COM pn_alloc(&linkpath);
27437988SJerry.Gilliam@Sun.COM if (error = pn_getsymlink(cvp, &linkpath, kcred)) {
27447988SJerry.Gilliam@Sun.COM pn_free(&linkpath);
27457988SJerry.Gilliam@Sun.COM break;
27467988SJerry.Gilliam@Sun.COM }
27477988SJerry.Gilliam@Sun.COM if (pn_pathleft(&linkpath) == 0)
27487988SJerry.Gilliam@Sun.COM (void) pn_set(&linkpath, ".");
27497988SJerry.Gilliam@Sun.COM error = pn_insert(&pn, &linkpath, strlen(nm));
27507988SJerry.Gilliam@Sun.COM pn_free(&linkpath);
27517988SJerry.Gilliam@Sun.COM if (pn.pn_pathlen == 0) {
27527988SJerry.Gilliam@Sun.COM VN_RELE(vp);
27537988SJerry.Gilliam@Sun.COM return (ENOENT);
27547988SJerry.Gilliam@Sun.COM }
27557988SJerry.Gilliam@Sun.COM if (pn.pn_path[0] == '/') {
27567988SJerry.Gilliam@Sun.COM pn_skipslash(&pn);
27577988SJerry.Gilliam@Sun.COM VN_RELE(vp);
27587988SJerry.Gilliam@Sun.COM VN_RELE(cvp);
27597988SJerry.Gilliam@Sun.COM vp = rootdir;
27607988SJerry.Gilliam@Sun.COM VN_HOLD(vp);
27617988SJerry.Gilliam@Sun.COM } else {
27627988SJerry.Gilliam@Sun.COM VN_RELE(cvp);
27637988SJerry.Gilliam@Sun.COM }
27647988SJerry.Gilliam@Sun.COM continue;
27657988SJerry.Gilliam@Sun.COM }
27667988SJerry.Gilliam@Sun.COM
27677988SJerry.Gilliam@Sun.COM VN_RELE(vp);
27687988SJerry.Gilliam@Sun.COM
27697988SJerry.Gilliam@Sun.COM /*
27702621Sllai1 * Direct the operation to the persisting filesystem
27712621Sllai1 * underlying /dev. Bail if we encounter a
27722621Sllai1 * non-persistent dev entity here.
27732621Sllai1 */
27742621Sllai1 if (cvp->v_vfsp->vfs_fstype == devtype) {
27752621Sllai1
27762621Sllai1 if ((VTOSDEV(cvp)->sdev_flags & SDEV_PERSIST) == 0) {
27772621Sllai1 error = ENOENT;
27782621Sllai1 VN_RELE(cvp);
27792621Sllai1 break;
27802621Sllai1 }
27812621Sllai1
27822621Sllai1 if (VTOSDEV(cvp) == NULL) {
27832621Sllai1 error = ENOENT;
27842621Sllai1 VN_RELE(cvp);
27852621Sllai1 break;
27862621Sllai1 }
27872621Sllai1 svp = VTOSDEV(cvp);
27882621Sllai1 if ((vp = svp->sdev_attrvp) == NULL) {
27892621Sllai1 error = ENOENT;
27902621Sllai1 VN_RELE(cvp);
27912621Sllai1 break;
27922621Sllai1 }
27932621Sllai1 persisted = 1;
27942621Sllai1 VN_HOLD(vp);
27952621Sllai1 VN_RELE(cvp);
27962621Sllai1 cvp = vp;
27972621Sllai1 }
27982621Sllai1
27992621Sllai1 vp = cvp;
28002621Sllai1 pn_skipslash(&pn);
28012621Sllai1 }
28022621Sllai1
28032621Sllai1 kmem_free(nm, MAXNAMELEN);
28042621Sllai1 pn_free(&pn);
28052621Sllai1
28062621Sllai1 if (error)
28072621Sllai1 return (error);
28082621Sllai1
28092621Sllai1 /*
28102621Sllai1 * Only return persisted nodes in the filesystem underlying /dev.
28112621Sllai1 */
28122621Sllai1 if (!persisted) {
28132621Sllai1 VN_RELE(vp);
28142621Sllai1 return (ENOENT);
28152621Sllai1 }
28162621Sllai1
28172621Sllai1 *r_vp = vp;
28182621Sllai1 return (0);
28192621Sllai1 }
28202621Sllai1
28212621Sllai1 int
sdev_modctl_readdir(const char * dir,char *** dirlistp,int * npathsp,int * npathsp_alloc,int checking_empty)28222621Sllai1 sdev_modctl_readdir(const char *dir, char ***dirlistp,
28236065Scth int *npathsp, int *npathsp_alloc, int checking_empty)
28242621Sllai1 {
28252621Sllai1 char **pathlist = NULL;
28262621Sllai1 char **newlist = NULL;
28272621Sllai1 int npaths = 0;
28282621Sllai1 int npaths_alloc = 0;
28292621Sllai1 dirent64_t *dbuf = NULL;
28302621Sllai1 int n;
28312621Sllai1 char *s;
28322621Sllai1 int error;
28332621Sllai1 vnode_t *vp;
28342621Sllai1 int eof;
28352621Sllai1 struct iovec iov;
28362621Sllai1 struct uio uio;
28372621Sllai1 struct dirent64 *dp;
28382621Sllai1 size_t dlen;
28392621Sllai1 size_t dbuflen;
28402621Sllai1 int ndirents = 64;
28412621Sllai1 char *nm;
28422621Sllai1
28432621Sllai1 error = sdev_modctl_lookup(dir, &vp);
28442621Sllai1 sdcmn_err11(("modctl readdir: %s by %s: %s\n",
28452621Sllai1 dir, curproc->p_user.u_comm,
28462621Sllai1 (error == 0) ? "ok" : "failed"));
28472621Sllai1 if (error)
28482621Sllai1 return (error);
28492621Sllai1
28502621Sllai1 dlen = ndirents * (sizeof (*dbuf));
28512621Sllai1 dbuf = kmem_alloc(dlen, KM_SLEEP);
28522621Sllai1
28532621Sllai1 uio.uio_iov = &iov;
28542621Sllai1 uio.uio_iovcnt = 1;
28552621Sllai1 uio.uio_segflg = UIO_SYSSPACE;
28562621Sllai1 uio.uio_fmode = 0;
28572621Sllai1 uio.uio_extflg = UIO_COPY_CACHED;
28582621Sllai1 uio.uio_loffset = 0;
28592621Sllai1 uio.uio_llimit = MAXOFFSET_T;
28602621Sllai1
28612621Sllai1 eof = 0;
28622621Sllai1 error = 0;
28632621Sllai1 while (!error && !eof) {
28642621Sllai1 uio.uio_resid = dlen;
28652621Sllai1 iov.iov_base = (char *)dbuf;
28662621Sllai1 iov.iov_len = dlen;
28672621Sllai1
28682621Sllai1 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
28695331Samw error = VOP_READDIR(vp, &uio, kcred, &eof, NULL, 0);
28702621Sllai1 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
28712621Sllai1
28722621Sllai1 dbuflen = dlen - uio.uio_resid;
28732621Sllai1
28742621Sllai1 if (error || dbuflen == 0)
28752621Sllai1 break;
28762621Sllai1
28772621Sllai1 for (dp = dbuf; ((intptr_t)dp < (intptr_t)dbuf + dbuflen);
28786065Scth dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
28792621Sllai1
28802621Sllai1 nm = dp->d_name;
28812621Sllai1
28822621Sllai1 if (strcmp(nm, ".") == 0 || strcmp(nm, "..") == 0)
28832621Sllai1 continue;
28842621Sllai1 if (npaths == npaths_alloc) {
28852621Sllai1 npaths_alloc += 64;
28862621Sllai1 newlist = (char **)
28872621Sllai1 kmem_zalloc((npaths_alloc + 1) *
28886065Scth sizeof (char *), KM_SLEEP);
28892621Sllai1 if (pathlist) {
28902621Sllai1 bcopy(pathlist, newlist,
28912621Sllai1 npaths * sizeof (char *));
28922621Sllai1 kmem_free(pathlist,
28932621Sllai1 (npaths + 1) * sizeof (char *));
28942621Sllai1 }
28952621Sllai1 pathlist = newlist;
28962621Sllai1 }
28972621Sllai1 n = strlen(nm) + 1;
28982621Sllai1 s = kmem_alloc(n, KM_SLEEP);
28992621Sllai1 bcopy(nm, s, n);
29002621Sllai1 pathlist[npaths++] = s;
29012621Sllai1 sdcmn_err11((" %s/%s\n", dir, s));
29026065Scth
29036065Scth /* if checking empty, one entry is as good as many */
29046065Scth if (checking_empty) {
29056065Scth eof = 1;
29066065Scth break;
29076065Scth }
29082621Sllai1 }
29092621Sllai1 }
29102621Sllai1
29112621Sllai1 exit:
29122621Sllai1 VN_RELE(vp);
29132621Sllai1
29142621Sllai1 if (dbuf)
29152621Sllai1 kmem_free(dbuf, dlen);
29162621Sllai1
29172621Sllai1 if (error)
29182621Sllai1 return (error);
29192621Sllai1
29202621Sllai1 *dirlistp = pathlist;
29212621Sllai1 *npathsp = npaths;
29222621Sllai1 *npathsp_alloc = npaths_alloc;
29232621Sllai1
29242621Sllai1 return (0);
29252621Sllai1 }
29262621Sllai1
29272621Sllai1 void
sdev_modctl_readdir_free(char ** pathlist,int npaths,int npaths_alloc)29282621Sllai1 sdev_modctl_readdir_free(char **pathlist, int npaths, int npaths_alloc)
29292621Sllai1 {
29302621Sllai1 int i, n;
29312621Sllai1
29322621Sllai1 for (i = 0; i < npaths; i++) {
29332621Sllai1 n = strlen(pathlist[i]) + 1;
29342621Sllai1 kmem_free(pathlist[i], n);
29352621Sllai1 }
29362621Sllai1
29372621Sllai1 kmem_free(pathlist, (npaths_alloc + 1) * sizeof (char *));
29382621Sllai1 }
29392621Sllai1
29402621Sllai1 int
sdev_modctl_devexists(const char * path)29412621Sllai1 sdev_modctl_devexists(const char *path)
29422621Sllai1 {
29432621Sllai1 vnode_t *vp;
29442621Sllai1 int error;
29452621Sllai1
29462621Sllai1 error = sdev_modctl_lookup(path, &vp);
29472621Sllai1 sdcmn_err11(("modctl dev exists: %s by %s: %s\n",
29482621Sllai1 path, curproc->p_user.u_comm,
29492621Sllai1 (error == 0) ? "ok" : "failed"));
29502621Sllai1 if (error == 0)
29512621Sllai1 VN_RELE(vp);
29522621Sllai1
29532621Sllai1 return (error);
29542621Sllai1 }
29552621Sllai1
29562621Sllai1 extern int sdev_vnodeops_tbl_size;
29572621Sllai1
29582621Sllai1 /*
29592621Sllai1 * construct a new template with overrides from vtab
29602621Sllai1 */
29612621Sllai1 static fs_operation_def_t *
sdev_merge_vtab(const fs_operation_def_t tab[])29622621Sllai1 sdev_merge_vtab(const fs_operation_def_t tab[])
29632621Sllai1 {
29642621Sllai1 fs_operation_def_t *new;
29652621Sllai1 const fs_operation_def_t *tab_entry;
29662621Sllai1
29672621Sllai1 /* make a copy of standard vnode ops table */
29682621Sllai1 new = kmem_alloc(sdev_vnodeops_tbl_size, KM_SLEEP);
29692621Sllai1 bcopy((void *)sdev_vnodeops_tbl, new, sdev_vnodeops_tbl_size);
29702621Sllai1
29712621Sllai1 /* replace the overrides from tab */
29722621Sllai1 for (tab_entry = tab; tab_entry->name != NULL; tab_entry++) {
29732621Sllai1 fs_operation_def_t *std_entry = new;
29742621Sllai1 while (std_entry->name) {
29752621Sllai1 if (strcmp(tab_entry->name, std_entry->name) == 0) {
29762621Sllai1 std_entry->func = tab_entry->func;
29772621Sllai1 break;
29782621Sllai1 }
29792621Sllai1 std_entry++;
29802621Sllai1 }
29812621Sllai1 if (std_entry->name == NULL)
29822621Sllai1 cmn_err(CE_NOTE, "sdev_merge_vtab: entry %s unused.",
29832621Sllai1 tab_entry->name);
29842621Sllai1 }
29852621Sllai1
29862621Sllai1 return (new);
29872621Sllai1 }
29882621Sllai1
29892621Sllai1 /* free memory allocated by sdev_merge_vtab */
29902621Sllai1 static void
sdev_free_vtab(fs_operation_def_t * new)29912621Sllai1 sdev_free_vtab(fs_operation_def_t *new)
29922621Sllai1 {
29932621Sllai1 kmem_free(new, sdev_vnodeops_tbl_size);
29942621Sllai1 }
29952621Sllai1
29962621Sllai1 /*
29972621Sllai1 * a generic setattr() function
29982621Sllai1 *
29992621Sllai1 * note: flags only supports AT_UID and AT_GID.
30002621Sllai1 * Future enhancements can be done for other types, e.g. AT_MODE
30012621Sllai1 */
30022621Sllai1 int
devname_setattr_func(struct vnode * vp,struct vattr * vap,int flags,struct cred * cred,int (* callback)(struct sdev_node *,struct vattr *,int),int protocol)30032621Sllai1 devname_setattr_func(struct vnode *vp, struct vattr *vap, int flags,
30042621Sllai1 struct cred *cred, int (*callback)(struct sdev_node *, struct vattr *,
30052621Sllai1 int), int protocol)
30062621Sllai1 {
30072621Sllai1 struct sdev_node *dv = VTOSDEV(vp);
30082621Sllai1 struct sdev_node *parent = dv->sdev_dotdot;
30092621Sllai1 struct vattr *get;
30102621Sllai1 uint_t mask = vap->va_mask;
30112621Sllai1 int error;
30122621Sllai1
30132621Sllai1 /* some sanity checks */
30142621Sllai1 if (vap->va_mask & AT_NOSET)
30152621Sllai1 return (EINVAL);
30162621Sllai1
30172621Sllai1 if (vap->va_mask & AT_SIZE) {
30182621Sllai1 if (vp->v_type == VDIR) {
30192621Sllai1 return (EISDIR);
30202621Sllai1 }
30212621Sllai1 }
30222621Sllai1
30232621Sllai1 /* no need to set attribute, but do not fail either */
30242621Sllai1 ASSERT(parent);
30252621Sllai1 rw_enter(&parent->sdev_contents, RW_READER);
30262621Sllai1 if (dv->sdev_state == SDEV_ZOMBIE) {
30272621Sllai1 rw_exit(&parent->sdev_contents);
30282621Sllai1 return (0);
30292621Sllai1 }
30302621Sllai1
30312621Sllai1 /* If backing store exists, just set it. */
30322621Sllai1 if (dv->sdev_attrvp) {
30332621Sllai1 rw_exit(&parent->sdev_contents);
30342621Sllai1 return (VOP_SETATTR(dv->sdev_attrvp, vap, flags, cred, NULL));
30352621Sllai1 }
30362621Sllai1
30372621Sllai1 /*
30382621Sllai1 * Otherwise, for nodes with the persistence attribute, create it.
30392621Sllai1 */
30402621Sllai1 ASSERT(dv->sdev_attr);
30412621Sllai1 if (SDEV_IS_PERSIST(dv) ||
30422621Sllai1 ((vap->va_mask & ~AT_TIMES) != 0 && !SDEV_IS_DYNAMIC(dv))) {
30432621Sllai1 sdev_vattr_merge(dv, vap);
30442621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER);
30452621Sllai1 error = sdev_shadow_node(dv, cred);
30462621Sllai1 rw_exit(&dv->sdev_contents);
30472621Sllai1 rw_exit(&parent->sdev_contents);
30482621Sllai1
30492621Sllai1 if (error)
30502621Sllai1 return (error);
30512621Sllai1 return (VOP_SETATTR(dv->sdev_attrvp, vap, flags, cred, NULL));
30522621Sllai1 }
30532621Sllai1
30542621Sllai1
30552621Sllai1 /*
30562621Sllai1 * sdev_attr was allocated in sdev_mknode
30572621Sllai1 */
30582621Sllai1 rw_enter(&dv->sdev_contents, RW_WRITER);
30595331Samw error = secpolicy_vnode_setattr(cred, vp, vap,
30605331Samw dv->sdev_attr, flags, sdev_unlocked_access, dv);
30612621Sllai1 if (error) {
30622621Sllai1 rw_exit(&dv->sdev_contents);
30632621Sllai1 rw_exit(&parent->sdev_contents);
30642621Sllai1 return (error);
30652621Sllai1 }
30662621Sllai1
30672621Sllai1 get = dv->sdev_attr;
30682621Sllai1 if (mask & AT_MODE) {
30692621Sllai1 get->va_mode &= S_IFMT;
30702621Sllai1 get->va_mode |= vap->va_mode & ~S_IFMT;
30712621Sllai1 }
30722621Sllai1
30732621Sllai1 if ((mask & AT_UID) || (mask & AT_GID)) {
30742621Sllai1 if (mask & AT_UID)
30752621Sllai1 get->va_uid = vap->va_uid;
30762621Sllai1 if (mask & AT_GID)
30772621Sllai1 get->va_gid = vap->va_gid;
30782621Sllai1 /*
30792621Sllai1 * a callback must be provided if the protocol is set
30802621Sllai1 */
30812621Sllai1 if ((protocol & AT_UID) || (protocol & AT_GID)) {
30822621Sllai1 ASSERT(callback);
30832621Sllai1 error = callback(dv, get, protocol);
30842621Sllai1 if (error) {
30852621Sllai1 rw_exit(&dv->sdev_contents);
30862621Sllai1 rw_exit(&parent->sdev_contents);
30872621Sllai1 return (error);
30882621Sllai1 }
30892621Sllai1 }
30902621Sllai1 }
30912621Sllai1
30922621Sllai1 if (mask & AT_ATIME)
30932621Sllai1 get->va_atime = vap->va_atime;
30942621Sllai1 if (mask & AT_MTIME)
30952621Sllai1 get->va_mtime = vap->va_mtime;
30962621Sllai1 if (mask & (AT_MODE | AT_UID | AT_GID | AT_CTIME)) {
30972621Sllai1 gethrestime(&get->va_ctime);
30982621Sllai1 }
30992621Sllai1
31002621Sllai1 sdev_vattr_merge(dv, get);
31012621Sllai1 rw_exit(&dv->sdev_contents);
31022621Sllai1 rw_exit(&parent->sdev_contents);
31032621Sllai1 return (0);
31042621Sllai1 }
31055895Syz147064
31065895Syz147064 /*
31075895Syz147064 * a generic inactive() function
31085895Syz147064 */
310910097SEric.Taylor@Sun.COM /*ARGSUSED*/
31105895Syz147064 void
devname_inactive_func(struct vnode * vp,struct cred * cred,void (* callback)(struct vnode *))31115895Syz147064 devname_inactive_func(struct vnode *vp, struct cred *cred,
31125895Syz147064 void (*callback)(struct vnode *))
31135895Syz147064 {
31145895Syz147064 int clean;
31155895Syz147064 struct sdev_node *dv = VTOSDEV(vp);
31165895Syz147064 struct sdev_node *ddv = dv->sdev_dotdot;
31175895Syz147064 int state;
31185895Syz147064
31195895Syz147064 rw_enter(&ddv->sdev_contents, RW_WRITER);
31205895Syz147064 state = dv->sdev_state;
31215895Syz147064
31225895Syz147064 mutex_enter(&vp->v_lock);
31235895Syz147064 ASSERT(vp->v_count >= 1);
31245895Syz147064
31255895Syz147064 if (vp->v_count == 1 && callback != NULL)
31265895Syz147064 callback(vp);
31275895Syz147064
31285895Syz147064 clean = (vp->v_count == 1) && (state == SDEV_ZOMBIE);
31295895Syz147064
31305895Syz147064 /*
31315895Syz147064 * last ref count on the ZOMBIE node is released.
31325895Syz147064 * clean up the sdev_node, and
31335895Syz147064 * release the hold on the backing store node so that
31345895Syz147064 * the ZOMBIE backing stores also cleaned out.
31355895Syz147064 */
31365895Syz147064 if (clean) {
31375895Syz147064 ASSERT(ddv);
31385895Syz147064
31395895Syz147064 ddv->sdev_nlink--;
31405895Syz147064 if (vp->v_type == VDIR) {
31415895Syz147064 dv->sdev_nlink--;
31425895Syz147064 }
31436347Sjg if ((dv->sdev_flags & SDEV_STALE) == 0)
31446347Sjg avl_remove(&ddv->sdev_entries, dv);
31455895Syz147064 dv->sdev_nlink--;
31465895Syz147064 --vp->v_count;
31475895Syz147064 mutex_exit(&vp->v_lock);
31485895Syz147064 sdev_nodedestroy(dv, 0);
31495895Syz147064 } else {
31505895Syz147064 --vp->v_count;
31515895Syz147064 mutex_exit(&vp->v_lock);
31525895Syz147064 }
31535895Syz147064 rw_exit(&ddv->sdev_contents);
31545895Syz147064 }
3155