1facf4a8dSllai1 /* 2facf4a8dSllai1 * CDDL HEADER START 3facf4a8dSllai1 * 4facf4a8dSllai1 * The contents of this file are subject to the terms of the 5facf4a8dSllai1 * Common Development and Distribution License (the "License"). 6facf4a8dSllai1 * You may not use this file except in compliance with the License. 7facf4a8dSllai1 * 8facf4a8dSllai1 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9facf4a8dSllai1 * or http://www.opensolaris.org/os/licensing. 10facf4a8dSllai1 * See the License for the specific language governing permissions 11facf4a8dSllai1 * and limitations under the License. 12facf4a8dSllai1 * 13facf4a8dSllai1 * When distributing Covered Code, include this CDDL HEADER in each 14facf4a8dSllai1 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15facf4a8dSllai1 * If applicable, add the following below this CDDL HEADER, with the 16facf4a8dSllai1 * fields enclosed by brackets "[]" replaced with your own identifying 17facf4a8dSllai1 * information: Portions Copyright [yyyy] [name of copyright owner] 18facf4a8dSllai1 * 19facf4a8dSllai1 * CDDL HEADER END 20facf4a8dSllai1 */ 21facf4a8dSllai1 /* 220fbb751dSJohn Levon * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 23*9e5aa9d8SRobert Mustacchi * Copyright (c) 2013, Joyent, Inc. All rights reserved. 24facf4a8dSllai1 */ 25facf4a8dSllai1 26facf4a8dSllai1 /* 27facf4a8dSllai1 * utility routines for the /dev fs 28facf4a8dSllai1 */ 29facf4a8dSllai1 30facf4a8dSllai1 #include <sys/types.h> 31facf4a8dSllai1 #include <sys/param.h> 32facf4a8dSllai1 #include <sys/t_lock.h> 33facf4a8dSllai1 #include <sys/systm.h> 34facf4a8dSllai1 #include <sys/sysmacros.h> 35facf4a8dSllai1 #include <sys/user.h> 36facf4a8dSllai1 #include <sys/time.h> 37facf4a8dSllai1 #include <sys/vfs.h> 38facf4a8dSllai1 #include <sys/vnode.h> 39facf4a8dSllai1 #include <sys/file.h> 40facf4a8dSllai1 #include <sys/fcntl.h> 41facf4a8dSllai1 #include <sys/flock.h> 42facf4a8dSllai1 #include <sys/kmem.h> 43facf4a8dSllai1 #include <sys/uio.h> 44facf4a8dSllai1 #include <sys/errno.h> 45facf4a8dSllai1 #include <sys/stat.h> 46facf4a8dSllai1 #include <sys/cred.h> 47facf4a8dSllai1 #include <sys/dirent.h> 48facf4a8dSllai1 #include <sys/pathname.h> 49facf4a8dSllai1 #include <sys/cmn_err.h> 50facf4a8dSllai1 #include <sys/debug.h> 51facf4a8dSllai1 #include <sys/mode.h> 52facf4a8dSllai1 #include <sys/policy.h> 53facf4a8dSllai1 #include <fs/fs_subr.h> 54facf4a8dSllai1 #include <sys/mount.h> 55facf4a8dSllai1 #include <sys/fs/snode.h> 56facf4a8dSllai1 #include <sys/fs/dv_node.h> 57facf4a8dSllai1 #include <sys/fs/sdev_impl.h> 58facf4a8dSllai1 #include <sys/sunndi.h> 59facf4a8dSllai1 #include <sys/sunmdi.h> 60facf4a8dSllai1 #include <sys/conf.h> 61facf4a8dSllai1 #include <sys/proc.h> 62facf4a8dSllai1 #include <sys/user.h> 63facf4a8dSllai1 #include <sys/modctl.h> 64facf4a8dSllai1 65facf4a8dSllai1 #ifdef DEBUG 66facf4a8dSllai1 int sdev_debug = 0x00000001; 67facf4a8dSllai1 int sdev_debug_cache_flags = 0; 68facf4a8dSllai1 #endif 69facf4a8dSllai1 70facf4a8dSllai1 /* 71facf4a8dSllai1 * globals 72facf4a8dSllai1 */ 73facf4a8dSllai1 /* prototype memory vattrs */ 74facf4a8dSllai1 vattr_t sdev_vattr_dir = { 75facf4a8dSllai1 AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */ 76facf4a8dSllai1 VDIR, /* va_type */ 77facf4a8dSllai1 SDEV_DIRMODE_DEFAULT, /* va_mode */ 78facf4a8dSllai1 SDEV_UID_DEFAULT, /* va_uid */ 79facf4a8dSllai1 SDEV_GID_DEFAULT, /* va_gid */ 80facf4a8dSllai1 0, /* va_fsid */ 81facf4a8dSllai1 0, /* va_nodeid */ 82facf4a8dSllai1 0, /* va_nlink */ 83facf4a8dSllai1 0, /* va_size */ 84facf4a8dSllai1 0, /* va_atime */ 85facf4a8dSllai1 0, /* va_mtime */ 86facf4a8dSllai1 0, /* va_ctime */ 87facf4a8dSllai1 0, /* va_rdev */ 88facf4a8dSllai1 0, /* va_blksize */ 89facf4a8dSllai1 0, /* va_nblocks */ 90facf4a8dSllai1 0 /* va_vcode */ 91facf4a8dSllai1 }; 92facf4a8dSllai1 93facf4a8dSllai1 vattr_t sdev_vattr_lnk = { 94facf4a8dSllai1 AT_TYPE|AT_MODE, /* va_mask */ 95facf4a8dSllai1 VLNK, /* va_type */ 96facf4a8dSllai1 SDEV_LNKMODE_DEFAULT, /* va_mode */ 97facf4a8dSllai1 SDEV_UID_DEFAULT, /* va_uid */ 98facf4a8dSllai1 SDEV_GID_DEFAULT, /* va_gid */ 99facf4a8dSllai1 0, /* va_fsid */ 100facf4a8dSllai1 0, /* va_nodeid */ 101facf4a8dSllai1 0, /* va_nlink */ 102facf4a8dSllai1 0, /* va_size */ 103facf4a8dSllai1 0, /* va_atime */ 104facf4a8dSllai1 0, /* va_mtime */ 105facf4a8dSllai1 0, /* va_ctime */ 106facf4a8dSllai1 0, /* va_rdev */ 107facf4a8dSllai1 0, /* va_blksize */ 108facf4a8dSllai1 0, /* va_nblocks */ 109facf4a8dSllai1 0 /* va_vcode */ 110facf4a8dSllai1 }; 111facf4a8dSllai1 112facf4a8dSllai1 vattr_t sdev_vattr_blk = { 113facf4a8dSllai1 AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */ 114facf4a8dSllai1 VBLK, /* va_type */ 115facf4a8dSllai1 S_IFBLK | SDEV_DEVMODE_DEFAULT, /* va_mode */ 116facf4a8dSllai1 SDEV_UID_DEFAULT, /* va_uid */ 117facf4a8dSllai1 SDEV_GID_DEFAULT, /* va_gid */ 118facf4a8dSllai1 0, /* va_fsid */ 119facf4a8dSllai1 0, /* va_nodeid */ 120facf4a8dSllai1 0, /* va_nlink */ 121facf4a8dSllai1 0, /* va_size */ 122facf4a8dSllai1 0, /* va_atime */ 123facf4a8dSllai1 0, /* va_mtime */ 124facf4a8dSllai1 0, /* va_ctime */ 125facf4a8dSllai1 0, /* va_rdev */ 126facf4a8dSllai1 0, /* va_blksize */ 127facf4a8dSllai1 0, /* va_nblocks */ 128facf4a8dSllai1 0 /* va_vcode */ 129facf4a8dSllai1 }; 130facf4a8dSllai1 131facf4a8dSllai1 vattr_t sdev_vattr_chr = { 132facf4a8dSllai1 AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */ 133facf4a8dSllai1 VCHR, /* va_type */ 134facf4a8dSllai1 S_IFCHR | SDEV_DEVMODE_DEFAULT, /* va_mode */ 135facf4a8dSllai1 SDEV_UID_DEFAULT, /* va_uid */ 136facf4a8dSllai1 SDEV_GID_DEFAULT, /* va_gid */ 137facf4a8dSllai1 0, /* va_fsid */ 138facf4a8dSllai1 0, /* va_nodeid */ 139facf4a8dSllai1 0, /* va_nlink */ 140facf4a8dSllai1 0, /* va_size */ 141facf4a8dSllai1 0, /* va_atime */ 142facf4a8dSllai1 0, /* va_mtime */ 143facf4a8dSllai1 0, /* va_ctime */ 144facf4a8dSllai1 0, /* va_rdev */ 145facf4a8dSllai1 0, /* va_blksize */ 146facf4a8dSllai1 0, /* va_nblocks */ 147facf4a8dSllai1 0 /* va_vcode */ 148facf4a8dSllai1 }; 149facf4a8dSllai1 150facf4a8dSllai1 kmem_cache_t *sdev_node_cache; /* sdev_node cache */ 151facf4a8dSllai1 int devtype; /* fstype */ 152facf4a8dSllai1 153facf4a8dSllai1 /* static */ 154facf4a8dSllai1 static struct vnodeops *sdev_get_vop(struct sdev_node *); 155681d9761SEric Taylor static void sdev_set_no_negcache(struct sdev_node *); 156facf4a8dSllai1 static fs_operation_def_t *sdev_merge_vtab(const fs_operation_def_t []); 157facf4a8dSllai1 static void sdev_free_vtab(fs_operation_def_t *); 158facf4a8dSllai1 159facf4a8dSllai1 static void 160facf4a8dSllai1 sdev_prof_free(struct sdev_node *dv) 161facf4a8dSllai1 { 162facf4a8dSllai1 ASSERT(!SDEV_IS_GLOBAL(dv)); 163facf4a8dSllai1 nvlist_free(dv->sdev_prof.dev_name); 164facf4a8dSllai1 nvlist_free(dv->sdev_prof.dev_map); 165facf4a8dSllai1 nvlist_free(dv->sdev_prof.dev_symlink); 166facf4a8dSllai1 nvlist_free(dv->sdev_prof.dev_glob_incdir); 167facf4a8dSllai1 nvlist_free(dv->sdev_prof.dev_glob_excdir); 168facf4a8dSllai1 bzero(&dv->sdev_prof, sizeof (dv->sdev_prof)); 169facf4a8dSllai1 } 170facf4a8dSllai1 171b5fca8f8Stomee /* sdev_node cache constructor */ 172facf4a8dSllai1 /*ARGSUSED1*/ 173facf4a8dSllai1 static int 174facf4a8dSllai1 i_sdev_node_ctor(void *buf, void *cfarg, int flag) 175facf4a8dSllai1 { 176facf4a8dSllai1 struct sdev_node *dv = (struct sdev_node *)buf; 177facf4a8dSllai1 struct vnode *vp; 178facf4a8dSllai1 179facf4a8dSllai1 bzero(buf, sizeof (struct sdev_node)); 180b5fca8f8Stomee vp = dv->sdev_vnode = vn_alloc(flag); 181b5fca8f8Stomee if (vp == NULL) { 182b5fca8f8Stomee return (-1); 183b5fca8f8Stomee } 184b5fca8f8Stomee vp->v_data = dv; 185facf4a8dSllai1 rw_init(&dv->sdev_contents, NULL, RW_DEFAULT, NULL); 186facf4a8dSllai1 return (0); 187facf4a8dSllai1 } 188facf4a8dSllai1 189b5fca8f8Stomee /* sdev_node cache destructor */ 190facf4a8dSllai1 /*ARGSUSED1*/ 191facf4a8dSllai1 static void 192facf4a8dSllai1 i_sdev_node_dtor(void *buf, void *arg) 193facf4a8dSllai1 { 194facf4a8dSllai1 struct sdev_node *dv = (struct sdev_node *)buf; 195facf4a8dSllai1 struct vnode *vp = SDEVTOV(dv); 196facf4a8dSllai1 197facf4a8dSllai1 rw_destroy(&dv->sdev_contents); 198facf4a8dSllai1 vn_free(vp); 199facf4a8dSllai1 } 200facf4a8dSllai1 201facf4a8dSllai1 /* initialize sdev_node cache */ 202facf4a8dSllai1 void 203facf4a8dSllai1 sdev_node_cache_init() 204facf4a8dSllai1 { 205facf4a8dSllai1 int flags = 0; 206facf4a8dSllai1 207facf4a8dSllai1 #ifdef DEBUG 208facf4a8dSllai1 flags = sdev_debug_cache_flags; 209facf4a8dSllai1 if (flags) 210facf4a8dSllai1 sdcmn_err(("cache debug flags 0x%x\n", flags)); 211facf4a8dSllai1 #endif /* DEBUG */ 212facf4a8dSllai1 213facf4a8dSllai1 ASSERT(sdev_node_cache == NULL); 214facf4a8dSllai1 sdev_node_cache = kmem_cache_create("sdev_node_cache", 215facf4a8dSllai1 sizeof (struct sdev_node), 0, i_sdev_node_ctor, i_sdev_node_dtor, 216facf4a8dSllai1 NULL, NULL, NULL, flags); 217facf4a8dSllai1 } 218facf4a8dSllai1 219facf4a8dSllai1 /* destroy sdev_node cache */ 220facf4a8dSllai1 void 221facf4a8dSllai1 sdev_node_cache_fini() 222facf4a8dSllai1 { 223facf4a8dSllai1 ASSERT(sdev_node_cache != NULL); 224facf4a8dSllai1 kmem_cache_destroy(sdev_node_cache); 225facf4a8dSllai1 sdev_node_cache = NULL; 226facf4a8dSllai1 } 227facf4a8dSllai1 228aac43a5fSjg /* 229aac43a5fSjg * Compare two nodes lexographically to balance avl tree 230aac43a5fSjg */ 231aac43a5fSjg static int 232aac43a5fSjg sdev_compare_nodes(const struct sdev_node *dv1, const struct sdev_node *dv2) 233aac43a5fSjg { 234aac43a5fSjg int rv; 235aac43a5fSjg if ((rv = strcmp(dv1->sdev_name, dv2->sdev_name)) == 0) 236aac43a5fSjg return (0); 237aac43a5fSjg return ((rv < 0) ? -1 : 1); 238aac43a5fSjg } 239aac43a5fSjg 240facf4a8dSllai1 void 241facf4a8dSllai1 sdev_set_nodestate(struct sdev_node *dv, sdev_node_state_t state) 242facf4a8dSllai1 { 243facf4a8dSllai1 ASSERT(dv); 244facf4a8dSllai1 ASSERT(RW_WRITE_HELD(&dv->sdev_contents)); 245facf4a8dSllai1 dv->sdev_state = state; 246facf4a8dSllai1 } 247facf4a8dSllai1 248facf4a8dSllai1 static void 249bb5fffbeSJerry Gilliam sdev_attr_update(struct sdev_node *dv, vattr_t *vap) 250facf4a8dSllai1 { 251facf4a8dSllai1 timestruc_t now; 252bb5fffbeSJerry Gilliam struct vattr *attrp; 253bb5fffbeSJerry Gilliam uint_t mask; 254facf4a8dSllai1 255bb5fffbeSJerry Gilliam ASSERT(dv->sdev_attr); 256facf4a8dSllai1 ASSERT(vap); 257facf4a8dSllai1 258bb5fffbeSJerry Gilliam attrp = dv->sdev_attr; 259bb5fffbeSJerry Gilliam mask = vap->va_mask; 260bb5fffbeSJerry Gilliam if (mask & AT_TYPE) 261bb5fffbeSJerry Gilliam attrp->va_type = vap->va_type; 262bb5fffbeSJerry Gilliam if (mask & AT_MODE) 263bb5fffbeSJerry Gilliam attrp->va_mode = vap->va_mode; 264bb5fffbeSJerry Gilliam if (mask & AT_UID) 265bb5fffbeSJerry Gilliam attrp->va_uid = vap->va_uid; 266bb5fffbeSJerry Gilliam if (mask & AT_GID) 267bb5fffbeSJerry Gilliam attrp->va_gid = vap->va_gid; 268bb5fffbeSJerry Gilliam if (mask & AT_RDEV) 269bb5fffbeSJerry Gilliam attrp->va_rdev = vap->va_rdev; 270facf4a8dSllai1 271facf4a8dSllai1 gethrestime(&now); 272bb5fffbeSJerry Gilliam attrp->va_atime = (mask & AT_ATIME) ? vap->va_atime : now; 273bb5fffbeSJerry Gilliam attrp->va_mtime = (mask & AT_MTIME) ? vap->va_mtime : now; 274bb5fffbeSJerry Gilliam attrp->va_ctime = (mask & AT_CTIME) ? vap->va_ctime : now; 275bb5fffbeSJerry Gilliam } 276bb5fffbeSJerry Gilliam 277bb5fffbeSJerry Gilliam static void 278bb5fffbeSJerry Gilliam sdev_attr_alloc(struct sdev_node *dv, vattr_t *vap) 279bb5fffbeSJerry Gilliam { 280bb5fffbeSJerry Gilliam ASSERT(dv->sdev_attr == NULL); 281bb5fffbeSJerry Gilliam ASSERT(vap->va_mask & AT_TYPE); 282bb5fffbeSJerry Gilliam ASSERT(vap->va_mask & AT_MODE); 283bb5fffbeSJerry Gilliam 284bb5fffbeSJerry Gilliam dv->sdev_attr = kmem_zalloc(sizeof (struct vattr), KM_SLEEP); 285bb5fffbeSJerry Gilliam sdev_attr_update(dv, vap); 286facf4a8dSllai1 } 287facf4a8dSllai1 288facf4a8dSllai1 /* alloc and initialize a sdev_node */ 289facf4a8dSllai1 int 290facf4a8dSllai1 sdev_nodeinit(struct sdev_node *ddv, char *nm, struct sdev_node **newdv, 291facf4a8dSllai1 vattr_t *vap) 292facf4a8dSllai1 { 293facf4a8dSllai1 struct sdev_node *dv = NULL; 294facf4a8dSllai1 struct vnode *vp; 295facf4a8dSllai1 size_t nmlen, len; 296facf4a8dSllai1 devname_handle_t *dhl; 297facf4a8dSllai1 298facf4a8dSllai1 nmlen = strlen(nm) + 1; 299facf4a8dSllai1 if (nmlen > MAXNAMELEN) { 300facf4a8dSllai1 sdcmn_err9(("sdev_nodeinit: node name %s" 301facf4a8dSllai1 " too long\n", nm)); 302facf4a8dSllai1 *newdv = NULL; 303facf4a8dSllai1 return (ENAMETOOLONG); 304facf4a8dSllai1 } 305facf4a8dSllai1 306facf4a8dSllai1 dv = kmem_cache_alloc(sdev_node_cache, KM_SLEEP); 307facf4a8dSllai1 308facf4a8dSllai1 dv->sdev_name = kmem_alloc(nmlen, KM_SLEEP); 309facf4a8dSllai1 bcopy(nm, dv->sdev_name, nmlen); 310facf4a8dSllai1 dv->sdev_namelen = nmlen - 1; /* '\0' not included */ 311facf4a8dSllai1 len = strlen(ddv->sdev_path) + strlen(nm) + 2; 312facf4a8dSllai1 dv->sdev_path = kmem_alloc(len, KM_SLEEP); 313facf4a8dSllai1 (void) snprintf(dv->sdev_path, len, "%s/%s", ddv->sdev_path, nm); 314facf4a8dSllai1 /* overwritten for VLNK nodes */ 315facf4a8dSllai1 dv->sdev_symlink = NULL; 316facf4a8dSllai1 317facf4a8dSllai1 vp = SDEVTOV(dv); 318facf4a8dSllai1 vn_reinit(vp); 319facf4a8dSllai1 vp->v_vfsp = SDEVTOV(ddv)->v_vfsp; 320facf4a8dSllai1 if (vap) 321facf4a8dSllai1 vp->v_type = vap->va_type; 322facf4a8dSllai1 323facf4a8dSllai1 /* 324facf4a8dSllai1 * initialized to the parent's vnodeops. 325facf4a8dSllai1 * maybe overwriten for a VDIR 326facf4a8dSllai1 */ 327facf4a8dSllai1 vn_setops(vp, vn_getops(SDEVTOV(ddv))); 328facf4a8dSllai1 vn_exists(vp); 329facf4a8dSllai1 330facf4a8dSllai1 dv->sdev_dotdot = NULL; 331facf4a8dSllai1 dv->sdev_attrvp = NULL; 332facf4a8dSllai1 if (vap) { 333bb5fffbeSJerry Gilliam sdev_attr_alloc(dv, vap); 334facf4a8dSllai1 } else { 335facf4a8dSllai1 dv->sdev_attr = NULL; 336facf4a8dSllai1 } 337facf4a8dSllai1 338facf4a8dSllai1 dv->sdev_ino = sdev_mkino(dv); 339facf4a8dSllai1 dv->sdev_nlink = 0; /* updated on insert */ 340facf4a8dSllai1 dv->sdev_flags = ddv->sdev_flags; /* inherit from the parent first */ 341facf4a8dSllai1 dv->sdev_flags |= SDEV_BUILD; 342facf4a8dSllai1 mutex_init(&dv->sdev_lookup_lock, NULL, MUTEX_DEFAULT, NULL); 343facf4a8dSllai1 cv_init(&dv->sdev_lookup_cv, NULL, CV_DEFAULT, NULL); 344facf4a8dSllai1 if (SDEV_IS_GLOBAL(ddv)) { 345facf4a8dSllai1 dv->sdev_flags |= SDEV_GLOBAL; 346facf4a8dSllai1 dhl = &(dv->sdev_handle); 347facf4a8dSllai1 dhl->dh_data = dv; 348facf4a8dSllai1 dhl->dh_args = NULL; 349681d9761SEric Taylor sdev_set_no_negcache(dv); 350facf4a8dSllai1 dv->sdev_gdir_gen = 0; 351facf4a8dSllai1 } else { 352facf4a8dSllai1 dv->sdev_flags &= ~SDEV_GLOBAL; 353facf4a8dSllai1 dv->sdev_origin = NULL; /* set later */ 354facf4a8dSllai1 bzero(&dv->sdev_prof, sizeof (dv->sdev_prof)); 355facf4a8dSllai1 dv->sdev_ldir_gen = 0; 356facf4a8dSllai1 dv->sdev_devtree_gen = 0; 357facf4a8dSllai1 } 358facf4a8dSllai1 359facf4a8dSllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 360facf4a8dSllai1 sdev_set_nodestate(dv, SDEV_INIT); 361facf4a8dSllai1 rw_exit(&dv->sdev_contents); 362facf4a8dSllai1 *newdv = dv; 363facf4a8dSllai1 364facf4a8dSllai1 return (0); 365facf4a8dSllai1 } 366facf4a8dSllai1 367facf4a8dSllai1 /* 368*9e5aa9d8SRobert Mustacchi * Transition a sdev_node into SDEV_READY state. If this fails, it is up to the 369*9e5aa9d8SRobert Mustacchi * caller to transition the node to the SDEV_ZOMBIE state. 370facf4a8dSllai1 */ 371facf4a8dSllai1 int 372facf4a8dSllai1 sdev_nodeready(struct sdev_node *dv, struct vattr *vap, struct vnode *avp, 373facf4a8dSllai1 void *args, struct cred *cred) 374facf4a8dSllai1 { 375facf4a8dSllai1 int error = 0; 376facf4a8dSllai1 struct vnode *vp = SDEVTOV(dv); 377facf4a8dSllai1 vtype_t type; 378facf4a8dSllai1 379facf4a8dSllai1 ASSERT(dv && (dv->sdev_state != SDEV_READY) && vap); 380facf4a8dSllai1 381facf4a8dSllai1 type = vap->va_type; 382facf4a8dSllai1 vp->v_type = type; 383facf4a8dSllai1 vp->v_rdev = vap->va_rdev; 384facf4a8dSllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 385facf4a8dSllai1 if (type == VDIR) { 386facf4a8dSllai1 dv->sdev_nlink = 2; 387facf4a8dSllai1 dv->sdev_flags &= ~SDEV_PERSIST; 388facf4a8dSllai1 dv->sdev_flags &= ~SDEV_DYNAMIC; 389facf4a8dSllai1 vn_setops(vp, sdev_get_vop(dv)); /* from internal vtab */ 390facf4a8dSllai1 ASSERT(dv->sdev_dotdot); 391facf4a8dSllai1 ASSERT(SDEVTOV(dv->sdev_dotdot)->v_type == VDIR); 392facf4a8dSllai1 vp->v_rdev = SDEVTOV(dv->sdev_dotdot)->v_rdev; 393aac43a5fSjg avl_create(&dv->sdev_entries, 394aac43a5fSjg (int (*)(const void *, const void *))sdev_compare_nodes, 395aac43a5fSjg sizeof (struct sdev_node), 396aac43a5fSjg offsetof(struct sdev_node, sdev_avllink)); 397facf4a8dSllai1 } else if (type == VLNK) { 398facf4a8dSllai1 ASSERT(args); 399facf4a8dSllai1 dv->sdev_nlink = 1; 400facf4a8dSllai1 dv->sdev_symlink = i_ddi_strdup((char *)args, KM_SLEEP); 401facf4a8dSllai1 } else { 402facf4a8dSllai1 dv->sdev_nlink = 1; 403facf4a8dSllai1 } 404facf4a8dSllai1 405facf4a8dSllai1 if (!(SDEV_IS_GLOBAL(dv))) { 406facf4a8dSllai1 dv->sdev_origin = (struct sdev_node *)args; 407facf4a8dSllai1 dv->sdev_flags &= ~SDEV_PERSIST; 408facf4a8dSllai1 } 409facf4a8dSllai1 410facf4a8dSllai1 /* 411facf4a8dSllai1 * shadow node is created here OR 412facf4a8dSllai1 * if failed (indicated by dv->sdev_attrvp == NULL), 413facf4a8dSllai1 * created later in sdev_setattr 414facf4a8dSllai1 */ 415facf4a8dSllai1 if (avp) { 416facf4a8dSllai1 dv->sdev_attrvp = avp; 417facf4a8dSllai1 } else { 418bb5fffbeSJerry Gilliam if (dv->sdev_attr == NULL) { 419bb5fffbeSJerry Gilliam sdev_attr_alloc(dv, vap); 420bb5fffbeSJerry Gilliam } else { 421bb5fffbeSJerry Gilliam sdev_attr_update(dv, vap); 422bb5fffbeSJerry Gilliam } 423facf4a8dSllai1 424681d9761SEric Taylor if ((dv->sdev_attrvp == NULL) && SDEV_IS_PERSIST(dv)) 425facf4a8dSllai1 error = sdev_shadow_node(dv, cred); 426facf4a8dSllai1 } 427facf4a8dSllai1 428b7beec95Sjg if (error == 0) { 429facf4a8dSllai1 /* transition to READY state */ 430facf4a8dSllai1 sdev_set_nodestate(dv, SDEV_READY); 431facf4a8dSllai1 sdev_nc_node_exists(dv); 432b7beec95Sjg } 433facf4a8dSllai1 rw_exit(&dv->sdev_contents); 434facf4a8dSllai1 return (error); 435facf4a8dSllai1 } 436facf4a8dSllai1 437facf4a8dSllai1 /* 438facf4a8dSllai1 * Build the VROOT sdev_node. 439facf4a8dSllai1 */ 440facf4a8dSllai1 /*ARGSUSED*/ 441facf4a8dSllai1 struct sdev_node * 442facf4a8dSllai1 sdev_mkroot(struct vfs *vfsp, dev_t devdev, struct vnode *mvp, 443facf4a8dSllai1 struct vnode *avp, struct cred *cred) 444facf4a8dSllai1 { 445facf4a8dSllai1 struct sdev_node *dv; 446facf4a8dSllai1 struct vnode *vp; 447facf4a8dSllai1 char devdir[] = "/dev"; 448facf4a8dSllai1 449facf4a8dSllai1 ASSERT(sdev_node_cache != NULL); 450facf4a8dSllai1 ASSERT(avp); 451facf4a8dSllai1 dv = kmem_cache_alloc(sdev_node_cache, KM_SLEEP); 452facf4a8dSllai1 vp = SDEVTOV(dv); 453facf4a8dSllai1 vn_reinit(vp); 454facf4a8dSllai1 vp->v_flag |= VROOT; 455facf4a8dSllai1 vp->v_vfsp = vfsp; 456facf4a8dSllai1 vp->v_type = VDIR; 457facf4a8dSllai1 vp->v_rdev = devdev; 458facf4a8dSllai1 vn_setops(vp, sdev_vnodeops); /* apply the default vnodeops at /dev */ 459facf4a8dSllai1 vn_exists(vp); 460facf4a8dSllai1 461facf4a8dSllai1 if (vfsp->vfs_mntpt) 462facf4a8dSllai1 dv->sdev_name = i_ddi_strdup( 463facf4a8dSllai1 (char *)refstr_value(vfsp->vfs_mntpt), KM_SLEEP); 464facf4a8dSllai1 else 465facf4a8dSllai1 /* vfs_mountdev1 set mount point later */ 466facf4a8dSllai1 dv->sdev_name = i_ddi_strdup("/dev", KM_SLEEP); 467facf4a8dSllai1 dv->sdev_namelen = strlen(dv->sdev_name); /* '\0' not included */ 468facf4a8dSllai1 dv->sdev_path = i_ddi_strdup(devdir, KM_SLEEP); 469facf4a8dSllai1 dv->sdev_ino = SDEV_ROOTINO; 470facf4a8dSllai1 dv->sdev_nlink = 2; /* name + . (no sdev_insert) */ 471facf4a8dSllai1 dv->sdev_dotdot = dv; /* .. == self */ 472facf4a8dSllai1 dv->sdev_attrvp = avp; 473facf4a8dSllai1 dv->sdev_attr = NULL; 474facf4a8dSllai1 mutex_init(&dv->sdev_lookup_lock, NULL, MUTEX_DEFAULT, NULL); 475facf4a8dSllai1 cv_init(&dv->sdev_lookup_cv, NULL, CV_DEFAULT, NULL); 476facf4a8dSllai1 if (strcmp(dv->sdev_name, "/dev") == 0) { 477facf4a8dSllai1 dv->sdev_flags = SDEV_BUILD|SDEV_GLOBAL|SDEV_PERSIST; 478facf4a8dSllai1 bzero(&dv->sdev_handle, sizeof (dv->sdev_handle)); 479facf4a8dSllai1 dv->sdev_gdir_gen = 0; 480facf4a8dSllai1 } else { 481facf4a8dSllai1 dv->sdev_flags = SDEV_BUILD; 482facf4a8dSllai1 dv->sdev_flags &= ~SDEV_PERSIST; 483facf4a8dSllai1 bzero(&dv->sdev_prof, sizeof (dv->sdev_prof)); 484facf4a8dSllai1 dv->sdev_ldir_gen = 0; 485facf4a8dSllai1 dv->sdev_devtree_gen = 0; 486facf4a8dSllai1 } 487facf4a8dSllai1 488aac43a5fSjg avl_create(&dv->sdev_entries, 489aac43a5fSjg (int (*)(const void *, const void *))sdev_compare_nodes, 490aac43a5fSjg sizeof (struct sdev_node), 491aac43a5fSjg offsetof(struct sdev_node, sdev_avllink)); 492aac43a5fSjg 493facf4a8dSllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 494facf4a8dSllai1 sdev_set_nodestate(dv, SDEV_READY); 495facf4a8dSllai1 rw_exit(&dv->sdev_contents); 496facf4a8dSllai1 sdev_nc_node_exists(dv); 497facf4a8dSllai1 return (dv); 498facf4a8dSllai1 } 499facf4a8dSllai1 500facf4a8dSllai1 /* directory dependent vop table */ 501facf4a8dSllai1 struct sdev_vop_table { 502facf4a8dSllai1 char *vt_name; /* subdirectory name */ 503facf4a8dSllai1 const fs_operation_def_t *vt_service; /* vnodeops table */ 504facf4a8dSllai1 struct vnodeops *vt_vops; /* constructed vop */ 505facf4a8dSllai1 struct vnodeops **vt_global_vops; /* global container for vop */ 506facf4a8dSllai1 int (*vt_vtor)(struct sdev_node *); /* validate sdev_node */ 507facf4a8dSllai1 int vt_flags; 508facf4a8dSllai1 }; 509facf4a8dSllai1 510facf4a8dSllai1 /* 511facf4a8dSllai1 * A nice improvement would be to provide a plug-in mechanism 512facf4a8dSllai1 * for this table instead of a const table. 513facf4a8dSllai1 */ 514facf4a8dSllai1 static struct sdev_vop_table vtab[] = 515facf4a8dSllai1 { 516facf4a8dSllai1 { "pts", devpts_vnodeops_tbl, NULL, &devpts_vnodeops, devpts_validate, 517facf4a8dSllai1 SDEV_DYNAMIC | SDEV_VTOR }, 518facf4a8dSllai1 519aecfc01dSrui zang - Sun Microsystems - Beijing China { "vt", devvt_vnodeops_tbl, NULL, &devvt_vnodeops, devvt_validate, 520aecfc01dSrui zang - Sun Microsystems - Beijing China SDEV_DYNAMIC | SDEV_VTOR }, 521aecfc01dSrui zang - Sun Microsystems - Beijing China 522681d9761SEric Taylor { "zvol", devzvol_vnodeops_tbl, NULL, &devzvol_vnodeops, 523dd9c3b29SJerry Jelinek devzvol_validate, SDEV_ZONED | SDEV_DYNAMIC | SDEV_VTOR | SDEV_SUBDIR }, 524681d9761SEric Taylor 525facf4a8dSllai1 { "zcons", NULL, NULL, NULL, NULL, SDEV_NO_NCACHE }, 526facf4a8dSllai1 527d62bc4baSyz147064 { "net", devnet_vnodeops_tbl, NULL, &devnet_vnodeops, devnet_validate, 528d62bc4baSyz147064 SDEV_DYNAMIC | SDEV_VTOR }, 529d62bc4baSyz147064 530b127ac41SPhilip Kirk { "ipnet", devipnet_vnodeops_tbl, NULL, &devipnet_vnodeops, 531b127ac41SPhilip Kirk devipnet_validate, SDEV_DYNAMIC | SDEV_VTOR | SDEV_NO_NCACHE }, 532b127ac41SPhilip Kirk 53367323fc4SJohn Levon /* 53467323fc4SJohn Levon * SDEV_DYNAMIC: prevent calling out to devfsadm, since only the 53567323fc4SJohn Levon * lofi driver controls child nodes. 53667323fc4SJohn Levon * 53767323fc4SJohn Levon * SDEV_PERSIST: ensure devfsadm knows to clean up any persisted 53867323fc4SJohn Levon * stale nodes (e.g. from devfsadm -R). 53967323fc4SJohn Levon * 54067323fc4SJohn Levon * In addition, devfsadm knows not to attempt a rmdir: a zone 54167323fc4SJohn Levon * may hold a reference, which would zombify the node, 54267323fc4SJohn Levon * preventing a mkdir. 54367323fc4SJohn Levon */ 54467323fc4SJohn Levon 54567323fc4SJohn Levon { "lofi", NULL, NULL, NULL, NULL, 54667323fc4SJohn Levon SDEV_ZONED | SDEV_DYNAMIC | SDEV_PERSIST }, 54767323fc4SJohn Levon { "rlofi", NULL, NULL, NULL, NULL, 54867323fc4SJohn Levon SDEV_ZONED | SDEV_DYNAMIC | SDEV_PERSIST }, 5490fbb751dSJohn Levon 550facf4a8dSllai1 { NULL, NULL, NULL, NULL, NULL, 0} 551facf4a8dSllai1 }; 552facf4a8dSllai1 553*9e5aa9d8SRobert Mustacchi /* 554*9e5aa9d8SRobert Mustacchi * We need to match off of the sdev_path, not the sdev_name. We are only allowed 555*9e5aa9d8SRobert Mustacchi * to exist directly under /dev. 556*9e5aa9d8SRobert Mustacchi */ 557681d9761SEric Taylor struct sdev_vop_table * 558681d9761SEric Taylor sdev_match(struct sdev_node *dv) 559681d9761SEric Taylor { 560681d9761SEric Taylor int vlen; 561681d9761SEric Taylor int i; 562*9e5aa9d8SRobert Mustacchi const char *path; 563*9e5aa9d8SRobert Mustacchi 564*9e5aa9d8SRobert Mustacchi if (strlen(dv->sdev_path) <= 5) 565*9e5aa9d8SRobert Mustacchi return (NULL); 566*9e5aa9d8SRobert Mustacchi 567*9e5aa9d8SRobert Mustacchi if (strncmp(dv->sdev_path, "/dev/", 5) != 0) 568*9e5aa9d8SRobert Mustacchi return (NULL); 569*9e5aa9d8SRobert Mustacchi path = dv->sdev_path + 5; 570681d9761SEric Taylor 571681d9761SEric Taylor for (i = 0; vtab[i].vt_name; i++) { 572*9e5aa9d8SRobert Mustacchi if (strcmp(vtab[i].vt_name, path) == 0) 573681d9761SEric Taylor return (&vtab[i]); 574681d9761SEric Taylor if (vtab[i].vt_flags & SDEV_SUBDIR) { 575681d9761SEric Taylor vlen = strlen(vtab[i].vt_name); 576*9e5aa9d8SRobert Mustacchi if ((strncmp(vtab[i].vt_name, path, 577*9e5aa9d8SRobert Mustacchi vlen - 1) == 0) && path[vlen] == '/') 578681d9761SEric Taylor return (&vtab[i]); 579681d9761SEric Taylor } 580681d9761SEric Taylor 581681d9761SEric Taylor } 582681d9761SEric Taylor return (NULL); 583681d9761SEric Taylor } 584facf4a8dSllai1 585facf4a8dSllai1 /* 586facf4a8dSllai1 * sets a directory's vnodeops if the directory is in the vtab; 587facf4a8dSllai1 */ 588facf4a8dSllai1 static struct vnodeops * 589facf4a8dSllai1 sdev_get_vop(struct sdev_node *dv) 590facf4a8dSllai1 { 591681d9761SEric Taylor struct sdev_vop_table *vtp; 592facf4a8dSllai1 char *path; 593facf4a8dSllai1 594facf4a8dSllai1 path = dv->sdev_path; 595facf4a8dSllai1 ASSERT(path); 596facf4a8dSllai1 597facf4a8dSllai1 /* gets the relative path to /dev/ */ 598facf4a8dSllai1 path += 5; 599facf4a8dSllai1 600681d9761SEric Taylor /* gets the vtab entry it matches */ 601681d9761SEric Taylor if ((vtp = sdev_match(dv)) != NULL) { 602681d9761SEric Taylor dv->sdev_flags |= vtp->vt_flags; 603*9e5aa9d8SRobert Mustacchi if (SDEV_IS_PERSIST(dv->sdev_dotdot) && 604*9e5aa9d8SRobert Mustacchi (SDEV_IS_PERSIST(dv) || !SDEV_IS_DYNAMIC(dv))) 605*9e5aa9d8SRobert Mustacchi dv->sdev_flags |= SDEV_PERSIST; 606facf4a8dSllai1 607681d9761SEric Taylor if (vtp->vt_vops) { 608681d9761SEric Taylor if (vtp->vt_global_vops) 609681d9761SEric Taylor *(vtp->vt_global_vops) = vtp->vt_vops; 610*9e5aa9d8SRobert Mustacchi 611681d9761SEric Taylor return (vtp->vt_vops); 612facf4a8dSllai1 } 613facf4a8dSllai1 614681d9761SEric Taylor if (vtp->vt_service) { 615facf4a8dSllai1 fs_operation_def_t *templ; 616681d9761SEric Taylor templ = sdev_merge_vtab(vtp->vt_service); 617681d9761SEric Taylor if (vn_make_ops(vtp->vt_name, 618facf4a8dSllai1 (const fs_operation_def_t *)templ, 619681d9761SEric Taylor &vtp->vt_vops) != 0) { 620facf4a8dSllai1 cmn_err(CE_PANIC, "%s: malformed vnode ops\n", 621681d9761SEric Taylor vtp->vt_name); 622facf4a8dSllai1 /*NOTREACHED*/ 623facf4a8dSllai1 } 624681d9761SEric Taylor if (vtp->vt_global_vops) { 625681d9761SEric Taylor *(vtp->vt_global_vops) = vtp->vt_vops; 626facf4a8dSllai1 } 627facf4a8dSllai1 sdev_free_vtab(templ); 628*9e5aa9d8SRobert Mustacchi 629681d9761SEric Taylor return (vtp->vt_vops); 630facf4a8dSllai1 } 631*9e5aa9d8SRobert Mustacchi 632facf4a8dSllai1 return (sdev_vnodeops); 633facf4a8dSllai1 } 634facf4a8dSllai1 635facf4a8dSllai1 /* child inherits the persistence of the parent */ 636facf4a8dSllai1 if (SDEV_IS_PERSIST(dv->sdev_dotdot)) 637facf4a8dSllai1 dv->sdev_flags |= SDEV_PERSIST; 638facf4a8dSllai1 639facf4a8dSllai1 return (sdev_vnodeops); 640facf4a8dSllai1 } 641facf4a8dSllai1 642facf4a8dSllai1 static void 643681d9761SEric Taylor sdev_set_no_negcache(struct sdev_node *dv) 644facf4a8dSllai1 { 645facf4a8dSllai1 int i; 646facf4a8dSllai1 char *path; 647facf4a8dSllai1 648facf4a8dSllai1 ASSERT(dv->sdev_path); 649facf4a8dSllai1 path = dv->sdev_path + strlen("/dev/"); 650facf4a8dSllai1 651facf4a8dSllai1 for (i = 0; vtab[i].vt_name; i++) { 652facf4a8dSllai1 if (strcmp(vtab[i].vt_name, path) == 0) { 653facf4a8dSllai1 if (vtab[i].vt_flags & SDEV_NO_NCACHE) 654facf4a8dSllai1 dv->sdev_flags |= SDEV_NO_NCACHE; 655facf4a8dSllai1 break; 656facf4a8dSllai1 } 657facf4a8dSllai1 } 658facf4a8dSllai1 } 659facf4a8dSllai1 660facf4a8dSllai1 void * 661facf4a8dSllai1 sdev_get_vtor(struct sdev_node *dv) 662facf4a8dSllai1 { 663681d9761SEric Taylor struct sdev_vop_table *vtp; 664facf4a8dSllai1 665681d9761SEric Taylor vtp = sdev_match(dv); 666681d9761SEric Taylor if (vtp) 667681d9761SEric Taylor return ((void *)vtp->vt_vtor); 668681d9761SEric Taylor else 669facf4a8dSllai1 return (NULL); 670facf4a8dSllai1 } 671facf4a8dSllai1 672facf4a8dSllai1 /* 673facf4a8dSllai1 * Build the base root inode 674facf4a8dSllai1 */ 675facf4a8dSllai1 ino_t 676facf4a8dSllai1 sdev_mkino(struct sdev_node *dv) 677facf4a8dSllai1 { 678facf4a8dSllai1 ino_t ino; 679facf4a8dSllai1 680facf4a8dSllai1 /* 681facf4a8dSllai1 * for now, follow the lead of tmpfs here 682facf4a8dSllai1 * need to someday understand the requirements here 683facf4a8dSllai1 */ 684facf4a8dSllai1 ino = (ino_t)(uint32_t)((uintptr_t)dv >> 3); 685facf4a8dSllai1 ino += SDEV_ROOTINO + 1; 686facf4a8dSllai1 687facf4a8dSllai1 return (ino); 688facf4a8dSllai1 } 689facf4a8dSllai1 690681d9761SEric Taylor int 691facf4a8dSllai1 sdev_getlink(struct vnode *linkvp, char **link) 692facf4a8dSllai1 { 693facf4a8dSllai1 int err; 694facf4a8dSllai1 char *buf; 695facf4a8dSllai1 struct uio uio = {0}; 696facf4a8dSllai1 struct iovec iov = {0}; 697facf4a8dSllai1 698facf4a8dSllai1 if (linkvp == NULL) 699facf4a8dSllai1 return (ENOENT); 700facf4a8dSllai1 ASSERT(linkvp->v_type == VLNK); 701facf4a8dSllai1 702facf4a8dSllai1 buf = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 703facf4a8dSllai1 iov.iov_base = buf; 704facf4a8dSllai1 iov.iov_len = MAXPATHLEN; 705facf4a8dSllai1 uio.uio_iov = &iov; 706facf4a8dSllai1 uio.uio_iovcnt = 1; 707facf4a8dSllai1 uio.uio_resid = MAXPATHLEN; 708facf4a8dSllai1 uio.uio_segflg = UIO_SYSSPACE; 709facf4a8dSllai1 uio.uio_llimit = MAXOFFSET_T; 710facf4a8dSllai1 711da6c28aaSamw err = VOP_READLINK(linkvp, &uio, kcred, NULL); 712facf4a8dSllai1 if (err) { 713facf4a8dSllai1 cmn_err(CE_WARN, "readlink %s failed in dev\n", buf); 714facf4a8dSllai1 kmem_free(buf, MAXPATHLEN); 715facf4a8dSllai1 return (ENOENT); 716facf4a8dSllai1 } 717facf4a8dSllai1 718facf4a8dSllai1 /* mission complete */ 719facf4a8dSllai1 *link = i_ddi_strdup(buf, KM_SLEEP); 720facf4a8dSllai1 kmem_free(buf, MAXPATHLEN); 721facf4a8dSllai1 return (0); 722facf4a8dSllai1 } 723facf4a8dSllai1 724facf4a8dSllai1 /* 725facf4a8dSllai1 * A convenient wrapper to get the devfs node vnode for a device 726facf4a8dSllai1 * minor functionality: readlink() of a /dev symlink 727facf4a8dSllai1 * Place the link into dv->sdev_symlink 728facf4a8dSllai1 */ 729facf4a8dSllai1 static int 730facf4a8dSllai1 sdev_follow_link(struct sdev_node *dv) 731facf4a8dSllai1 { 732facf4a8dSllai1 int err; 733facf4a8dSllai1 struct vnode *linkvp; 734facf4a8dSllai1 char *link = NULL; 735facf4a8dSllai1 736facf4a8dSllai1 linkvp = SDEVTOV(dv); 737facf4a8dSllai1 if (linkvp == NULL) 738facf4a8dSllai1 return (ENOENT); 739facf4a8dSllai1 ASSERT(linkvp->v_type == VLNK); 740facf4a8dSllai1 err = sdev_getlink(linkvp, &link); 741facf4a8dSllai1 if (err) { 742facf4a8dSllai1 dv->sdev_symlink = NULL; 743facf4a8dSllai1 return (ENOENT); 744facf4a8dSllai1 } 745facf4a8dSllai1 746facf4a8dSllai1 ASSERT(link != NULL); 747facf4a8dSllai1 dv->sdev_symlink = link; 748facf4a8dSllai1 return (0); 749facf4a8dSllai1 } 750facf4a8dSllai1 751facf4a8dSllai1 static int 752facf4a8dSllai1 sdev_node_check(struct sdev_node *dv, struct vattr *nvap, void *nargs) 753facf4a8dSllai1 { 754facf4a8dSllai1 vtype_t otype = SDEVTOV(dv)->v_type; 755facf4a8dSllai1 756facf4a8dSllai1 /* 757facf4a8dSllai1 * existing sdev_node has a different type. 758facf4a8dSllai1 */ 759facf4a8dSllai1 if (otype != nvap->va_type) { 760facf4a8dSllai1 sdcmn_err9(("sdev_node_check: existing node " 761facf4a8dSllai1 " %s type %d does not match new node type %d\n", 762facf4a8dSllai1 dv->sdev_name, otype, nvap->va_type)); 763facf4a8dSllai1 return (EEXIST); 764facf4a8dSllai1 } 765facf4a8dSllai1 766facf4a8dSllai1 /* 767facf4a8dSllai1 * For a symlink, the target should be the same. 768facf4a8dSllai1 */ 769facf4a8dSllai1 if (otype == VLNK) { 770facf4a8dSllai1 ASSERT(nargs != NULL); 771facf4a8dSllai1 ASSERT(dv->sdev_symlink != NULL); 772facf4a8dSllai1 if (strcmp(dv->sdev_symlink, (char *)nargs) != 0) { 773facf4a8dSllai1 sdcmn_err9(("sdev_node_check: existing node " 774facf4a8dSllai1 " %s has different symlink %s as new node " 775facf4a8dSllai1 " %s\n", dv->sdev_name, dv->sdev_symlink, 776facf4a8dSllai1 (char *)nargs)); 777facf4a8dSllai1 return (EEXIST); 778facf4a8dSllai1 } 779facf4a8dSllai1 } 780facf4a8dSllai1 781facf4a8dSllai1 return (0); 782facf4a8dSllai1 } 783facf4a8dSllai1 784facf4a8dSllai1 /* 785facf4a8dSllai1 * sdev_mknode - a wrapper for sdev_nodeinit(), sdev_nodeready() 786facf4a8dSllai1 * 787facf4a8dSllai1 * arguments: 788facf4a8dSllai1 * - ddv (parent) 789facf4a8dSllai1 * - nm (child name) 790facf4a8dSllai1 * - newdv (sdev_node for nm is returned here) 791facf4a8dSllai1 * - vap (vattr for the node to be created, va_type should be set. 792b7beec95Sjg * - avp (attribute vnode) 793facf4a8dSllai1 * the defaults should be used if unknown) 794facf4a8dSllai1 * - cred 795facf4a8dSllai1 * - args 796facf4a8dSllai1 * . tnm (for VLNK) 797facf4a8dSllai1 * . global sdev_node (for !SDEV_GLOBAL) 798facf4a8dSllai1 * - state: SDEV_INIT, SDEV_READY 799facf4a8dSllai1 * 800facf4a8dSllai1 * only ddv, nm, newddv, vap, cred are required for sdev_mknode(SDEV_INIT) 801facf4a8dSllai1 * 802facf4a8dSllai1 * NOTE: directory contents writers lock needs to be held before 803facf4a8dSllai1 * calling this routine. 804facf4a8dSllai1 */ 805facf4a8dSllai1 int 806facf4a8dSllai1 sdev_mknode(struct sdev_node *ddv, char *nm, struct sdev_node **newdv, 807facf4a8dSllai1 struct vattr *vap, struct vnode *avp, void *args, struct cred *cred, 808facf4a8dSllai1 sdev_node_state_t state) 809facf4a8dSllai1 { 810facf4a8dSllai1 int error = 0; 811facf4a8dSllai1 sdev_node_state_t node_state; 812facf4a8dSllai1 struct sdev_node *dv = NULL; 813facf4a8dSllai1 814facf4a8dSllai1 ASSERT(state != SDEV_ZOMBIE); 815facf4a8dSllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 816facf4a8dSllai1 817facf4a8dSllai1 if (*newdv) { 818facf4a8dSllai1 dv = *newdv; 819facf4a8dSllai1 } else { 820facf4a8dSllai1 /* allocate and initialize a sdev_node */ 821facf4a8dSllai1 if (ddv->sdev_state == SDEV_ZOMBIE) { 822facf4a8dSllai1 sdcmn_err9(("sdev_mknode: parent %s ZOMBIEd\n", 823facf4a8dSllai1 ddv->sdev_path)); 824facf4a8dSllai1 return (ENOENT); 825facf4a8dSllai1 } 826facf4a8dSllai1 827facf4a8dSllai1 error = sdev_nodeinit(ddv, nm, &dv, vap); 828facf4a8dSllai1 if (error != 0) { 829facf4a8dSllai1 sdcmn_err9(("sdev_mknode: error %d," 830facf4a8dSllai1 " name %s can not be initialized\n", 831facf4a8dSllai1 error, nm)); 832b7beec95Sjg return (error); 833facf4a8dSllai1 } 834facf4a8dSllai1 ASSERT(dv); 835facf4a8dSllai1 836facf4a8dSllai1 /* insert into the directory cache */ 837*9e5aa9d8SRobert Mustacchi sdev_cache_update(ddv, &dv, nm, SDEV_CACHE_ADD); 838facf4a8dSllai1 } 839facf4a8dSllai1 840facf4a8dSllai1 ASSERT(dv); 841facf4a8dSllai1 node_state = dv->sdev_state; 842facf4a8dSllai1 ASSERT(node_state != SDEV_ZOMBIE); 843facf4a8dSllai1 844facf4a8dSllai1 if (state == SDEV_READY) { 845facf4a8dSllai1 switch (node_state) { 846facf4a8dSllai1 case SDEV_INIT: 847facf4a8dSllai1 error = sdev_nodeready(dv, vap, avp, args, cred); 848facf4a8dSllai1 if (error) { 849facf4a8dSllai1 sdcmn_err9(("sdev_mknode: node %s can NOT" 850facf4a8dSllai1 " be transitioned into READY state, " 851facf4a8dSllai1 "error %d\n", nm, error)); 852facf4a8dSllai1 } 853facf4a8dSllai1 break; 854facf4a8dSllai1 case SDEV_READY: 855facf4a8dSllai1 /* 856facf4a8dSllai1 * Do some sanity checking to make sure 857facf4a8dSllai1 * the existing sdev_node is what has been 858facf4a8dSllai1 * asked for. 859facf4a8dSllai1 */ 860facf4a8dSllai1 error = sdev_node_check(dv, vap, args); 861facf4a8dSllai1 break; 862facf4a8dSllai1 default: 863facf4a8dSllai1 break; 864facf4a8dSllai1 } 865facf4a8dSllai1 } 866facf4a8dSllai1 867facf4a8dSllai1 if (!error) { 868facf4a8dSllai1 *newdv = dv; 869facf4a8dSllai1 ASSERT((*newdv)->sdev_state != SDEV_ZOMBIE); 870facf4a8dSllai1 } else { 871*9e5aa9d8SRobert Mustacchi sdev_cache_update(ddv, &dv, nm, SDEV_CACHE_DELETE); 872*9e5aa9d8SRobert Mustacchi /* 873*9e5aa9d8SRobert Mustacchi * We created this node, it wasn't passed into us. Therefore it 874*9e5aa9d8SRobert Mustacchi * is up to us to delete it. 875*9e5aa9d8SRobert Mustacchi */ 876*9e5aa9d8SRobert Mustacchi if (*newdv == NULL) 877facf4a8dSllai1 SDEV_SIMPLE_RELE(dv); 878facf4a8dSllai1 *newdv = NULL; 879facf4a8dSllai1 } 880facf4a8dSllai1 881facf4a8dSllai1 return (error); 882facf4a8dSllai1 } 883facf4a8dSllai1 884facf4a8dSllai1 /* 885b7beec95Sjg * convenient wrapper to change vp's ATIME, CTIME and MTIME 886facf4a8dSllai1 */ 887facf4a8dSllai1 void 888facf4a8dSllai1 sdev_update_timestamps(struct vnode *vp, cred_t *cred, uint_t mask) 889facf4a8dSllai1 { 890facf4a8dSllai1 struct vattr attr; 891facf4a8dSllai1 timestruc_t now; 892facf4a8dSllai1 int err; 893facf4a8dSllai1 894facf4a8dSllai1 ASSERT(vp); 895facf4a8dSllai1 gethrestime(&now); 896facf4a8dSllai1 if (mask & AT_CTIME) 897facf4a8dSllai1 attr.va_ctime = now; 898facf4a8dSllai1 if (mask & AT_MTIME) 899facf4a8dSllai1 attr.va_mtime = now; 900facf4a8dSllai1 if (mask & AT_ATIME) 901facf4a8dSllai1 attr.va_atime = now; 902facf4a8dSllai1 903facf4a8dSllai1 attr.va_mask = (mask & AT_TIMES); 904facf4a8dSllai1 err = VOP_SETATTR(vp, &attr, 0, cred, NULL); 905facf4a8dSllai1 if (err && (err != EROFS)) { 906facf4a8dSllai1 sdcmn_err(("update timestamps error %d\n", err)); 907facf4a8dSllai1 } 908facf4a8dSllai1 } 909facf4a8dSllai1 910facf4a8dSllai1 /* 911facf4a8dSllai1 * the backing store vnode is released here 912facf4a8dSllai1 */ 913facf4a8dSllai1 /*ARGSUSED1*/ 914facf4a8dSllai1 void 915facf4a8dSllai1 sdev_nodedestroy(struct sdev_node *dv, uint_t flags) 916facf4a8dSllai1 { 917facf4a8dSllai1 /* no references */ 918facf4a8dSllai1 ASSERT(dv->sdev_nlink == 0); 919facf4a8dSllai1 920facf4a8dSllai1 if (dv->sdev_attrvp != NULLVP) { 921facf4a8dSllai1 VN_RELE(dv->sdev_attrvp); 922facf4a8dSllai1 /* 923facf4a8dSllai1 * reset the attrvp so that no more 924facf4a8dSllai1 * references can be made on this already 925facf4a8dSllai1 * vn_rele() vnode 926facf4a8dSllai1 */ 927facf4a8dSllai1 dv->sdev_attrvp = NULLVP; 928facf4a8dSllai1 } 929facf4a8dSllai1 930facf4a8dSllai1 if (dv->sdev_attr != NULL) { 931facf4a8dSllai1 kmem_free(dv->sdev_attr, sizeof (struct vattr)); 932facf4a8dSllai1 dv->sdev_attr = NULL; 933facf4a8dSllai1 } 934facf4a8dSllai1 935facf4a8dSllai1 if (dv->sdev_name != NULL) { 936facf4a8dSllai1 kmem_free(dv->sdev_name, dv->sdev_namelen + 1); 937facf4a8dSllai1 dv->sdev_name = NULL; 938facf4a8dSllai1 } 939facf4a8dSllai1 940facf4a8dSllai1 if (dv->sdev_symlink != NULL) { 941facf4a8dSllai1 kmem_free(dv->sdev_symlink, strlen(dv->sdev_symlink) + 1); 942facf4a8dSllai1 dv->sdev_symlink = NULL; 943facf4a8dSllai1 } 944facf4a8dSllai1 945facf4a8dSllai1 if (dv->sdev_path) { 946facf4a8dSllai1 kmem_free(dv->sdev_path, strlen(dv->sdev_path) + 1); 947facf4a8dSllai1 dv->sdev_path = NULL; 948facf4a8dSllai1 } 949facf4a8dSllai1 950facf4a8dSllai1 if (!SDEV_IS_GLOBAL(dv)) 951facf4a8dSllai1 sdev_prof_free(dv); 952facf4a8dSllai1 953aac43a5fSjg if (SDEVTOV(dv)->v_type == VDIR) { 954aac43a5fSjg ASSERT(SDEV_FIRST_ENTRY(dv) == NULL); 955aac43a5fSjg avl_destroy(&dv->sdev_entries); 956aac43a5fSjg } 957aac43a5fSjg 958facf4a8dSllai1 mutex_destroy(&dv->sdev_lookup_lock); 959facf4a8dSllai1 cv_destroy(&dv->sdev_lookup_cv); 960facf4a8dSllai1 961facf4a8dSllai1 /* return node to initial state as per constructor */ 962facf4a8dSllai1 (void) memset((void *)&dv->sdev_instance_data, 0, 963facf4a8dSllai1 sizeof (dv->sdev_instance_data)); 964facf4a8dSllai1 vn_invalid(SDEVTOV(dv)); 965facf4a8dSllai1 kmem_cache_free(sdev_node_cache, dv); 966facf4a8dSllai1 } 967facf4a8dSllai1 968facf4a8dSllai1 /* 969facf4a8dSllai1 * DIRECTORY CACHE lookup 970facf4a8dSllai1 */ 971facf4a8dSllai1 struct sdev_node * 972facf4a8dSllai1 sdev_findbyname(struct sdev_node *ddv, char *nm) 973facf4a8dSllai1 { 974facf4a8dSllai1 struct sdev_node *dv; 975aac43a5fSjg struct sdev_node dvtmp; 976aac43a5fSjg avl_index_t where; 977facf4a8dSllai1 978facf4a8dSllai1 ASSERT(RW_LOCK_HELD(&ddv->sdev_contents)); 979facf4a8dSllai1 980aac43a5fSjg dvtmp.sdev_name = nm; 981aac43a5fSjg dv = avl_find(&ddv->sdev_entries, &dvtmp, &where); 982aac43a5fSjg if (dv) { 983aac43a5fSjg ASSERT(dv->sdev_dotdot == ddv); 984aac43a5fSjg ASSERT(strcmp(dv->sdev_name, nm) == 0); 985*9e5aa9d8SRobert Mustacchi ASSERT(dv->sdev_state != SDEV_ZOMBIE); 986facf4a8dSllai1 SDEV_HOLD(dv); 987facf4a8dSllai1 return (dv); 988facf4a8dSllai1 } 989facf4a8dSllai1 return (NULL); 990facf4a8dSllai1 } 991facf4a8dSllai1 992facf4a8dSllai1 /* 993facf4a8dSllai1 * Inserts a new sdev_node in a parent directory 994facf4a8dSllai1 */ 995facf4a8dSllai1 void 996facf4a8dSllai1 sdev_direnter(struct sdev_node *ddv, struct sdev_node *dv) 997facf4a8dSllai1 { 998aac43a5fSjg avl_index_t where; 999aac43a5fSjg 1000facf4a8dSllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 1001facf4a8dSllai1 ASSERT(SDEVTOV(ddv)->v_type == VDIR); 1002facf4a8dSllai1 ASSERT(ddv->sdev_nlink >= 2); 1003facf4a8dSllai1 ASSERT(dv->sdev_nlink == 0); 1004*9e5aa9d8SRobert Mustacchi ASSERT(dv->sdev_state != SDEV_ZOMBIE); 1005facf4a8dSllai1 1006facf4a8dSllai1 dv->sdev_dotdot = ddv; 1007aac43a5fSjg VERIFY(avl_find(&ddv->sdev_entries, dv, &where) == NULL); 1008aac43a5fSjg avl_insert(&ddv->sdev_entries, dv, where); 1009facf4a8dSllai1 ddv->sdev_nlink++; 1010facf4a8dSllai1 } 1011facf4a8dSllai1 1012facf4a8dSllai1 /* 1013facf4a8dSllai1 * The following check is needed because while sdev_nodes are linked 1014facf4a8dSllai1 * in SDEV_INIT state, they have their link counts incremented only 1015facf4a8dSllai1 * in SDEV_READY state. 1016facf4a8dSllai1 */ 1017facf4a8dSllai1 static void 1018facf4a8dSllai1 decr_link(struct sdev_node *dv) 1019facf4a8dSllai1 { 1020*9e5aa9d8SRobert Mustacchi VERIFY(RW_WRITE_HELD(&dv->sdev_contents)); 1021*9e5aa9d8SRobert Mustacchi if (dv->sdev_state != SDEV_INIT) { 1022*9e5aa9d8SRobert Mustacchi VERIFY(dv->sdev_nlink >= 1); 1023facf4a8dSllai1 dv->sdev_nlink--; 1024*9e5aa9d8SRobert Mustacchi } else { 1025*9e5aa9d8SRobert Mustacchi VERIFY(dv->sdev_nlink == 0); 1026*9e5aa9d8SRobert Mustacchi } 1027facf4a8dSllai1 } 1028facf4a8dSllai1 1029facf4a8dSllai1 /* 1030facf4a8dSllai1 * Delete an existing dv from directory cache 1031facf4a8dSllai1 * 1032*9e5aa9d8SRobert Mustacchi * In the case of a node is still held by non-zero reference count, the node is 1033*9e5aa9d8SRobert Mustacchi * put into ZOMBIE state. The node is always unlinked from its parent, but it is 1034*9e5aa9d8SRobert Mustacchi * not destroyed via sdev_inactive until its reference count reaches "0". 1035facf4a8dSllai1 */ 1036*9e5aa9d8SRobert Mustacchi static void 1037facf4a8dSllai1 sdev_dirdelete(struct sdev_node *ddv, struct sdev_node *dv) 1038facf4a8dSllai1 { 1039facf4a8dSllai1 struct vnode *vp; 1040*9e5aa9d8SRobert Mustacchi sdev_node_state_t os; 1041facf4a8dSllai1 1042facf4a8dSllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 1043facf4a8dSllai1 1044facf4a8dSllai1 vp = SDEVTOV(dv); 1045facf4a8dSllai1 mutex_enter(&vp->v_lock); 1046facf4a8dSllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 1047*9e5aa9d8SRobert Mustacchi os = dv->sdev_state; 1048*9e5aa9d8SRobert Mustacchi ASSERT(os != SDEV_ZOMBIE); 1049facf4a8dSllai1 dv->sdev_state = SDEV_ZOMBIE; 1050facf4a8dSllai1 1051*9e5aa9d8SRobert Mustacchi /* 1052*9e5aa9d8SRobert Mustacchi * unlink ourselves from the parent directory now to take care of the .. 1053*9e5aa9d8SRobert Mustacchi * link. However, if we're a directory, we don't remove our reference to 1054*9e5aa9d8SRobert Mustacchi * ourself eg. '.' until we are torn down in the inactive callback. 1055*9e5aa9d8SRobert Mustacchi */ 1056*9e5aa9d8SRobert Mustacchi decr_link(ddv); 1057aac43a5fSjg avl_remove(&ddv->sdev_entries, dv); 1058*9e5aa9d8SRobert Mustacchi /* 1059*9e5aa9d8SRobert Mustacchi * sdev_inactive expects nodes to have a link to themselves when we're 1060*9e5aa9d8SRobert Mustacchi * tearing them down. If we're transitioning from the initial state to 1061*9e5aa9d8SRobert Mustacchi * zombie and not via ready, then we're not going to have this link that 1062*9e5aa9d8SRobert Mustacchi * comes from the node being ready. As a result, we need to increment 1063*9e5aa9d8SRobert Mustacchi * our link count by one to account for this. 1064*9e5aa9d8SRobert Mustacchi */ 1065*9e5aa9d8SRobert Mustacchi if (os == SDEV_INIT && dv->sdev_nlink == 0) 1066*9e5aa9d8SRobert Mustacchi dv->sdev_nlink++; 1067*9e5aa9d8SRobert Mustacchi rw_exit(&dv->sdev_contents); 1068facf4a8dSllai1 mutex_exit(&vp->v_lock); 1069facf4a8dSllai1 } 1070facf4a8dSllai1 1071facf4a8dSllai1 /* 1072facf4a8dSllai1 * check if the source is in the path of the target 1073facf4a8dSllai1 * 1074facf4a8dSllai1 * source and target are different 1075facf4a8dSllai1 */ 1076facf4a8dSllai1 /*ARGSUSED2*/ 1077facf4a8dSllai1 static int 1078facf4a8dSllai1 sdev_checkpath(struct sdev_node *sdv, struct sdev_node *tdv, struct cred *cred) 1079facf4a8dSllai1 { 1080facf4a8dSllai1 int error = 0; 1081facf4a8dSllai1 struct sdev_node *dotdot, *dir; 1082facf4a8dSllai1 1083facf4a8dSllai1 dotdot = tdv->sdev_dotdot; 1084facf4a8dSllai1 ASSERT(dotdot); 1085facf4a8dSllai1 1086facf4a8dSllai1 /* fs root */ 1087facf4a8dSllai1 if (dotdot == tdv) { 1088facf4a8dSllai1 return (0); 1089facf4a8dSllai1 } 1090facf4a8dSllai1 1091facf4a8dSllai1 for (;;) { 1092facf4a8dSllai1 /* 1093facf4a8dSllai1 * avoid error cases like 1094facf4a8dSllai1 * mv a a/b 1095facf4a8dSllai1 * mv a a/b/c 1096facf4a8dSllai1 * etc. 1097facf4a8dSllai1 */ 1098facf4a8dSllai1 if (dotdot == sdv) { 1099facf4a8dSllai1 error = EINVAL; 1100facf4a8dSllai1 break; 1101facf4a8dSllai1 } 1102facf4a8dSllai1 1103facf4a8dSllai1 dir = dotdot; 1104facf4a8dSllai1 dotdot = dir->sdev_dotdot; 1105facf4a8dSllai1 1106facf4a8dSllai1 /* done checking because root is reached */ 1107facf4a8dSllai1 if (dir == dotdot) { 1108facf4a8dSllai1 break; 1109facf4a8dSllai1 } 1110facf4a8dSllai1 } 1111facf4a8dSllai1 return (error); 1112facf4a8dSllai1 } 1113facf4a8dSllai1 1114facf4a8dSllai1 int 1115facf4a8dSllai1 sdev_rnmnode(struct sdev_node *oddv, struct sdev_node *odv, 1116facf4a8dSllai1 struct sdev_node *nddv, struct sdev_node **ndvp, char *nnm, 1117facf4a8dSllai1 struct cred *cred) 1118facf4a8dSllai1 { 1119facf4a8dSllai1 int error = 0; 1120facf4a8dSllai1 struct vnode *ovp = SDEVTOV(odv); 1121facf4a8dSllai1 struct vnode *nvp; 1122facf4a8dSllai1 struct vattr vattr; 1123facf4a8dSllai1 int doingdir = (ovp->v_type == VDIR); 1124facf4a8dSllai1 char *link = NULL; 11250bfaec69Sllai1 int samedir = (oddv == nddv) ? 1 : 0; 11260bfaec69Sllai1 int bkstore = 0; 11270bfaec69Sllai1 struct sdev_node *idv = NULL; 11280bfaec69Sllai1 struct sdev_node *ndv = NULL; 11290bfaec69Sllai1 timestruc_t now; 11300bfaec69Sllai1 1131bb5fffbeSJerry Gilliam vattr.va_mask = AT_TYPE|AT_MODE|AT_UID|AT_GID; 1132da6c28aaSamw error = VOP_GETATTR(ovp, &vattr, 0, cred, NULL); 11330bfaec69Sllai1 if (error) 11340bfaec69Sllai1 return (error); 11350bfaec69Sllai1 11360bfaec69Sllai1 if (!samedir) 11370bfaec69Sllai1 rw_enter(&oddv->sdev_contents, RW_WRITER); 11380bfaec69Sllai1 rw_enter(&nddv->sdev_contents, RW_WRITER); 11390bfaec69Sllai1 11400bfaec69Sllai1 /* 11410bfaec69Sllai1 * the source may have been deleted by another thread before 11420bfaec69Sllai1 * we gets here. 11430bfaec69Sllai1 */ 11440bfaec69Sllai1 if (odv->sdev_state != SDEV_READY) { 11450bfaec69Sllai1 error = ENOENT; 11460bfaec69Sllai1 goto err_out; 11470bfaec69Sllai1 } 11480bfaec69Sllai1 11490bfaec69Sllai1 if (doingdir && (odv == nddv)) { 11500bfaec69Sllai1 error = EINVAL; 11510bfaec69Sllai1 goto err_out; 11520bfaec69Sllai1 } 1153facf4a8dSllai1 1154facf4a8dSllai1 /* 1155facf4a8dSllai1 * If renaming a directory, and the parents are different (".." must be 1156facf4a8dSllai1 * changed) then the source dir must not be in the dir hierarchy above 1157facf4a8dSllai1 * the target since it would orphan everything below the source dir. 1158facf4a8dSllai1 */ 1159facf4a8dSllai1 if (doingdir && (oddv != nddv)) { 1160facf4a8dSllai1 error = sdev_checkpath(odv, nddv, cred); 1161facf4a8dSllai1 if (error) 11620bfaec69Sllai1 goto err_out; 1163facf4a8dSllai1 } 1164facf4a8dSllai1 1165*9e5aa9d8SRobert Mustacchi /* fix the source for a symlink */ 1166*9e5aa9d8SRobert Mustacchi if (vattr.va_type == VLNK) { 1167*9e5aa9d8SRobert Mustacchi if (odv->sdev_symlink == NULL) { 1168*9e5aa9d8SRobert Mustacchi error = sdev_follow_link(odv); 1169*9e5aa9d8SRobert Mustacchi if (error) { 1170*9e5aa9d8SRobert Mustacchi /* 1171*9e5aa9d8SRobert Mustacchi * The underlying symlink doesn't exist. This 1172*9e5aa9d8SRobert Mustacchi * node probably shouldn't even exist. While 1173*9e5aa9d8SRobert Mustacchi * it's a bit jarring to consumers, we're going 1174*9e5aa9d8SRobert Mustacchi * to remove the node from /dev. 1175*9e5aa9d8SRobert Mustacchi */ 1176*9e5aa9d8SRobert Mustacchi if (SDEV_IS_PERSIST((*ndvp))) 1177*9e5aa9d8SRobert Mustacchi bkstore = 1; 1178*9e5aa9d8SRobert Mustacchi sdev_dirdelete(oddv, odv); 1179*9e5aa9d8SRobert Mustacchi if (bkstore) { 1180*9e5aa9d8SRobert Mustacchi ASSERT(nddv->sdev_attrvp); 1181*9e5aa9d8SRobert Mustacchi error = VOP_REMOVE(nddv->sdev_attrvp, 1182*9e5aa9d8SRobert Mustacchi nnm, cred, NULL, 0); 1183*9e5aa9d8SRobert Mustacchi if (error) 1184*9e5aa9d8SRobert Mustacchi goto err_out; 1185*9e5aa9d8SRobert Mustacchi } 1186*9e5aa9d8SRobert Mustacchi error = ENOENT; 1187*9e5aa9d8SRobert Mustacchi goto err_out; 1188*9e5aa9d8SRobert Mustacchi } 1189*9e5aa9d8SRobert Mustacchi } 1190*9e5aa9d8SRobert Mustacchi ASSERT(odv->sdev_symlink); 1191*9e5aa9d8SRobert Mustacchi link = i_ddi_strdup(odv->sdev_symlink, KM_SLEEP); 1192*9e5aa9d8SRobert Mustacchi } 1193*9e5aa9d8SRobert Mustacchi 1194facf4a8dSllai1 /* destination existing */ 11950bfaec69Sllai1 if (*ndvp) { 1196facf4a8dSllai1 nvp = SDEVTOV(*ndvp); 1197facf4a8dSllai1 ASSERT(nvp); 1198facf4a8dSllai1 1199facf4a8dSllai1 /* handling renaming to itself */ 12000bfaec69Sllai1 if (odv == *ndvp) { 12010bfaec69Sllai1 error = 0; 12020bfaec69Sllai1 goto err_out; 12030bfaec69Sllai1 } 1204facf4a8dSllai1 12050bfaec69Sllai1 if (nvp->v_type == VDIR) { 12060bfaec69Sllai1 if (!doingdir) { 12070bfaec69Sllai1 error = EISDIR; 12080bfaec69Sllai1 goto err_out; 12090bfaec69Sllai1 } 12100bfaec69Sllai1 12110bfaec69Sllai1 if (vn_vfswlock(nvp)) { 12120bfaec69Sllai1 error = EBUSY; 12130bfaec69Sllai1 goto err_out; 12140bfaec69Sllai1 } 12150bfaec69Sllai1 12160bfaec69Sllai1 if (vn_mountedvfs(nvp) != NULL) { 12170bfaec69Sllai1 vn_vfsunlock(nvp); 12180bfaec69Sllai1 error = EBUSY; 12190bfaec69Sllai1 goto err_out; 12200bfaec69Sllai1 } 12210bfaec69Sllai1 12220bfaec69Sllai1 /* in case dir1 exists in dir2 and "mv dir1 dir2" */ 12230bfaec69Sllai1 if ((*ndvp)->sdev_nlink > 2) { 12240bfaec69Sllai1 vn_vfsunlock(nvp); 12250bfaec69Sllai1 error = EEXIST; 12260bfaec69Sllai1 goto err_out; 12270bfaec69Sllai1 } 12280bfaec69Sllai1 vn_vfsunlock(nvp); 12290bfaec69Sllai1 1230*9e5aa9d8SRobert Mustacchi /* 1231*9e5aa9d8SRobert Mustacchi * We did not place the hold on *ndvp, so even though 1232*9e5aa9d8SRobert Mustacchi * we're deleting the node, we should not get rid of our 1233*9e5aa9d8SRobert Mustacchi * reference. 1234*9e5aa9d8SRobert Mustacchi */ 1235*9e5aa9d8SRobert Mustacchi sdev_dirdelete(nddv, *ndvp); 12360bfaec69Sllai1 *ndvp = NULL; 1237b7beec95Sjg ASSERT(nddv->sdev_attrvp); 12380bfaec69Sllai1 error = VOP_RMDIR(nddv->sdev_attrvp, nnm, 1239da6c28aaSamw nddv->sdev_attrvp, cred, NULL, 0); 12400bfaec69Sllai1 if (error) 12410bfaec69Sllai1 goto err_out; 12420bfaec69Sllai1 } else { 1243facf4a8dSllai1 if (doingdir) { 12440bfaec69Sllai1 error = ENOTDIR; 12450bfaec69Sllai1 goto err_out; 12460bfaec69Sllai1 } 12470bfaec69Sllai1 12480bfaec69Sllai1 if (SDEV_IS_PERSIST((*ndvp))) { 12490bfaec69Sllai1 bkstore = 1; 12500bfaec69Sllai1 } 1251facf4a8dSllai1 1252facf4a8dSllai1 /* 1253*9e5aa9d8SRobert Mustacchi * Get rid of the node from the directory cache note. 1254*9e5aa9d8SRobert Mustacchi * Don't forget that it's not up to us to remove the vn 1255*9e5aa9d8SRobert Mustacchi * ref on the sdev node, as we did not place it. 1256facf4a8dSllai1 */ 1257*9e5aa9d8SRobert Mustacchi sdev_dirdelete(nddv, *ndvp); 12580bfaec69Sllai1 *ndvp = NULL; 12590bfaec69Sllai1 if (bkstore) { 1260b7beec95Sjg ASSERT(nddv->sdev_attrvp); 12610bfaec69Sllai1 error = VOP_REMOVE(nddv->sdev_attrvp, 1262da6c28aaSamw nnm, cred, NULL, 0); 1263facf4a8dSllai1 if (error) 12640bfaec69Sllai1 goto err_out; 12650bfaec69Sllai1 } 12660bfaec69Sllai1 } 1267facf4a8dSllai1 } 1268facf4a8dSllai1 12690bfaec69Sllai1 /* 12700bfaec69Sllai1 * make a fresh node from the source attrs 12710bfaec69Sllai1 */ 12720bfaec69Sllai1 ASSERT(RW_WRITE_HELD(&nddv->sdev_contents)); 12730bfaec69Sllai1 error = sdev_mknode(nddv, nnm, ndvp, &vattr, 12740bfaec69Sllai1 NULL, (void *)link, cred, SDEV_READY); 1275facf4a8dSllai1 1276*9e5aa9d8SRobert Mustacchi if (link != NULL) { 1277facf4a8dSllai1 kmem_free(link, strlen(link) + 1); 1278*9e5aa9d8SRobert Mustacchi link = NULL; 1279*9e5aa9d8SRobert Mustacchi } 1280facf4a8dSllai1 12810bfaec69Sllai1 if (error) 12820bfaec69Sllai1 goto err_out; 12830bfaec69Sllai1 ASSERT(*ndvp); 12840bfaec69Sllai1 ASSERT((*ndvp)->sdev_state == SDEV_READY); 12850bfaec69Sllai1 12860bfaec69Sllai1 /* move dir contents */ 12870bfaec69Sllai1 if (doingdir) { 1288aac43a5fSjg for (idv = SDEV_FIRST_ENTRY(odv); idv; 1289aac43a5fSjg idv = SDEV_NEXT_ENTRY(odv, idv)) { 1290*9e5aa9d8SRobert Mustacchi SDEV_HOLD(idv); 12910bfaec69Sllai1 error = sdev_rnmnode(odv, idv, 12920bfaec69Sllai1 (struct sdev_node *)(*ndvp), &ndv, 12930bfaec69Sllai1 idv->sdev_name, cred); 1294*9e5aa9d8SRobert Mustacchi SDEV_RELE(idv); 12950bfaec69Sllai1 if (error) 12960bfaec69Sllai1 goto err_out; 12970bfaec69Sllai1 ndv = NULL; 12980bfaec69Sllai1 } 12990bfaec69Sllai1 } 13000bfaec69Sllai1 13010bfaec69Sllai1 if ((*ndvp)->sdev_attrvp) { 13020bfaec69Sllai1 sdev_update_timestamps((*ndvp)->sdev_attrvp, kcred, 13030bfaec69Sllai1 AT_CTIME|AT_ATIME); 13040bfaec69Sllai1 } else { 13050bfaec69Sllai1 ASSERT((*ndvp)->sdev_attr); 13060bfaec69Sllai1 gethrestime(&now); 13070bfaec69Sllai1 (*ndvp)->sdev_attr->va_ctime = now; 13080bfaec69Sllai1 (*ndvp)->sdev_attr->va_atime = now; 13090bfaec69Sllai1 } 13100bfaec69Sllai1 13110bfaec69Sllai1 if (nddv->sdev_attrvp) { 13120bfaec69Sllai1 sdev_update_timestamps(nddv->sdev_attrvp, kcred, 13130bfaec69Sllai1 AT_MTIME|AT_ATIME); 13140bfaec69Sllai1 } else { 13150bfaec69Sllai1 ASSERT(nddv->sdev_attr); 13160bfaec69Sllai1 gethrestime(&now); 13170bfaec69Sllai1 nddv->sdev_attr->va_mtime = now; 13180bfaec69Sllai1 nddv->sdev_attr->va_atime = now; 13190bfaec69Sllai1 } 13200bfaec69Sllai1 rw_exit(&nddv->sdev_contents); 13210bfaec69Sllai1 if (!samedir) 13220bfaec69Sllai1 rw_exit(&oddv->sdev_contents); 13230bfaec69Sllai1 1324facf4a8dSllai1 SDEV_RELE(*ndvp); 13250bfaec69Sllai1 return (error); 13260bfaec69Sllai1 13270bfaec69Sllai1 err_out: 1328*9e5aa9d8SRobert Mustacchi if (link != NULL) { 1329*9e5aa9d8SRobert Mustacchi kmem_free(link, strlen(link) + 1); 1330*9e5aa9d8SRobert Mustacchi link = NULL; 1331*9e5aa9d8SRobert Mustacchi } 1332*9e5aa9d8SRobert Mustacchi 13330bfaec69Sllai1 rw_exit(&nddv->sdev_contents); 13340bfaec69Sllai1 if (!samedir) 13350bfaec69Sllai1 rw_exit(&oddv->sdev_contents); 13360bfaec69Sllai1 return (error); 1337facf4a8dSllai1 } 1338facf4a8dSllai1 1339facf4a8dSllai1 /* 1340facf4a8dSllai1 * Merge sdev_node specific information into an attribute structure. 1341facf4a8dSllai1 * 1342facf4a8dSllai1 * note: sdev_node is not locked here 1343facf4a8dSllai1 */ 1344facf4a8dSllai1 void 1345facf4a8dSllai1 sdev_vattr_merge(struct sdev_node *dv, struct vattr *vap) 1346facf4a8dSllai1 { 1347facf4a8dSllai1 struct vnode *vp = SDEVTOV(dv); 1348facf4a8dSllai1 1349facf4a8dSllai1 vap->va_nlink = dv->sdev_nlink; 1350facf4a8dSllai1 vap->va_nodeid = dv->sdev_ino; 1351facf4a8dSllai1 vap->va_fsid = SDEVTOV(dv->sdev_dotdot)->v_rdev; 1352facf4a8dSllai1 vap->va_type = vp->v_type; 1353facf4a8dSllai1 1354facf4a8dSllai1 if (vp->v_type == VDIR) { 1355facf4a8dSllai1 vap->va_rdev = 0; 1356facf4a8dSllai1 vap->va_fsid = vp->v_rdev; 1357facf4a8dSllai1 } else if (vp->v_type == VLNK) { 1358facf4a8dSllai1 vap->va_rdev = 0; 1359facf4a8dSllai1 vap->va_mode &= ~S_IFMT; 1360facf4a8dSllai1 vap->va_mode |= S_IFLNK; 1361facf4a8dSllai1 } else if ((vp->v_type == VCHR) || (vp->v_type == VBLK)) { 1362facf4a8dSllai1 vap->va_rdev = vp->v_rdev; 1363facf4a8dSllai1 vap->va_mode &= ~S_IFMT; 1364facf4a8dSllai1 if (vap->va_type == VCHR) 1365facf4a8dSllai1 vap->va_mode |= S_IFCHR; 1366facf4a8dSllai1 else 1367facf4a8dSllai1 vap->va_mode |= S_IFBLK; 1368facf4a8dSllai1 } else { 1369facf4a8dSllai1 vap->va_rdev = 0; 1370facf4a8dSllai1 } 1371facf4a8dSllai1 } 1372facf4a8dSllai1 1373681d9761SEric Taylor struct vattr * 1374facf4a8dSllai1 sdev_getdefault_attr(enum vtype type) 1375facf4a8dSllai1 { 1376facf4a8dSllai1 if (type == VDIR) 1377facf4a8dSllai1 return (&sdev_vattr_dir); 1378facf4a8dSllai1 else if (type == VCHR) 1379facf4a8dSllai1 return (&sdev_vattr_chr); 1380facf4a8dSllai1 else if (type == VBLK) 1381facf4a8dSllai1 return (&sdev_vattr_blk); 1382facf4a8dSllai1 else if (type == VLNK) 1383facf4a8dSllai1 return (&sdev_vattr_lnk); 1384facf4a8dSllai1 else 1385facf4a8dSllai1 return (NULL); 1386facf4a8dSllai1 } 1387facf4a8dSllai1 int 1388facf4a8dSllai1 sdev_to_vp(struct sdev_node *dv, struct vnode **vpp) 1389facf4a8dSllai1 { 1390facf4a8dSllai1 int rv = 0; 1391facf4a8dSllai1 struct vnode *vp = SDEVTOV(dv); 1392facf4a8dSllai1 1393facf4a8dSllai1 switch (vp->v_type) { 1394facf4a8dSllai1 case VCHR: 1395facf4a8dSllai1 case VBLK: 1396facf4a8dSllai1 /* 1397facf4a8dSllai1 * If vnode is a device, return special vnode instead 1398facf4a8dSllai1 * (though it knows all about -us- via sp->s_realvp) 1399facf4a8dSllai1 */ 1400facf4a8dSllai1 *vpp = specvp(vp, vp->v_rdev, vp->v_type, kcred); 1401facf4a8dSllai1 VN_RELE(vp); 1402facf4a8dSllai1 if (*vpp == NULLVP) 1403facf4a8dSllai1 rv = ENOSYS; 1404facf4a8dSllai1 break; 1405facf4a8dSllai1 default: /* most types are returned as is */ 1406facf4a8dSllai1 *vpp = vp; 1407facf4a8dSllai1 break; 1408facf4a8dSllai1 } 1409facf4a8dSllai1 return (rv); 1410facf4a8dSllai1 } 1411facf4a8dSllai1 1412facf4a8dSllai1 /* 1413facf4a8dSllai1 * junction between devname and root file system, e.g. ufs 1414facf4a8dSllai1 */ 1415facf4a8dSllai1 int 1416facf4a8dSllai1 devname_backstore_lookup(struct sdev_node *ddv, char *nm, struct vnode **rvp) 1417facf4a8dSllai1 { 1418facf4a8dSllai1 struct vnode *rdvp = ddv->sdev_attrvp; 1419facf4a8dSllai1 int rval = 0; 1420facf4a8dSllai1 1421facf4a8dSllai1 ASSERT(rdvp); 1422facf4a8dSllai1 1423da6c28aaSamw rval = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, kcred, NULL, NULL, 1424da6c28aaSamw NULL); 1425facf4a8dSllai1 return (rval); 1426facf4a8dSllai1 } 1427facf4a8dSllai1 1428facf4a8dSllai1 static int 1429facf4a8dSllai1 sdev_filldir_from_store(struct sdev_node *ddv, int dlen, struct cred *cred) 1430facf4a8dSllai1 { 1431facf4a8dSllai1 struct sdev_node *dv = NULL; 1432facf4a8dSllai1 char *nm; 1433facf4a8dSllai1 struct vnode *dirvp; 1434facf4a8dSllai1 int error; 1435facf4a8dSllai1 vnode_t *vp; 1436facf4a8dSllai1 int eof; 1437facf4a8dSllai1 struct iovec iov; 1438facf4a8dSllai1 struct uio uio; 1439facf4a8dSllai1 struct dirent64 *dp; 1440facf4a8dSllai1 dirent64_t *dbuf; 1441facf4a8dSllai1 size_t dbuflen; 1442facf4a8dSllai1 struct vattr vattr; 1443facf4a8dSllai1 char *link = NULL; 1444facf4a8dSllai1 1445facf4a8dSllai1 if (ddv->sdev_attrvp == NULL) 1446facf4a8dSllai1 return (0); 1447facf4a8dSllai1 if (!(ddv->sdev_flags & SDEV_BUILD)) 1448facf4a8dSllai1 return (0); 1449facf4a8dSllai1 1450facf4a8dSllai1 dirvp = ddv->sdev_attrvp; 1451facf4a8dSllai1 VN_HOLD(dirvp); 1452facf4a8dSllai1 dbuf = kmem_zalloc(dlen, KM_SLEEP); 1453facf4a8dSllai1 1454facf4a8dSllai1 uio.uio_iov = &iov; 1455facf4a8dSllai1 uio.uio_iovcnt = 1; 1456facf4a8dSllai1 uio.uio_segflg = UIO_SYSSPACE; 1457facf4a8dSllai1 uio.uio_fmode = 0; 1458facf4a8dSllai1 uio.uio_extflg = UIO_COPY_CACHED; 1459facf4a8dSllai1 uio.uio_loffset = 0; 1460facf4a8dSllai1 uio.uio_llimit = MAXOFFSET_T; 1461facf4a8dSllai1 1462facf4a8dSllai1 eof = 0; 1463facf4a8dSllai1 error = 0; 1464facf4a8dSllai1 while (!error && !eof) { 1465facf4a8dSllai1 uio.uio_resid = dlen; 1466facf4a8dSllai1 iov.iov_base = (char *)dbuf; 1467facf4a8dSllai1 iov.iov_len = dlen; 1468facf4a8dSllai1 (void) VOP_RWLOCK(dirvp, V_WRITELOCK_FALSE, NULL); 1469da6c28aaSamw error = VOP_READDIR(dirvp, &uio, kcred, &eof, NULL, 0); 1470facf4a8dSllai1 VOP_RWUNLOCK(dirvp, V_WRITELOCK_FALSE, NULL); 1471facf4a8dSllai1 1472facf4a8dSllai1 dbuflen = dlen - uio.uio_resid; 1473facf4a8dSllai1 if (error || dbuflen == 0) 1474facf4a8dSllai1 break; 1475facf4a8dSllai1 1476681d9761SEric Taylor if (!(ddv->sdev_flags & SDEV_BUILD)) 1477facf4a8dSllai1 break; 1478facf4a8dSllai1 1479facf4a8dSllai1 for (dp = dbuf; ((intptr_t)dp < 1480facf4a8dSllai1 (intptr_t)dbuf + dbuflen); 1481facf4a8dSllai1 dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) { 1482facf4a8dSllai1 nm = dp->d_name; 1483facf4a8dSllai1 1484facf4a8dSllai1 if (strcmp(nm, ".") == 0 || 1485facf4a8dSllai1 strcmp(nm, "..") == 0) 1486facf4a8dSllai1 continue; 1487facf4a8dSllai1 1488facf4a8dSllai1 vp = NULLVP; 1489facf4a8dSllai1 dv = sdev_cache_lookup(ddv, nm); 1490facf4a8dSllai1 if (dv) { 1491*9e5aa9d8SRobert Mustacchi VERIFY(dv->sdev_state != SDEV_ZOMBIE); 1492facf4a8dSllai1 SDEV_SIMPLE_RELE(dv); 1493facf4a8dSllai1 continue; 1494facf4a8dSllai1 } 1495facf4a8dSllai1 1496facf4a8dSllai1 /* refill the cache if not already */ 1497facf4a8dSllai1 error = devname_backstore_lookup(ddv, nm, &vp); 1498facf4a8dSllai1 if (error) 1499facf4a8dSllai1 continue; 1500facf4a8dSllai1 1501bb5fffbeSJerry Gilliam vattr.va_mask = AT_TYPE|AT_MODE|AT_UID|AT_GID; 1502da6c28aaSamw error = VOP_GETATTR(vp, &vattr, 0, cred, NULL); 1503facf4a8dSllai1 if (error) 1504facf4a8dSllai1 continue; 1505facf4a8dSllai1 1506facf4a8dSllai1 if (vattr.va_type == VLNK) { 1507facf4a8dSllai1 error = sdev_getlink(vp, &link); 1508facf4a8dSllai1 if (error) { 1509facf4a8dSllai1 continue; 1510facf4a8dSllai1 } 1511facf4a8dSllai1 ASSERT(link != NULL); 1512facf4a8dSllai1 } 1513facf4a8dSllai1 1514facf4a8dSllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 1515facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 1516facf4a8dSllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 1517facf4a8dSllai1 } 1518facf4a8dSllai1 error = sdev_mknode(ddv, nm, &dv, &vattr, vp, link, 1519facf4a8dSllai1 cred, SDEV_READY); 1520facf4a8dSllai1 rw_downgrade(&ddv->sdev_contents); 1521facf4a8dSllai1 1522facf4a8dSllai1 if (link != NULL) { 1523facf4a8dSllai1 kmem_free(link, strlen(link) + 1); 1524facf4a8dSllai1 link = NULL; 1525facf4a8dSllai1 } 1526facf4a8dSllai1 1527facf4a8dSllai1 if (!error) { 1528facf4a8dSllai1 ASSERT(dv); 1529facf4a8dSllai1 ASSERT(dv->sdev_state != SDEV_ZOMBIE); 1530facf4a8dSllai1 SDEV_SIMPLE_RELE(dv); 1531facf4a8dSllai1 } 1532facf4a8dSllai1 vp = NULL; 1533facf4a8dSllai1 dv = NULL; 1534facf4a8dSllai1 } 1535facf4a8dSllai1 } 1536facf4a8dSllai1 1537facf4a8dSllai1 done: 1538facf4a8dSllai1 VN_RELE(dirvp); 1539facf4a8dSllai1 kmem_free(dbuf, dlen); 1540facf4a8dSllai1 1541facf4a8dSllai1 return (error); 1542facf4a8dSllai1 } 1543facf4a8dSllai1 15446b938478Sjg void 1545facf4a8dSllai1 sdev_filldir_dynamic(struct sdev_node *ddv) 1546facf4a8dSllai1 { 1547facf4a8dSllai1 int error; 1548facf4a8dSllai1 int i; 1549bb5fffbeSJerry Gilliam struct vattr vattr; 1550bb5fffbeSJerry Gilliam struct vattr *vap = &vattr; 1551facf4a8dSllai1 char *nm = NULL; 1552facf4a8dSllai1 struct sdev_node *dv = NULL; 1553facf4a8dSllai1 15546b938478Sjg ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 15556b938478Sjg ASSERT((ddv->sdev_flags & SDEV_BUILD)); 1556facf4a8dSllai1 1557bb5fffbeSJerry Gilliam *vap = *sdev_getdefault_attr(VDIR); /* note structure copy here */ 1558681d9761SEric Taylor gethrestime(&vap->va_atime); 1559681d9761SEric Taylor vap->va_mtime = vap->va_atime; 1560681d9761SEric Taylor vap->va_ctime = vap->va_atime; 1561facf4a8dSllai1 for (i = 0; vtab[i].vt_name != NULL; i++) { 156267323fc4SJohn Levon /* 1563*9e5aa9d8SRobert Mustacchi * This early, we may be in a read-only /dev environment: leave 1564*9e5aa9d8SRobert Mustacchi * the creation of any nodes we'd attempt to persist to 1565*9e5aa9d8SRobert Mustacchi * devfsadm. Because /dev itself is normally persistent, any 1566*9e5aa9d8SRobert Mustacchi * node which is not marked dynamic will end up being marked 1567*9e5aa9d8SRobert Mustacchi * persistent. However, some nodes are both dynamic and 1568*9e5aa9d8SRobert Mustacchi * persistent, mostly lofi and rlofi, so we need to be careful 1569*9e5aa9d8SRobert Mustacchi * in our check. 157067323fc4SJohn Levon */ 1571*9e5aa9d8SRobert Mustacchi if ((vtab[i].vt_flags & SDEV_PERSIST) || 1572*9e5aa9d8SRobert Mustacchi !(vtab[i].vt_flags & SDEV_DYNAMIC)) 157367323fc4SJohn Levon continue; 1574facf4a8dSllai1 nm = vtab[i].vt_name; 1575facf4a8dSllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 15766b938478Sjg dv = NULL; 1577facf4a8dSllai1 error = sdev_mknode(ddv, nm, &dv, vap, NULL, 1578facf4a8dSllai1 NULL, kcred, SDEV_READY); 15796b938478Sjg if (error) { 15806b938478Sjg cmn_err(CE_WARN, "%s/%s: error %d\n", 15816b938478Sjg ddv->sdev_name, nm, error); 15826b938478Sjg } else { 1583facf4a8dSllai1 ASSERT(dv); 1584facf4a8dSllai1 ASSERT(dv->sdev_state != SDEV_ZOMBIE); 1585facf4a8dSllai1 SDEV_SIMPLE_RELE(dv); 1586facf4a8dSllai1 } 15876b938478Sjg } 1588facf4a8dSllai1 } 1589facf4a8dSllai1 1590facf4a8dSllai1 /* 1591facf4a8dSllai1 * Creating a backing store entry based on sdev_attr. 1592facf4a8dSllai1 * This is called either as part of node creation in a persistent directory 1593facf4a8dSllai1 * or from setattr/setsecattr to persist access attributes across reboot. 1594facf4a8dSllai1 */ 1595facf4a8dSllai1 int 1596facf4a8dSllai1 sdev_shadow_node(struct sdev_node *dv, struct cred *cred) 1597facf4a8dSllai1 { 1598facf4a8dSllai1 int error = 0; 1599facf4a8dSllai1 struct vnode *dvp = SDEVTOV(dv->sdev_dotdot); 1600facf4a8dSllai1 struct vnode *rdvp = VTOSDEV(dvp)->sdev_attrvp; 1601facf4a8dSllai1 struct vattr *vap = dv->sdev_attr; 1602facf4a8dSllai1 char *nm = dv->sdev_name; 1603facf4a8dSllai1 struct vnode *tmpvp, **rvp = &tmpvp, *rrvp = NULL; 1604facf4a8dSllai1 1605facf4a8dSllai1 ASSERT(dv && dv->sdev_name && rdvp); 1606facf4a8dSllai1 ASSERT(RW_WRITE_HELD(&dv->sdev_contents) && dv->sdev_attrvp == NULL); 1607facf4a8dSllai1 1608facf4a8dSllai1 lookup: 1609facf4a8dSllai1 /* try to find it in the backing store */ 1610da6c28aaSamw error = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, cred, NULL, NULL, 1611da6c28aaSamw NULL); 1612facf4a8dSllai1 if (error == 0) { 1613da6c28aaSamw if (VOP_REALVP(*rvp, &rrvp, NULL) == 0) { 1614facf4a8dSllai1 VN_HOLD(rrvp); 1615facf4a8dSllai1 VN_RELE(*rvp); 1616facf4a8dSllai1 *rvp = rrvp; 1617facf4a8dSllai1 } 1618facf4a8dSllai1 1619facf4a8dSllai1 kmem_free(dv->sdev_attr, sizeof (vattr_t)); 1620facf4a8dSllai1 dv->sdev_attr = NULL; 1621facf4a8dSllai1 dv->sdev_attrvp = *rvp; 1622facf4a8dSllai1 return (0); 1623facf4a8dSllai1 } 1624facf4a8dSllai1 1625facf4a8dSllai1 /* let's try to persist the node */ 1626facf4a8dSllai1 gethrestime(&vap->va_atime); 1627facf4a8dSllai1 vap->va_mtime = vap->va_atime; 1628facf4a8dSllai1 vap->va_ctime = vap->va_atime; 1629facf4a8dSllai1 vap->va_mask |= AT_TYPE|AT_MODE; 1630facf4a8dSllai1 switch (vap->va_type) { 1631facf4a8dSllai1 case VDIR: 1632da6c28aaSamw error = VOP_MKDIR(rdvp, nm, vap, rvp, cred, NULL, 0, NULL); 1633facf4a8dSllai1 sdcmn_err9(("sdev_shadow_node: mkdir vp %p error %d\n", 1634facf4a8dSllai1 (void *)(*rvp), error)); 1635*9e5aa9d8SRobert Mustacchi if (!error) 1636*9e5aa9d8SRobert Mustacchi VN_RELE(*rvp); 1637facf4a8dSllai1 break; 1638facf4a8dSllai1 case VCHR: 1639facf4a8dSllai1 case VBLK: 1640facf4a8dSllai1 case VREG: 1641facf4a8dSllai1 case VDOOR: 1642facf4a8dSllai1 error = VOP_CREATE(rdvp, nm, vap, NONEXCL, VREAD|VWRITE, 1643da6c28aaSamw rvp, cred, 0, NULL, NULL); 1644facf4a8dSllai1 sdcmn_err9(("sdev_shadow_node: create vp %p, error %d\n", 1645facf4a8dSllai1 (void *)(*rvp), error)); 1646facf4a8dSllai1 if (!error) 1647facf4a8dSllai1 VN_RELE(*rvp); 1648facf4a8dSllai1 break; 1649facf4a8dSllai1 case VLNK: 1650facf4a8dSllai1 ASSERT(dv->sdev_symlink); 1651da6c28aaSamw error = VOP_SYMLINK(rdvp, nm, vap, dv->sdev_symlink, cred, 1652da6c28aaSamw NULL, 0); 1653facf4a8dSllai1 sdcmn_err9(("sdev_shadow_node: create symlink error %d\n", 1654facf4a8dSllai1 error)); 1655facf4a8dSllai1 break; 1656facf4a8dSllai1 default: 1657facf4a8dSllai1 cmn_err(CE_PANIC, "dev: %s: sdev_shadow_node " 1658facf4a8dSllai1 "create\n", nm); 1659facf4a8dSllai1 /*NOTREACHED*/ 1660facf4a8dSllai1 } 1661facf4a8dSllai1 1662facf4a8dSllai1 /* go back to lookup to factor out spec node and set attrvp */ 1663facf4a8dSllai1 if (error == 0) 1664facf4a8dSllai1 goto lookup; 1665facf4a8dSllai1 1666b7beec95Sjg sdcmn_err(("cannot persist %s - error %d\n", dv->sdev_path, error)); 1667facf4a8dSllai1 return (error); 1668facf4a8dSllai1 } 1669facf4a8dSllai1 1670*9e5aa9d8SRobert Mustacchi static void 1671facf4a8dSllai1 sdev_cache_add(struct sdev_node *ddv, struct sdev_node **dv, char *nm) 1672facf4a8dSllai1 { 1673facf4a8dSllai1 struct sdev_node *dup = NULL; 1674facf4a8dSllai1 1675facf4a8dSllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 1676facf4a8dSllai1 if ((dup = sdev_findbyname(ddv, nm)) == NULL) { 1677facf4a8dSllai1 sdev_direnter(ddv, *dv); 1678facf4a8dSllai1 } else { 1679*9e5aa9d8SRobert Mustacchi VERIFY(dup->sdev_state != SDEV_ZOMBIE); 1680facf4a8dSllai1 SDEV_SIMPLE_RELE(*dv); 1681facf4a8dSllai1 sdev_nodedestroy(*dv, 0); 1682facf4a8dSllai1 *dv = dup; 1683facf4a8dSllai1 } 1684facf4a8dSllai1 } 1685facf4a8dSllai1 1686*9e5aa9d8SRobert Mustacchi static void 1687facf4a8dSllai1 sdev_cache_delete(struct sdev_node *ddv, struct sdev_node **dv) 1688facf4a8dSllai1 { 1689facf4a8dSllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 1690*9e5aa9d8SRobert Mustacchi sdev_dirdelete(ddv, *dv); 1691facf4a8dSllai1 } 1692facf4a8dSllai1 1693facf4a8dSllai1 /* 1694facf4a8dSllai1 * update the in-core directory cache 1695facf4a8dSllai1 */ 1696*9e5aa9d8SRobert Mustacchi void 1697facf4a8dSllai1 sdev_cache_update(struct sdev_node *ddv, struct sdev_node **dv, char *nm, 1698facf4a8dSllai1 sdev_cache_ops_t ops) 1699facf4a8dSllai1 { 1700facf4a8dSllai1 ASSERT((SDEV_HELD(*dv))); 1701facf4a8dSllai1 1702facf4a8dSllai1 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 1703facf4a8dSllai1 switch (ops) { 1704facf4a8dSllai1 case SDEV_CACHE_ADD: 1705*9e5aa9d8SRobert Mustacchi sdev_cache_add(ddv, dv, nm); 1706facf4a8dSllai1 break; 1707facf4a8dSllai1 case SDEV_CACHE_DELETE: 1708*9e5aa9d8SRobert Mustacchi sdev_cache_delete(ddv, dv); 1709facf4a8dSllai1 break; 1710facf4a8dSllai1 default: 1711facf4a8dSllai1 break; 1712facf4a8dSllai1 } 1713facf4a8dSllai1 } 1714facf4a8dSllai1 1715facf4a8dSllai1 /* 1716da6c28aaSamw * retrieve the named entry from the directory cache 1717facf4a8dSllai1 */ 1718facf4a8dSllai1 struct sdev_node * 1719facf4a8dSllai1 sdev_cache_lookup(struct sdev_node *ddv, char *nm) 1720facf4a8dSllai1 { 1721facf4a8dSllai1 struct sdev_node *dv = NULL; 1722facf4a8dSllai1 1723facf4a8dSllai1 ASSERT(RW_LOCK_HELD(&ddv->sdev_contents)); 1724facf4a8dSllai1 dv = sdev_findbyname(ddv, nm); 1725facf4a8dSllai1 1726facf4a8dSllai1 return (dv); 1727facf4a8dSllai1 } 1728facf4a8dSllai1 1729facf4a8dSllai1 /* 1730facf4a8dSllai1 * Implicit reconfig for nodes constructed by a link generator 1731facf4a8dSllai1 * Start devfsadm if needed, or if devfsadm is in progress, 1732facf4a8dSllai1 * prepare to block on devfsadm either completing or 1733facf4a8dSllai1 * constructing the desired node. As devfsadmd is global 1734facf4a8dSllai1 * in scope, constructing all necessary nodes, we only 1735facf4a8dSllai1 * need to initiate it once. 1736facf4a8dSllai1 */ 1737facf4a8dSllai1 static int 1738facf4a8dSllai1 sdev_call_devfsadmd(struct sdev_node *ddv, struct sdev_node *dv, char *nm) 1739facf4a8dSllai1 { 1740facf4a8dSllai1 int error = 0; 1741facf4a8dSllai1 1742facf4a8dSllai1 if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) { 1743facf4a8dSllai1 sdcmn_err6(("lookup: waiting for %s/%s, 0x%x\n", 1744facf4a8dSllai1 ddv->sdev_name, nm, devfsadm_state)); 1745facf4a8dSllai1 mutex_enter(&dv->sdev_lookup_lock); 1746facf4a8dSllai1 SDEV_BLOCK_OTHERS(dv, (SDEV_LOOKUP | SDEV_LGWAITING)); 1747facf4a8dSllai1 mutex_exit(&dv->sdev_lookup_lock); 1748facf4a8dSllai1 error = 0; 1749facf4a8dSllai1 } else if (!DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state)) { 1750facf4a8dSllai1 sdcmn_err6(("lookup %s/%s starting devfsadm, 0x%x\n", 1751facf4a8dSllai1 ddv->sdev_name, nm, devfsadm_state)); 1752facf4a8dSllai1 1753facf4a8dSllai1 sdev_devfsadmd_thread(ddv, dv, kcred); 1754facf4a8dSllai1 mutex_enter(&dv->sdev_lookup_lock); 1755facf4a8dSllai1 SDEV_BLOCK_OTHERS(dv, 1756facf4a8dSllai1 (SDEV_LOOKUP | SDEV_LGWAITING)); 1757facf4a8dSllai1 mutex_exit(&dv->sdev_lookup_lock); 1758facf4a8dSllai1 error = 0; 1759facf4a8dSllai1 } else { 1760facf4a8dSllai1 error = -1; 1761facf4a8dSllai1 } 1762facf4a8dSllai1 1763facf4a8dSllai1 return (error); 1764facf4a8dSllai1 } 1765facf4a8dSllai1 1766facf4a8dSllai1 /* 1767facf4a8dSllai1 * Support for specialized device naming construction mechanisms 1768facf4a8dSllai1 */ 1769facf4a8dSllai1 static int 1770facf4a8dSllai1 sdev_call_dircallback(struct sdev_node *ddv, struct sdev_node **dvp, char *nm, 1771facf4a8dSllai1 int (*callback)(struct sdev_node *, char *, void **, struct cred *, 1772facf4a8dSllai1 void *, char *), int flags, struct cred *cred) 1773facf4a8dSllai1 { 1774facf4a8dSllai1 int rv = 0; 1775facf4a8dSllai1 char *physpath = NULL; 1776facf4a8dSllai1 struct vattr vattr; 1777bb5fffbeSJerry Gilliam struct vattr *vap = &vattr; 1778681d9761SEric Taylor struct sdev_node *dv = NULL; 1779facf4a8dSllai1 1780681d9761SEric Taylor ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 1781681d9761SEric Taylor if (flags & SDEV_VLINK) { 1782aecfc01dSrui zang - Sun Microsystems - Beijing China physpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 1783aecfc01dSrui zang - Sun Microsystems - Beijing China rv = callback(ddv, nm, (void *)&physpath, kcred, NULL, 1784aecfc01dSrui zang - Sun Microsystems - Beijing China NULL); 1785aecfc01dSrui zang - Sun Microsystems - Beijing China if (rv) { 1786aecfc01dSrui zang - Sun Microsystems - Beijing China kmem_free(physpath, MAXPATHLEN); 1787aecfc01dSrui zang - Sun Microsystems - Beijing China return (-1); 1788aecfc01dSrui zang - Sun Microsystems - Beijing China } 1789aecfc01dSrui zang - Sun Microsystems - Beijing China 1790bb5fffbeSJerry Gilliam *vap = *sdev_getdefault_attr(VLNK); /* structure copy */ 1791aecfc01dSrui zang - Sun Microsystems - Beijing China vap->va_size = strlen(physpath); 1792681d9761SEric Taylor gethrestime(&vap->va_atime); 1793681d9761SEric Taylor vap->va_mtime = vap->va_atime; 1794681d9761SEric Taylor vap->va_ctime = vap->va_atime; 1795aecfc01dSrui zang - Sun Microsystems - Beijing China 1796aecfc01dSrui zang - Sun Microsystems - Beijing China rv = sdev_mknode(ddv, nm, &dv, vap, NULL, 1797aecfc01dSrui zang - Sun Microsystems - Beijing China (void *)physpath, cred, SDEV_READY); 1798aecfc01dSrui zang - Sun Microsystems - Beijing China kmem_free(physpath, MAXPATHLEN); 1799aecfc01dSrui zang - Sun Microsystems - Beijing China if (rv) 1800aecfc01dSrui zang - Sun Microsystems - Beijing China return (rv); 1801facf4a8dSllai1 } else if (flags & SDEV_VATTR) { 1802facf4a8dSllai1 /* 1803facf4a8dSllai1 * /dev/pts 1804facf4a8dSllai1 * 1805facf4a8dSllai1 * callback is responsible to set the basic attributes, 1806facf4a8dSllai1 * e.g. va_type/va_uid/va_gid/ 1807facf4a8dSllai1 * dev_t if VCHR or VBLK/ 1808facf4a8dSllai1 */ 1809facf4a8dSllai1 ASSERT(callback); 1810facf4a8dSllai1 rv = callback(ddv, nm, (void *)&vattr, kcred, NULL, NULL); 1811facf4a8dSllai1 if (rv) { 1812facf4a8dSllai1 sdcmn_err3(("devname_lookup_func: SDEV_NONE " 1813facf4a8dSllai1 "callback failed \n")); 1814facf4a8dSllai1 return (-1); 1815facf4a8dSllai1 } 1816facf4a8dSllai1 1817facf4a8dSllai1 rv = sdev_mknode(ddv, nm, &dv, &vattr, NULL, NULL, 1818facf4a8dSllai1 cred, SDEV_READY); 1819facf4a8dSllai1 1820facf4a8dSllai1 if (rv) 1821facf4a8dSllai1 return (rv); 1822facf4a8dSllai1 1823facf4a8dSllai1 } else { 1824facf4a8dSllai1 impossible(("lookup: %s/%s by %s not supported (%d)\n", 1825facf4a8dSllai1 SDEVTOV(ddv)->v_path, nm, curproc->p_user.u_comm, 1826facf4a8dSllai1 __LINE__)); 1827facf4a8dSllai1 rv = -1; 1828facf4a8dSllai1 } 1829facf4a8dSllai1 1830facf4a8dSllai1 *dvp = dv; 1831facf4a8dSllai1 return (rv); 1832facf4a8dSllai1 } 1833facf4a8dSllai1 1834facf4a8dSllai1 static int 1835facf4a8dSllai1 is_devfsadm_thread(char *exec_name) 1836facf4a8dSllai1 { 1837facf4a8dSllai1 /* 1838facf4a8dSllai1 * note: because devfsadmd -> /usr/sbin/devfsadm 1839facf4a8dSllai1 * it is safe to use "devfsadm" to capture the lookups 1840facf4a8dSllai1 * from devfsadm and its daemon version. 1841facf4a8dSllai1 */ 1842facf4a8dSllai1 if (strcmp(exec_name, "devfsadm") == 0) 1843facf4a8dSllai1 return (1); 1844facf4a8dSllai1 return (0); 1845facf4a8dSllai1 } 1846facf4a8dSllai1 1847facf4a8dSllai1 /* 1848facf4a8dSllai1 * Lookup Order: 1849facf4a8dSllai1 * sdev_node cache; 1850facf4a8dSllai1 * backing store (SDEV_PERSIST); 1851facf4a8dSllai1 * DBNR: a. dir_ops implemented in the loadable modules; 1852facf4a8dSllai1 * b. vnode ops in vtab. 1853facf4a8dSllai1 */ 1854facf4a8dSllai1 int 1855facf4a8dSllai1 devname_lookup_func(struct sdev_node *ddv, char *nm, struct vnode **vpp, 1856facf4a8dSllai1 struct cred *cred, int (*callback)(struct sdev_node *, char *, void **, 1857facf4a8dSllai1 struct cred *, void *, char *), int flags) 1858facf4a8dSllai1 { 1859facf4a8dSllai1 int rv = 0, nmlen; 1860facf4a8dSllai1 struct vnode *rvp = NULL; 1861facf4a8dSllai1 struct sdev_node *dv = NULL; 1862facf4a8dSllai1 int retried = 0; 1863facf4a8dSllai1 int error = 0; 1864facf4a8dSllai1 struct vattr vattr; 1865facf4a8dSllai1 char *lookup_thread = curproc->p_user.u_comm; 1866facf4a8dSllai1 int failed_flags = 0; 1867facf4a8dSllai1 int (*vtor)(struct sdev_node *) = NULL; 1868facf4a8dSllai1 int state; 1869facf4a8dSllai1 int parent_state; 1870facf4a8dSllai1 char *link = NULL; 1871facf4a8dSllai1 1872facf4a8dSllai1 if (SDEVTOV(ddv)->v_type != VDIR) 1873facf4a8dSllai1 return (ENOTDIR); 1874facf4a8dSllai1 1875facf4a8dSllai1 /* 1876facf4a8dSllai1 * Empty name or ., return node itself. 1877facf4a8dSllai1 */ 1878facf4a8dSllai1 nmlen = strlen(nm); 1879facf4a8dSllai1 if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) { 1880facf4a8dSllai1 *vpp = SDEVTOV(ddv); 1881facf4a8dSllai1 VN_HOLD(*vpp); 1882facf4a8dSllai1 return (0); 1883facf4a8dSllai1 } 1884facf4a8dSllai1 1885facf4a8dSllai1 /* 1886facf4a8dSllai1 * .., return the parent directory 1887facf4a8dSllai1 */ 1888facf4a8dSllai1 if ((nmlen == 2) && (strcmp(nm, "..") == 0)) { 1889facf4a8dSllai1 *vpp = SDEVTOV(ddv->sdev_dotdot); 1890facf4a8dSllai1 VN_HOLD(*vpp); 1891facf4a8dSllai1 return (0); 1892facf4a8dSllai1 } 1893facf4a8dSllai1 1894facf4a8dSllai1 rw_enter(&ddv->sdev_contents, RW_READER); 1895facf4a8dSllai1 if (ddv->sdev_flags & SDEV_VTOR) { 1896facf4a8dSllai1 vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv); 1897facf4a8dSllai1 ASSERT(vtor); 1898facf4a8dSllai1 } 1899facf4a8dSllai1 1900facf4a8dSllai1 tryagain: 1901facf4a8dSllai1 /* 1902facf4a8dSllai1 * (a) directory cache lookup: 1903facf4a8dSllai1 */ 1904facf4a8dSllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 1905facf4a8dSllai1 parent_state = ddv->sdev_state; 1906facf4a8dSllai1 dv = sdev_cache_lookup(ddv, nm); 1907facf4a8dSllai1 if (dv) { 1908facf4a8dSllai1 state = dv->sdev_state; 1909facf4a8dSllai1 switch (state) { 1910facf4a8dSllai1 case SDEV_INIT: 1911facf4a8dSllai1 if (is_devfsadm_thread(lookup_thread)) 1912facf4a8dSllai1 break; 1913facf4a8dSllai1 1914facf4a8dSllai1 /* ZOMBIED parent won't allow node creation */ 1915facf4a8dSllai1 if (parent_state == SDEV_ZOMBIE) { 1916facf4a8dSllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, 1917facf4a8dSllai1 retried); 1918facf4a8dSllai1 goto nolock_notfound; 1919facf4a8dSllai1 } 1920facf4a8dSllai1 1921facf4a8dSllai1 mutex_enter(&dv->sdev_lookup_lock); 1922facf4a8dSllai1 /* compensate the threads started after devfsadm */ 1923facf4a8dSllai1 if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) && 1924facf4a8dSllai1 !(SDEV_IS_LOOKUP(dv))) 1925facf4a8dSllai1 SDEV_BLOCK_OTHERS(dv, 1926facf4a8dSllai1 (SDEV_LOOKUP | SDEV_LGWAITING)); 1927facf4a8dSllai1 1928facf4a8dSllai1 if (SDEV_IS_LOOKUP(dv)) { 1929facf4a8dSllai1 failed_flags |= SLF_REBUILT; 1930facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 1931facf4a8dSllai1 error = sdev_wait4lookup(dv, SDEV_LOOKUP); 1932facf4a8dSllai1 mutex_exit(&dv->sdev_lookup_lock); 1933facf4a8dSllai1 rw_enter(&ddv->sdev_contents, RW_READER); 1934facf4a8dSllai1 1935facf4a8dSllai1 if (error != 0) { 1936facf4a8dSllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, 1937facf4a8dSllai1 retried); 1938facf4a8dSllai1 goto nolock_notfound; 1939facf4a8dSllai1 } 1940facf4a8dSllai1 1941facf4a8dSllai1 state = dv->sdev_state; 1942facf4a8dSllai1 if (state == SDEV_INIT) { 1943facf4a8dSllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, 1944facf4a8dSllai1 retried); 1945facf4a8dSllai1 goto nolock_notfound; 1946facf4a8dSllai1 } else if (state == SDEV_READY) { 1947facf4a8dSllai1 goto found; 1948facf4a8dSllai1 } else if (state == SDEV_ZOMBIE) { 1949facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 1950facf4a8dSllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, 1951facf4a8dSllai1 retried); 1952facf4a8dSllai1 SDEV_RELE(dv); 1953facf4a8dSllai1 goto lookup_failed; 1954facf4a8dSllai1 } 1955facf4a8dSllai1 } else { 1956facf4a8dSllai1 mutex_exit(&dv->sdev_lookup_lock); 1957facf4a8dSllai1 } 1958facf4a8dSllai1 break; 1959facf4a8dSllai1 case SDEV_READY: 1960facf4a8dSllai1 goto found; 1961facf4a8dSllai1 case SDEV_ZOMBIE: 1962facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 1963facf4a8dSllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 1964facf4a8dSllai1 SDEV_RELE(dv); 1965facf4a8dSllai1 goto lookup_failed; 1966facf4a8dSllai1 default: 1967facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 1968facf4a8dSllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 1969facf4a8dSllai1 sdev_lookup_failed(ddv, nm, failed_flags); 1970facf4a8dSllai1 *vpp = NULLVP; 1971facf4a8dSllai1 return (ENOENT); 1972facf4a8dSllai1 } 1973facf4a8dSllai1 } 1974facf4a8dSllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 1975facf4a8dSllai1 1976facf4a8dSllai1 /* 1977facf4a8dSllai1 * ZOMBIED parent does not allow new node creation. 1978facf4a8dSllai1 * bail out early 1979facf4a8dSllai1 */ 1980facf4a8dSllai1 if (parent_state == SDEV_ZOMBIE) { 1981facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 1982681d9761SEric Taylor *vpp = NULLVP; 1983facf4a8dSllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 1984facf4a8dSllai1 return (ENOENT); 1985facf4a8dSllai1 } 1986facf4a8dSllai1 1987facf4a8dSllai1 /* 1988facf4a8dSllai1 * (b0): backing store lookup 1989facf4a8dSllai1 * SDEV_PERSIST is default except: 1990facf4a8dSllai1 * 1) pts nodes 1991facf4a8dSllai1 * 2) non-chmod'ed local nodes 1992681d9761SEric Taylor * 3) zvol nodes 1993facf4a8dSllai1 */ 1994facf4a8dSllai1 if (SDEV_IS_PERSIST(ddv)) { 1995facf4a8dSllai1 error = devname_backstore_lookup(ddv, nm, &rvp); 1996facf4a8dSllai1 1997facf4a8dSllai1 if (!error) { 1998facf4a8dSllai1 1999bb5fffbeSJerry Gilliam vattr.va_mask = AT_TYPE|AT_MODE|AT_UID|AT_GID; 2000da6c28aaSamw error = VOP_GETATTR(rvp, &vattr, 0, cred, NULL); 2001facf4a8dSllai1 if (error) { 2002facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 2003facf4a8dSllai1 if (dv) 2004facf4a8dSllai1 SDEV_RELE(dv); 2005facf4a8dSllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2006facf4a8dSllai1 sdev_lookup_failed(ddv, nm, failed_flags); 2007facf4a8dSllai1 *vpp = NULLVP; 2008facf4a8dSllai1 return (ENOENT); 2009facf4a8dSllai1 } 2010facf4a8dSllai1 2011facf4a8dSllai1 if (vattr.va_type == VLNK) { 2012facf4a8dSllai1 error = sdev_getlink(rvp, &link); 2013facf4a8dSllai1 if (error) { 2014facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 2015facf4a8dSllai1 if (dv) 2016facf4a8dSllai1 SDEV_RELE(dv); 2017facf4a8dSllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, 2018facf4a8dSllai1 retried); 2019facf4a8dSllai1 sdev_lookup_failed(ddv, nm, 2020facf4a8dSllai1 failed_flags); 2021facf4a8dSllai1 *vpp = NULLVP; 2022facf4a8dSllai1 return (ENOENT); 2023facf4a8dSllai1 } 2024facf4a8dSllai1 ASSERT(link != NULL); 2025facf4a8dSllai1 } 2026facf4a8dSllai1 2027facf4a8dSllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 2028facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 2029facf4a8dSllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 2030facf4a8dSllai1 } 2031facf4a8dSllai1 error = sdev_mknode(ddv, nm, &dv, &vattr, 2032facf4a8dSllai1 rvp, link, cred, SDEV_READY); 2033facf4a8dSllai1 rw_downgrade(&ddv->sdev_contents); 2034facf4a8dSllai1 2035facf4a8dSllai1 if (link != NULL) { 2036facf4a8dSllai1 kmem_free(link, strlen(link) + 1); 2037facf4a8dSllai1 link = NULL; 2038facf4a8dSllai1 } 2039facf4a8dSllai1 2040facf4a8dSllai1 if (error) { 2041facf4a8dSllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2042facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 2043facf4a8dSllai1 if (dv) 2044facf4a8dSllai1 SDEV_RELE(dv); 2045facf4a8dSllai1 goto lookup_failed; 2046facf4a8dSllai1 } else { 2047facf4a8dSllai1 goto found; 2048facf4a8dSllai1 } 2049facf4a8dSllai1 } else if (retried) { 2050facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 2051facf4a8dSllai1 sdcmn_err3(("retry of lookup of %s/%s: failed\n", 2052facf4a8dSllai1 ddv->sdev_name, nm)); 2053facf4a8dSllai1 if (dv) 2054facf4a8dSllai1 SDEV_RELE(dv); 2055facf4a8dSllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2056facf4a8dSllai1 sdev_lookup_failed(ddv, nm, failed_flags); 2057facf4a8dSllai1 *vpp = NULLVP; 2058facf4a8dSllai1 return (ENOENT); 2059facf4a8dSllai1 } 2060facf4a8dSllai1 } 2061facf4a8dSllai1 2062b127ac41SPhilip Kirk lookup_create_node: 2063facf4a8dSllai1 /* first thread that is doing the lookup on this node */ 2064681d9761SEric Taylor if (callback) { 2065681d9761SEric Taylor ASSERT(dv == NULL); 2066681d9761SEric Taylor if (!rw_tryupgrade(&ddv->sdev_contents)) { 2067681d9761SEric Taylor rw_exit(&ddv->sdev_contents); 2068681d9761SEric Taylor rw_enter(&ddv->sdev_contents, RW_WRITER); 2069681d9761SEric Taylor } 2070681d9761SEric Taylor error = sdev_call_dircallback(ddv, &dv, nm, callback, 2071681d9761SEric Taylor flags, cred); 2072681d9761SEric Taylor rw_downgrade(&ddv->sdev_contents); 2073681d9761SEric Taylor if (error == 0) { 2074681d9761SEric Taylor goto found; 2075681d9761SEric Taylor } else { 2076681d9761SEric Taylor SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2077681d9761SEric Taylor rw_exit(&ddv->sdev_contents); 2078681d9761SEric Taylor goto lookup_failed; 2079681d9761SEric Taylor } 2080681d9761SEric Taylor } 2081facf4a8dSllai1 if (!dv) { 2082facf4a8dSllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 2083facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 2084facf4a8dSllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 2085facf4a8dSllai1 } 2086facf4a8dSllai1 error = sdev_mknode(ddv, nm, &dv, NULL, NULL, NULL, 2087facf4a8dSllai1 cred, SDEV_INIT); 2088facf4a8dSllai1 if (!dv) { 2089facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 2090facf4a8dSllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2091facf4a8dSllai1 sdev_lookup_failed(ddv, nm, failed_flags); 2092facf4a8dSllai1 *vpp = NULLVP; 2093facf4a8dSllai1 return (ENOENT); 2094facf4a8dSllai1 } 2095facf4a8dSllai1 rw_downgrade(&ddv->sdev_contents); 2096facf4a8dSllai1 } 2097facf4a8dSllai1 2098facf4a8dSllai1 /* 2099facf4a8dSllai1 * (b1) invoking devfsadm once per life time for devfsadm nodes 2100facf4a8dSllai1 */ 2101681d9761SEric Taylor ASSERT(SDEV_HELD(dv)); 2102facf4a8dSllai1 2103681d9761SEric Taylor if (SDEV_IS_NO_NCACHE(dv)) 2104681d9761SEric Taylor failed_flags |= SLF_NO_NCACHE; 2105facf4a8dSllai1 if (sdev_reconfig_boot || !i_ddi_io_initialized() || 2106facf4a8dSllai1 SDEV_IS_DYNAMIC(ddv) || SDEV_IS_NO_NCACHE(dv) || 2107facf4a8dSllai1 ((moddebug & MODDEBUG_FINI_EBUSY) != 0)) { 2108facf4a8dSllai1 ASSERT(SDEV_HELD(dv)); 2109facf4a8dSllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2110facf4a8dSllai1 goto nolock_notfound; 2111facf4a8dSllai1 } 2112facf4a8dSllai1 2113facf4a8dSllai1 /* 2114facf4a8dSllai1 * filter out known non-existent devices recorded 2115facf4a8dSllai1 * during initial reconfiguration boot for which 2116facf4a8dSllai1 * reconfig should not be done and lookup may 2117facf4a8dSllai1 * be short-circuited now. 2118facf4a8dSllai1 */ 2119facf4a8dSllai1 if (sdev_lookup_filter(ddv, nm)) { 2120facf4a8dSllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2121facf4a8dSllai1 goto nolock_notfound; 2122facf4a8dSllai1 } 2123facf4a8dSllai1 2124facf4a8dSllai1 /* bypassing devfsadm internal nodes */ 2125facf4a8dSllai1 if (is_devfsadm_thread(lookup_thread)) { 2126facf4a8dSllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2127facf4a8dSllai1 goto nolock_notfound; 2128facf4a8dSllai1 } 2129facf4a8dSllai1 2130facf4a8dSllai1 if (sdev_reconfig_disable) { 2131facf4a8dSllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2132facf4a8dSllai1 goto nolock_notfound; 2133facf4a8dSllai1 } 2134facf4a8dSllai1 2135facf4a8dSllai1 error = sdev_call_devfsadmd(ddv, dv, nm); 2136facf4a8dSllai1 if (error == 0) { 2137facf4a8dSllai1 sdcmn_err8(("lookup of %s/%s by %s: reconfig\n", 2138facf4a8dSllai1 ddv->sdev_name, nm, curproc->p_user.u_comm)); 2139facf4a8dSllai1 if (sdev_reconfig_verbose) { 2140facf4a8dSllai1 cmn_err(CE_CONT, 2141facf4a8dSllai1 "?lookup of %s/%s by %s: reconfig\n", 2142facf4a8dSllai1 ddv->sdev_name, nm, curproc->p_user.u_comm); 2143facf4a8dSllai1 } 2144facf4a8dSllai1 retried = 1; 2145facf4a8dSllai1 failed_flags |= SLF_REBUILT; 2146facf4a8dSllai1 ASSERT(dv->sdev_state != SDEV_ZOMBIE); 2147facf4a8dSllai1 SDEV_SIMPLE_RELE(dv); 2148facf4a8dSllai1 goto tryagain; 2149facf4a8dSllai1 } else { 2150facf4a8dSllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2151facf4a8dSllai1 goto nolock_notfound; 2152facf4a8dSllai1 } 2153facf4a8dSllai1 2154facf4a8dSllai1 found: 2155facf4a8dSllai1 ASSERT(dv->sdev_state == SDEV_READY); 2156facf4a8dSllai1 if (vtor) { 2157facf4a8dSllai1 /* 2158facf4a8dSllai1 * Check validity of returned node 2159facf4a8dSllai1 */ 2160facf4a8dSllai1 switch (vtor(dv)) { 2161facf4a8dSllai1 case SDEV_VTOR_VALID: 2162facf4a8dSllai1 break; 2163b127ac41SPhilip Kirk case SDEV_VTOR_STALE: 2164b127ac41SPhilip Kirk /* 2165b127ac41SPhilip Kirk * The name exists, but the cache entry is 2166b127ac41SPhilip Kirk * stale and needs to be re-created. 2167b127ac41SPhilip Kirk */ 2168b127ac41SPhilip Kirk ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 2169b127ac41SPhilip Kirk if (rw_tryupgrade(&ddv->sdev_contents) == 0) { 2170b127ac41SPhilip Kirk rw_exit(&ddv->sdev_contents); 2171b127ac41SPhilip Kirk rw_enter(&ddv->sdev_contents, RW_WRITER); 2172b127ac41SPhilip Kirk } 2173*9e5aa9d8SRobert Mustacchi sdev_cache_update(ddv, &dv, nm, SDEV_CACHE_DELETE); 2174b127ac41SPhilip Kirk rw_downgrade(&ddv->sdev_contents); 2175*9e5aa9d8SRobert Mustacchi SDEV_RELE(dv); 2176b127ac41SPhilip Kirk dv = NULL; 2177b127ac41SPhilip Kirk goto lookup_create_node; 2178b127ac41SPhilip Kirk /* FALLTHRU */ 2179facf4a8dSllai1 case SDEV_VTOR_INVALID: 2180facf4a8dSllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2181facf4a8dSllai1 sdcmn_err7(("lookup: destroy invalid " 2182facf4a8dSllai1 "node: %s(%p)\n", dv->sdev_name, (void *)dv)); 2183facf4a8dSllai1 goto nolock_notfound; 2184facf4a8dSllai1 case SDEV_VTOR_SKIP: 2185facf4a8dSllai1 sdcmn_err7(("lookup: node not applicable - " 2186facf4a8dSllai1 "skipping: %s(%p)\n", dv->sdev_name, (void *)dv)); 2187facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 2188facf4a8dSllai1 SD_TRACE_FAILED_LOOKUP(ddv, nm, retried); 2189facf4a8dSllai1 SDEV_RELE(dv); 2190facf4a8dSllai1 goto lookup_failed; 2191facf4a8dSllai1 default: 2192facf4a8dSllai1 cmn_err(CE_PANIC, 2193facf4a8dSllai1 "dev fs: validator failed: %s(%p)\n", 2194facf4a8dSllai1 dv->sdev_name, (void *)dv); 2195facf4a8dSllai1 break; 2196facf4a8dSllai1 } 2197facf4a8dSllai1 } 2198facf4a8dSllai1 2199facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 2200facf4a8dSllai1 rv = sdev_to_vp(dv, vpp); 2201facf4a8dSllai1 sdcmn_err3(("devname_lookup_func: returning vp %p v_count %d state %d " 2202facf4a8dSllai1 "for nm %s, error %d\n", (void *)*vpp, (*vpp)->v_count, 2203facf4a8dSllai1 dv->sdev_state, nm, rv)); 2204facf4a8dSllai1 return (rv); 2205facf4a8dSllai1 2206facf4a8dSllai1 nolock_notfound: 2207facf4a8dSllai1 /* 2208facf4a8dSllai1 * Destroy the node that is created for synchronization purposes. 2209facf4a8dSllai1 */ 2210facf4a8dSllai1 sdcmn_err3(("devname_lookup_func: %s with state %d\n", 2211facf4a8dSllai1 nm, dv->sdev_state)); 2212facf4a8dSllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 2213facf4a8dSllai1 if (dv->sdev_state == SDEV_INIT) { 2214facf4a8dSllai1 if (!rw_tryupgrade(&ddv->sdev_contents)) { 2215facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 2216facf4a8dSllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 2217facf4a8dSllai1 } 2218facf4a8dSllai1 2219facf4a8dSllai1 /* 2220facf4a8dSllai1 * Node state may have changed during the lock 2221facf4a8dSllai1 * changes. Re-check. 2222facf4a8dSllai1 */ 2223facf4a8dSllai1 if (dv->sdev_state == SDEV_INIT) { 2224*9e5aa9d8SRobert Mustacchi sdev_dirdelete(ddv, dv); 2225facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 2226facf4a8dSllai1 sdev_lookup_failed(ddv, nm, failed_flags); 2227*9e5aa9d8SRobert Mustacchi SDEV_RELE(dv); 2228facf4a8dSllai1 *vpp = NULL; 2229facf4a8dSllai1 return (ENOENT); 2230facf4a8dSllai1 } 2231facf4a8dSllai1 } 2232facf4a8dSllai1 2233facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 2234facf4a8dSllai1 SDEV_RELE(dv); 2235facf4a8dSllai1 2236facf4a8dSllai1 lookup_failed: 2237facf4a8dSllai1 sdev_lookup_failed(ddv, nm, failed_flags); 2238facf4a8dSllai1 *vpp = NULL; 2239facf4a8dSllai1 return (ENOENT); 2240facf4a8dSllai1 } 2241facf4a8dSllai1 2242facf4a8dSllai1 /* 2243facf4a8dSllai1 * Given a directory node, mark all nodes beneath as 2244facf4a8dSllai1 * STALE, i.e. nodes that don't exist as far as new 2245ecb4d93aSjg * consumers are concerned. Remove them from the 2246ecb4d93aSjg * list of directory entries so that no lookup or 2247ecb4d93aSjg * directory traversal will find them. The node 2248ecb4d93aSjg * not deallocated so existing holds are not affected. 2249facf4a8dSllai1 */ 2250facf4a8dSllai1 void 2251facf4a8dSllai1 sdev_stale(struct sdev_node *ddv) 2252facf4a8dSllai1 { 2253facf4a8dSllai1 struct sdev_node *dv; 2254facf4a8dSllai1 struct vnode *vp; 2255facf4a8dSllai1 2256facf4a8dSllai1 ASSERT(SDEVTOV(ddv)->v_type == VDIR); 2257facf4a8dSllai1 2258facf4a8dSllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 2259*9e5aa9d8SRobert Mustacchi while ((dv = SDEV_FIRST_ENTRY(ddv)) != NULL) { 2260facf4a8dSllai1 vp = SDEVTOV(dv); 2261*9e5aa9d8SRobert Mustacchi SDEV_HOLD(dv); 2262facf4a8dSllai1 if (vp->v_type == VDIR) 2263facf4a8dSllai1 sdev_stale(dv); 2264facf4a8dSllai1 2265*9e5aa9d8SRobert Mustacchi sdev_dirdelete(ddv, dv); 2266*9e5aa9d8SRobert Mustacchi SDEV_RELE(dv); 2267facf4a8dSllai1 } 2268facf4a8dSllai1 ddv->sdev_flags |= SDEV_BUILD; 2269facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 2270facf4a8dSllai1 } 2271facf4a8dSllai1 2272facf4a8dSllai1 /* 2273facf4a8dSllai1 * Given a directory node, clean out all the nodes beneath. 2274facf4a8dSllai1 * If expr is specified, clean node with names matching expr. 2275facf4a8dSllai1 * If SDEV_ENFORCE is specified in flags, busy nodes are made stale, 2276facf4a8dSllai1 * so they are excluded from future lookups. 2277facf4a8dSllai1 */ 2278facf4a8dSllai1 int 2279facf4a8dSllai1 sdev_cleandir(struct sdev_node *ddv, char *expr, uint_t flags) 2280facf4a8dSllai1 { 2281facf4a8dSllai1 int error = 0; 2282facf4a8dSllai1 int busy = 0; 2283facf4a8dSllai1 struct vnode *vp; 2284*9e5aa9d8SRobert Mustacchi struct sdev_node *dv; 2285facf4a8dSllai1 int bkstore = 0; 2286facf4a8dSllai1 int len = 0; 2287facf4a8dSllai1 char *bks_name = NULL; 2288facf4a8dSllai1 2289facf4a8dSllai1 ASSERT(SDEVTOV(ddv)->v_type == VDIR); 2290facf4a8dSllai1 2291facf4a8dSllai1 /* 2292facf4a8dSllai1 * We try our best to destroy all unused sdev_node's 2293facf4a8dSllai1 */ 2294facf4a8dSllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 2295*9e5aa9d8SRobert Mustacchi while ((dv = SDEV_FIRST_ENTRY(ddv)) != NULL) { 2296facf4a8dSllai1 vp = SDEVTOV(dv); 2297facf4a8dSllai1 2298facf4a8dSllai1 if (expr && gmatch(dv->sdev_name, expr) == 0) 2299facf4a8dSllai1 continue; 2300facf4a8dSllai1 2301facf4a8dSllai1 if (vp->v_type == VDIR && 2302facf4a8dSllai1 sdev_cleandir(dv, NULL, flags) != 0) { 2303facf4a8dSllai1 sdcmn_err9(("sdev_cleandir: dir %s busy\n", 2304facf4a8dSllai1 dv->sdev_name)); 2305facf4a8dSllai1 busy++; 2306facf4a8dSllai1 continue; 2307facf4a8dSllai1 } 2308facf4a8dSllai1 2309facf4a8dSllai1 if (vp->v_count > 0 && (flags & SDEV_ENFORCE) == 0) { 2310facf4a8dSllai1 sdcmn_err9(("sdev_cleandir: dir %s busy\n", 2311facf4a8dSllai1 dv->sdev_name)); 2312facf4a8dSllai1 busy++; 2313facf4a8dSllai1 continue; 2314facf4a8dSllai1 } 2315facf4a8dSllai1 2316facf4a8dSllai1 /* 2317facf4a8dSllai1 * at this point, either dv is not held or SDEV_ENFORCE 2318facf4a8dSllai1 * is specified. In either case, dv needs to be deleted 2319facf4a8dSllai1 */ 2320facf4a8dSllai1 SDEV_HOLD(dv); 2321facf4a8dSllai1 2322facf4a8dSllai1 bkstore = SDEV_IS_PERSIST(dv) ? 1 : 0; 2323facf4a8dSllai1 if (bkstore && (vp->v_type == VDIR)) 2324facf4a8dSllai1 bkstore += 1; 2325facf4a8dSllai1 2326facf4a8dSllai1 if (bkstore) { 2327facf4a8dSllai1 len = strlen(dv->sdev_name) + 1; 2328facf4a8dSllai1 bks_name = kmem_alloc(len, KM_SLEEP); 2329facf4a8dSllai1 bcopy(dv->sdev_name, bks_name, len); 2330facf4a8dSllai1 } 2331facf4a8dSllai1 2332*9e5aa9d8SRobert Mustacchi sdev_dirdelete(ddv, dv); 2333facf4a8dSllai1 2334facf4a8dSllai1 /* take care the backing store clean up */ 2335*9e5aa9d8SRobert Mustacchi if (bkstore) { 2336facf4a8dSllai1 ASSERT(bks_name); 2337facf4a8dSllai1 ASSERT(ddv->sdev_attrvp); 2338facf4a8dSllai1 2339facf4a8dSllai1 if (bkstore == 1) { 2340facf4a8dSllai1 error = VOP_REMOVE(ddv->sdev_attrvp, 2341da6c28aaSamw bks_name, kcred, NULL, 0); 2342facf4a8dSllai1 } else if (bkstore == 2) { 2343facf4a8dSllai1 error = VOP_RMDIR(ddv->sdev_attrvp, 2344da6c28aaSamw bks_name, ddv->sdev_attrvp, kcred, NULL, 0); 2345facf4a8dSllai1 } 2346facf4a8dSllai1 2347facf4a8dSllai1 /* do not propagate the backing store errors */ 2348facf4a8dSllai1 if (error) { 2349facf4a8dSllai1 sdcmn_err9(("sdev_cleandir: backing store" 2350facf4a8dSllai1 "not cleaned\n")); 2351facf4a8dSllai1 error = 0; 2352facf4a8dSllai1 } 2353facf4a8dSllai1 2354facf4a8dSllai1 bkstore = 0; 2355facf4a8dSllai1 kmem_free(bks_name, len); 2356facf4a8dSllai1 bks_name = NULL; 2357facf4a8dSllai1 len = 0; 2358facf4a8dSllai1 } 2359*9e5aa9d8SRobert Mustacchi 2360*9e5aa9d8SRobert Mustacchi ddv->sdev_flags |= SDEV_BUILD; 2361*9e5aa9d8SRobert Mustacchi SDEV_RELE(dv); 2362facf4a8dSllai1 } 2363facf4a8dSllai1 2364facf4a8dSllai1 ddv->sdev_flags |= SDEV_BUILD; 2365facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 2366facf4a8dSllai1 2367facf4a8dSllai1 if (busy) { 2368facf4a8dSllai1 error = EBUSY; 2369facf4a8dSllai1 } 2370facf4a8dSllai1 2371facf4a8dSllai1 return (error); 2372facf4a8dSllai1 } 2373facf4a8dSllai1 2374facf4a8dSllai1 /* 2375facf4a8dSllai1 * a convenient wrapper for readdir() funcs 2376facf4a8dSllai1 */ 2377facf4a8dSllai1 size_t 2378facf4a8dSllai1 add_dir_entry(dirent64_t *de, char *nm, size_t size, ino_t ino, offset_t off) 2379facf4a8dSllai1 { 2380facf4a8dSllai1 size_t reclen = DIRENT64_RECLEN(strlen(nm)); 2381facf4a8dSllai1 if (reclen > size) 2382facf4a8dSllai1 return (0); 2383facf4a8dSllai1 2384facf4a8dSllai1 de->d_ino = (ino64_t)ino; 2385facf4a8dSllai1 de->d_off = (off64_t)off + 1; 2386facf4a8dSllai1 de->d_reclen = (ushort_t)reclen; 2387facf4a8dSllai1 (void) strncpy(de->d_name, nm, DIRENT64_NAMELEN(reclen)); 2388facf4a8dSllai1 return (reclen); 2389facf4a8dSllai1 } 2390facf4a8dSllai1 2391facf4a8dSllai1 /* 2392facf4a8dSllai1 * sdev_mount service routines 2393facf4a8dSllai1 */ 2394facf4a8dSllai1 int 2395facf4a8dSllai1 sdev_copyin_mountargs(struct mounta *uap, struct sdev_mountargs *args) 2396facf4a8dSllai1 { 2397facf4a8dSllai1 int error; 2398facf4a8dSllai1 2399facf4a8dSllai1 if (uap->datalen != sizeof (*args)) 2400facf4a8dSllai1 return (EINVAL); 2401facf4a8dSllai1 2402facf4a8dSllai1 if (error = copyin(uap->dataptr, args, sizeof (*args))) { 2403facf4a8dSllai1 cmn_err(CE_WARN, "sdev_copyin_mountargs: can not" 2404facf4a8dSllai1 "get user data. error %d\n", error); 2405facf4a8dSllai1 return (EFAULT); 2406facf4a8dSllai1 } 2407facf4a8dSllai1 2408facf4a8dSllai1 return (0); 2409facf4a8dSllai1 } 2410facf4a8dSllai1 2411facf4a8dSllai1 #ifdef nextdp 2412facf4a8dSllai1 #undef nextdp 2413facf4a8dSllai1 #endif 2414bc1009abSjg #define nextdp(dp) ((struct dirent64 *) \ 2415bc1009abSjg (intptr_t)((char *)(dp) + (dp)->d_reclen)) 2416facf4a8dSllai1 2417facf4a8dSllai1 /* 2418facf4a8dSllai1 * readdir helper func 2419facf4a8dSllai1 */ 2420facf4a8dSllai1 int 2421facf4a8dSllai1 devname_readdir_func(vnode_t *vp, uio_t *uiop, cred_t *cred, int *eofp, 2422facf4a8dSllai1 int flags) 2423facf4a8dSllai1 { 2424facf4a8dSllai1 struct sdev_node *ddv = VTOSDEV(vp); 2425facf4a8dSllai1 struct sdev_node *dv; 2426facf4a8dSllai1 dirent64_t *dp; 2427facf4a8dSllai1 ulong_t outcount = 0; 2428facf4a8dSllai1 size_t namelen; 2429facf4a8dSllai1 ulong_t alloc_count; 2430facf4a8dSllai1 void *outbuf; 2431facf4a8dSllai1 struct iovec *iovp; 2432facf4a8dSllai1 int error = 0; 2433facf4a8dSllai1 size_t reclen; 2434facf4a8dSllai1 offset_t diroff; 2435facf4a8dSllai1 offset_t soff; 2436facf4a8dSllai1 int this_reclen; 2437facf4a8dSllai1 int (*vtor)(struct sdev_node *) = NULL; 2438facf4a8dSllai1 struct vattr attr; 2439facf4a8dSllai1 timestruc_t now; 2440facf4a8dSllai1 2441facf4a8dSllai1 ASSERT(ddv->sdev_attr || ddv->sdev_attrvp); 2442facf4a8dSllai1 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 2443facf4a8dSllai1 2444facf4a8dSllai1 if (uiop->uio_loffset >= MAXOFF_T) { 2445facf4a8dSllai1 if (eofp) 2446facf4a8dSllai1 *eofp = 1; 2447facf4a8dSllai1 return (0); 2448facf4a8dSllai1 } 2449facf4a8dSllai1 2450facf4a8dSllai1 if (uiop->uio_iovcnt != 1) 2451facf4a8dSllai1 return (EINVAL); 2452facf4a8dSllai1 2453facf4a8dSllai1 if (vp->v_type != VDIR) 2454facf4a8dSllai1 return (ENOTDIR); 2455facf4a8dSllai1 2456facf4a8dSllai1 if (ddv->sdev_flags & SDEV_VTOR) { 2457facf4a8dSllai1 vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv); 2458facf4a8dSllai1 ASSERT(vtor); 2459facf4a8dSllai1 } 2460facf4a8dSllai1 2461facf4a8dSllai1 if (eofp != NULL) 2462facf4a8dSllai1 *eofp = 0; 2463facf4a8dSllai1 2464bc1009abSjg soff = uiop->uio_loffset; 2465facf4a8dSllai1 iovp = uiop->uio_iov; 2466facf4a8dSllai1 alloc_count = iovp->iov_len; 2467facf4a8dSllai1 dp = outbuf = kmem_alloc(alloc_count, KM_SLEEP); 2468facf4a8dSllai1 outcount = 0; 2469facf4a8dSllai1 2470facf4a8dSllai1 if (ddv->sdev_state == SDEV_ZOMBIE) 2471facf4a8dSllai1 goto get_cache; 2472facf4a8dSllai1 2473b774fca8Sszhou if (SDEV_IS_GLOBAL(ddv)) { 2474facf4a8dSllai1 24753c5e027bSEric Taylor if ((sdev_boot_state == SDEV_BOOT_STATE_COMPLETE) && 2476facf4a8dSllai1 !sdev_reconfig_boot && (flags & SDEV_BROWSE) && 2477facf4a8dSllai1 !SDEV_IS_DYNAMIC(ddv) && !SDEV_IS_NO_NCACHE(ddv) && 2478facf4a8dSllai1 ((moddebug & MODDEBUG_FINI_EBUSY) == 0) && 2479facf4a8dSllai1 !DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state) && 2480facf4a8dSllai1 !DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) && 2481facf4a8dSllai1 !sdev_reconfig_disable) { 2482facf4a8dSllai1 /* 2483facf4a8dSllai1 * invoking "devfsadm" to do system device reconfig 2484facf4a8dSllai1 */ 2485facf4a8dSllai1 mutex_enter(&ddv->sdev_lookup_lock); 2486facf4a8dSllai1 SDEV_BLOCK_OTHERS(ddv, 2487facf4a8dSllai1 (SDEV_READDIR|SDEV_LGWAITING)); 2488facf4a8dSllai1 mutex_exit(&ddv->sdev_lookup_lock); 2489facf4a8dSllai1 2490facf4a8dSllai1 sdcmn_err8(("readdir of %s by %s: reconfig\n", 2491facf4a8dSllai1 ddv->sdev_path, curproc->p_user.u_comm)); 2492facf4a8dSllai1 if (sdev_reconfig_verbose) { 2493facf4a8dSllai1 cmn_err(CE_CONT, 2494facf4a8dSllai1 "?readdir of %s by %s: reconfig\n", 2495facf4a8dSllai1 ddv->sdev_path, curproc->p_user.u_comm); 2496facf4a8dSllai1 } 2497facf4a8dSllai1 2498facf4a8dSllai1 sdev_devfsadmd_thread(ddv, NULL, kcred); 2499facf4a8dSllai1 } else if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) { 2500facf4a8dSllai1 /* 2501facf4a8dSllai1 * compensate the "ls" started later than "devfsadm" 2502facf4a8dSllai1 */ 2503facf4a8dSllai1 mutex_enter(&ddv->sdev_lookup_lock); 2504facf4a8dSllai1 SDEV_BLOCK_OTHERS(ddv, (SDEV_READDIR|SDEV_LGWAITING)); 2505facf4a8dSllai1 mutex_exit(&ddv->sdev_lookup_lock); 2506facf4a8dSllai1 } 2507facf4a8dSllai1 2508facf4a8dSllai1 /* 2509facf4a8dSllai1 * release the contents lock so that 2510facf4a8dSllai1 * the cache may be updated by devfsadmd 2511facf4a8dSllai1 */ 2512facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 2513facf4a8dSllai1 mutex_enter(&ddv->sdev_lookup_lock); 2514facf4a8dSllai1 if (SDEV_IS_READDIR(ddv)) 2515facf4a8dSllai1 (void) sdev_wait4lookup(ddv, SDEV_READDIR); 2516facf4a8dSllai1 mutex_exit(&ddv->sdev_lookup_lock); 2517facf4a8dSllai1 rw_enter(&ddv->sdev_contents, RW_READER); 2518facf4a8dSllai1 2519facf4a8dSllai1 sdcmn_err4(("readdir of directory %s by %s\n", 2520facf4a8dSllai1 ddv->sdev_name, curproc->p_user.u_comm)); 25216b938478Sjg if (ddv->sdev_flags & SDEV_BUILD) { 2522facf4a8dSllai1 if (SDEV_IS_PERSIST(ddv)) { 2523facf4a8dSllai1 error = sdev_filldir_from_store(ddv, 2524facf4a8dSllai1 alloc_count, cred); 2525facf4a8dSllai1 } 2526facf4a8dSllai1 ddv->sdev_flags &= ~SDEV_BUILD; 2527facf4a8dSllai1 } 2528facf4a8dSllai1 } 2529facf4a8dSllai1 2530facf4a8dSllai1 get_cache: 2531facf4a8dSllai1 /* handle "." and ".." */ 2532facf4a8dSllai1 diroff = 0; 2533facf4a8dSllai1 if (soff == 0) { 2534facf4a8dSllai1 /* first time */ 2535facf4a8dSllai1 this_reclen = DIRENT64_RECLEN(1); 2536facf4a8dSllai1 if (alloc_count < this_reclen) { 2537facf4a8dSllai1 error = EINVAL; 2538facf4a8dSllai1 goto done; 2539facf4a8dSllai1 } 2540facf4a8dSllai1 2541facf4a8dSllai1 dp->d_ino = (ino64_t)ddv->sdev_ino; 2542facf4a8dSllai1 dp->d_off = (off64_t)1; 2543facf4a8dSllai1 dp->d_reclen = (ushort_t)this_reclen; 2544facf4a8dSllai1 2545facf4a8dSllai1 (void) strncpy(dp->d_name, ".", 2546facf4a8dSllai1 DIRENT64_NAMELEN(this_reclen)); 2547facf4a8dSllai1 outcount += dp->d_reclen; 2548facf4a8dSllai1 dp = nextdp(dp); 2549facf4a8dSllai1 } 2550facf4a8dSllai1 2551facf4a8dSllai1 diroff++; 2552facf4a8dSllai1 if (soff <= 1) { 2553facf4a8dSllai1 this_reclen = DIRENT64_RECLEN(2); 2554facf4a8dSllai1 if (alloc_count < outcount + this_reclen) { 2555facf4a8dSllai1 error = EINVAL; 2556facf4a8dSllai1 goto done; 2557facf4a8dSllai1 } 2558facf4a8dSllai1 2559facf4a8dSllai1 dp->d_reclen = (ushort_t)this_reclen; 2560facf4a8dSllai1 dp->d_ino = (ino64_t)ddv->sdev_dotdot->sdev_ino; 2561facf4a8dSllai1 dp->d_off = (off64_t)2; 2562facf4a8dSllai1 2563facf4a8dSllai1 (void) strncpy(dp->d_name, "..", 2564facf4a8dSllai1 DIRENT64_NAMELEN(this_reclen)); 2565facf4a8dSllai1 outcount += dp->d_reclen; 2566facf4a8dSllai1 2567facf4a8dSllai1 dp = nextdp(dp); 2568facf4a8dSllai1 } 2569facf4a8dSllai1 2570facf4a8dSllai1 2571facf4a8dSllai1 /* gets the cache */ 2572facf4a8dSllai1 diroff++; 2573aac43a5fSjg for (dv = SDEV_FIRST_ENTRY(ddv); dv; 2574aac43a5fSjg dv = SDEV_NEXT_ENTRY(ddv, dv), diroff++) { 2575facf4a8dSllai1 sdcmn_err3(("sdev_readdir: diroff %lld soff %lld for '%s' \n", 2576facf4a8dSllai1 diroff, soff, dv->sdev_name)); 2577facf4a8dSllai1 2578facf4a8dSllai1 /* bypassing pre-matured nodes */ 2579facf4a8dSllai1 if (diroff < soff || (dv->sdev_state != SDEV_READY)) { 2580facf4a8dSllai1 sdcmn_err3(("sdev_readdir: pre-mature node " 2581681d9761SEric Taylor "%s %d\n", dv->sdev_name, dv->sdev_state)); 2582facf4a8dSllai1 continue; 2583facf4a8dSllai1 } 2584facf4a8dSllai1 2585facf4a8dSllai1 /* 2586facf4a8dSllai1 * Check validity of node 25872b080a34SJerry Gilliam * Drop invalid and nodes to be skipped. 25882b080a34SJerry Gilliam * A node the validator indicates as stale needs 25892b080a34SJerry Gilliam * to be returned as presumably the node name itself 25902b080a34SJerry Gilliam * is valid and the node data itself will be refreshed 25912b080a34SJerry Gilliam * on lookup. An application performing a readdir then 25922b080a34SJerry Gilliam * stat on each entry should thus always see consistent 25932b080a34SJerry Gilliam * data. In any case, it is not possible to synchronize 25942b080a34SJerry Gilliam * with dynamic kernel state, and any view we return can 25952b080a34SJerry Gilliam * never be anything more than a snapshot at a point in time. 2596facf4a8dSllai1 */ 2597facf4a8dSllai1 if (vtor) { 2598facf4a8dSllai1 switch (vtor(dv)) { 2599facf4a8dSllai1 case SDEV_VTOR_VALID: 2600facf4a8dSllai1 break; 2601facf4a8dSllai1 case SDEV_VTOR_INVALID: 2602facf4a8dSllai1 case SDEV_VTOR_SKIP: 2603facf4a8dSllai1 continue; 26042b080a34SJerry Gilliam case SDEV_VTOR_STALE: 26052b080a34SJerry Gilliam sdcmn_err3(("sdev_readir: %s stale\n", 26062b080a34SJerry Gilliam dv->sdev_name)); 26072b080a34SJerry Gilliam break; 2608facf4a8dSllai1 default: 2609facf4a8dSllai1 cmn_err(CE_PANIC, 2610facf4a8dSllai1 "dev fs: validator failed: %s(%p)\n", 2611facf4a8dSllai1 dv->sdev_name, (void *)dv); 2612facf4a8dSllai1 break; 2613facf4a8dSllai1 /*NOTREACHED*/ 2614facf4a8dSllai1 } 2615facf4a8dSllai1 } 2616facf4a8dSllai1 2617facf4a8dSllai1 namelen = strlen(dv->sdev_name); 2618facf4a8dSllai1 reclen = DIRENT64_RECLEN(namelen); 2619facf4a8dSllai1 if (outcount + reclen > alloc_count) { 2620facf4a8dSllai1 goto full; 2621facf4a8dSllai1 } 2622facf4a8dSllai1 dp->d_reclen = (ushort_t)reclen; 2623facf4a8dSllai1 dp->d_ino = (ino64_t)dv->sdev_ino; 2624facf4a8dSllai1 dp->d_off = (off64_t)diroff + 1; 2625facf4a8dSllai1 (void) strncpy(dp->d_name, dv->sdev_name, 2626facf4a8dSllai1 DIRENT64_NAMELEN(reclen)); 2627facf4a8dSllai1 outcount += reclen; 2628facf4a8dSllai1 dp = nextdp(dp); 2629facf4a8dSllai1 } 2630facf4a8dSllai1 2631facf4a8dSllai1 full: 2632facf4a8dSllai1 sdcmn_err4(("sdev_readdir: moving %lu bytes: " 2633facf4a8dSllai1 "diroff %lld, soff %lld, dv %p\n", outcount, diroff, soff, 2634facf4a8dSllai1 (void *)dv)); 2635facf4a8dSllai1 2636facf4a8dSllai1 if (outcount) 2637facf4a8dSllai1 error = uiomove(outbuf, outcount, UIO_READ, uiop); 2638facf4a8dSllai1 2639facf4a8dSllai1 if (!error) { 2640bc1009abSjg uiop->uio_loffset = diroff; 2641facf4a8dSllai1 if (eofp) 2642facf4a8dSllai1 *eofp = dv ? 0 : 1; 2643facf4a8dSllai1 } 2644facf4a8dSllai1 2645facf4a8dSllai1 2646facf4a8dSllai1 if (ddv->sdev_attrvp) { 2647facf4a8dSllai1 gethrestime(&now); 2648facf4a8dSllai1 attr.va_ctime = now; 2649facf4a8dSllai1 attr.va_atime = now; 2650facf4a8dSllai1 attr.va_mask = AT_CTIME|AT_ATIME; 2651facf4a8dSllai1 2652facf4a8dSllai1 (void) VOP_SETATTR(ddv->sdev_attrvp, &attr, 0, kcred, NULL); 2653facf4a8dSllai1 } 2654facf4a8dSllai1 done: 2655facf4a8dSllai1 kmem_free(outbuf, alloc_count); 2656facf4a8dSllai1 return (error); 2657facf4a8dSllai1 } 2658facf4a8dSllai1 2659facf4a8dSllai1 static int 2660facf4a8dSllai1 sdev_modctl_lookup(const char *path, vnode_t **r_vp) 2661facf4a8dSllai1 { 2662facf4a8dSllai1 vnode_t *vp; 2663facf4a8dSllai1 vnode_t *cvp; 2664facf4a8dSllai1 struct sdev_node *svp; 2665facf4a8dSllai1 char *nm; 2666facf4a8dSllai1 struct pathname pn; 2667facf4a8dSllai1 int error; 2668facf4a8dSllai1 int persisted = 0; 2669facf4a8dSllai1 267074bb9a80SJerry Gilliam ASSERT(INGLOBALZONE(curproc)); 267174bb9a80SJerry Gilliam 2672facf4a8dSllai1 if (error = pn_get((char *)path, UIO_SYSSPACE, &pn)) 2673facf4a8dSllai1 return (error); 2674facf4a8dSllai1 nm = kmem_alloc(MAXNAMELEN, KM_SLEEP); 2675facf4a8dSllai1 2676facf4a8dSllai1 vp = rootdir; 2677facf4a8dSllai1 VN_HOLD(vp); 2678facf4a8dSllai1 2679facf4a8dSllai1 while (pn_pathleft(&pn)) { 268074bb9a80SJerry Gilliam ASSERT(vp->v_type == VDIR || vp->v_type == VLNK); 2681facf4a8dSllai1 (void) pn_getcomponent(&pn, nm); 268274bb9a80SJerry Gilliam 268374bb9a80SJerry Gilliam /* 268474bb9a80SJerry Gilliam * Deal with the .. special case where we may be 268574bb9a80SJerry Gilliam * traversing up across a mount point, to the 268674bb9a80SJerry Gilliam * root of this filesystem or global root. 268774bb9a80SJerry Gilliam */ 268874bb9a80SJerry Gilliam if (nm[0] == '.' && nm[1] == '.' && nm[2] == 0) { 268974bb9a80SJerry Gilliam checkforroot: 269074bb9a80SJerry Gilliam if (VN_CMP(vp, rootdir)) { 269174bb9a80SJerry Gilliam nm[1] = 0; 269274bb9a80SJerry Gilliam } else if (vp->v_flag & VROOT) { 269374bb9a80SJerry Gilliam vfs_t *vfsp; 269474bb9a80SJerry Gilliam cvp = vp; 269574bb9a80SJerry Gilliam vfsp = cvp->v_vfsp; 269674bb9a80SJerry Gilliam vfs_rlock_wait(vfsp); 269774bb9a80SJerry Gilliam vp = cvp->v_vfsp->vfs_vnodecovered; 269874bb9a80SJerry Gilliam if (vp == NULL || 269974bb9a80SJerry Gilliam (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) { 270074bb9a80SJerry Gilliam vfs_unlock(vfsp); 270174bb9a80SJerry Gilliam VN_RELE(cvp); 270274bb9a80SJerry Gilliam error = EIO; 270374bb9a80SJerry Gilliam break; 270474bb9a80SJerry Gilliam } 270574bb9a80SJerry Gilliam VN_HOLD(vp); 270674bb9a80SJerry Gilliam vfs_unlock(vfsp); 270774bb9a80SJerry Gilliam VN_RELE(cvp); 270874bb9a80SJerry Gilliam cvp = NULL; 270974bb9a80SJerry Gilliam goto checkforroot; 271074bb9a80SJerry Gilliam } 271174bb9a80SJerry Gilliam } 271274bb9a80SJerry Gilliam 2713da6c28aaSamw error = VOP_LOOKUP(vp, nm, &cvp, NULL, 0, NULL, kcred, NULL, 2714da6c28aaSamw NULL, NULL); 271574bb9a80SJerry Gilliam if (error) { 2716facf4a8dSllai1 VN_RELE(vp); 2717facf4a8dSllai1 break; 271874bb9a80SJerry Gilliam } 2719facf4a8dSllai1 2720facf4a8dSllai1 /* traverse mount points encountered on our journey */ 2721facf4a8dSllai1 if (vn_ismntpt(cvp) && (error = traverse(&cvp)) != 0) { 272274bb9a80SJerry Gilliam VN_RELE(vp); 2723facf4a8dSllai1 VN_RELE(cvp); 2724facf4a8dSllai1 break; 2725facf4a8dSllai1 } 2726facf4a8dSllai1 2727facf4a8dSllai1 /* 272874bb9a80SJerry Gilliam * symbolic link, can be either relative and absolute 272974bb9a80SJerry Gilliam */ 273074bb9a80SJerry Gilliam if ((cvp->v_type == VLNK) && pn_pathleft(&pn)) { 273174bb9a80SJerry Gilliam struct pathname linkpath; 273274bb9a80SJerry Gilliam pn_alloc(&linkpath); 273374bb9a80SJerry Gilliam if (error = pn_getsymlink(cvp, &linkpath, kcred)) { 273474bb9a80SJerry Gilliam pn_free(&linkpath); 273574bb9a80SJerry Gilliam break; 273674bb9a80SJerry Gilliam } 273774bb9a80SJerry Gilliam if (pn_pathleft(&linkpath) == 0) 273874bb9a80SJerry Gilliam (void) pn_set(&linkpath, "."); 273974bb9a80SJerry Gilliam error = pn_insert(&pn, &linkpath, strlen(nm)); 274074bb9a80SJerry Gilliam pn_free(&linkpath); 274174bb9a80SJerry Gilliam if (pn.pn_pathlen == 0) { 274274bb9a80SJerry Gilliam VN_RELE(vp); 274374bb9a80SJerry Gilliam return (ENOENT); 274474bb9a80SJerry Gilliam } 274574bb9a80SJerry Gilliam if (pn.pn_path[0] == '/') { 274674bb9a80SJerry Gilliam pn_skipslash(&pn); 274774bb9a80SJerry Gilliam VN_RELE(vp); 274874bb9a80SJerry Gilliam VN_RELE(cvp); 274974bb9a80SJerry Gilliam vp = rootdir; 275074bb9a80SJerry Gilliam VN_HOLD(vp); 275174bb9a80SJerry Gilliam } else { 275274bb9a80SJerry Gilliam VN_RELE(cvp); 275374bb9a80SJerry Gilliam } 275474bb9a80SJerry Gilliam continue; 275574bb9a80SJerry Gilliam } 275674bb9a80SJerry Gilliam 275774bb9a80SJerry Gilliam VN_RELE(vp); 275874bb9a80SJerry Gilliam 275974bb9a80SJerry Gilliam /* 2760facf4a8dSllai1 * Direct the operation to the persisting filesystem 2761facf4a8dSllai1 * underlying /dev. Bail if we encounter a 2762facf4a8dSllai1 * non-persistent dev entity here. 2763facf4a8dSllai1 */ 2764facf4a8dSllai1 if (cvp->v_vfsp->vfs_fstype == devtype) { 2765facf4a8dSllai1 2766facf4a8dSllai1 if ((VTOSDEV(cvp)->sdev_flags & SDEV_PERSIST) == 0) { 2767facf4a8dSllai1 error = ENOENT; 2768facf4a8dSllai1 VN_RELE(cvp); 2769facf4a8dSllai1 break; 2770facf4a8dSllai1 } 2771facf4a8dSllai1 2772facf4a8dSllai1 if (VTOSDEV(cvp) == NULL) { 2773facf4a8dSllai1 error = ENOENT; 2774facf4a8dSllai1 VN_RELE(cvp); 2775facf4a8dSllai1 break; 2776facf4a8dSllai1 } 2777facf4a8dSllai1 svp = VTOSDEV(cvp); 2778facf4a8dSllai1 if ((vp = svp->sdev_attrvp) == NULL) { 2779facf4a8dSllai1 error = ENOENT; 2780facf4a8dSllai1 VN_RELE(cvp); 2781facf4a8dSllai1 break; 2782facf4a8dSllai1 } 2783facf4a8dSllai1 persisted = 1; 2784facf4a8dSllai1 VN_HOLD(vp); 2785facf4a8dSllai1 VN_RELE(cvp); 2786facf4a8dSllai1 cvp = vp; 2787facf4a8dSllai1 } 2788facf4a8dSllai1 2789facf4a8dSllai1 vp = cvp; 2790facf4a8dSllai1 pn_skipslash(&pn); 2791facf4a8dSllai1 } 2792facf4a8dSllai1 2793facf4a8dSllai1 kmem_free(nm, MAXNAMELEN); 2794facf4a8dSllai1 pn_free(&pn); 2795facf4a8dSllai1 2796facf4a8dSllai1 if (error) 2797facf4a8dSllai1 return (error); 2798facf4a8dSllai1 2799facf4a8dSllai1 /* 2800facf4a8dSllai1 * Only return persisted nodes in the filesystem underlying /dev. 2801facf4a8dSllai1 */ 2802facf4a8dSllai1 if (!persisted) { 2803facf4a8dSllai1 VN_RELE(vp); 2804facf4a8dSllai1 return (ENOENT); 2805facf4a8dSllai1 } 2806facf4a8dSllai1 2807facf4a8dSllai1 *r_vp = vp; 2808facf4a8dSllai1 return (0); 2809facf4a8dSllai1 } 2810facf4a8dSllai1 2811facf4a8dSllai1 int 2812facf4a8dSllai1 sdev_modctl_readdir(const char *dir, char ***dirlistp, 2813e37c6c37Scth int *npathsp, int *npathsp_alloc, int checking_empty) 2814facf4a8dSllai1 { 2815facf4a8dSllai1 char **pathlist = NULL; 2816facf4a8dSllai1 char **newlist = NULL; 2817facf4a8dSllai1 int npaths = 0; 2818facf4a8dSllai1 int npaths_alloc = 0; 2819facf4a8dSllai1 dirent64_t *dbuf = NULL; 2820facf4a8dSllai1 int n; 2821facf4a8dSllai1 char *s; 2822facf4a8dSllai1 int error; 2823facf4a8dSllai1 vnode_t *vp; 2824facf4a8dSllai1 int eof; 2825facf4a8dSllai1 struct iovec iov; 2826facf4a8dSllai1 struct uio uio; 2827facf4a8dSllai1 struct dirent64 *dp; 2828facf4a8dSllai1 size_t dlen; 2829facf4a8dSllai1 size_t dbuflen; 2830facf4a8dSllai1 int ndirents = 64; 2831facf4a8dSllai1 char *nm; 2832facf4a8dSllai1 2833facf4a8dSllai1 error = sdev_modctl_lookup(dir, &vp); 2834facf4a8dSllai1 sdcmn_err11(("modctl readdir: %s by %s: %s\n", 2835facf4a8dSllai1 dir, curproc->p_user.u_comm, 2836facf4a8dSllai1 (error == 0) ? "ok" : "failed")); 2837facf4a8dSllai1 if (error) 2838facf4a8dSllai1 return (error); 2839facf4a8dSllai1 2840facf4a8dSllai1 dlen = ndirents * (sizeof (*dbuf)); 2841facf4a8dSllai1 dbuf = kmem_alloc(dlen, KM_SLEEP); 2842facf4a8dSllai1 2843facf4a8dSllai1 uio.uio_iov = &iov; 2844facf4a8dSllai1 uio.uio_iovcnt = 1; 2845facf4a8dSllai1 uio.uio_segflg = UIO_SYSSPACE; 2846facf4a8dSllai1 uio.uio_fmode = 0; 2847facf4a8dSllai1 uio.uio_extflg = UIO_COPY_CACHED; 2848facf4a8dSllai1 uio.uio_loffset = 0; 2849facf4a8dSllai1 uio.uio_llimit = MAXOFFSET_T; 2850facf4a8dSllai1 2851facf4a8dSllai1 eof = 0; 2852facf4a8dSllai1 error = 0; 2853facf4a8dSllai1 while (!error && !eof) { 2854facf4a8dSllai1 uio.uio_resid = dlen; 2855facf4a8dSllai1 iov.iov_base = (char *)dbuf; 2856facf4a8dSllai1 iov.iov_len = dlen; 2857facf4a8dSllai1 2858facf4a8dSllai1 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); 2859da6c28aaSamw error = VOP_READDIR(vp, &uio, kcred, &eof, NULL, 0); 2860facf4a8dSllai1 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); 2861facf4a8dSllai1 2862facf4a8dSllai1 dbuflen = dlen - uio.uio_resid; 2863facf4a8dSllai1 2864facf4a8dSllai1 if (error || dbuflen == 0) 2865facf4a8dSllai1 break; 2866facf4a8dSllai1 2867facf4a8dSllai1 for (dp = dbuf; ((intptr_t)dp < (intptr_t)dbuf + dbuflen); 2868facf4a8dSllai1 dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) { 2869facf4a8dSllai1 2870facf4a8dSllai1 nm = dp->d_name; 2871facf4a8dSllai1 2872facf4a8dSllai1 if (strcmp(nm, ".") == 0 || strcmp(nm, "..") == 0) 2873facf4a8dSllai1 continue; 2874facf4a8dSllai1 if (npaths == npaths_alloc) { 2875facf4a8dSllai1 npaths_alloc += 64; 2876facf4a8dSllai1 newlist = (char **) 2877facf4a8dSllai1 kmem_zalloc((npaths_alloc + 1) * 2878facf4a8dSllai1 sizeof (char *), KM_SLEEP); 2879facf4a8dSllai1 if (pathlist) { 2880facf4a8dSllai1 bcopy(pathlist, newlist, 2881facf4a8dSllai1 npaths * sizeof (char *)); 2882facf4a8dSllai1 kmem_free(pathlist, 2883facf4a8dSllai1 (npaths + 1) * sizeof (char *)); 2884facf4a8dSllai1 } 2885facf4a8dSllai1 pathlist = newlist; 2886facf4a8dSllai1 } 2887facf4a8dSllai1 n = strlen(nm) + 1; 2888facf4a8dSllai1 s = kmem_alloc(n, KM_SLEEP); 2889facf4a8dSllai1 bcopy(nm, s, n); 2890facf4a8dSllai1 pathlist[npaths++] = s; 2891facf4a8dSllai1 sdcmn_err11((" %s/%s\n", dir, s)); 2892e37c6c37Scth 2893e37c6c37Scth /* if checking empty, one entry is as good as many */ 2894e37c6c37Scth if (checking_empty) { 2895e37c6c37Scth eof = 1; 2896e37c6c37Scth break; 2897e37c6c37Scth } 2898facf4a8dSllai1 } 2899facf4a8dSllai1 } 2900facf4a8dSllai1 2901facf4a8dSllai1 exit: 2902facf4a8dSllai1 VN_RELE(vp); 2903facf4a8dSllai1 2904facf4a8dSllai1 if (dbuf) 2905facf4a8dSllai1 kmem_free(dbuf, dlen); 2906facf4a8dSllai1 2907facf4a8dSllai1 if (error) 2908facf4a8dSllai1 return (error); 2909facf4a8dSllai1 2910facf4a8dSllai1 *dirlistp = pathlist; 2911facf4a8dSllai1 *npathsp = npaths; 2912facf4a8dSllai1 *npathsp_alloc = npaths_alloc; 2913facf4a8dSllai1 2914facf4a8dSllai1 return (0); 2915facf4a8dSllai1 } 2916facf4a8dSllai1 2917facf4a8dSllai1 void 2918facf4a8dSllai1 sdev_modctl_readdir_free(char **pathlist, int npaths, int npaths_alloc) 2919facf4a8dSllai1 { 2920facf4a8dSllai1 int i, n; 2921facf4a8dSllai1 2922facf4a8dSllai1 for (i = 0; i < npaths; i++) { 2923facf4a8dSllai1 n = strlen(pathlist[i]) + 1; 2924facf4a8dSllai1 kmem_free(pathlist[i], n); 2925facf4a8dSllai1 } 2926facf4a8dSllai1 2927facf4a8dSllai1 kmem_free(pathlist, (npaths_alloc + 1) * sizeof (char *)); 2928facf4a8dSllai1 } 2929facf4a8dSllai1 2930facf4a8dSllai1 int 2931facf4a8dSllai1 sdev_modctl_devexists(const char *path) 2932facf4a8dSllai1 { 2933facf4a8dSllai1 vnode_t *vp; 2934facf4a8dSllai1 int error; 2935facf4a8dSllai1 2936facf4a8dSllai1 error = sdev_modctl_lookup(path, &vp); 2937facf4a8dSllai1 sdcmn_err11(("modctl dev exists: %s by %s: %s\n", 2938facf4a8dSllai1 path, curproc->p_user.u_comm, 2939facf4a8dSllai1 (error == 0) ? "ok" : "failed")); 2940facf4a8dSllai1 if (error == 0) 2941facf4a8dSllai1 VN_RELE(vp); 2942facf4a8dSllai1 2943facf4a8dSllai1 return (error); 2944facf4a8dSllai1 } 2945facf4a8dSllai1 2946facf4a8dSllai1 extern int sdev_vnodeops_tbl_size; 2947facf4a8dSllai1 2948facf4a8dSllai1 /* 2949facf4a8dSllai1 * construct a new template with overrides from vtab 2950facf4a8dSllai1 */ 2951facf4a8dSllai1 static fs_operation_def_t * 2952facf4a8dSllai1 sdev_merge_vtab(const fs_operation_def_t tab[]) 2953facf4a8dSllai1 { 2954facf4a8dSllai1 fs_operation_def_t *new; 2955facf4a8dSllai1 const fs_operation_def_t *tab_entry; 2956facf4a8dSllai1 2957facf4a8dSllai1 /* make a copy of standard vnode ops table */ 2958facf4a8dSllai1 new = kmem_alloc(sdev_vnodeops_tbl_size, KM_SLEEP); 2959facf4a8dSllai1 bcopy((void *)sdev_vnodeops_tbl, new, sdev_vnodeops_tbl_size); 2960facf4a8dSllai1 2961facf4a8dSllai1 /* replace the overrides from tab */ 2962facf4a8dSllai1 for (tab_entry = tab; tab_entry->name != NULL; tab_entry++) { 2963facf4a8dSllai1 fs_operation_def_t *std_entry = new; 2964facf4a8dSllai1 while (std_entry->name) { 2965facf4a8dSllai1 if (strcmp(tab_entry->name, std_entry->name) == 0) { 2966facf4a8dSllai1 std_entry->func = tab_entry->func; 2967facf4a8dSllai1 break; 2968facf4a8dSllai1 } 2969facf4a8dSllai1 std_entry++; 2970facf4a8dSllai1 } 2971facf4a8dSllai1 if (std_entry->name == NULL) 2972facf4a8dSllai1 cmn_err(CE_NOTE, "sdev_merge_vtab: entry %s unused.", 2973facf4a8dSllai1 tab_entry->name); 2974facf4a8dSllai1 } 2975facf4a8dSllai1 2976facf4a8dSllai1 return (new); 2977facf4a8dSllai1 } 2978facf4a8dSllai1 2979facf4a8dSllai1 /* free memory allocated by sdev_merge_vtab */ 2980facf4a8dSllai1 static void 2981facf4a8dSllai1 sdev_free_vtab(fs_operation_def_t *new) 2982facf4a8dSllai1 { 2983facf4a8dSllai1 kmem_free(new, sdev_vnodeops_tbl_size); 2984facf4a8dSllai1 } 2985facf4a8dSllai1 2986facf4a8dSllai1 /* 2987facf4a8dSllai1 * a generic setattr() function 2988facf4a8dSllai1 * 2989facf4a8dSllai1 * note: flags only supports AT_UID and AT_GID. 2990facf4a8dSllai1 * Future enhancements can be done for other types, e.g. AT_MODE 2991facf4a8dSllai1 */ 2992facf4a8dSllai1 int 2993facf4a8dSllai1 devname_setattr_func(struct vnode *vp, struct vattr *vap, int flags, 2994facf4a8dSllai1 struct cred *cred, int (*callback)(struct sdev_node *, struct vattr *, 2995facf4a8dSllai1 int), int protocol) 2996facf4a8dSllai1 { 2997facf4a8dSllai1 struct sdev_node *dv = VTOSDEV(vp); 2998facf4a8dSllai1 struct sdev_node *parent = dv->sdev_dotdot; 2999facf4a8dSllai1 struct vattr *get; 3000facf4a8dSllai1 uint_t mask = vap->va_mask; 3001facf4a8dSllai1 int error; 3002facf4a8dSllai1 3003facf4a8dSllai1 /* some sanity checks */ 3004facf4a8dSllai1 if (vap->va_mask & AT_NOSET) 3005facf4a8dSllai1 return (EINVAL); 3006facf4a8dSllai1 3007facf4a8dSllai1 if (vap->va_mask & AT_SIZE) { 3008facf4a8dSllai1 if (vp->v_type == VDIR) { 3009facf4a8dSllai1 return (EISDIR); 3010facf4a8dSllai1 } 3011facf4a8dSllai1 } 3012facf4a8dSllai1 3013facf4a8dSllai1 /* no need to set attribute, but do not fail either */ 3014facf4a8dSllai1 ASSERT(parent); 3015facf4a8dSllai1 rw_enter(&parent->sdev_contents, RW_READER); 3016facf4a8dSllai1 if (dv->sdev_state == SDEV_ZOMBIE) { 3017facf4a8dSllai1 rw_exit(&parent->sdev_contents); 3018facf4a8dSllai1 return (0); 3019facf4a8dSllai1 } 3020facf4a8dSllai1 3021facf4a8dSllai1 /* If backing store exists, just set it. */ 3022facf4a8dSllai1 if (dv->sdev_attrvp) { 3023facf4a8dSllai1 rw_exit(&parent->sdev_contents); 3024facf4a8dSllai1 return (VOP_SETATTR(dv->sdev_attrvp, vap, flags, cred, NULL)); 3025facf4a8dSllai1 } 3026facf4a8dSllai1 3027facf4a8dSllai1 /* 3028facf4a8dSllai1 * Otherwise, for nodes with the persistence attribute, create it. 3029facf4a8dSllai1 */ 3030facf4a8dSllai1 ASSERT(dv->sdev_attr); 3031facf4a8dSllai1 if (SDEV_IS_PERSIST(dv) || 3032facf4a8dSllai1 ((vap->va_mask & ~AT_TIMES) != 0 && !SDEV_IS_DYNAMIC(dv))) { 3033facf4a8dSllai1 sdev_vattr_merge(dv, vap); 3034facf4a8dSllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 3035facf4a8dSllai1 error = sdev_shadow_node(dv, cred); 3036facf4a8dSllai1 rw_exit(&dv->sdev_contents); 3037facf4a8dSllai1 rw_exit(&parent->sdev_contents); 3038facf4a8dSllai1 3039facf4a8dSllai1 if (error) 3040facf4a8dSllai1 return (error); 3041facf4a8dSllai1 return (VOP_SETATTR(dv->sdev_attrvp, vap, flags, cred, NULL)); 3042facf4a8dSllai1 } 3043facf4a8dSllai1 3044facf4a8dSllai1 3045facf4a8dSllai1 /* 3046facf4a8dSllai1 * sdev_attr was allocated in sdev_mknode 3047facf4a8dSllai1 */ 3048facf4a8dSllai1 rw_enter(&dv->sdev_contents, RW_WRITER); 3049da6c28aaSamw error = secpolicy_vnode_setattr(cred, vp, vap, 3050da6c28aaSamw dv->sdev_attr, flags, sdev_unlocked_access, dv); 3051facf4a8dSllai1 if (error) { 3052facf4a8dSllai1 rw_exit(&dv->sdev_contents); 3053facf4a8dSllai1 rw_exit(&parent->sdev_contents); 3054facf4a8dSllai1 return (error); 3055facf4a8dSllai1 } 3056facf4a8dSllai1 3057facf4a8dSllai1 get = dv->sdev_attr; 3058facf4a8dSllai1 if (mask & AT_MODE) { 3059facf4a8dSllai1 get->va_mode &= S_IFMT; 3060facf4a8dSllai1 get->va_mode |= vap->va_mode & ~S_IFMT; 3061facf4a8dSllai1 } 3062facf4a8dSllai1 3063facf4a8dSllai1 if ((mask & AT_UID) || (mask & AT_GID)) { 3064facf4a8dSllai1 if (mask & AT_UID) 3065facf4a8dSllai1 get->va_uid = vap->va_uid; 3066facf4a8dSllai1 if (mask & AT_GID) 3067facf4a8dSllai1 get->va_gid = vap->va_gid; 3068facf4a8dSllai1 /* 3069facf4a8dSllai1 * a callback must be provided if the protocol is set 3070facf4a8dSllai1 */ 3071facf4a8dSllai1 if ((protocol & AT_UID) || (protocol & AT_GID)) { 3072facf4a8dSllai1 ASSERT(callback); 3073facf4a8dSllai1 error = callback(dv, get, protocol); 3074facf4a8dSllai1 if (error) { 3075facf4a8dSllai1 rw_exit(&dv->sdev_contents); 3076facf4a8dSllai1 rw_exit(&parent->sdev_contents); 3077facf4a8dSllai1 return (error); 3078facf4a8dSllai1 } 3079facf4a8dSllai1 } 3080facf4a8dSllai1 } 3081facf4a8dSllai1 3082facf4a8dSllai1 if (mask & AT_ATIME) 3083facf4a8dSllai1 get->va_atime = vap->va_atime; 3084facf4a8dSllai1 if (mask & AT_MTIME) 3085facf4a8dSllai1 get->va_mtime = vap->va_mtime; 3086facf4a8dSllai1 if (mask & (AT_MODE | AT_UID | AT_GID | AT_CTIME)) { 3087facf4a8dSllai1 gethrestime(&get->va_ctime); 3088facf4a8dSllai1 } 3089facf4a8dSllai1 3090facf4a8dSllai1 sdev_vattr_merge(dv, get); 3091facf4a8dSllai1 rw_exit(&dv->sdev_contents); 3092facf4a8dSllai1 rw_exit(&parent->sdev_contents); 3093facf4a8dSllai1 return (0); 3094facf4a8dSllai1 } 3095d62bc4baSyz147064 3096d62bc4baSyz147064 /* 3097d62bc4baSyz147064 * a generic inactive() function 3098d62bc4baSyz147064 */ 30993c5e027bSEric Taylor /*ARGSUSED*/ 3100d62bc4baSyz147064 void 3101d62bc4baSyz147064 devname_inactive_func(struct vnode *vp, struct cred *cred, 3102d62bc4baSyz147064 void (*callback)(struct vnode *)) 3103d62bc4baSyz147064 { 3104d62bc4baSyz147064 int clean; 3105d62bc4baSyz147064 struct sdev_node *dv = VTOSDEV(vp); 3106d62bc4baSyz147064 int state; 3107d62bc4baSyz147064 3108d62bc4baSyz147064 mutex_enter(&vp->v_lock); 3109d62bc4baSyz147064 ASSERT(vp->v_count >= 1); 3110d62bc4baSyz147064 3111*9e5aa9d8SRobert Mustacchi 3112d62bc4baSyz147064 if (vp->v_count == 1 && callback != NULL) 3113d62bc4baSyz147064 callback(vp); 3114d62bc4baSyz147064 3115*9e5aa9d8SRobert Mustacchi rw_enter(&dv->sdev_contents, RW_WRITER); 3116*9e5aa9d8SRobert Mustacchi state = dv->sdev_state; 3117*9e5aa9d8SRobert Mustacchi 3118d62bc4baSyz147064 clean = (vp->v_count == 1) && (state == SDEV_ZOMBIE); 3119d62bc4baSyz147064 3120d62bc4baSyz147064 /* 3121*9e5aa9d8SRobert Mustacchi * sdev is a rather bad public citizen. It violates the general 3122*9e5aa9d8SRobert Mustacchi * agreement that in memory nodes should always have a valid reference 3123*9e5aa9d8SRobert Mustacchi * count on their vnode. But that's not the case here. This means that 3124*9e5aa9d8SRobert Mustacchi * we do actually have to distinguish between getting inactive callbacks 3125*9e5aa9d8SRobert Mustacchi * for zombies and otherwise. This should probably be fixed. 3126d62bc4baSyz147064 */ 3127d62bc4baSyz147064 if (clean) { 3128*9e5aa9d8SRobert Mustacchi /* Remove the . entry to ourselves */ 3129d62bc4baSyz147064 if (vp->v_type == VDIR) { 3130*9e5aa9d8SRobert Mustacchi decr_link(dv); 3131d62bc4baSyz147064 } 3132*9e5aa9d8SRobert Mustacchi VERIFY(dv->sdev_nlink == 1); 3133*9e5aa9d8SRobert Mustacchi decr_link(dv); 3134d62bc4baSyz147064 --vp->v_count; 3135*9e5aa9d8SRobert Mustacchi rw_exit(&dv->sdev_contents); 3136d62bc4baSyz147064 mutex_exit(&vp->v_lock); 3137d62bc4baSyz147064 sdev_nodedestroy(dv, 0); 3138d62bc4baSyz147064 } else { 3139d62bc4baSyz147064 --vp->v_count; 3140*9e5aa9d8SRobert Mustacchi rw_exit(&dv->sdev_contents); 3141d62bc4baSyz147064 mutex_exit(&vp->v_lock); 3142d62bc4baSyz147064 } 3143d62bc4baSyz147064 } 3144