17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * 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. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 2294c894bbSVikram Hegde * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. 237c478bd9Sstevel@tonic-gate */ 247c478bd9Sstevel@tonic-gate 257c478bd9Sstevel@tonic-gate /* 267c478bd9Sstevel@tonic-gate * miscellaneous routines for the devfs 277c478bd9Sstevel@tonic-gate */ 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate #include <sys/types.h> 307c478bd9Sstevel@tonic-gate #include <sys/param.h> 317c478bd9Sstevel@tonic-gate #include <sys/t_lock.h> 327c478bd9Sstevel@tonic-gate #include <sys/systm.h> 337c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 347c478bd9Sstevel@tonic-gate #include <sys/user.h> 357c478bd9Sstevel@tonic-gate #include <sys/time.h> 367c478bd9Sstevel@tonic-gate #include <sys/vfs.h> 377c478bd9Sstevel@tonic-gate #include <sys/vnode.h> 387c478bd9Sstevel@tonic-gate #include <sys/file.h> 397c478bd9Sstevel@tonic-gate #include <sys/fcntl.h> 407c478bd9Sstevel@tonic-gate #include <sys/flock.h> 417c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 427c478bd9Sstevel@tonic-gate #include <sys/uio.h> 437c478bd9Sstevel@tonic-gate #include <sys/errno.h> 447c478bd9Sstevel@tonic-gate #include <sys/stat.h> 457c478bd9Sstevel@tonic-gate #include <sys/cred.h> 467c478bd9Sstevel@tonic-gate #include <sys/dirent.h> 477c478bd9Sstevel@tonic-gate #include <sys/pathname.h> 487c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 497c478bd9Sstevel@tonic-gate #include <sys/debug.h> 507c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 517c478bd9Sstevel@tonic-gate #include <fs/fs_subr.h> 527c478bd9Sstevel@tonic-gate #include <sys/fs/dv_node.h> 537c478bd9Sstevel@tonic-gate #include <sys/fs/snode.h> 547c478bd9Sstevel@tonic-gate #include <sys/sunndi.h> 557c478bd9Sstevel@tonic-gate #include <sys/sunmdi.h> 567c478bd9Sstevel@tonic-gate #include <sys/conf.h> 577c478bd9Sstevel@tonic-gate 587c478bd9Sstevel@tonic-gate #ifdef DEBUG 597c478bd9Sstevel@tonic-gate int devfs_debug = 0x0; 607c478bd9Sstevel@tonic-gate #endif 617c478bd9Sstevel@tonic-gate 627c478bd9Sstevel@tonic-gate const char dvnm[] = "devfs"; 637c478bd9Sstevel@tonic-gate kmem_cache_t *dv_node_cache; /* dv_node cache */ 64f94a2171Sdf125853 65f94a2171Sdf125853 /* 66f94a2171Sdf125853 * The devfs_clean_key is taken during a devfs_clean operation: it is used to 67f94a2171Sdf125853 * prevent unnecessary code execution and for detection of potential deadlocks. 68f94a2171Sdf125853 */ 697c478bd9Sstevel@tonic-gate uint_t devfs_clean_key; 70f94a2171Sdf125853 717c478bd9Sstevel@tonic-gate struct dv_node *dvroot; 727c478bd9Sstevel@tonic-gate 737c478bd9Sstevel@tonic-gate /* prototype memory vattrs */ 747c478bd9Sstevel@tonic-gate vattr_t dv_vattr_dir = { 757c478bd9Sstevel@tonic-gate AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */ 767c478bd9Sstevel@tonic-gate VDIR, /* va_type */ 777c478bd9Sstevel@tonic-gate DV_DIRMODE_DEFAULT, /* va_mode */ 787c478bd9Sstevel@tonic-gate DV_UID_DEFAULT, /* va_uid */ 797c478bd9Sstevel@tonic-gate DV_GID_DEFAULT, /* va_gid */ 807c478bd9Sstevel@tonic-gate 0, /* va_fsid; */ 817c478bd9Sstevel@tonic-gate 0, /* va_nodeid; */ 827c478bd9Sstevel@tonic-gate 0, /* va_nlink; */ 837c478bd9Sstevel@tonic-gate 0, /* va_size; */ 847c478bd9Sstevel@tonic-gate 0, /* va_atime; */ 857c478bd9Sstevel@tonic-gate 0, /* va_mtime; */ 867c478bd9Sstevel@tonic-gate 0, /* va_ctime; */ 877c478bd9Sstevel@tonic-gate 0, /* va_rdev; */ 887c478bd9Sstevel@tonic-gate 0, /* va_blksize; */ 897c478bd9Sstevel@tonic-gate 0, /* va_nblocks; */ 907c478bd9Sstevel@tonic-gate 0, /* va_seq; */ 917c478bd9Sstevel@tonic-gate }; 927c478bd9Sstevel@tonic-gate 937c478bd9Sstevel@tonic-gate vattr_t dv_vattr_file = { 947c478bd9Sstevel@tonic-gate AT_TYPE|AT_MODE|AT_SIZE|AT_UID|AT_GID|AT_RDEV, /* va_mask */ 957c478bd9Sstevel@tonic-gate 0, /* va_type */ 967c478bd9Sstevel@tonic-gate DV_DEVMODE_DEFAULT, /* va_mode */ 977c478bd9Sstevel@tonic-gate DV_UID_DEFAULT, /* va_uid */ 987c478bd9Sstevel@tonic-gate DV_GID_DEFAULT, /* va_gid */ 997c478bd9Sstevel@tonic-gate 0, /* va_fsid; */ 1007c478bd9Sstevel@tonic-gate 0, /* va_nodeid; */ 1017c478bd9Sstevel@tonic-gate 0, /* va_nlink; */ 1027c478bd9Sstevel@tonic-gate 0, /* va_size; */ 1037c478bd9Sstevel@tonic-gate 0, /* va_atime; */ 1047c478bd9Sstevel@tonic-gate 0, /* va_mtime; */ 1057c478bd9Sstevel@tonic-gate 0, /* va_ctime; */ 1067c478bd9Sstevel@tonic-gate 0, /* va_rdev; */ 1077c478bd9Sstevel@tonic-gate 0, /* va_blksize; */ 1087c478bd9Sstevel@tonic-gate 0, /* va_nblocks; */ 1097c478bd9Sstevel@tonic-gate 0, /* va_seq; */ 1107c478bd9Sstevel@tonic-gate }; 1117c478bd9Sstevel@tonic-gate 1127c478bd9Sstevel@tonic-gate vattr_t dv_vattr_priv = { 1137c478bd9Sstevel@tonic-gate AT_TYPE|AT_MODE|AT_SIZE|AT_UID|AT_GID|AT_RDEV, /* va_mask */ 1147c478bd9Sstevel@tonic-gate 0, /* va_type */ 1157c478bd9Sstevel@tonic-gate DV_DEVMODE_PRIV, /* va_mode */ 1167c478bd9Sstevel@tonic-gate DV_UID_DEFAULT, /* va_uid */ 1177c478bd9Sstevel@tonic-gate DV_GID_DEFAULT, /* va_gid */ 1187c478bd9Sstevel@tonic-gate 0, /* va_fsid; */ 1197c478bd9Sstevel@tonic-gate 0, /* va_nodeid; */ 1207c478bd9Sstevel@tonic-gate 0, /* va_nlink; */ 1217c478bd9Sstevel@tonic-gate 0, /* va_size; */ 1227c478bd9Sstevel@tonic-gate 0, /* va_atime; */ 1237c478bd9Sstevel@tonic-gate 0, /* va_mtime; */ 1247c478bd9Sstevel@tonic-gate 0, /* va_ctime; */ 1257c478bd9Sstevel@tonic-gate 0, /* va_rdev; */ 1267c478bd9Sstevel@tonic-gate 0, /* va_blksize; */ 1277c478bd9Sstevel@tonic-gate 0, /* va_nblocks; */ 1287c478bd9Sstevel@tonic-gate 0, /* va_seq; */ 1297c478bd9Sstevel@tonic-gate }; 1307c478bd9Sstevel@tonic-gate 1317c478bd9Sstevel@tonic-gate extern dev_info_t *clone_dip; 1327c478bd9Sstevel@tonic-gate extern major_t clone_major; 1337c478bd9Sstevel@tonic-gate extern struct dev_ops *ddi_hold_driver(major_t); 1347c478bd9Sstevel@tonic-gate 135027021c7SChris Horne /* dv_node node constructor for kmem cache */ 1367c478bd9Sstevel@tonic-gate static int 1377c478bd9Sstevel@tonic-gate i_dv_node_ctor(void *buf, void *cfarg, int flag) 1387c478bd9Sstevel@tonic-gate { 139027021c7SChris Horne _NOTE(ARGUNUSED(cfarg, flag)) 1407c478bd9Sstevel@tonic-gate struct dv_node *dv = (struct dv_node *)buf; 1417c478bd9Sstevel@tonic-gate struct vnode *vp; 1427c478bd9Sstevel@tonic-gate 1437c478bd9Sstevel@tonic-gate bzero(buf, sizeof (struct dv_node)); 144b5fca8f8Stomee vp = dv->dv_vnode = vn_alloc(flag); 145b5fca8f8Stomee if (vp == NULL) { 146b5fca8f8Stomee return (-1); 147b5fca8f8Stomee } 148b5fca8f8Stomee vp->v_data = dv; 1497c478bd9Sstevel@tonic-gate rw_init(&dv->dv_contents, NULL, RW_DEFAULT, NULL); 1507c478bd9Sstevel@tonic-gate return (0); 1517c478bd9Sstevel@tonic-gate } 1527c478bd9Sstevel@tonic-gate 153027021c7SChris Horne /* dv_node node destructor for kmem cache */ 1547c478bd9Sstevel@tonic-gate static void 1557c478bd9Sstevel@tonic-gate i_dv_node_dtor(void *buf, void *arg) 1567c478bd9Sstevel@tonic-gate { 157027021c7SChris Horne _NOTE(ARGUNUSED(arg)) 1587c478bd9Sstevel@tonic-gate struct dv_node *dv = (struct dv_node *)buf; 1597c478bd9Sstevel@tonic-gate struct vnode *vp = DVTOV(dv); 1607c478bd9Sstevel@tonic-gate 1617c478bd9Sstevel@tonic-gate rw_destroy(&dv->dv_contents); 1627c478bd9Sstevel@tonic-gate vn_invalid(vp); 1637c478bd9Sstevel@tonic-gate vn_free(vp); 1647c478bd9Sstevel@tonic-gate } 1657c478bd9Sstevel@tonic-gate 1667c478bd9Sstevel@tonic-gate 167027021c7SChris Horne /* initialize dv_node node cache */ 1687c478bd9Sstevel@tonic-gate void 1697c478bd9Sstevel@tonic-gate dv_node_cache_init() 1707c478bd9Sstevel@tonic-gate { 1717c478bd9Sstevel@tonic-gate ASSERT(dv_node_cache == NULL); 1727c478bd9Sstevel@tonic-gate dv_node_cache = kmem_cache_create("dv_node_cache", 1737c478bd9Sstevel@tonic-gate sizeof (struct dv_node), 0, i_dv_node_ctor, i_dv_node_dtor, 1747c478bd9Sstevel@tonic-gate NULL, NULL, NULL, 0); 1757c478bd9Sstevel@tonic-gate 1767c478bd9Sstevel@tonic-gate tsd_create(&devfs_clean_key, NULL); 1777c478bd9Sstevel@tonic-gate } 1787c478bd9Sstevel@tonic-gate 179027021c7SChris Horne /* destroy dv_node node cache */ 1807c478bd9Sstevel@tonic-gate void 1817c478bd9Sstevel@tonic-gate dv_node_cache_fini() 1827c478bd9Sstevel@tonic-gate { 1837c478bd9Sstevel@tonic-gate ASSERT(dv_node_cache != NULL); 1847c478bd9Sstevel@tonic-gate kmem_cache_destroy(dv_node_cache); 1857c478bd9Sstevel@tonic-gate dv_node_cache = NULL; 1867c478bd9Sstevel@tonic-gate 1877c478bd9Sstevel@tonic-gate tsd_destroy(&devfs_clean_key); 1887c478bd9Sstevel@tonic-gate } 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate /* 1917c478bd9Sstevel@tonic-gate * dv_mkino - Generate a unique inode number for devfs nodes. 1927c478bd9Sstevel@tonic-gate * 1937c478bd9Sstevel@tonic-gate * Although ino_t is 64 bits, the inode number is truncated to 32 bits for 32 1947c478bd9Sstevel@tonic-gate * bit non-LARGEFILE applications. This means that there is a requirement to 1957c478bd9Sstevel@tonic-gate * maintain the inode number as a 32 bit value or applications will have 1967c478bd9Sstevel@tonic-gate * stat(2) calls fail with EOVERFLOW. We form a 32 bit inode number from the 1977c478bd9Sstevel@tonic-gate * dev_t. but if the minor number is larger than L_MAXMIN32 we fold extra minor 1987c478bd9Sstevel@tonic-gate * 1997c478bd9Sstevel@tonic-gate * To generate inode numbers for directories, we assume that we will never use 2007c478bd9Sstevel@tonic-gate * more than half the major space - this allows for ~8190 drivers. We use this 2017c478bd9Sstevel@tonic-gate * upper major number space to allocate inode numbers for directories by 2027c478bd9Sstevel@tonic-gate * encoding the major and instance into this space. 2037c478bd9Sstevel@tonic-gate * 2047c478bd9Sstevel@tonic-gate * We also skew the result so that inode 2 is reserved for the root of the file 2057c478bd9Sstevel@tonic-gate * system. 2067c478bd9Sstevel@tonic-gate * 2077c478bd9Sstevel@tonic-gate * As part of the future support for 64-bit dev_t APIs, the upper minor bits 2087c478bd9Sstevel@tonic-gate * should be folded into the high inode bits by adding the following code 2097c478bd9Sstevel@tonic-gate * after "ino |= 1": 2107c478bd9Sstevel@tonic-gate * 2117c478bd9Sstevel@tonic-gate * #if (L_BITSMINOR32 != L_BITSMINOR) 2127c478bd9Sstevel@tonic-gate * |* fold overflow minor bits into high bits of inode number *| 2137c478bd9Sstevel@tonic-gate * ino |= ((ino_t)(minor >> L_BITSMINOR32)) << L_BITSMINOR; 2147c478bd9Sstevel@tonic-gate * #endif |* (L_BITSMINOR32 != L_BITSMINOR) *| 2157c478bd9Sstevel@tonic-gate * 2167c478bd9Sstevel@tonic-gate * This way only applications that use devices that overflow their minor 2177c478bd9Sstevel@tonic-gate * space will have an application level impact. 2187c478bd9Sstevel@tonic-gate */ 2197c478bd9Sstevel@tonic-gate static ino_t 2207c478bd9Sstevel@tonic-gate dv_mkino(dev_info_t *devi, vtype_t typ, dev_t dev) 2217c478bd9Sstevel@tonic-gate { 2227c478bd9Sstevel@tonic-gate major_t major; 2237c478bd9Sstevel@tonic-gate minor_t minor; 2247c478bd9Sstevel@tonic-gate ino_t ino; 2257c478bd9Sstevel@tonic-gate static int warn; 2267c478bd9Sstevel@tonic-gate 2277c478bd9Sstevel@tonic-gate if (typ == VDIR) { 2287c478bd9Sstevel@tonic-gate major = ((L_MAXMAJ32 + 1) >> 1) + DEVI(devi)->devi_major; 2297c478bd9Sstevel@tonic-gate minor = ddi_get_instance(devi); 2307c478bd9Sstevel@tonic-gate 2317c478bd9Sstevel@tonic-gate /* makedevice32 in high half of major number space */ 2327c478bd9Sstevel@tonic-gate ino = (ino_t)((major << L_BITSMINOR32) | (minor & L_MAXMIN32)); 2337c478bd9Sstevel@tonic-gate 2347c478bd9Sstevel@tonic-gate major = DEVI(devi)->devi_major; 2357c478bd9Sstevel@tonic-gate } else { 2367c478bd9Sstevel@tonic-gate major = getmajor(dev); 2377c478bd9Sstevel@tonic-gate minor = getminor(dev); 2387c478bd9Sstevel@tonic-gate 2397c478bd9Sstevel@tonic-gate /* makedevice32 */ 2407c478bd9Sstevel@tonic-gate ino = (ino_t)((major << L_BITSMINOR32) | (minor & L_MAXMIN32)); 2417c478bd9Sstevel@tonic-gate 2427c478bd9Sstevel@tonic-gate /* make ino for VCHR different than VBLK */ 2437c478bd9Sstevel@tonic-gate ino <<= 1; 2447c478bd9Sstevel@tonic-gate if (typ == VCHR) 2457c478bd9Sstevel@tonic-gate ino |= 1; 2467c478bd9Sstevel@tonic-gate } 2477c478bd9Sstevel@tonic-gate 2487c478bd9Sstevel@tonic-gate ino += DV_ROOTINO + 1; /* skew */ 2497c478bd9Sstevel@tonic-gate 2507c478bd9Sstevel@tonic-gate /* 2517c478bd9Sstevel@tonic-gate * diagnose things a little early because adding the skew to a large 2527c478bd9Sstevel@tonic-gate * minor number could roll over the major. 2537c478bd9Sstevel@tonic-gate */ 2547c478bd9Sstevel@tonic-gate if ((major >= (L_MAXMAJ32 >> 1)) && (warn == 0)) { 2557c478bd9Sstevel@tonic-gate warn = 1; 2567c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s: inode numbers are not unique", dvnm); 2577c478bd9Sstevel@tonic-gate } 2587c478bd9Sstevel@tonic-gate 2597c478bd9Sstevel@tonic-gate return (ino); 2607c478bd9Sstevel@tonic-gate } 2617c478bd9Sstevel@tonic-gate 2627c478bd9Sstevel@tonic-gate /* 263aac43a5fSjg * Compare two nodes lexographically to balance avl tree 264aac43a5fSjg */ 265aac43a5fSjg static int 266aac43a5fSjg dv_compare_nodes(const struct dv_node *dv1, const struct dv_node *dv2) 267aac43a5fSjg { 268aac43a5fSjg int rv; 269b9ccdc5aScth 270aac43a5fSjg if ((rv = strcmp(dv1->dv_name, dv2->dv_name)) == 0) 271aac43a5fSjg return (0); 272aac43a5fSjg return ((rv < 0) ? -1 : 1); 273aac43a5fSjg } 274aac43a5fSjg 275aac43a5fSjg /* 2767c478bd9Sstevel@tonic-gate * dv_mkroot 2777c478bd9Sstevel@tonic-gate * 2787c478bd9Sstevel@tonic-gate * Build the first VDIR dv_node. 2797c478bd9Sstevel@tonic-gate */ 2807c478bd9Sstevel@tonic-gate struct dv_node * 2817c478bd9Sstevel@tonic-gate dv_mkroot(struct vfs *vfsp, dev_t devfsdev) 2827c478bd9Sstevel@tonic-gate { 2837c478bd9Sstevel@tonic-gate struct dv_node *dv; 2847c478bd9Sstevel@tonic-gate struct vnode *vp; 2857c478bd9Sstevel@tonic-gate 2867c478bd9Sstevel@tonic-gate ASSERT(ddi_root_node() != NULL); 2877c478bd9Sstevel@tonic-gate ASSERT(dv_node_cache != NULL); 2887c478bd9Sstevel@tonic-gate 2897c478bd9Sstevel@tonic-gate dcmn_err3(("dv_mkroot\n")); 2907c478bd9Sstevel@tonic-gate dv = kmem_cache_alloc(dv_node_cache, KM_SLEEP); 2917c478bd9Sstevel@tonic-gate vp = DVTOV(dv); 2927c478bd9Sstevel@tonic-gate vn_reinit(vp); 2937c478bd9Sstevel@tonic-gate vp->v_flag = VROOT; 2947c478bd9Sstevel@tonic-gate vp->v_vfsp = vfsp; 2957c478bd9Sstevel@tonic-gate vp->v_type = VDIR; 2967c478bd9Sstevel@tonic-gate vp->v_rdev = devfsdev; 2977c478bd9Sstevel@tonic-gate vn_setops(vp, dv_vnodeops); 2987c478bd9Sstevel@tonic-gate vn_exists(vp); 2997c478bd9Sstevel@tonic-gate 3007c478bd9Sstevel@tonic-gate dvroot = dv; 3017c478bd9Sstevel@tonic-gate 3027c478bd9Sstevel@tonic-gate dv->dv_name = NULL; /* not needed */ 3037c478bd9Sstevel@tonic-gate dv->dv_namelen = 0; 3047c478bd9Sstevel@tonic-gate 3057c478bd9Sstevel@tonic-gate dv->dv_devi = ddi_root_node(); 3067c478bd9Sstevel@tonic-gate 3077c478bd9Sstevel@tonic-gate dv->dv_ino = DV_ROOTINO; 3087c478bd9Sstevel@tonic-gate dv->dv_nlink = 2; /* name + . (no dv_insert) */ 3097c478bd9Sstevel@tonic-gate dv->dv_dotdot = dv; /* .. == self */ 3107c478bd9Sstevel@tonic-gate dv->dv_attrvp = NULLVP; 3117c478bd9Sstevel@tonic-gate dv->dv_attr = NULL; 3127c478bd9Sstevel@tonic-gate dv->dv_flags = DV_BUILD; 3137c478bd9Sstevel@tonic-gate dv->dv_priv = NULL; 3147c478bd9Sstevel@tonic-gate dv->dv_busy = 0; 3157c478bd9Sstevel@tonic-gate dv->dv_dflt_mode = 0; 3167c478bd9Sstevel@tonic-gate 317aac43a5fSjg avl_create(&dv->dv_entries, 318aac43a5fSjg (int (*)(const void *, const void *))dv_compare_nodes, 319aac43a5fSjg sizeof (struct dv_node), offsetof(struct dv_node, dv_avllink)); 320aac43a5fSjg 3217c478bd9Sstevel@tonic-gate return (dv); 3227c478bd9Sstevel@tonic-gate } 3237c478bd9Sstevel@tonic-gate 3247c478bd9Sstevel@tonic-gate /* 3257c478bd9Sstevel@tonic-gate * dv_mkdir 3267c478bd9Sstevel@tonic-gate * 3277c478bd9Sstevel@tonic-gate * Given an probed or attached nexus node, create a VDIR dv_node. 3287c478bd9Sstevel@tonic-gate * No dv_attrvp is created at this point. 3297c478bd9Sstevel@tonic-gate */ 3307c478bd9Sstevel@tonic-gate struct dv_node * 3317c478bd9Sstevel@tonic-gate dv_mkdir(struct dv_node *ddv, dev_info_t *devi, char *nm) 3327c478bd9Sstevel@tonic-gate { 3337c478bd9Sstevel@tonic-gate struct dv_node *dv; 3347c478bd9Sstevel@tonic-gate struct vnode *vp; 3357c478bd9Sstevel@tonic-gate size_t nmlen; 3367c478bd9Sstevel@tonic-gate 3377c478bd9Sstevel@tonic-gate ASSERT((devi)); 3387c478bd9Sstevel@tonic-gate dcmn_err4(("dv_mkdir: %s\n", nm)); 3397c478bd9Sstevel@tonic-gate 3407c478bd9Sstevel@tonic-gate dv = kmem_cache_alloc(dv_node_cache, KM_SLEEP); 3417c478bd9Sstevel@tonic-gate nmlen = strlen(nm) + 1; 3427c478bd9Sstevel@tonic-gate dv->dv_name = kmem_alloc(nmlen, KM_SLEEP); 3437c478bd9Sstevel@tonic-gate bcopy(nm, dv->dv_name, nmlen); 3447c478bd9Sstevel@tonic-gate dv->dv_namelen = nmlen - 1; /* '\0' not included */ 345aac43a5fSjg 3467c478bd9Sstevel@tonic-gate vp = DVTOV(dv); 3477c478bd9Sstevel@tonic-gate vn_reinit(vp); 3487c478bd9Sstevel@tonic-gate vp->v_flag = 0; 3497c478bd9Sstevel@tonic-gate vp->v_vfsp = DVTOV(ddv)->v_vfsp; 3507c478bd9Sstevel@tonic-gate vp->v_type = VDIR; 3517c478bd9Sstevel@tonic-gate vp->v_rdev = DVTOV(ddv)->v_rdev; 3527c478bd9Sstevel@tonic-gate vn_setops(vp, vn_getops(DVTOV(ddv))); 3537c478bd9Sstevel@tonic-gate vn_exists(vp); 3547c478bd9Sstevel@tonic-gate 3557c478bd9Sstevel@tonic-gate dv->dv_devi = devi; 3567c478bd9Sstevel@tonic-gate ndi_hold_devi(devi); 3577c478bd9Sstevel@tonic-gate 3587c478bd9Sstevel@tonic-gate dv->dv_ino = dv_mkino(devi, VDIR, NODEV); 3597c478bd9Sstevel@tonic-gate dv->dv_nlink = 0; /* updated on insert */ 3607c478bd9Sstevel@tonic-gate dv->dv_dotdot = ddv; 3617c478bd9Sstevel@tonic-gate dv->dv_attrvp = NULLVP; 3627c478bd9Sstevel@tonic-gate dv->dv_attr = NULL; 3637c478bd9Sstevel@tonic-gate dv->dv_flags = DV_BUILD; 3647c478bd9Sstevel@tonic-gate dv->dv_priv = NULL; 3657c478bd9Sstevel@tonic-gate dv->dv_busy = 0; 3667c478bd9Sstevel@tonic-gate dv->dv_dflt_mode = 0; 3677c478bd9Sstevel@tonic-gate 368aac43a5fSjg avl_create(&dv->dv_entries, 369aac43a5fSjg (int (*)(const void *, const void *))dv_compare_nodes, 370aac43a5fSjg sizeof (struct dv_node), offsetof(struct dv_node, dv_avllink)); 371aac43a5fSjg 3727c478bd9Sstevel@tonic-gate return (dv); 3737c478bd9Sstevel@tonic-gate } 3747c478bd9Sstevel@tonic-gate 3757c478bd9Sstevel@tonic-gate /* 3767c478bd9Sstevel@tonic-gate * dv_mknod 3777c478bd9Sstevel@tonic-gate * 3787c478bd9Sstevel@tonic-gate * Given a minor node, create a VCHR or VBLK dv_node. 3797c478bd9Sstevel@tonic-gate * No dv_attrvp is created at this point. 3807c478bd9Sstevel@tonic-gate */ 3817c478bd9Sstevel@tonic-gate static struct dv_node * 3827c478bd9Sstevel@tonic-gate dv_mknod(struct dv_node *ddv, dev_info_t *devi, char *nm, 3837c478bd9Sstevel@tonic-gate struct ddi_minor_data *dmd) 3847c478bd9Sstevel@tonic-gate { 3857c478bd9Sstevel@tonic-gate struct dv_node *dv; 3867c478bd9Sstevel@tonic-gate struct vnode *vp; 3877c478bd9Sstevel@tonic-gate size_t nmlen; 3887c478bd9Sstevel@tonic-gate 3897c478bd9Sstevel@tonic-gate dcmn_err4(("dv_mknod: %s\n", nm)); 3907c478bd9Sstevel@tonic-gate 3917c478bd9Sstevel@tonic-gate dv = kmem_cache_alloc(dv_node_cache, KM_SLEEP); 3927c478bd9Sstevel@tonic-gate nmlen = strlen(nm) + 1; 3937c478bd9Sstevel@tonic-gate dv->dv_name = kmem_alloc(nmlen, KM_SLEEP); 3947c478bd9Sstevel@tonic-gate bcopy(nm, dv->dv_name, nmlen); 3957c478bd9Sstevel@tonic-gate dv->dv_namelen = nmlen - 1; /* no '\0' */ 396aac43a5fSjg 3977c478bd9Sstevel@tonic-gate vp = DVTOV(dv); 3987c478bd9Sstevel@tonic-gate vn_reinit(vp); 3997c478bd9Sstevel@tonic-gate vp->v_flag = 0; 4007c478bd9Sstevel@tonic-gate vp->v_vfsp = DVTOV(ddv)->v_vfsp; 4017c478bd9Sstevel@tonic-gate vp->v_type = dmd->ddm_spec_type == S_IFCHR ? VCHR : VBLK; 4027c478bd9Sstevel@tonic-gate vp->v_rdev = dmd->ddm_dev; 4037c478bd9Sstevel@tonic-gate vn_setops(vp, vn_getops(DVTOV(ddv))); 4047c478bd9Sstevel@tonic-gate vn_exists(vp); 4057c478bd9Sstevel@tonic-gate 406b9ccdc5aScth /* increment dev_ref with devi_lock held */ 407b9ccdc5aScth ASSERT(DEVI_BUSY_OWNED(devi)); 408b9ccdc5aScth mutex_enter(&DEVI(devi)->devi_lock); 4097c478bd9Sstevel@tonic-gate dv->dv_devi = devi; 410027021c7SChris Horne DEVI(devi)->devi_ref++; /* ndi_hold_devi(dip) */ 411b9ccdc5aScth mutex_exit(&DEVI(devi)->devi_lock); 4127c478bd9Sstevel@tonic-gate 4137c478bd9Sstevel@tonic-gate dv->dv_ino = dv_mkino(devi, vp->v_type, vp->v_rdev); 4147c478bd9Sstevel@tonic-gate dv->dv_nlink = 0; /* updated on insert */ 4157c478bd9Sstevel@tonic-gate dv->dv_dotdot = ddv; 4167c478bd9Sstevel@tonic-gate dv->dv_attrvp = NULLVP; 4177c478bd9Sstevel@tonic-gate dv->dv_attr = NULL; 4187c478bd9Sstevel@tonic-gate dv->dv_flags = 0; 4197c478bd9Sstevel@tonic-gate 4207c478bd9Sstevel@tonic-gate if (dmd->type == DDM_INTERNAL_PATH) 4217c478bd9Sstevel@tonic-gate dv->dv_flags |= DV_INTERNAL; 4227c478bd9Sstevel@tonic-gate if (dmd->ddm_flags & DM_NO_FSPERM) 4237c478bd9Sstevel@tonic-gate dv->dv_flags |= DV_NO_FSPERM; 4247c478bd9Sstevel@tonic-gate 4257c478bd9Sstevel@tonic-gate dv->dv_priv = dmd->ddm_node_priv; 4267c478bd9Sstevel@tonic-gate if (dv->dv_priv) 4277c478bd9Sstevel@tonic-gate dphold(dv->dv_priv); 4287c478bd9Sstevel@tonic-gate 4297c478bd9Sstevel@tonic-gate /* 4307c478bd9Sstevel@tonic-gate * Minors created with ddi_create_priv_minor_node can specify 4317c478bd9Sstevel@tonic-gate * a default mode permission other than the devfs default. 4327c478bd9Sstevel@tonic-gate */ 4337c478bd9Sstevel@tonic-gate if (dv->dv_priv || dv->dv_flags & DV_NO_FSPERM) { 4347c478bd9Sstevel@tonic-gate dcmn_err5(("%s: dv_mknod default priv mode 0%o\n", 4357c478bd9Sstevel@tonic-gate dv->dv_name, dmd->ddm_priv_mode)); 4367c478bd9Sstevel@tonic-gate dv->dv_flags |= DV_DFLT_MODE; 4377c478bd9Sstevel@tonic-gate dv->dv_dflt_mode = dmd->ddm_priv_mode & S_IAMB; 4387c478bd9Sstevel@tonic-gate } 4397c478bd9Sstevel@tonic-gate 4407c478bd9Sstevel@tonic-gate return (dv); 4417c478bd9Sstevel@tonic-gate } 4427c478bd9Sstevel@tonic-gate 4437c478bd9Sstevel@tonic-gate /* 4447c478bd9Sstevel@tonic-gate * dv_destroy 4457c478bd9Sstevel@tonic-gate * 4467c478bd9Sstevel@tonic-gate * Destroy what we created in dv_mkdir or dv_mknod. 4477c478bd9Sstevel@tonic-gate * In the case of a *referenced* directory, do nothing. 4487c478bd9Sstevel@tonic-gate */ 4497c478bd9Sstevel@tonic-gate void 4507c478bd9Sstevel@tonic-gate dv_destroy(struct dv_node *dv, uint_t flags) 4517c478bd9Sstevel@tonic-gate { 4527c478bd9Sstevel@tonic-gate vnode_t *vp = DVTOV(dv); 4537c478bd9Sstevel@tonic-gate ASSERT(dv->dv_nlink == 0); /* no references */ 4547c478bd9Sstevel@tonic-gate 4557c478bd9Sstevel@tonic-gate dcmn_err4(("dv_destroy: %s\n", dv->dv_name)); 4567c478bd9Sstevel@tonic-gate 4577c478bd9Sstevel@tonic-gate /* 4587c478bd9Sstevel@tonic-gate * We may be asked to unlink referenced directories. 4597c478bd9Sstevel@tonic-gate * In this case, there is nothing to be done. 4607c478bd9Sstevel@tonic-gate * The eventual memory free will be done in 4617c478bd9Sstevel@tonic-gate * devfs_inactive. 4627c478bd9Sstevel@tonic-gate */ 4637c478bd9Sstevel@tonic-gate if (vp->v_count != 0) { 4647c478bd9Sstevel@tonic-gate ASSERT(vp->v_type == VDIR); 4657c478bd9Sstevel@tonic-gate ASSERT(flags & DV_CLEAN_FORCE); 4667c478bd9Sstevel@tonic-gate ASSERT(DV_STALE(dv)); 4677c478bd9Sstevel@tonic-gate return; 4687c478bd9Sstevel@tonic-gate } 4697c478bd9Sstevel@tonic-gate 470aac43a5fSjg if (vp->v_type == VDIR) { 471aac43a5fSjg ASSERT(DV_FIRST_ENTRY(dv) == NULL); 472aac43a5fSjg avl_destroy(&dv->dv_entries); 473aac43a5fSjg } 474aac43a5fSjg 4757c478bd9Sstevel@tonic-gate if (dv->dv_attrvp != NULLVP) 4767c478bd9Sstevel@tonic-gate VN_RELE(dv->dv_attrvp); 4777c478bd9Sstevel@tonic-gate if (dv->dv_attr != NULL) 4787c478bd9Sstevel@tonic-gate kmem_free(dv->dv_attr, sizeof (struct vattr)); 4797c478bd9Sstevel@tonic-gate if (dv->dv_name != NULL) 4807c478bd9Sstevel@tonic-gate kmem_free(dv->dv_name, dv->dv_namelen + 1); 4817c478bd9Sstevel@tonic-gate if (dv->dv_devi != NULL) { 4827c478bd9Sstevel@tonic-gate ndi_rele_devi(dv->dv_devi); 4837c478bd9Sstevel@tonic-gate } 4847c478bd9Sstevel@tonic-gate if (dv->dv_priv != NULL) { 4857c478bd9Sstevel@tonic-gate dpfree(dv->dv_priv); 4867c478bd9Sstevel@tonic-gate } 4877c478bd9Sstevel@tonic-gate 4887c478bd9Sstevel@tonic-gate kmem_cache_free(dv_node_cache, dv); 4897c478bd9Sstevel@tonic-gate } 4907c478bd9Sstevel@tonic-gate 4917c478bd9Sstevel@tonic-gate /* 4927c478bd9Sstevel@tonic-gate * Find and hold dv_node by name 4937c478bd9Sstevel@tonic-gate */ 494aac43a5fSjg static struct dv_node * 4957c478bd9Sstevel@tonic-gate dv_findbyname(struct dv_node *ddv, char *nm) 4967c478bd9Sstevel@tonic-gate { 4977c478bd9Sstevel@tonic-gate struct dv_node *dv; 498aac43a5fSjg avl_index_t where; 499aac43a5fSjg struct dv_node dvtmp; 5007c478bd9Sstevel@tonic-gate 5017c478bd9Sstevel@tonic-gate ASSERT(RW_LOCK_HELD(&ddv->dv_contents)); 5027c478bd9Sstevel@tonic-gate dcmn_err3(("dv_findbyname: %s\n", nm)); 503aac43a5fSjg 504aac43a5fSjg dvtmp.dv_name = nm; 505aac43a5fSjg dv = avl_find(&ddv->dv_entries, &dvtmp, &where); 506aac43a5fSjg if (dv) { 507aac43a5fSjg ASSERT(dv->dv_dotdot == ddv); 508aac43a5fSjg ASSERT(strcmp(dv->dv_name, nm) == 0); 5097c478bd9Sstevel@tonic-gate VN_HOLD(DVTOV(dv)); 5107c478bd9Sstevel@tonic-gate return (dv); 5117c478bd9Sstevel@tonic-gate } 5127c478bd9Sstevel@tonic-gate return (NULL); 5137c478bd9Sstevel@tonic-gate } 5147c478bd9Sstevel@tonic-gate 5157c478bd9Sstevel@tonic-gate /* 5167c478bd9Sstevel@tonic-gate * Inserts a new dv_node in a parent directory 5177c478bd9Sstevel@tonic-gate */ 5187c478bd9Sstevel@tonic-gate void 5197c478bd9Sstevel@tonic-gate dv_insert(struct dv_node *ddv, struct dv_node *dv) 5207c478bd9Sstevel@tonic-gate { 521aac43a5fSjg avl_index_t where; 522aac43a5fSjg 5237c478bd9Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&ddv->dv_contents)); 5247c478bd9Sstevel@tonic-gate ASSERT(DVTOV(ddv)->v_type == VDIR); 5257c478bd9Sstevel@tonic-gate ASSERT(ddv->dv_nlink >= 2); 5267c478bd9Sstevel@tonic-gate ASSERT(dv->dv_nlink == 0); 5277c478bd9Sstevel@tonic-gate 5287c478bd9Sstevel@tonic-gate dcmn_err3(("dv_insert: %s\n", dv->dv_name)); 5297c478bd9Sstevel@tonic-gate 5307c478bd9Sstevel@tonic-gate dv->dv_dotdot = ddv; 5317c478bd9Sstevel@tonic-gate if (DVTOV(dv)->v_type == VDIR) { 5327c478bd9Sstevel@tonic-gate ddv->dv_nlink++; /* .. to containing directory */ 5337c478bd9Sstevel@tonic-gate dv->dv_nlink = 2; /* name + . */ 5347c478bd9Sstevel@tonic-gate } else { 5357c478bd9Sstevel@tonic-gate dv->dv_nlink = 1; /* name */ 5367c478bd9Sstevel@tonic-gate } 537aac43a5fSjg 538aac43a5fSjg /* enter node in the avl tree */ 539aac43a5fSjg VERIFY(avl_find(&ddv->dv_entries, dv, &where) == NULL); 540aac43a5fSjg avl_insert(&ddv->dv_entries, dv, where); 5417c478bd9Sstevel@tonic-gate } 5427c478bd9Sstevel@tonic-gate 5437c478bd9Sstevel@tonic-gate /* 54445a9d961Scth * Unlink a dv_node from a perent directory 54545a9d961Scth */ 54645a9d961Scth void 547aac43a5fSjg dv_unlink(struct dv_node *ddv, struct dv_node *dv) 54845a9d961Scth { 54945a9d961Scth /* verify linkage of arguments */ 550aac43a5fSjg ASSERT(ddv && dv); 55145a9d961Scth ASSERT(dv->dv_dotdot == ddv); 55245a9d961Scth ASSERT(RW_WRITE_HELD(&ddv->dv_contents)); 55345a9d961Scth ASSERT(DVTOV(ddv)->v_type == VDIR); 55445a9d961Scth 55545a9d961Scth dcmn_err3(("dv_unlink: %s\n", dv->dv_name)); 55645a9d961Scth 55745a9d961Scth if (DVTOV(dv)->v_type == VDIR) { 55845a9d961Scth ddv->dv_nlink--; /* .. to containing directory */ 55945a9d961Scth dv->dv_nlink -= 2; /* name + . */ 56045a9d961Scth } else { 56145a9d961Scth dv->dv_nlink -= 1; /* name */ 56245a9d961Scth } 56345a9d961Scth ASSERT(ddv->dv_nlink >= 2); 56445a9d961Scth ASSERT(dv->dv_nlink == 0); 56545a9d961Scth 56645a9d961Scth dv->dv_dotdot = NULL; 567aac43a5fSjg 568aac43a5fSjg /* remove from avl tree */ 569aac43a5fSjg avl_remove(&ddv->dv_entries, dv); 57045a9d961Scth } 57145a9d961Scth 57245a9d961Scth /* 5737c478bd9Sstevel@tonic-gate * Merge devfs node specific information into an attribute structure. 5747c478bd9Sstevel@tonic-gate * 5757c478bd9Sstevel@tonic-gate * NOTE: specfs provides ATIME,MTIME,CTIME,SIZE,BLKSIZE,NBLOCKS on leaf node. 5767c478bd9Sstevel@tonic-gate */ 5777c478bd9Sstevel@tonic-gate void 5787c478bd9Sstevel@tonic-gate dv_vattr_merge(struct dv_node *dv, struct vattr *vap) 5797c478bd9Sstevel@tonic-gate { 5807c478bd9Sstevel@tonic-gate struct vnode *vp = DVTOV(dv); 5817c478bd9Sstevel@tonic-gate 5827c478bd9Sstevel@tonic-gate vap->va_nodeid = dv->dv_ino; 5837c478bd9Sstevel@tonic-gate vap->va_nlink = dv->dv_nlink; 5847c478bd9Sstevel@tonic-gate 5857c478bd9Sstevel@tonic-gate if (vp->v_type == VDIR) { 5867c478bd9Sstevel@tonic-gate vap->va_rdev = 0; 5877c478bd9Sstevel@tonic-gate vap->va_fsid = vp->v_rdev; 5887c478bd9Sstevel@tonic-gate } else { 5897c478bd9Sstevel@tonic-gate vap->va_rdev = vp->v_rdev; 5907c478bd9Sstevel@tonic-gate vap->va_fsid = DVTOV(dv->dv_dotdot)->v_rdev; 5917c478bd9Sstevel@tonic-gate vap->va_type = vp->v_type; 5927c478bd9Sstevel@tonic-gate /* don't trust the shadow file type */ 5937c478bd9Sstevel@tonic-gate vap->va_mode &= ~S_IFMT; 5947c478bd9Sstevel@tonic-gate if (vap->va_type == VCHR) 5957c478bd9Sstevel@tonic-gate vap->va_mode |= S_IFCHR; 5967c478bd9Sstevel@tonic-gate else 5977c478bd9Sstevel@tonic-gate vap->va_mode |= S_IFBLK; 5987c478bd9Sstevel@tonic-gate } 5997c478bd9Sstevel@tonic-gate } 6007c478bd9Sstevel@tonic-gate 6017c478bd9Sstevel@tonic-gate /* 602facf4a8dSllai1 * Get default device permission by consulting rules in 603facf4a8dSllai1 * privilege specification in minor node and /etc/minor_perm. 604facf4a8dSllai1 * 605facf4a8dSllai1 * This function is called from the devname filesystem to get default 606facf4a8dSllai1 * permissions for a device exported to a non-global zone. 607facf4a8dSllai1 */ 608facf4a8dSllai1 void 609facf4a8dSllai1 devfs_get_defattr(struct vnode *vp, struct vattr *vap, int *no_fs_perm) 610facf4a8dSllai1 { 611facf4a8dSllai1 mperm_t mp; 612facf4a8dSllai1 struct dv_node *dv; 613facf4a8dSllai1 614facf4a8dSllai1 /* If vp isn't a dv_node, return something sensible */ 615facf4a8dSllai1 if (!vn_matchops(vp, dv_vnodeops)) { 616facf4a8dSllai1 if (no_fs_perm) 617facf4a8dSllai1 *no_fs_perm = 0; 618facf4a8dSllai1 *vap = dv_vattr_file; 619facf4a8dSllai1 return; 620facf4a8dSllai1 } 621facf4a8dSllai1 622facf4a8dSllai1 /* 623facf4a8dSllai1 * For minors not created by ddi_create_priv_minor_node(), 624facf4a8dSllai1 * use devfs defaults. 625facf4a8dSllai1 */ 626facf4a8dSllai1 dv = VTODV(vp); 627facf4a8dSllai1 if (vp->v_type == VDIR) { 628facf4a8dSllai1 *vap = dv_vattr_dir; 629facf4a8dSllai1 } else if (dv->dv_flags & DV_NO_FSPERM) { 630facf4a8dSllai1 if (no_fs_perm) 631facf4a8dSllai1 *no_fs_perm = 1; 632facf4a8dSllai1 *vap = dv_vattr_priv; 633facf4a8dSllai1 } else { 634facf4a8dSllai1 /* 635facf4a8dSllai1 * look up perm bits from minor_perm 636facf4a8dSllai1 */ 637facf4a8dSllai1 *vap = dv_vattr_file; 638facf4a8dSllai1 if (dev_minorperm(dv->dv_devi, dv->dv_name, &mp) == 0) { 639facf4a8dSllai1 VATTR_MP_MERGE((*vap), mp); 640facf4a8dSllai1 dcmn_err5(("%s: minor perm mode 0%o\n", 641facf4a8dSllai1 dv->dv_name, vap->va_mode)); 642facf4a8dSllai1 } else if (dv->dv_flags & DV_DFLT_MODE) { 643facf4a8dSllai1 ASSERT((dv->dv_dflt_mode & ~S_IAMB) == 0); 644facf4a8dSllai1 vap->va_mode &= ~S_IAMB; 645facf4a8dSllai1 vap->va_mode |= dv->dv_dflt_mode; 646facf4a8dSllai1 dcmn_err5(("%s: priv mode 0%o\n", 647facf4a8dSllai1 dv->dv_name, vap->va_mode)); 648facf4a8dSllai1 } 649facf4a8dSllai1 } 650facf4a8dSllai1 } 651facf4a8dSllai1 652facf4a8dSllai1 /* 6537c478bd9Sstevel@tonic-gate * dv_shadow_node 6547c478bd9Sstevel@tonic-gate * 6557c478bd9Sstevel@tonic-gate * Given a VDIR dv_node, find/create the associated VDIR 6567c478bd9Sstevel@tonic-gate * node in the shadow attribute filesystem. 6577c478bd9Sstevel@tonic-gate * 6587c478bd9Sstevel@tonic-gate * Given a VCHR/VBLK dv_node, find the associated VREG 6597c478bd9Sstevel@tonic-gate * node in the shadow attribute filesystem. These nodes 6607c478bd9Sstevel@tonic-gate * are only created to persist non-default attributes. 6617c478bd9Sstevel@tonic-gate * Lack of such a node implies the default permissions 6627c478bd9Sstevel@tonic-gate * are sufficient. 6637c478bd9Sstevel@tonic-gate * 6647c478bd9Sstevel@tonic-gate * Managing the attribute file entries is slightly tricky (mostly 6657c478bd9Sstevel@tonic-gate * because we can't intercept VN_HOLD and VN_RELE except on the last 6667c478bd9Sstevel@tonic-gate * release). 6677c478bd9Sstevel@tonic-gate * 6687c478bd9Sstevel@tonic-gate * We assert that if the dv_attrvp pointer is non-NULL, it points 6697c478bd9Sstevel@tonic-gate * to a singly-held (by us) vnode that represents the shadow entry 6707c478bd9Sstevel@tonic-gate * in the underlying filesystem. To avoid store-ordering issues, 6717c478bd9Sstevel@tonic-gate * we assert that the pointer can only be tested under the dv_contents 6727c478bd9Sstevel@tonic-gate * READERS lock. 6737c478bd9Sstevel@tonic-gate */ 6747c478bd9Sstevel@tonic-gate 6757c478bd9Sstevel@tonic-gate void 6767c478bd9Sstevel@tonic-gate dv_shadow_node( 6777c478bd9Sstevel@tonic-gate struct vnode *dvp, /* devfs parent directory vnode */ 6787c478bd9Sstevel@tonic-gate char *nm, /* name component */ 6797c478bd9Sstevel@tonic-gate struct vnode *vp, /* devfs vnode */ 6807c478bd9Sstevel@tonic-gate struct pathname *pnp, /* the path .. */ 6817c478bd9Sstevel@tonic-gate struct vnode *rdir, /* the root .. */ 6827c478bd9Sstevel@tonic-gate struct cred *cred, /* who's asking? */ 6837c478bd9Sstevel@tonic-gate int flags) /* optionally create shadow node */ 6847c478bd9Sstevel@tonic-gate { 6857c478bd9Sstevel@tonic-gate struct dv_node *dv; /* dv_node of named directory */ 6867c478bd9Sstevel@tonic-gate struct vnode *rdvp; /* shadow parent directory vnode */ 6877c478bd9Sstevel@tonic-gate struct vnode *rvp; /* shadow vnode */ 6887c478bd9Sstevel@tonic-gate struct vnode *rrvp; /* realvp of shadow vnode */ 6897c478bd9Sstevel@tonic-gate struct vattr vattr; 6907c478bd9Sstevel@tonic-gate int create_tried; 6917c478bd9Sstevel@tonic-gate int error; 6927c478bd9Sstevel@tonic-gate 6937c478bd9Sstevel@tonic-gate ASSERT(vp->v_type == VDIR || vp->v_type == VCHR || vp->v_type == VBLK); 6947c478bd9Sstevel@tonic-gate dv = VTODV(vp); 6957c478bd9Sstevel@tonic-gate dcmn_err3(("dv_shadow_node: name %s attr %p\n", 6967c478bd9Sstevel@tonic-gate nm, (void *)dv->dv_attrvp)); 6977c478bd9Sstevel@tonic-gate 6987c478bd9Sstevel@tonic-gate if ((flags & DV_SHADOW_WRITE_HELD) == 0) { 6997c478bd9Sstevel@tonic-gate ASSERT(RW_READ_HELD(&dv->dv_contents)); 7007c478bd9Sstevel@tonic-gate if (dv->dv_attrvp != NULLVP) 7017c478bd9Sstevel@tonic-gate return; 7027c478bd9Sstevel@tonic-gate if (!rw_tryupgrade(&dv->dv_contents)) { 7037c478bd9Sstevel@tonic-gate rw_exit(&dv->dv_contents); 7047c478bd9Sstevel@tonic-gate rw_enter(&dv->dv_contents, RW_WRITER); 7057c478bd9Sstevel@tonic-gate if (dv->dv_attrvp != NULLVP) { 7067c478bd9Sstevel@tonic-gate rw_downgrade(&dv->dv_contents); 7077c478bd9Sstevel@tonic-gate return; 7087c478bd9Sstevel@tonic-gate } 7097c478bd9Sstevel@tonic-gate } 7107c478bd9Sstevel@tonic-gate } else { 7117c478bd9Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&dv->dv_contents)); 7127c478bd9Sstevel@tonic-gate if (dv->dv_attrvp != NULLVP) 7137c478bd9Sstevel@tonic-gate return; 7147c478bd9Sstevel@tonic-gate } 7157c478bd9Sstevel@tonic-gate 7167c478bd9Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&dv->dv_contents) && dv->dv_attrvp == NULL); 7177c478bd9Sstevel@tonic-gate 7187c478bd9Sstevel@tonic-gate rdvp = VTODV(dvp)->dv_attrvp; 7197c478bd9Sstevel@tonic-gate create_tried = 0; 7207c478bd9Sstevel@tonic-gate lookup: 7217c478bd9Sstevel@tonic-gate if (rdvp && (dv->dv_flags & DV_NO_FSPERM) == 0) { 722da6c28aaSamw error = VOP_LOOKUP(rdvp, nm, &rvp, pnp, LOOKUP_DIR, rdir, cred, 723da6c28aaSamw NULL, NULL, NULL); 7247c478bd9Sstevel@tonic-gate 7257c478bd9Sstevel@tonic-gate /* factor out the snode since we only want the attribute node */ 726da6c28aaSamw if ((error == 0) && (VOP_REALVP(rvp, &rrvp, NULL) == 0)) { 7277c478bd9Sstevel@tonic-gate VN_HOLD(rrvp); 7287c478bd9Sstevel@tonic-gate VN_RELE(rvp); 7297c478bd9Sstevel@tonic-gate rvp = rrvp; 7307c478bd9Sstevel@tonic-gate } 7317c478bd9Sstevel@tonic-gate } else 7327c478bd9Sstevel@tonic-gate error = EROFS; /* no parent, no entry */ 7337c478bd9Sstevel@tonic-gate 7347c478bd9Sstevel@tonic-gate /* 7357c478bd9Sstevel@tonic-gate * All we want is the permissions (and maybe ACLs and 7367c478bd9Sstevel@tonic-gate * extended attributes), and we want to perform lookups 7377c478bd9Sstevel@tonic-gate * by name. Drivers occasionally change their minor 7387c478bd9Sstevel@tonic-gate * number space. If something changes, there's no 7397c478bd9Sstevel@tonic-gate * much we can do about it here. 7407c478bd9Sstevel@tonic-gate */ 7417c478bd9Sstevel@tonic-gate 7427c478bd9Sstevel@tonic-gate /* The shadow node checks out. We are done */ 7437c478bd9Sstevel@tonic-gate if (error == 0) { 7447c478bd9Sstevel@tonic-gate dv->dv_attrvp = rvp; /* with one hold */ 7457c478bd9Sstevel@tonic-gate 7467c478bd9Sstevel@tonic-gate /* 747fa9e4066Sahrens * Determine if we have non-trivial ACLs on this node. 748fa9e4066Sahrens * It is not necessary to VOP_RWLOCK since fs_acl_nontrivial 749fa9e4066Sahrens * only does VOP_GETSECATTR. 7507c478bd9Sstevel@tonic-gate */ 7517c478bd9Sstevel@tonic-gate dv->dv_flags &= ~DV_ACL; 752fa9e4066Sahrens 753fa9e4066Sahrens if (fs_acl_nontrivial(rvp, cred)) 754fa9e4066Sahrens dv->dv_flags |= DV_ACL; 7557c478bd9Sstevel@tonic-gate 7567c478bd9Sstevel@tonic-gate /* 7577c478bd9Sstevel@tonic-gate * If we have synced out the memory attributes, free 7587c478bd9Sstevel@tonic-gate * them and switch back to using the persistent store. 7597c478bd9Sstevel@tonic-gate */ 7607c478bd9Sstevel@tonic-gate if (rvp && dv->dv_attr) { 7617c478bd9Sstevel@tonic-gate kmem_free(dv->dv_attr, sizeof (struct vattr)); 7627c478bd9Sstevel@tonic-gate dv->dv_attr = NULL; 7637c478bd9Sstevel@tonic-gate } 7647c478bd9Sstevel@tonic-gate if ((flags & DV_SHADOW_WRITE_HELD) == 0) 7657c478bd9Sstevel@tonic-gate rw_downgrade(&dv->dv_contents); 7667c478bd9Sstevel@tonic-gate ASSERT(RW_LOCK_HELD(&dv->dv_contents)); 7677c478bd9Sstevel@tonic-gate return; 7687c478bd9Sstevel@tonic-gate } 7697c478bd9Sstevel@tonic-gate 7707c478bd9Sstevel@tonic-gate /* 7717c478bd9Sstevel@tonic-gate * Failed to find attribute in persistent backing store, 772facf4a8dSllai1 * get default permission bits. 7737c478bd9Sstevel@tonic-gate */ 774facf4a8dSllai1 devfs_get_defattr(vp, &vattr, NULL); 7757c478bd9Sstevel@tonic-gate 7767c478bd9Sstevel@tonic-gate dv_vattr_merge(dv, &vattr); 7777c478bd9Sstevel@tonic-gate gethrestime(&vattr.va_atime); 7787c478bd9Sstevel@tonic-gate vattr.va_mtime = vattr.va_atime; 7797c478bd9Sstevel@tonic-gate vattr.va_ctime = vattr.va_atime; 7807c478bd9Sstevel@tonic-gate 7817c478bd9Sstevel@tonic-gate /* 7827c478bd9Sstevel@tonic-gate * Try to create shadow dir. This is necessary in case 7837c478bd9Sstevel@tonic-gate * we need to create a shadow leaf node later, when user 7847c478bd9Sstevel@tonic-gate * executes chmod. 7857c478bd9Sstevel@tonic-gate */ 7867c478bd9Sstevel@tonic-gate if ((error == ENOENT) && !create_tried) { 7877c478bd9Sstevel@tonic-gate switch (vp->v_type) { 7887c478bd9Sstevel@tonic-gate case VDIR: 789da6c28aaSamw error = VOP_MKDIR(rdvp, nm, &vattr, &rvp, kcred, 790da6c28aaSamw NULL, 0, NULL); 7917c478bd9Sstevel@tonic-gate dsysdebug(error, ("vop_mkdir %s %s %d\n", 7927c478bd9Sstevel@tonic-gate VTODV(dvp)->dv_name, nm, error)); 7937c478bd9Sstevel@tonic-gate create_tried = 1; 7947c478bd9Sstevel@tonic-gate break; 7957c478bd9Sstevel@tonic-gate 7967c478bd9Sstevel@tonic-gate case VCHR: 7977c478bd9Sstevel@tonic-gate case VBLK: 7987c478bd9Sstevel@tonic-gate /* 7997c478bd9Sstevel@tonic-gate * Shadow nodes are only created on demand 8007c478bd9Sstevel@tonic-gate */ 8017c478bd9Sstevel@tonic-gate if (flags & DV_SHADOW_CREATE) { 8027c478bd9Sstevel@tonic-gate error = VOP_CREATE(rdvp, nm, &vattr, NONEXCL, 803da6c28aaSamw VREAD|VWRITE, &rvp, kcred, 0, NULL, NULL); 8047c478bd9Sstevel@tonic-gate dsysdebug(error, ("vop_create %s %s %d\n", 8057c478bd9Sstevel@tonic-gate VTODV(dvp)->dv_name, nm, error)); 8067c478bd9Sstevel@tonic-gate create_tried = 1; 8077c478bd9Sstevel@tonic-gate } 8087c478bd9Sstevel@tonic-gate break; 8097c478bd9Sstevel@tonic-gate 8107c478bd9Sstevel@tonic-gate default: 8117c478bd9Sstevel@tonic-gate cmn_err(CE_PANIC, "devfs: %s: create", dvnm); 8127c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 8137c478bd9Sstevel@tonic-gate } 8147c478bd9Sstevel@tonic-gate 8157c478bd9Sstevel@tonic-gate if (create_tried && 8167c478bd9Sstevel@tonic-gate (error == 0) || (error == EEXIST)) { 8177c478bd9Sstevel@tonic-gate VN_RELE(rvp); 8187c478bd9Sstevel@tonic-gate goto lookup; 8197c478bd9Sstevel@tonic-gate } 8207c478bd9Sstevel@tonic-gate } 8217c478bd9Sstevel@tonic-gate 8227c478bd9Sstevel@tonic-gate /* Store attribute in memory */ 8237c478bd9Sstevel@tonic-gate if (dv->dv_attr == NULL) { 8247c478bd9Sstevel@tonic-gate dv->dv_attr = kmem_alloc(sizeof (struct vattr), KM_SLEEP); 8257c478bd9Sstevel@tonic-gate *(dv->dv_attr) = vattr; 8267c478bd9Sstevel@tonic-gate } 8277c478bd9Sstevel@tonic-gate 8287c478bd9Sstevel@tonic-gate if ((flags & DV_SHADOW_WRITE_HELD) == 0) 8297c478bd9Sstevel@tonic-gate rw_downgrade(&dv->dv_contents); 8307c478bd9Sstevel@tonic-gate ASSERT(RW_LOCK_HELD(&dv->dv_contents)); 8317c478bd9Sstevel@tonic-gate } 8327c478bd9Sstevel@tonic-gate 8337c478bd9Sstevel@tonic-gate /* 8347c478bd9Sstevel@tonic-gate * Given a devinfo node, and a name, returns the appropriate 8357c478bd9Sstevel@tonic-gate * minor information for that named node, if it exists. 8367c478bd9Sstevel@tonic-gate */ 8377c478bd9Sstevel@tonic-gate static int 8387c478bd9Sstevel@tonic-gate dv_find_leafnode(dev_info_t *devi, char *minor_nm, struct ddi_minor_data *r_mi) 8397c478bd9Sstevel@tonic-gate { 8407c478bd9Sstevel@tonic-gate struct ddi_minor_data *dmd; 8417c478bd9Sstevel@tonic-gate 842737d277aScth ASSERT(i_ddi_devi_attached(devi)); 8437c478bd9Sstevel@tonic-gate 8447c478bd9Sstevel@tonic-gate dcmn_err3(("dv_find_leafnode: %s\n", minor_nm)); 845b9ccdc5aScth ASSERT(DEVI_BUSY_OWNED(devi)); 8467c478bd9Sstevel@tonic-gate for (dmd = DEVI(devi)->devi_minor; dmd; dmd = dmd->next) { 8477c478bd9Sstevel@tonic-gate 8487c478bd9Sstevel@tonic-gate /* 8497c478bd9Sstevel@tonic-gate * Skip alias nodes and nodes without a name. 8507c478bd9Sstevel@tonic-gate */ 8517c478bd9Sstevel@tonic-gate if ((dmd->type == DDM_ALIAS) || (dmd->ddm_name == NULL)) 8527c478bd9Sstevel@tonic-gate continue; 8537c478bd9Sstevel@tonic-gate 8547c478bd9Sstevel@tonic-gate dcmn_err4(("dv_find_leafnode: (%s,%s)\n", 8557c478bd9Sstevel@tonic-gate minor_nm, dmd->ddm_name)); 8567c478bd9Sstevel@tonic-gate if (strcmp(minor_nm, dmd->ddm_name) == 0) { 8577c478bd9Sstevel@tonic-gate r_mi->ddm_dev = dmd->ddm_dev; 8587c478bd9Sstevel@tonic-gate r_mi->ddm_spec_type = dmd->ddm_spec_type; 8597c478bd9Sstevel@tonic-gate r_mi->type = dmd->type; 8607c478bd9Sstevel@tonic-gate r_mi->ddm_flags = dmd->ddm_flags; 8617c478bd9Sstevel@tonic-gate r_mi->ddm_node_priv = dmd->ddm_node_priv; 8627c478bd9Sstevel@tonic-gate r_mi->ddm_priv_mode = dmd->ddm_priv_mode; 8637c478bd9Sstevel@tonic-gate if (r_mi->ddm_node_priv) 8647c478bd9Sstevel@tonic-gate dphold(r_mi->ddm_node_priv); 8657c478bd9Sstevel@tonic-gate return (0); 8667c478bd9Sstevel@tonic-gate } 8677c478bd9Sstevel@tonic-gate } 8687c478bd9Sstevel@tonic-gate 8697c478bd9Sstevel@tonic-gate dcmn_err3(("dv_find_leafnode: %s: ENOENT\n", minor_nm)); 8707c478bd9Sstevel@tonic-gate return (ENOENT); 8717c478bd9Sstevel@tonic-gate } 8727c478bd9Sstevel@tonic-gate 8737c478bd9Sstevel@tonic-gate /* 8747c478bd9Sstevel@tonic-gate * Special handling for clone node: 8757c478bd9Sstevel@tonic-gate * Clone minor name is a driver name, the minor number will 8767c478bd9Sstevel@tonic-gate * be the major number of the driver. There is no minor 8777c478bd9Sstevel@tonic-gate * node under the clone driver, so we'll manufacture the 8787c478bd9Sstevel@tonic-gate * dev_t. 8797c478bd9Sstevel@tonic-gate */ 8807c478bd9Sstevel@tonic-gate static struct dv_node * 8817c478bd9Sstevel@tonic-gate dv_clone_mknod(struct dv_node *ddv, char *drvname) 8827c478bd9Sstevel@tonic-gate { 8837c478bd9Sstevel@tonic-gate major_t major; 8847c478bd9Sstevel@tonic-gate struct dv_node *dvp; 8857c478bd9Sstevel@tonic-gate char *devnm; 8867c478bd9Sstevel@tonic-gate struct ddi_minor_data *dmd; 8877c478bd9Sstevel@tonic-gate 8887c478bd9Sstevel@tonic-gate /* 8897c478bd9Sstevel@tonic-gate * Make sure drvname is a STREAMS driver. We load the driver, 8907c478bd9Sstevel@tonic-gate * but don't attach to any instances. This makes stat(2) 8917c478bd9Sstevel@tonic-gate * relatively cheap. 8927c478bd9Sstevel@tonic-gate */ 8937c478bd9Sstevel@tonic-gate major = ddi_name_to_major(drvname); 894a204de77Scth if (major == DDI_MAJOR_T_NONE) 8957c478bd9Sstevel@tonic-gate return (NULL); 8967c478bd9Sstevel@tonic-gate 8977c478bd9Sstevel@tonic-gate if (ddi_hold_driver(major) == NULL) 8987c478bd9Sstevel@tonic-gate return (NULL); 8997c478bd9Sstevel@tonic-gate 9007c478bd9Sstevel@tonic-gate if (STREAMSTAB(major) == NULL) { 9017c478bd9Sstevel@tonic-gate ddi_rele_driver(major); 9027c478bd9Sstevel@tonic-gate return (NULL); 9037c478bd9Sstevel@tonic-gate } 9047c478bd9Sstevel@tonic-gate 9057c478bd9Sstevel@tonic-gate ddi_rele_driver(major); 9067c478bd9Sstevel@tonic-gate devnm = kmem_alloc(MAXNAMELEN, KM_SLEEP); 9077c478bd9Sstevel@tonic-gate (void) snprintf(devnm, MAXNAMELEN, "clone@0:%s", drvname); 9087c478bd9Sstevel@tonic-gate dmd = kmem_zalloc(sizeof (*dmd), KM_SLEEP); 9097c478bd9Sstevel@tonic-gate dmd->ddm_dev = makedevice(clone_major, (minor_t)major); 9107c478bd9Sstevel@tonic-gate dmd->ddm_spec_type = S_IFCHR; 9117c478bd9Sstevel@tonic-gate dvp = dv_mknod(ddv, clone_dip, devnm, dmd); 9127c478bd9Sstevel@tonic-gate kmem_free(dmd, sizeof (*dmd)); 9137c478bd9Sstevel@tonic-gate kmem_free(devnm, MAXNAMELEN); 9147c478bd9Sstevel@tonic-gate return (dvp); 9157c478bd9Sstevel@tonic-gate } 9167c478bd9Sstevel@tonic-gate 9177c478bd9Sstevel@tonic-gate /* 9187c478bd9Sstevel@tonic-gate * Given the parent directory node, and a name in it, returns the 9197c478bd9Sstevel@tonic-gate * named dv_node to the caller (as a vnode). 9207c478bd9Sstevel@tonic-gate * 9217c478bd9Sstevel@tonic-gate * (We need pnp and rdir for doing shadow lookups; they can be NULL) 9227c478bd9Sstevel@tonic-gate */ 9237c478bd9Sstevel@tonic-gate int 9247c478bd9Sstevel@tonic-gate dv_find(struct dv_node *ddv, char *nm, struct vnode **vpp, struct pathname *pnp, 9257c478bd9Sstevel@tonic-gate struct vnode *rdir, struct cred *cred, uint_t ndi_flags) 9267c478bd9Sstevel@tonic-gate { 9277c478bd9Sstevel@tonic-gate extern int isminiroot; /* see modctl.c */ 9287c478bd9Sstevel@tonic-gate 929b9ccdc5aScth int circ; 930f94a2171Sdf125853 int rv = 0, was_busy = 0, nmlen, write_held = 0; 9317c478bd9Sstevel@tonic-gate struct vnode *vp; 9327c478bd9Sstevel@tonic-gate struct dv_node *dv, *dup; 9337c478bd9Sstevel@tonic-gate dev_info_t *pdevi, *devi = NULL; 9347c478bd9Sstevel@tonic-gate char *mnm; 9357c478bd9Sstevel@tonic-gate struct ddi_minor_data *dmd; 9367c478bd9Sstevel@tonic-gate 9377c478bd9Sstevel@tonic-gate dcmn_err3(("dv_find %s\n", nm)); 9387c478bd9Sstevel@tonic-gate 939*9f787237SJakub Jermar if (!rw_tryenter(&ddv->dv_contents, RW_READER)) { 940*9f787237SJakub Jermar if (tsd_get(devfs_clean_key)) 941*9f787237SJakub Jermar return (EBUSY); 9427c478bd9Sstevel@tonic-gate rw_enter(&ddv->dv_contents, RW_READER); 943*9f787237SJakub Jermar } 9447c478bd9Sstevel@tonic-gate start: 9457c478bd9Sstevel@tonic-gate if (DV_STALE(ddv)) { 9467c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 9477c478bd9Sstevel@tonic-gate return (ESTALE); 9487c478bd9Sstevel@tonic-gate } 9497c478bd9Sstevel@tonic-gate 9507c478bd9Sstevel@tonic-gate /* 9517c478bd9Sstevel@tonic-gate * Empty name or ., return node itself. 9527c478bd9Sstevel@tonic-gate */ 9537c478bd9Sstevel@tonic-gate nmlen = strlen(nm); 9547c478bd9Sstevel@tonic-gate if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) { 9557c478bd9Sstevel@tonic-gate *vpp = DVTOV(ddv); 9567c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 9577c478bd9Sstevel@tonic-gate VN_HOLD(*vpp); 9587c478bd9Sstevel@tonic-gate return (0); 9597c478bd9Sstevel@tonic-gate } 9607c478bd9Sstevel@tonic-gate 9617c478bd9Sstevel@tonic-gate /* 9627c478bd9Sstevel@tonic-gate * .., return the parent directory 9637c478bd9Sstevel@tonic-gate */ 9647c478bd9Sstevel@tonic-gate if ((nmlen == 2) && (strcmp(nm, "..") == 0)) { 9657c478bd9Sstevel@tonic-gate *vpp = DVTOV(ddv->dv_dotdot); 9667c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 9677c478bd9Sstevel@tonic-gate VN_HOLD(*vpp); 9687c478bd9Sstevel@tonic-gate return (0); 9697c478bd9Sstevel@tonic-gate } 9707c478bd9Sstevel@tonic-gate 9717c478bd9Sstevel@tonic-gate /* 9727c478bd9Sstevel@tonic-gate * Fail anything without a valid device name component 9737c478bd9Sstevel@tonic-gate */ 9747c478bd9Sstevel@tonic-gate if (nm[0] == '@' || nm[0] == ':') { 9757c478bd9Sstevel@tonic-gate dcmn_err3(("devfs: no driver '%s'\n", nm)); 9767c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 9777c478bd9Sstevel@tonic-gate return (ENOENT); 9787c478bd9Sstevel@tonic-gate } 9797c478bd9Sstevel@tonic-gate 9807c478bd9Sstevel@tonic-gate /* 9817c478bd9Sstevel@tonic-gate * So, now we have to deal with the trickier stuff. 9827c478bd9Sstevel@tonic-gate * 9837c478bd9Sstevel@tonic-gate * (a) search the existing list of dv_nodes on this directory 9847c478bd9Sstevel@tonic-gate */ 9857c478bd9Sstevel@tonic-gate if ((dv = dv_findbyname(ddv, nm)) != NULL) { 9867c478bd9Sstevel@tonic-gate founddv: 9877c478bd9Sstevel@tonic-gate ASSERT(RW_LOCK_HELD(&ddv->dv_contents)); 988f94a2171Sdf125853 989f94a2171Sdf125853 if (!rw_tryenter(&dv->dv_contents, RW_READER)) { 990f94a2171Sdf125853 if (tsd_get(devfs_clean_key)) { 991f94a2171Sdf125853 VN_RELE(DVTOV(dv)); 992f94a2171Sdf125853 rw_exit(&ddv->dv_contents); 993f94a2171Sdf125853 return (EBUSY); 994f94a2171Sdf125853 } 9957c478bd9Sstevel@tonic-gate rw_enter(&dv->dv_contents, RW_READER); 996f94a2171Sdf125853 } 997f94a2171Sdf125853 9987c478bd9Sstevel@tonic-gate vp = DVTOV(dv); 9997c478bd9Sstevel@tonic-gate if ((dv->dv_attrvp != NULLVP) || 10007c478bd9Sstevel@tonic-gate (vp->v_type != VDIR && dv->dv_attr != NULL)) { 10017c478bd9Sstevel@tonic-gate /* 10027c478bd9Sstevel@tonic-gate * Common case - we already have attributes 10037c478bd9Sstevel@tonic-gate */ 10047c478bd9Sstevel@tonic-gate rw_exit(&dv->dv_contents); 10057c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 10067c478bd9Sstevel@tonic-gate goto found; 10077c478bd9Sstevel@tonic-gate } 10087c478bd9Sstevel@tonic-gate 10097c478bd9Sstevel@tonic-gate /* 10107c478bd9Sstevel@tonic-gate * No attribute vp, try and build one. 1011f94a2171Sdf125853 * 1012f94a2171Sdf125853 * dv_shadow_node() can briefly drop &dv->dv_contents lock 1013f94a2171Sdf125853 * if it is unable to upgrade it to a write lock. If the 1014f94a2171Sdf125853 * current thread has come in through the bottom-up device 1015f94a2171Sdf125853 * configuration devfs_clean() path, we may deadlock against 1016f94a2171Sdf125853 * a thread performing top-down device configuration if it 1017f94a2171Sdf125853 * grabs the contents lock. To avoid this, when we are on the 1018f94a2171Sdf125853 * devfs_clean() path we attempt to upgrade the dv_contents 1019f94a2171Sdf125853 * lock before we call dv_shadow_node(). 10207c478bd9Sstevel@tonic-gate */ 1021f94a2171Sdf125853 if (tsd_get(devfs_clean_key)) { 1022f94a2171Sdf125853 if (!rw_tryupgrade(&dv->dv_contents)) { 1023f94a2171Sdf125853 VN_RELE(DVTOV(dv)); 1024f94a2171Sdf125853 rw_exit(&dv->dv_contents); 1025f94a2171Sdf125853 rw_exit(&ddv->dv_contents); 1026f94a2171Sdf125853 return (EBUSY); 1027f94a2171Sdf125853 } 1028f94a2171Sdf125853 1029f94a2171Sdf125853 write_held = DV_SHADOW_WRITE_HELD; 1030f94a2171Sdf125853 } 1031f94a2171Sdf125853 1032f94a2171Sdf125853 dv_shadow_node(DVTOV(ddv), nm, vp, pnp, rdir, cred, 1033f94a2171Sdf125853 write_held); 1034f94a2171Sdf125853 10357c478bd9Sstevel@tonic-gate rw_exit(&dv->dv_contents); 10367c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 10377c478bd9Sstevel@tonic-gate goto found; 10387c478bd9Sstevel@tonic-gate } 10397c478bd9Sstevel@tonic-gate 10407c478bd9Sstevel@tonic-gate /* 10417c478bd9Sstevel@tonic-gate * (b) Search the child devinfo nodes of our parent directory, 10427c478bd9Sstevel@tonic-gate * looking for the named node. If we find it, build a new 10437c478bd9Sstevel@tonic-gate * node, then grab the writers lock, search the directory 10447c478bd9Sstevel@tonic-gate * if it's still not there, then insert it. 10457c478bd9Sstevel@tonic-gate * 10467c478bd9Sstevel@tonic-gate * We drop the devfs locks before accessing the device tree. 10477c478bd9Sstevel@tonic-gate * Take care to mark the node BUSY so that a forced devfs_clean 10487c478bd9Sstevel@tonic-gate * doesn't mark the directory node stale. 10497c478bd9Sstevel@tonic-gate * 10507c478bd9Sstevel@tonic-gate * Also, check if we are called as part of devfs_clean or 10517c478bd9Sstevel@tonic-gate * reset_perm. If so, simply return not found because there 10527c478bd9Sstevel@tonic-gate * is nothing to clean. 10537c478bd9Sstevel@tonic-gate */ 10547c478bd9Sstevel@tonic-gate if (tsd_get(devfs_clean_key)) { 10557c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 10567c478bd9Sstevel@tonic-gate return (ENOENT); 10577c478bd9Sstevel@tonic-gate } 10587c478bd9Sstevel@tonic-gate 10597c478bd9Sstevel@tonic-gate /* 10607c478bd9Sstevel@tonic-gate * We could be either READ or WRITE locked at 10617c478bd9Sstevel@tonic-gate * this point. Upgrade if we are read locked. 10627c478bd9Sstevel@tonic-gate */ 10637c478bd9Sstevel@tonic-gate ASSERT(RW_LOCK_HELD(&ddv->dv_contents)); 10647c478bd9Sstevel@tonic-gate if (rw_read_locked(&ddv->dv_contents) && 10657c478bd9Sstevel@tonic-gate !rw_tryupgrade(&ddv->dv_contents)) { 10667c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 10677c478bd9Sstevel@tonic-gate rw_enter(&ddv->dv_contents, RW_WRITER); 10687c478bd9Sstevel@tonic-gate /* 10697c478bd9Sstevel@tonic-gate * Things may have changed when we dropped 10707c478bd9Sstevel@tonic-gate * the contents lock, so start from top again 10717c478bd9Sstevel@tonic-gate */ 10727c478bd9Sstevel@tonic-gate goto start; 10737c478bd9Sstevel@tonic-gate } 10747c478bd9Sstevel@tonic-gate ddv->dv_busy++; /* mark busy before dropping lock */ 10757c478bd9Sstevel@tonic-gate was_busy++; 10767c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 10777c478bd9Sstevel@tonic-gate 10787c478bd9Sstevel@tonic-gate pdevi = ddv->dv_devi; 10797c478bd9Sstevel@tonic-gate ASSERT(pdevi != NULL); 10807c478bd9Sstevel@tonic-gate 10817c478bd9Sstevel@tonic-gate mnm = strchr(nm, ':'); 10827c478bd9Sstevel@tonic-gate if (mnm) 10837c478bd9Sstevel@tonic-gate *mnm = (char)0; 10847c478bd9Sstevel@tonic-gate 10857c478bd9Sstevel@tonic-gate /* 10867c478bd9Sstevel@tonic-gate * Configure one nexus child, will call nexus's bus_ops 10877c478bd9Sstevel@tonic-gate * If successful, devi is held upon returning. 10887c478bd9Sstevel@tonic-gate * Note: devfs lookup should not be configuring grandchildren. 10897c478bd9Sstevel@tonic-gate */ 10907c478bd9Sstevel@tonic-gate ASSERT((ndi_flags & NDI_CONFIG) == 0); 10917c478bd9Sstevel@tonic-gate 10927c478bd9Sstevel@tonic-gate rv = ndi_devi_config_one(pdevi, nm, &devi, ndi_flags | NDI_NO_EVENT); 10937c478bd9Sstevel@tonic-gate if (mnm) 10947c478bd9Sstevel@tonic-gate *mnm = ':'; 10957c478bd9Sstevel@tonic-gate if (rv != NDI_SUCCESS) { 10967c478bd9Sstevel@tonic-gate rv = ENOENT; 10977c478bd9Sstevel@tonic-gate goto notfound; 10987c478bd9Sstevel@tonic-gate } 10997c478bd9Sstevel@tonic-gate 110094c894bbSVikram Hegde ASSERT(devi); 110194c894bbSVikram Hegde 110294c894bbSVikram Hegde /* Check if this is a path alias */ 110394c894bbSVikram Hegde if (ddi_aliases_present == B_TRUE && ddi_get_parent(devi) != pdevi) { 110494c894bbSVikram Hegde char *curr = kmem_alloc(MAXPATHLEN, KM_SLEEP); 110594c894bbSVikram Hegde 110694c894bbSVikram Hegde (void) ddi_pathname(devi, curr); 110794c894bbSVikram Hegde 110894c894bbSVikram Hegde vp = NULL; 110994c894bbSVikram Hegde if (devfs_lookupname(curr, NULL, &vp) == 0 && vp) { 111094c894bbSVikram Hegde dv = VTODV(vp); 111194c894bbSVikram Hegde kmem_free(curr, MAXPATHLEN); 111294c894bbSVikram Hegde goto found; 111394c894bbSVikram Hegde } 111494c894bbSVikram Hegde kmem_free(curr, MAXPATHLEN); 111594c894bbSVikram Hegde } 111694c894bbSVikram Hegde 11177c478bd9Sstevel@tonic-gate /* 1118027021c7SChris Horne * If we configured a hidden node, consider it notfound. 1119027021c7SChris Horne */ 1120027021c7SChris Horne if (ndi_dev_is_hidden_node(devi)) { 1121027021c7SChris Horne ndi_rele_devi(devi); 1122027021c7SChris Horne rv = ENOENT; 1123027021c7SChris Horne goto notfound; 1124027021c7SChris Horne } 1125027021c7SChris Horne 1126027021c7SChris Horne /* 11277c478bd9Sstevel@tonic-gate * Don't make vhci clients visible under phci, unless we 11287c478bd9Sstevel@tonic-gate * are in miniroot. 11297c478bd9Sstevel@tonic-gate */ 11307c478bd9Sstevel@tonic-gate if (isminiroot == 0 && ddi_get_parent(devi) != pdevi) { 11317c478bd9Sstevel@tonic-gate ndi_rele_devi(devi); 11327c478bd9Sstevel@tonic-gate rv = ENOENT; 11337c478bd9Sstevel@tonic-gate goto notfound; 11347c478bd9Sstevel@tonic-gate } 11357c478bd9Sstevel@tonic-gate 1136737d277aScth ASSERT(devi && i_ddi_devi_attached(devi)); 11377c478bd9Sstevel@tonic-gate 11387c478bd9Sstevel@tonic-gate /* 11397c478bd9Sstevel@tonic-gate * Invalidate cache to notice newly created minor nodes. 11407c478bd9Sstevel@tonic-gate */ 11417c478bd9Sstevel@tonic-gate rw_enter(&ddv->dv_contents, RW_WRITER); 11427c478bd9Sstevel@tonic-gate ddv->dv_flags |= DV_BUILD; 11437c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 11447c478bd9Sstevel@tonic-gate 11457c478bd9Sstevel@tonic-gate /* 11467c478bd9Sstevel@tonic-gate * mkdir for nexus drivers and leaf nodes as well. If we are racing 11477c478bd9Sstevel@tonic-gate * and create a duplicate, the duplicate will be destroyed below. 11487c478bd9Sstevel@tonic-gate */ 11497c478bd9Sstevel@tonic-gate if (mnm == NULL) { 11507c478bd9Sstevel@tonic-gate dv = dv_mkdir(ddv, devi, nm); 11517c478bd9Sstevel@tonic-gate } else { 11527c478bd9Sstevel@tonic-gate /* 1153b9ccdc5aScth * Allocate dmd first to avoid KM_SLEEP with active 1154b9ccdc5aScth * ndi_devi_enter. 11557c478bd9Sstevel@tonic-gate */ 1156b9ccdc5aScth dmd = kmem_zalloc(sizeof (*dmd), KM_SLEEP); 1157b9ccdc5aScth ndi_devi_enter(devi, &circ); 11587c478bd9Sstevel@tonic-gate if (devi == clone_dip) { 1159b9ccdc5aScth /* 1160b9ccdc5aScth * For clone minors, load the driver indicated by 1161b9ccdc5aScth * minor name. 1162b9ccdc5aScth */ 11637c478bd9Sstevel@tonic-gate dv = dv_clone_mknod(ddv, mnm + 1); 11647c478bd9Sstevel@tonic-gate } else { 11657c478bd9Sstevel@tonic-gate /* 11667c478bd9Sstevel@tonic-gate * Find minor node and make a dv_node 11677c478bd9Sstevel@tonic-gate */ 11687c478bd9Sstevel@tonic-gate if (dv_find_leafnode(devi, mnm + 1, dmd) == 0) { 11697c478bd9Sstevel@tonic-gate dv = dv_mknod(ddv, devi, nm, dmd); 11707c478bd9Sstevel@tonic-gate if (dmd->ddm_node_priv) 11717c478bd9Sstevel@tonic-gate dpfree(dmd->ddm_node_priv); 11727c478bd9Sstevel@tonic-gate } 11737c478bd9Sstevel@tonic-gate } 1174b9ccdc5aScth ndi_devi_exit(devi, circ); 1175b9ccdc5aScth kmem_free(dmd, sizeof (*dmd)); 11767c478bd9Sstevel@tonic-gate } 11777c478bd9Sstevel@tonic-gate /* 11787c478bd9Sstevel@tonic-gate * Release hold from ndi_devi_config_one() 11797c478bd9Sstevel@tonic-gate */ 11807c478bd9Sstevel@tonic-gate ndi_rele_devi(devi); 11817c478bd9Sstevel@tonic-gate 11827c478bd9Sstevel@tonic-gate if (dv == NULL) { 11837c478bd9Sstevel@tonic-gate rv = ENOENT; 11847c478bd9Sstevel@tonic-gate goto notfound; 11857c478bd9Sstevel@tonic-gate } 11867c478bd9Sstevel@tonic-gate 11877c478bd9Sstevel@tonic-gate /* 11887c478bd9Sstevel@tonic-gate * We have released the dv_contents lock, need to check 11897c478bd9Sstevel@tonic-gate * if another thread already created a duplicate node 11907c478bd9Sstevel@tonic-gate */ 11917c478bd9Sstevel@tonic-gate rw_enter(&ddv->dv_contents, RW_WRITER); 11927c478bd9Sstevel@tonic-gate if ((dup = dv_findbyname(ddv, nm)) == NULL) { 11937c478bd9Sstevel@tonic-gate dv_insert(ddv, dv); 11947c478bd9Sstevel@tonic-gate } else { 11957c478bd9Sstevel@tonic-gate /* 11967c478bd9Sstevel@tonic-gate * Duplicate found, use the existing node 11977c478bd9Sstevel@tonic-gate */ 11987c478bd9Sstevel@tonic-gate VN_RELE(DVTOV(dv)); 11997c478bd9Sstevel@tonic-gate dv_destroy(dv, 0); 12007c478bd9Sstevel@tonic-gate dv = dup; 12017c478bd9Sstevel@tonic-gate } 12027c478bd9Sstevel@tonic-gate goto founddv; 12037c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 12047c478bd9Sstevel@tonic-gate 12057c478bd9Sstevel@tonic-gate found: 12067c478bd9Sstevel@tonic-gate /* 12074c06356bSdh142964 * Fail lookup of device that has now become hidden (typically via 12084c06356bSdh142964 * hot removal of open device). 12094c06356bSdh142964 */ 12104c06356bSdh142964 if (dv->dv_devi && ndi_dev_is_hidden_node(dv->dv_devi)) { 12114c06356bSdh142964 dcmn_err2(("dv_find: nm %s failed: hidden/removed\n", nm)); 12124c06356bSdh142964 VN_RELE(vp); 12134c06356bSdh142964 rv = ENOENT; 12144c06356bSdh142964 goto notfound; 12154c06356bSdh142964 } 12164c06356bSdh142964 12174c06356bSdh142964 /* 12187c478bd9Sstevel@tonic-gate * Skip non-kernel lookups of internal nodes. 12197c478bd9Sstevel@tonic-gate * This use of kcred to distinguish between user and 12207c478bd9Sstevel@tonic-gate * internal kernel lookups is unfortunate. The information 12217c478bd9Sstevel@tonic-gate * provided by the seg argument to lookupnameat should 12227c478bd9Sstevel@tonic-gate * evolve into a lookup flag for filesystems that need 12237c478bd9Sstevel@tonic-gate * this distinction. 12247c478bd9Sstevel@tonic-gate */ 12257c478bd9Sstevel@tonic-gate if ((dv->dv_flags & DV_INTERNAL) && (cred != kcred)) { 12264c06356bSdh142964 dcmn_err2(("dv_find: nm %s failed: internal\n", nm)); 12277c478bd9Sstevel@tonic-gate VN_RELE(vp); 12287c478bd9Sstevel@tonic-gate rv = ENOENT; 12297c478bd9Sstevel@tonic-gate goto notfound; 12307c478bd9Sstevel@tonic-gate } 12317c478bd9Sstevel@tonic-gate 12327c478bd9Sstevel@tonic-gate dcmn_err2(("dv_find: returning vp for nm %s\n", nm)); 12337c478bd9Sstevel@tonic-gate if (vp->v_type == VCHR || vp->v_type == VBLK) { 12347c478bd9Sstevel@tonic-gate /* 12357c478bd9Sstevel@tonic-gate * If vnode is a device, return special vnode instead 12367c478bd9Sstevel@tonic-gate * (though it knows all about -us- via sp->s_realvp, 12377c478bd9Sstevel@tonic-gate * sp->s_devvp, and sp->s_dip) 12387c478bd9Sstevel@tonic-gate */ 12397c478bd9Sstevel@tonic-gate *vpp = specvp_devfs(vp, vp->v_rdev, vp->v_type, cred, 12407c478bd9Sstevel@tonic-gate dv->dv_devi); 12417c478bd9Sstevel@tonic-gate VN_RELE(vp); 12427c478bd9Sstevel@tonic-gate if (*vpp == NULLVP) 12437c478bd9Sstevel@tonic-gate rv = ENOSYS; 12447c478bd9Sstevel@tonic-gate } else 12457c478bd9Sstevel@tonic-gate *vpp = vp; 12467c478bd9Sstevel@tonic-gate 12477c478bd9Sstevel@tonic-gate notfound: 1248*9f787237SJakub Jermar if (was_busy) { 1249*9f787237SJakub Jermar /* 1250*9f787237SJakub Jermar * Non-zero was_busy tells us that we are not in the 1251*9f787237SJakub Jermar * devfs_clean() path which in turn means that we can afford 1252*9f787237SJakub Jermar * to take the contents lock unconditionally. 1253*9f787237SJakub Jermar */ 12547c478bd9Sstevel@tonic-gate rw_enter(&ddv->dv_contents, RW_WRITER); 12557c478bd9Sstevel@tonic-gate ddv->dv_busy--; 12567c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 1257*9f787237SJakub Jermar } 12587c478bd9Sstevel@tonic-gate return (rv); 12597c478bd9Sstevel@tonic-gate } 12607c478bd9Sstevel@tonic-gate 12617c478bd9Sstevel@tonic-gate /* 12627c478bd9Sstevel@tonic-gate * The given directory node is out-of-date; that is, it has been 12637c478bd9Sstevel@tonic-gate * marked as needing to be rebuilt, possibly because some new devinfo 12647c478bd9Sstevel@tonic-gate * node has come into existence, or possibly because this is the first 12657c478bd9Sstevel@tonic-gate * time we've been here. 12667c478bd9Sstevel@tonic-gate */ 12677c478bd9Sstevel@tonic-gate void 12687c478bd9Sstevel@tonic-gate dv_filldir(struct dv_node *ddv) 12697c478bd9Sstevel@tonic-gate { 12707c478bd9Sstevel@tonic-gate struct dv_node *dv; 12717c478bd9Sstevel@tonic-gate dev_info_t *devi, *pdevi; 12727c478bd9Sstevel@tonic-gate struct ddi_minor_data *dmd; 12737c478bd9Sstevel@tonic-gate char devnm[MAXNAMELEN]; 1274b9ccdc5aScth int circ, ccirc; 12757c478bd9Sstevel@tonic-gate 12767c478bd9Sstevel@tonic-gate ASSERT(DVTOV(ddv)->v_type == VDIR); 12777c478bd9Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&ddv->dv_contents)); 12787c478bd9Sstevel@tonic-gate ASSERT(ddv->dv_flags & DV_BUILD); 12797c478bd9Sstevel@tonic-gate 12807c478bd9Sstevel@tonic-gate dcmn_err3(("dv_filldir: %s\n", ddv->dv_name)); 12817c478bd9Sstevel@tonic-gate if (DV_STALE(ddv)) 12827c478bd9Sstevel@tonic-gate return; 12837c478bd9Sstevel@tonic-gate pdevi = ddv->dv_devi; 12847c478bd9Sstevel@tonic-gate 12857c478bd9Sstevel@tonic-gate if (ndi_devi_config(pdevi, NDI_NO_EVENT) != NDI_SUCCESS) { 1286b5fca8f8Stomee dcmn_err3(("dv_filldir: config error %s\n", ddv->dv_name)); 12877c478bd9Sstevel@tonic-gate } 12887c478bd9Sstevel@tonic-gate 12897c478bd9Sstevel@tonic-gate ndi_devi_enter(pdevi, &circ); 12907c478bd9Sstevel@tonic-gate for (devi = ddi_get_child(pdevi); devi; 12917c478bd9Sstevel@tonic-gate devi = ddi_get_next_sibling(devi)) { 129267027fa7SMark Logan /* 129367027fa7SMark Logan * While we know enough to create a directory at DS_INITIALIZED, 129467027fa7SMark Logan * the directory will be empty until DS_ATTACHED. The existence 129567027fa7SMark Logan * of an empty directory dv_node will cause a devi_ref, which 129667027fa7SMark Logan * has caused problems for existing code paths doing offline/DR 129767027fa7SMark Logan * type operations - making devfs_clean coordination even more 129867027fa7SMark Logan * sensitive and error prone. Given this, the 'continue' below 129967027fa7SMark Logan * is checking for DS_ATTACHED instead of DS_INITIALIZED. 130067027fa7SMark Logan */ 130167027fa7SMark Logan if (i_ddi_node_state(devi) < DS_ATTACHED) 13027c478bd9Sstevel@tonic-gate continue; 13037c478bd9Sstevel@tonic-gate 1304027021c7SChris Horne /* skip hidden nodes */ 1305027021c7SChris Horne if (ndi_dev_is_hidden_node(devi)) 1306027021c7SChris Horne continue; 1307027021c7SChris Horne 13087c478bd9Sstevel@tonic-gate dcmn_err3(("dv_filldir: node %s\n", ddi_node_name(devi))); 13097c478bd9Sstevel@tonic-gate 1310b9ccdc5aScth ndi_devi_enter(devi, &ccirc); 13117c478bd9Sstevel@tonic-gate for (dmd = DEVI(devi)->devi_minor; dmd; dmd = dmd->next) { 13127c478bd9Sstevel@tonic-gate char *addr; 13137c478bd9Sstevel@tonic-gate 13147c478bd9Sstevel@tonic-gate /* 13157c478bd9Sstevel@tonic-gate * Skip alias nodes, internal nodes, and nodes 13167c478bd9Sstevel@tonic-gate * without a name. We allow DDM_DEFAULT nodes 13177c478bd9Sstevel@tonic-gate * to appear in readdir. 13187c478bd9Sstevel@tonic-gate */ 13197c478bd9Sstevel@tonic-gate if ((dmd->type == DDM_ALIAS) || 13207c478bd9Sstevel@tonic-gate (dmd->type == DDM_INTERNAL_PATH) || 13217c478bd9Sstevel@tonic-gate (dmd->ddm_name == NULL)) 13227c478bd9Sstevel@tonic-gate continue; 13237c478bd9Sstevel@tonic-gate 13247c478bd9Sstevel@tonic-gate addr = ddi_get_name_addr(devi); 13257c478bd9Sstevel@tonic-gate if (addr && *addr) 13267c478bd9Sstevel@tonic-gate (void) sprintf(devnm, "%s@%s:%s", 13277c478bd9Sstevel@tonic-gate ddi_node_name(devi), addr, dmd->ddm_name); 13287c478bd9Sstevel@tonic-gate else 13297c478bd9Sstevel@tonic-gate (void) sprintf(devnm, "%s:%s", 13307c478bd9Sstevel@tonic-gate ddi_node_name(devi), dmd->ddm_name); 13317c478bd9Sstevel@tonic-gate 13327c478bd9Sstevel@tonic-gate if ((dv = dv_findbyname(ddv, devnm)) != NULL) { 13337c478bd9Sstevel@tonic-gate /* dv_node already exists */ 13347c478bd9Sstevel@tonic-gate VN_RELE(DVTOV(dv)); 13357c478bd9Sstevel@tonic-gate continue; 13367c478bd9Sstevel@tonic-gate } 13377c478bd9Sstevel@tonic-gate 13387c478bd9Sstevel@tonic-gate dv = dv_mknod(ddv, devi, devnm, dmd); 13397c478bd9Sstevel@tonic-gate dv_insert(ddv, dv); 13407c478bd9Sstevel@tonic-gate VN_RELE(DVTOV(dv)); 13417c478bd9Sstevel@tonic-gate } 1342b9ccdc5aScth ndi_devi_exit(devi, ccirc); 13437c478bd9Sstevel@tonic-gate 13447c478bd9Sstevel@tonic-gate (void) ddi_deviname(devi, devnm); 13457c478bd9Sstevel@tonic-gate if ((dv = dv_findbyname(ddv, devnm + 1)) == NULL) { 13467c478bd9Sstevel@tonic-gate /* directory doesn't exist */ 13477c478bd9Sstevel@tonic-gate dv = dv_mkdir(ddv, devi, devnm + 1); 13487c478bd9Sstevel@tonic-gate dv_insert(ddv, dv); 13497c478bd9Sstevel@tonic-gate } 13507c478bd9Sstevel@tonic-gate VN_RELE(DVTOV(dv)); 13517c478bd9Sstevel@tonic-gate } 13527c478bd9Sstevel@tonic-gate ndi_devi_exit(pdevi, circ); 13537c478bd9Sstevel@tonic-gate 13547c478bd9Sstevel@tonic-gate ddv->dv_flags &= ~DV_BUILD; 13557c478bd9Sstevel@tonic-gate } 13567c478bd9Sstevel@tonic-gate 13577c478bd9Sstevel@tonic-gate /* 13587c478bd9Sstevel@tonic-gate * Given a directory node, clean out all the nodes beneath. 13597c478bd9Sstevel@tonic-gate * 13607c478bd9Sstevel@tonic-gate * VDIR: Reinvoke to clean them, then delete the directory. 13617c478bd9Sstevel@tonic-gate * VCHR, VBLK: Just blow them away. 13627c478bd9Sstevel@tonic-gate * 13637c478bd9Sstevel@tonic-gate * Mark the directories touched as in need of a rebuild, in case 13647c478bd9Sstevel@tonic-gate * we fall over part way through. When DV_CLEAN_FORCE is specified, 13657c478bd9Sstevel@tonic-gate * we mark referenced empty directories as stale to facilitate DR. 13667c478bd9Sstevel@tonic-gate */ 13677c478bd9Sstevel@tonic-gate int 13687c478bd9Sstevel@tonic-gate dv_cleandir(struct dv_node *ddv, char *devnm, uint_t flags) 13697c478bd9Sstevel@tonic-gate { 137045a9d961Scth struct dv_node *dv; 1371aac43a5fSjg struct dv_node *next; 13727c478bd9Sstevel@tonic-gate struct vnode *vp; 137345a9d961Scth int busy = 0; 13747c478bd9Sstevel@tonic-gate 1375f94a2171Sdf125853 /* 1376f94a2171Sdf125853 * We should always be holding the tsd_clean_key here: dv_cleandir() 1377f94a2171Sdf125853 * will be called as a result of a devfs_clean request and the 1378f94a2171Sdf125853 * tsd_clean_key will be set in either in devfs_clean() itself or in 1379f94a2171Sdf125853 * devfs_clean_vhci(). 1380f94a2171Sdf125853 * 1381f94a2171Sdf125853 * Since we are on the devfs_clean path, we return EBUSY if we cannot 1382f94a2171Sdf125853 * get the contents lock: if we blocked here we might deadlock against 1383f94a2171Sdf125853 * a thread performing top-down device configuration. 1384f94a2171Sdf125853 */ 1385f94a2171Sdf125853 ASSERT(tsd_get(devfs_clean_key)); 1386f94a2171Sdf125853 13877c478bd9Sstevel@tonic-gate dcmn_err3(("dv_cleandir: %s\n", ddv->dv_name)); 13887c478bd9Sstevel@tonic-gate 1389f94a2171Sdf125853 if (!(flags & DV_CLEANDIR_LCK) && 1390f94a2171Sdf125853 !rw_tryenter(&ddv->dv_contents, RW_WRITER)) 1391f94a2171Sdf125853 return (EBUSY); 1392f94a2171Sdf125853 1393aac43a5fSjg for (dv = DV_FIRST_ENTRY(ddv); dv; dv = next) { 1394aac43a5fSjg next = DV_NEXT_ENTRY(ddv, dv); 13957c478bd9Sstevel@tonic-gate 139645a9d961Scth /* 139745a9d961Scth * If devnm is specified, the non-minor portion of the 139845a9d961Scth * name must match devnm. 139945a9d961Scth */ 140045a9d961Scth if (devnm && 140145a9d961Scth (strncmp(devnm, dv->dv_name, strlen(devnm)) || 14027c478bd9Sstevel@tonic-gate (dv->dv_name[strlen(devnm)] != ':' && 14037c478bd9Sstevel@tonic-gate dv->dv_name[strlen(devnm)] != '\0'))) 14047c478bd9Sstevel@tonic-gate continue; 14057c478bd9Sstevel@tonic-gate 140645a9d961Scth /* check type of what we are cleaning */ 14077c478bd9Sstevel@tonic-gate vp = DVTOV(dv); 14087c478bd9Sstevel@tonic-gate if (vp->v_type == VDIR) { 140945a9d961Scth /* recurse on directories */ 14107c478bd9Sstevel@tonic-gate rw_enter(&dv->dv_contents, RW_WRITER); 141145a9d961Scth if (dv_cleandir(dv, NULL, 141245a9d961Scth flags | DV_CLEANDIR_LCK) == EBUSY) { 14137c478bd9Sstevel@tonic-gate rw_exit(&dv->dv_contents); 141445a9d961Scth goto set_busy; 14157c478bd9Sstevel@tonic-gate } 14167c478bd9Sstevel@tonic-gate 141745a9d961Scth /* A clean directory is an empty directory... */ 141845a9d961Scth ASSERT(dv->dv_nlink == 2); 141945a9d961Scth mutex_enter(&vp->v_lock); 142045a9d961Scth if (vp->v_count > 0) { 142145a9d961Scth /* 142245a9d961Scth * ... but an empty directory can still have 142345a9d961Scth * references to it. If we have dv_busy or 142445a9d961Scth * DV_CLEAN_FORCE is *not* specified then a 142545a9d961Scth * referenced directory is considered busy. 142645a9d961Scth */ 142745a9d961Scth if (dv->dv_busy || !(flags & DV_CLEAN_FORCE)) { 142845a9d961Scth mutex_exit(&vp->v_lock); 142945a9d961Scth rw_exit(&dv->dv_contents); 143045a9d961Scth goto set_busy; 143145a9d961Scth } 143245a9d961Scth 143345a9d961Scth /* 143445a9d961Scth * Mark referenced directory stale so that DR 143545a9d961Scth * will succeed even if a shell has 143645a9d961Scth * /devices/xxx as current directory (causing 143745a9d961Scth * VN_HOLD reference to an empty directory). 143845a9d961Scth */ 143945a9d961Scth ASSERT(!DV_STALE(dv)); 144045a9d961Scth ndi_rele_devi(dv->dv_devi); 144145a9d961Scth dv->dv_devi = NULL; /* mark DV_STALE */ 144245a9d961Scth } 144345a9d961Scth } else { 144445a9d961Scth ASSERT((vp->v_type == VCHR) || (vp->v_type == VBLK)); 144545a9d961Scth ASSERT(dv->dv_nlink == 1); /* no hard links */ 144645a9d961Scth mutex_enter(&vp->v_lock); 144745a9d961Scth if (vp->v_count > 0) { 144845a9d961Scth mutex_exit(&vp->v_lock); 144945a9d961Scth goto set_busy; 145045a9d961Scth } 14517c478bd9Sstevel@tonic-gate } 14527c478bd9Sstevel@tonic-gate 14537c478bd9Sstevel@tonic-gate /* unlink from directory */ 1454aac43a5fSjg dv_unlink(ddv, dv); 14557c478bd9Sstevel@tonic-gate 145645a9d961Scth /* drop locks */ 145745a9d961Scth mutex_exit(&vp->v_lock); 145845a9d961Scth if (vp->v_type == VDIR) 145945a9d961Scth rw_exit(&dv->dv_contents); 146045a9d961Scth 146145a9d961Scth /* destroy vnode if ref count is zero */ 146245a9d961Scth if (vp->v_count == 0) 146345a9d961Scth dv_destroy(dv, flags); 146445a9d961Scth 146545a9d961Scth continue; 14667c478bd9Sstevel@tonic-gate 14677c478bd9Sstevel@tonic-gate /* 146845a9d961Scth * If devnm is not NULL we return immediately on busy, 146945a9d961Scth * otherwise we continue destroying unused dv_node's. 14707c478bd9Sstevel@tonic-gate */ 147145a9d961Scth set_busy: busy++; 147245a9d961Scth if (devnm) 147345a9d961Scth break; 14747c478bd9Sstevel@tonic-gate } 147545a9d961Scth 14767c478bd9Sstevel@tonic-gate /* 14777c478bd9Sstevel@tonic-gate * This code may be invoked to inform devfs that a new node has 14787c478bd9Sstevel@tonic-gate * been created in the kernel device tree. So we always set 14797c478bd9Sstevel@tonic-gate * the DV_BUILD flag to allow the next dv_filldir() to pick 14807c478bd9Sstevel@tonic-gate * the new devinfo nodes. 14817c478bd9Sstevel@tonic-gate */ 14827c478bd9Sstevel@tonic-gate ddv->dv_flags |= DV_BUILD; 14837c478bd9Sstevel@tonic-gate 148445a9d961Scth if (!(flags & DV_CLEANDIR_LCK)) 14857c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 14867c478bd9Sstevel@tonic-gate 148745a9d961Scth return (busy ? EBUSY : 0); 14887c478bd9Sstevel@tonic-gate } 14897c478bd9Sstevel@tonic-gate 14907c478bd9Sstevel@tonic-gate /* 14917c478bd9Sstevel@tonic-gate * Walk through the devfs hierarchy, correcting the permissions of 14927c478bd9Sstevel@tonic-gate * devices with default permissions that do not match those specified 14937c478bd9Sstevel@tonic-gate * by minor perm. This can only be done for all drivers for now. 14947c478bd9Sstevel@tonic-gate */ 14957c478bd9Sstevel@tonic-gate static int 14967c478bd9Sstevel@tonic-gate dv_reset_perm_dir(struct dv_node *ddv, uint_t flags) 14977c478bd9Sstevel@tonic-gate { 1498aac43a5fSjg struct dv_node *dv; 14997c478bd9Sstevel@tonic-gate struct vnode *vp; 15007c478bd9Sstevel@tonic-gate int retval = 0; 15017c478bd9Sstevel@tonic-gate struct vattr *attrp; 15027c478bd9Sstevel@tonic-gate mperm_t mp; 15037c478bd9Sstevel@tonic-gate char *nm; 15047c478bd9Sstevel@tonic-gate uid_t old_uid; 15057c478bd9Sstevel@tonic-gate gid_t old_gid; 15067c478bd9Sstevel@tonic-gate mode_t old_mode; 15077c478bd9Sstevel@tonic-gate 15087c478bd9Sstevel@tonic-gate rw_enter(&ddv->dv_contents, RW_WRITER); 1509aac43a5fSjg for (dv = DV_FIRST_ENTRY(ddv); dv; dv = DV_NEXT_ENTRY(ddv, dv)) { 15107c478bd9Sstevel@tonic-gate int error = 0; 15117c478bd9Sstevel@tonic-gate nm = dv->dv_name; 15127c478bd9Sstevel@tonic-gate 15137c478bd9Sstevel@tonic-gate rw_enter(&dv->dv_contents, RW_READER); 15147c478bd9Sstevel@tonic-gate vp = DVTOV(dv); 15157c478bd9Sstevel@tonic-gate if (vp->v_type == VDIR) { 15167c478bd9Sstevel@tonic-gate rw_exit(&dv->dv_contents); 15177c478bd9Sstevel@tonic-gate if (dv_reset_perm_dir(dv, flags) != 0) { 15187c478bd9Sstevel@tonic-gate error = EBUSY; 15197c478bd9Sstevel@tonic-gate } 15207c478bd9Sstevel@tonic-gate } else { 15217c478bd9Sstevel@tonic-gate ASSERT(vp->v_type == VCHR || vp->v_type == VBLK); 15227c478bd9Sstevel@tonic-gate 15237c478bd9Sstevel@tonic-gate /* 15247c478bd9Sstevel@tonic-gate * Check for permissions from minor_perm 15257c478bd9Sstevel@tonic-gate * If there are none, we're done 15267c478bd9Sstevel@tonic-gate */ 15277c478bd9Sstevel@tonic-gate rw_exit(&dv->dv_contents); 15287c478bd9Sstevel@tonic-gate if (dev_minorperm(dv->dv_devi, nm, &mp) != 0) 15297c478bd9Sstevel@tonic-gate continue; 15307c478bd9Sstevel@tonic-gate 15317c478bd9Sstevel@tonic-gate rw_enter(&dv->dv_contents, RW_READER); 15327c478bd9Sstevel@tonic-gate 15337c478bd9Sstevel@tonic-gate /* 15347c478bd9Sstevel@tonic-gate * Allow a node's permissions to be altered 15357c478bd9Sstevel@tonic-gate * permanently from the defaults by chmod, 15367c478bd9Sstevel@tonic-gate * using the shadow node as backing store. 15377c478bd9Sstevel@tonic-gate * Otherwise, update node to minor_perm permissions. 15387c478bd9Sstevel@tonic-gate */ 15397c478bd9Sstevel@tonic-gate if (dv->dv_attrvp == NULLVP) { 15407c478bd9Sstevel@tonic-gate /* 15417c478bd9Sstevel@tonic-gate * No attribute vp, try to find one. 15427c478bd9Sstevel@tonic-gate */ 15437c478bd9Sstevel@tonic-gate dv_shadow_node(DVTOV(ddv), nm, vp, 15447c478bd9Sstevel@tonic-gate NULL, NULLVP, kcred, 0); 15457c478bd9Sstevel@tonic-gate } 15467c478bd9Sstevel@tonic-gate if (dv->dv_attrvp != NULLVP || dv->dv_attr == NULL) { 15477c478bd9Sstevel@tonic-gate rw_exit(&dv->dv_contents); 15487c478bd9Sstevel@tonic-gate continue; 15497c478bd9Sstevel@tonic-gate } 15507c478bd9Sstevel@tonic-gate 15517c478bd9Sstevel@tonic-gate attrp = dv->dv_attr; 15527c478bd9Sstevel@tonic-gate 15537c478bd9Sstevel@tonic-gate if (VATTRP_MP_CMP(attrp, mp) == 0) { 15547c478bd9Sstevel@tonic-gate dcmn_err5(("%s: no perm change: " 15557c478bd9Sstevel@tonic-gate "%d %d 0%o\n", nm, attrp->va_uid, 15567c478bd9Sstevel@tonic-gate attrp->va_gid, attrp->va_mode)); 15577c478bd9Sstevel@tonic-gate rw_exit(&dv->dv_contents); 15587c478bd9Sstevel@tonic-gate continue; 15597c478bd9Sstevel@tonic-gate } 15607c478bd9Sstevel@tonic-gate 15617c478bd9Sstevel@tonic-gate old_uid = attrp->va_uid; 15627c478bd9Sstevel@tonic-gate old_gid = attrp->va_gid; 15637c478bd9Sstevel@tonic-gate old_mode = attrp->va_mode; 15647c478bd9Sstevel@tonic-gate 15657c478bd9Sstevel@tonic-gate VATTRP_MP_MERGE(attrp, mp); 15667c478bd9Sstevel@tonic-gate mutex_enter(&vp->v_lock); 15677c478bd9Sstevel@tonic-gate if (vp->v_count > 0) { 15687c478bd9Sstevel@tonic-gate error = EBUSY; 15697c478bd9Sstevel@tonic-gate } 15707c478bd9Sstevel@tonic-gate mutex_exit(&vp->v_lock); 15717c478bd9Sstevel@tonic-gate 15727c478bd9Sstevel@tonic-gate dcmn_err5(("%s: perm %d/%d/0%o -> %d/%d/0%o (%d)\n", 15737c478bd9Sstevel@tonic-gate nm, old_uid, old_gid, old_mode, attrp->va_uid, 15747c478bd9Sstevel@tonic-gate attrp->va_gid, attrp->va_mode, error)); 15757c478bd9Sstevel@tonic-gate 15767c478bd9Sstevel@tonic-gate rw_exit(&dv->dv_contents); 15777c478bd9Sstevel@tonic-gate } 15787c478bd9Sstevel@tonic-gate 15797c478bd9Sstevel@tonic-gate if (error != 0) { 15807c478bd9Sstevel@tonic-gate retval = error; 15817c478bd9Sstevel@tonic-gate } 15827c478bd9Sstevel@tonic-gate } 15837c478bd9Sstevel@tonic-gate 15847c478bd9Sstevel@tonic-gate ddv->dv_flags |= DV_BUILD; 15857c478bd9Sstevel@tonic-gate 15867c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 15877c478bd9Sstevel@tonic-gate 15887c478bd9Sstevel@tonic-gate return (retval); 15897c478bd9Sstevel@tonic-gate } 15907c478bd9Sstevel@tonic-gate 15917c478bd9Sstevel@tonic-gate int 15927c478bd9Sstevel@tonic-gate devfs_reset_perm(uint_t flags) 15937c478bd9Sstevel@tonic-gate { 15947c478bd9Sstevel@tonic-gate struct dv_node *dvp; 15957c478bd9Sstevel@tonic-gate int rval; 15967c478bd9Sstevel@tonic-gate 15977c478bd9Sstevel@tonic-gate if ((dvp = devfs_dip_to_dvnode(ddi_root_node())) == NULL) 15987c478bd9Sstevel@tonic-gate return (0); 15997c478bd9Sstevel@tonic-gate 16007c478bd9Sstevel@tonic-gate VN_HOLD(DVTOV(dvp)); 16017c478bd9Sstevel@tonic-gate rval = dv_reset_perm_dir(dvp, flags); 16027c478bd9Sstevel@tonic-gate VN_RELE(DVTOV(dvp)); 16037c478bd9Sstevel@tonic-gate return (rval); 16047c478bd9Sstevel@tonic-gate } 16057c478bd9Sstevel@tonic-gate 16067c478bd9Sstevel@tonic-gate /* 16077c478bd9Sstevel@tonic-gate * Clean up dangling devfs shadow nodes for removed 16087c478bd9Sstevel@tonic-gate * drivers so that, in the event the driver is re-added 16097c478bd9Sstevel@tonic-gate * to the system, newly created nodes won't incorrectly 16107c478bd9Sstevel@tonic-gate * pick up these stale shadow node permissions. 16117c478bd9Sstevel@tonic-gate * 16127c478bd9Sstevel@tonic-gate * This is accomplished by walking down the pathname 16137c478bd9Sstevel@tonic-gate * to the directory, starting at the root's attribute 16147c478bd9Sstevel@tonic-gate * node, then removing all minors matching the specified 16157c478bd9Sstevel@tonic-gate * node name. Care must be taken to remove all entries 16167c478bd9Sstevel@tonic-gate * in a directory before the directory itself, so that 16177c478bd9Sstevel@tonic-gate * the clean-up associated with rem_drv'ing a nexus driver 16187c478bd9Sstevel@tonic-gate * does not inadvertently result in an inconsistent 16197c478bd9Sstevel@tonic-gate * filesystem underlying devfs. 16207c478bd9Sstevel@tonic-gate */ 16217c478bd9Sstevel@tonic-gate 16227c478bd9Sstevel@tonic-gate static int 16230466df59Sjg devfs_remdrv_rmdir(vnode_t *dirvp, const char *dir, vnode_t *rvp) 16247c478bd9Sstevel@tonic-gate { 16257c478bd9Sstevel@tonic-gate int error; 16267c478bd9Sstevel@tonic-gate vnode_t *vp; 16277c478bd9Sstevel@tonic-gate int eof; 16287c478bd9Sstevel@tonic-gate struct iovec iov; 16297c478bd9Sstevel@tonic-gate struct uio uio; 16307c478bd9Sstevel@tonic-gate struct dirent64 *dp; 16317c478bd9Sstevel@tonic-gate dirent64_t *dbuf; 16327c478bd9Sstevel@tonic-gate size_t dlen; 16337c478bd9Sstevel@tonic-gate size_t dbuflen; 16347c478bd9Sstevel@tonic-gate int ndirents = 64; 16357c478bd9Sstevel@tonic-gate char *nm; 16367c478bd9Sstevel@tonic-gate 16377c478bd9Sstevel@tonic-gate VN_HOLD(dirvp); 16387c478bd9Sstevel@tonic-gate 16397c478bd9Sstevel@tonic-gate dlen = ndirents * (sizeof (*dbuf)); 16407c478bd9Sstevel@tonic-gate dbuf = kmem_alloc(dlen, KM_SLEEP); 16417c478bd9Sstevel@tonic-gate 16427c478bd9Sstevel@tonic-gate uio.uio_iov = &iov; 16437c478bd9Sstevel@tonic-gate uio.uio_iovcnt = 1; 16447c478bd9Sstevel@tonic-gate uio.uio_segflg = UIO_SYSSPACE; 16457c478bd9Sstevel@tonic-gate uio.uio_fmode = 0; 16467c478bd9Sstevel@tonic-gate uio.uio_extflg = UIO_COPY_CACHED; 16477c478bd9Sstevel@tonic-gate uio.uio_loffset = 0; 16487c478bd9Sstevel@tonic-gate uio.uio_llimit = MAXOFFSET_T; 16497c478bd9Sstevel@tonic-gate 16507c478bd9Sstevel@tonic-gate eof = 0; 16517c478bd9Sstevel@tonic-gate error = 0; 16527c478bd9Sstevel@tonic-gate while (!error && !eof) { 16537c478bd9Sstevel@tonic-gate uio.uio_resid = dlen; 16547c478bd9Sstevel@tonic-gate iov.iov_base = (char *)dbuf; 16557c478bd9Sstevel@tonic-gate iov.iov_len = dlen; 16567c478bd9Sstevel@tonic-gate 16577c478bd9Sstevel@tonic-gate (void) VOP_RWLOCK(dirvp, V_WRITELOCK_FALSE, NULL); 1658da6c28aaSamw error = VOP_READDIR(dirvp, &uio, kcred, &eof, NULL, 0); 16597c478bd9Sstevel@tonic-gate VOP_RWUNLOCK(dirvp, V_WRITELOCK_FALSE, NULL); 16607c478bd9Sstevel@tonic-gate 16617c478bd9Sstevel@tonic-gate dbuflen = dlen - uio.uio_resid; 16627c478bd9Sstevel@tonic-gate 16637c478bd9Sstevel@tonic-gate if (error || dbuflen == 0) 16647c478bd9Sstevel@tonic-gate break; 16657c478bd9Sstevel@tonic-gate 16667c478bd9Sstevel@tonic-gate for (dp = dbuf; ((intptr_t)dp < (intptr_t)dbuf + dbuflen); 16677c478bd9Sstevel@tonic-gate dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) { 16687c478bd9Sstevel@tonic-gate 16697c478bd9Sstevel@tonic-gate nm = dp->d_name; 16707c478bd9Sstevel@tonic-gate 16717c478bd9Sstevel@tonic-gate if (strcmp(nm, ".") == 0 || strcmp(nm, "..") == 0) 16727c478bd9Sstevel@tonic-gate continue; 16737c478bd9Sstevel@tonic-gate 1674da6c28aaSamw error = VOP_LOOKUP(dirvp, nm, 1675da6c28aaSamw &vp, NULL, 0, NULL, kcred, NULL, NULL, NULL); 16767c478bd9Sstevel@tonic-gate 16777c478bd9Sstevel@tonic-gate dsysdebug(error, 16787c478bd9Sstevel@tonic-gate ("rem_drv %s/%s lookup (%d)\n", 16797c478bd9Sstevel@tonic-gate dir, nm, error)); 16807c478bd9Sstevel@tonic-gate 16817c478bd9Sstevel@tonic-gate if (error) 16827c478bd9Sstevel@tonic-gate continue; 16837c478bd9Sstevel@tonic-gate 16847c478bd9Sstevel@tonic-gate ASSERT(vp->v_type == VDIR || 16857c478bd9Sstevel@tonic-gate vp->v_type == VCHR || vp->v_type == VBLK); 16867c478bd9Sstevel@tonic-gate 16877c478bd9Sstevel@tonic-gate if (vp->v_type == VDIR) { 16880466df59Sjg error = devfs_remdrv_rmdir(vp, nm, rvp); 16897c478bd9Sstevel@tonic-gate if (error == 0) { 16907c478bd9Sstevel@tonic-gate error = VOP_RMDIR(dirvp, 1691da6c28aaSamw (char *)nm, rvp, kcred, NULL, 0); 16927c478bd9Sstevel@tonic-gate dsysdebug(error, 16937c478bd9Sstevel@tonic-gate ("rem_drv %s/%s rmdir (%d)\n", 16947c478bd9Sstevel@tonic-gate dir, nm, error)); 16957c478bd9Sstevel@tonic-gate } 16967c478bd9Sstevel@tonic-gate } else { 1697da6c28aaSamw error = VOP_REMOVE(dirvp, (char *)nm, kcred, 1698da6c28aaSamw NULL, 0); 16997c478bd9Sstevel@tonic-gate dsysdebug(error, 17007c478bd9Sstevel@tonic-gate ("rem_drv %s/%s remove (%d)\n", 17017c478bd9Sstevel@tonic-gate dir, nm, error)); 17027c478bd9Sstevel@tonic-gate } 17037c478bd9Sstevel@tonic-gate 17047c478bd9Sstevel@tonic-gate VN_RELE(vp); 17057c478bd9Sstevel@tonic-gate if (error) { 17067c478bd9Sstevel@tonic-gate goto exit; 17077c478bd9Sstevel@tonic-gate } 17087c478bd9Sstevel@tonic-gate } 17097c478bd9Sstevel@tonic-gate } 17107c478bd9Sstevel@tonic-gate 17117c478bd9Sstevel@tonic-gate exit: 17127c478bd9Sstevel@tonic-gate VN_RELE(dirvp); 17137c478bd9Sstevel@tonic-gate kmem_free(dbuf, dlen); 17147c478bd9Sstevel@tonic-gate 17157c478bd9Sstevel@tonic-gate return (error); 17167c478bd9Sstevel@tonic-gate } 17177c478bd9Sstevel@tonic-gate 17187c478bd9Sstevel@tonic-gate int 17197c478bd9Sstevel@tonic-gate devfs_remdrv_cleanup(const char *dir, const char *nodename) 17207c478bd9Sstevel@tonic-gate { 17217c478bd9Sstevel@tonic-gate int error; 17227c478bd9Sstevel@tonic-gate vnode_t *vp; 17237c478bd9Sstevel@tonic-gate vnode_t *dirvp; 17247c478bd9Sstevel@tonic-gate int eof; 17257c478bd9Sstevel@tonic-gate struct iovec iov; 17267c478bd9Sstevel@tonic-gate struct uio uio; 17277c478bd9Sstevel@tonic-gate struct dirent64 *dp; 17287c478bd9Sstevel@tonic-gate dirent64_t *dbuf; 17297c478bd9Sstevel@tonic-gate size_t dlen; 17307c478bd9Sstevel@tonic-gate size_t dbuflen; 17317c478bd9Sstevel@tonic-gate int ndirents = 64; 17327c478bd9Sstevel@tonic-gate int nodenamelen = strlen(nodename); 17337c478bd9Sstevel@tonic-gate char *nm; 17347c478bd9Sstevel@tonic-gate struct pathname pn; 17350466df59Sjg vnode_t *rvp; /* root node of the underlying attribute fs */ 17367c478bd9Sstevel@tonic-gate 17377c478bd9Sstevel@tonic-gate dcmn_err5(("devfs_remdrv_cleanup: %s %s\n", dir, nodename)); 17387c478bd9Sstevel@tonic-gate 17397c478bd9Sstevel@tonic-gate if (error = pn_get((char *)dir, UIO_SYSSPACE, &pn)) 17407c478bd9Sstevel@tonic-gate return (0); 17417c478bd9Sstevel@tonic-gate 17420466df59Sjg rvp = dvroot->dv_attrvp; 17430466df59Sjg ASSERT(rvp != NULL); 17440466df59Sjg VN_HOLD(rvp); 17457c478bd9Sstevel@tonic-gate 17467c478bd9Sstevel@tonic-gate pn_skipslash(&pn); 17470466df59Sjg dirvp = rvp; 17487c478bd9Sstevel@tonic-gate VN_HOLD(dirvp); 17497c478bd9Sstevel@tonic-gate 17507c478bd9Sstevel@tonic-gate nm = kmem_alloc(MAXNAMELEN, KM_SLEEP); 17517c478bd9Sstevel@tonic-gate 17527c478bd9Sstevel@tonic-gate while (pn_pathleft(&pn)) { 17537c478bd9Sstevel@tonic-gate ASSERT(dirvp->v_type == VDIR); 17547c478bd9Sstevel@tonic-gate (void) pn_getcomponent(&pn, nm); 17557c478bd9Sstevel@tonic-gate ASSERT((strcmp(nm, ".") != 0) && (strcmp(nm, "..") != 0)); 1756da6c28aaSamw error = VOP_LOOKUP(dirvp, nm, &vp, NULL, 0, rvp, kcred, 1757da6c28aaSamw NULL, NULL, NULL); 17587c478bd9Sstevel@tonic-gate if (error) { 17597c478bd9Sstevel@tonic-gate dcmn_err5(("remdrv_cleanup %s lookup error %d\n", 17607c478bd9Sstevel@tonic-gate nm, error)); 17617c478bd9Sstevel@tonic-gate VN_RELE(dirvp); 17620466df59Sjg if (dirvp != rvp) 17630466df59Sjg VN_RELE(rvp); 17647c478bd9Sstevel@tonic-gate pn_free(&pn); 17657c478bd9Sstevel@tonic-gate kmem_free(nm, MAXNAMELEN); 17667c478bd9Sstevel@tonic-gate return (0); 17677c478bd9Sstevel@tonic-gate } 17687c478bd9Sstevel@tonic-gate VN_RELE(dirvp); 17697c478bd9Sstevel@tonic-gate dirvp = vp; 17707c478bd9Sstevel@tonic-gate pn_skipslash(&pn); 17717c478bd9Sstevel@tonic-gate } 17727c478bd9Sstevel@tonic-gate 17737c478bd9Sstevel@tonic-gate ASSERT(dirvp->v_type == VDIR); 17740466df59Sjg if (dirvp != rvp) 17750466df59Sjg VN_RELE(rvp); 17767c478bd9Sstevel@tonic-gate pn_free(&pn); 17777c478bd9Sstevel@tonic-gate kmem_free(nm, MAXNAMELEN); 17787c478bd9Sstevel@tonic-gate 17797c478bd9Sstevel@tonic-gate dlen = ndirents * (sizeof (*dbuf)); 17807c478bd9Sstevel@tonic-gate dbuf = kmem_alloc(dlen, KM_SLEEP); 17817c478bd9Sstevel@tonic-gate 17827c478bd9Sstevel@tonic-gate uio.uio_iov = &iov; 17837c478bd9Sstevel@tonic-gate uio.uio_iovcnt = 1; 17847c478bd9Sstevel@tonic-gate uio.uio_segflg = UIO_SYSSPACE; 17857c478bd9Sstevel@tonic-gate uio.uio_fmode = 0; 17867c478bd9Sstevel@tonic-gate uio.uio_extflg = UIO_COPY_CACHED; 17877c478bd9Sstevel@tonic-gate uio.uio_loffset = 0; 17887c478bd9Sstevel@tonic-gate uio.uio_llimit = MAXOFFSET_T; 17897c478bd9Sstevel@tonic-gate 17907c478bd9Sstevel@tonic-gate eof = 0; 17917c478bd9Sstevel@tonic-gate error = 0; 17927c478bd9Sstevel@tonic-gate while (!error && !eof) { 17937c478bd9Sstevel@tonic-gate uio.uio_resid = dlen; 17947c478bd9Sstevel@tonic-gate iov.iov_base = (char *)dbuf; 17957c478bd9Sstevel@tonic-gate iov.iov_len = dlen; 17967c478bd9Sstevel@tonic-gate 17977c478bd9Sstevel@tonic-gate (void) VOP_RWLOCK(dirvp, V_WRITELOCK_FALSE, NULL); 1798da6c28aaSamw error = VOP_READDIR(dirvp, &uio, kcred, &eof, NULL, 0); 17997c478bd9Sstevel@tonic-gate VOP_RWUNLOCK(dirvp, V_WRITELOCK_FALSE, NULL); 18007c478bd9Sstevel@tonic-gate 18017c478bd9Sstevel@tonic-gate dbuflen = dlen - uio.uio_resid; 18027c478bd9Sstevel@tonic-gate 18037c478bd9Sstevel@tonic-gate if (error || dbuflen == 0) 18047c478bd9Sstevel@tonic-gate break; 18057c478bd9Sstevel@tonic-gate 18067c478bd9Sstevel@tonic-gate for (dp = dbuf; ((intptr_t)dp < (intptr_t)dbuf + dbuflen); 18077c478bd9Sstevel@tonic-gate dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) { 18087c478bd9Sstevel@tonic-gate 18097c478bd9Sstevel@tonic-gate nm = dp->d_name; 18107c478bd9Sstevel@tonic-gate 18117c478bd9Sstevel@tonic-gate if (strcmp(nm, ".") == 0 || strcmp(nm, "..") == 0) 18127c478bd9Sstevel@tonic-gate continue; 18137c478bd9Sstevel@tonic-gate 18147c478bd9Sstevel@tonic-gate if (strncmp(nm, nodename, nodenamelen) != 0) 18157c478bd9Sstevel@tonic-gate continue; 18167c478bd9Sstevel@tonic-gate 18177c478bd9Sstevel@tonic-gate error = VOP_LOOKUP(dirvp, nm, &vp, 1818da6c28aaSamw NULL, 0, NULL, kcred, NULL, NULL, NULL); 18197c478bd9Sstevel@tonic-gate 18207c478bd9Sstevel@tonic-gate dsysdebug(error, 18217c478bd9Sstevel@tonic-gate ("rem_drv %s/%s lookup (%d)\n", 18227c478bd9Sstevel@tonic-gate dir, nm, error)); 18237c478bd9Sstevel@tonic-gate 18247c478bd9Sstevel@tonic-gate if (error) 18257c478bd9Sstevel@tonic-gate continue; 18267c478bd9Sstevel@tonic-gate 18277c478bd9Sstevel@tonic-gate ASSERT(vp->v_type == VDIR || 18287c478bd9Sstevel@tonic-gate vp->v_type == VCHR || vp->v_type == VBLK); 18297c478bd9Sstevel@tonic-gate 18307c478bd9Sstevel@tonic-gate if (vp->v_type == VDIR) { 18310466df59Sjg error = devfs_remdrv_rmdir(vp, nm, rvp); 18327c478bd9Sstevel@tonic-gate if (error == 0) { 1833da6c28aaSamw error = VOP_RMDIR(dirvp, (char *)nm, 1834da6c28aaSamw rvp, kcred, NULL, 0); 18357c478bd9Sstevel@tonic-gate dsysdebug(error, 18367c478bd9Sstevel@tonic-gate ("rem_drv %s/%s rmdir (%d)\n", 18377c478bd9Sstevel@tonic-gate dir, nm, error)); 18387c478bd9Sstevel@tonic-gate } 18397c478bd9Sstevel@tonic-gate } else { 1840da6c28aaSamw error = VOP_REMOVE(dirvp, (char *)nm, kcred, 1841da6c28aaSamw NULL, 0); 18427c478bd9Sstevel@tonic-gate dsysdebug(error, 18437c478bd9Sstevel@tonic-gate ("rem_drv %s/%s remove (%d)\n", 18447c478bd9Sstevel@tonic-gate dir, nm, error)); 18457c478bd9Sstevel@tonic-gate } 18467c478bd9Sstevel@tonic-gate 18477c478bd9Sstevel@tonic-gate VN_RELE(vp); 18487c478bd9Sstevel@tonic-gate if (error) 18497c478bd9Sstevel@tonic-gate goto exit; 18507c478bd9Sstevel@tonic-gate } 18517c478bd9Sstevel@tonic-gate } 18527c478bd9Sstevel@tonic-gate 18537c478bd9Sstevel@tonic-gate exit: 18547c478bd9Sstevel@tonic-gate VN_RELE(dirvp); 18557c478bd9Sstevel@tonic-gate 18567c478bd9Sstevel@tonic-gate kmem_free(dbuf, dlen); 18577c478bd9Sstevel@tonic-gate 18587c478bd9Sstevel@tonic-gate return (0); 18597c478bd9Sstevel@tonic-gate } 18607c478bd9Sstevel@tonic-gate 18617c478bd9Sstevel@tonic-gate struct dv_list { 18627c478bd9Sstevel@tonic-gate struct dv_node *dv; 18637c478bd9Sstevel@tonic-gate struct dv_list *next; 18647c478bd9Sstevel@tonic-gate }; 18657c478bd9Sstevel@tonic-gate 18667c478bd9Sstevel@tonic-gate void 18677c478bd9Sstevel@tonic-gate dv_walk( 18687c478bd9Sstevel@tonic-gate struct dv_node *ddv, 18697c478bd9Sstevel@tonic-gate char *devnm, 18707c478bd9Sstevel@tonic-gate void (*callback)(struct dv_node *, void *), 18717c478bd9Sstevel@tonic-gate void *arg) 18727c478bd9Sstevel@tonic-gate { 18737c478bd9Sstevel@tonic-gate struct vnode *dvp; 18747c478bd9Sstevel@tonic-gate struct dv_node *dv; 18757c478bd9Sstevel@tonic-gate struct dv_list *head, *tail, *next; 187645a9d961Scth int len; 18777c478bd9Sstevel@tonic-gate 18787c478bd9Sstevel@tonic-gate dcmn_err3(("dv_walk: ddv = %s, devnm = %s\n", 18797c478bd9Sstevel@tonic-gate ddv->dv_name, devnm ? devnm : "<null>")); 18807c478bd9Sstevel@tonic-gate 18817c478bd9Sstevel@tonic-gate dvp = DVTOV(ddv); 18827c478bd9Sstevel@tonic-gate 18837c478bd9Sstevel@tonic-gate ASSERT(dvp->v_type == VDIR); 18847c478bd9Sstevel@tonic-gate 18857c478bd9Sstevel@tonic-gate head = tail = next = NULL; 18867c478bd9Sstevel@tonic-gate 18877c478bd9Sstevel@tonic-gate rw_enter(&ddv->dv_contents, RW_READER); 188845a9d961Scth mutex_enter(&dvp->v_lock); 1889aac43a5fSjg for (dv = DV_FIRST_ENTRY(ddv); dv; dv = DV_NEXT_ENTRY(ddv, dv)) { 18907c478bd9Sstevel@tonic-gate /* 18917c478bd9Sstevel@tonic-gate * If devnm is not NULL and is not the empty string, 18927c478bd9Sstevel@tonic-gate * select only dv_nodes with matching non-minor name 18937c478bd9Sstevel@tonic-gate */ 18947c478bd9Sstevel@tonic-gate if (devnm && (len = strlen(devnm)) && 18957c478bd9Sstevel@tonic-gate (strncmp(devnm, dv->dv_name, len) || 18967c478bd9Sstevel@tonic-gate (dv->dv_name[len] != ':' && dv->dv_name[len] != '\0'))) 18977c478bd9Sstevel@tonic-gate continue; 18987c478bd9Sstevel@tonic-gate 18997c478bd9Sstevel@tonic-gate callback(dv, arg); 19007c478bd9Sstevel@tonic-gate 19017c478bd9Sstevel@tonic-gate if (DVTOV(dv)->v_type != VDIR) 19027c478bd9Sstevel@tonic-gate continue; 19037c478bd9Sstevel@tonic-gate 19047c478bd9Sstevel@tonic-gate next = kmem_zalloc(sizeof (*next), KM_SLEEP); 19057c478bd9Sstevel@tonic-gate next->dv = dv; 19067c478bd9Sstevel@tonic-gate 19077c478bd9Sstevel@tonic-gate if (tail) 19087c478bd9Sstevel@tonic-gate tail->next = next; 19097c478bd9Sstevel@tonic-gate else 19107c478bd9Sstevel@tonic-gate head = next; 19117c478bd9Sstevel@tonic-gate 19127c478bd9Sstevel@tonic-gate tail = next; 19137c478bd9Sstevel@tonic-gate } 19147c478bd9Sstevel@tonic-gate 19157c478bd9Sstevel@tonic-gate while (head) { 19167c478bd9Sstevel@tonic-gate dv_walk(head->dv, NULL, callback, arg); 19177c478bd9Sstevel@tonic-gate next = head->next; 19187c478bd9Sstevel@tonic-gate kmem_free(head, sizeof (*head)); 19197c478bd9Sstevel@tonic-gate head = next; 19207c478bd9Sstevel@tonic-gate } 19217c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 19227c478bd9Sstevel@tonic-gate mutex_exit(&dvp->v_lock); 19237c478bd9Sstevel@tonic-gate } 1924