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 5*facf4a8dSllai1 * Common Development and Distribution License (the "License"). 6*facf4a8dSllai1 * 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 /* 22737d277aScth * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate /* 297c478bd9Sstevel@tonic-gate * miscellaneous routines for the devfs 307c478bd9Sstevel@tonic-gate */ 317c478bd9Sstevel@tonic-gate 327c478bd9Sstevel@tonic-gate #include <sys/types.h> 337c478bd9Sstevel@tonic-gate #include <sys/param.h> 347c478bd9Sstevel@tonic-gate #include <sys/t_lock.h> 357c478bd9Sstevel@tonic-gate #include <sys/systm.h> 367c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 377c478bd9Sstevel@tonic-gate #include <sys/user.h> 387c478bd9Sstevel@tonic-gate #include <sys/time.h> 397c478bd9Sstevel@tonic-gate #include <sys/vfs.h> 407c478bd9Sstevel@tonic-gate #include <sys/vnode.h> 417c478bd9Sstevel@tonic-gate #include <sys/file.h> 427c478bd9Sstevel@tonic-gate #include <sys/fcntl.h> 437c478bd9Sstevel@tonic-gate #include <sys/flock.h> 447c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 457c478bd9Sstevel@tonic-gate #include <sys/uio.h> 467c478bd9Sstevel@tonic-gate #include <sys/errno.h> 477c478bd9Sstevel@tonic-gate #include <sys/stat.h> 487c478bd9Sstevel@tonic-gate #include <sys/cred.h> 497c478bd9Sstevel@tonic-gate #include <sys/dirent.h> 507c478bd9Sstevel@tonic-gate #include <sys/pathname.h> 517c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 527c478bd9Sstevel@tonic-gate #include <sys/debug.h> 537c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 547c478bd9Sstevel@tonic-gate #include <fs/fs_subr.h> 557c478bd9Sstevel@tonic-gate #include <sys/fs/dv_node.h> 567c478bd9Sstevel@tonic-gate #include <sys/fs/snode.h> 577c478bd9Sstevel@tonic-gate #include <sys/sunndi.h> 587c478bd9Sstevel@tonic-gate #include <sys/sunmdi.h> 597c478bd9Sstevel@tonic-gate #include <sys/conf.h> 607c478bd9Sstevel@tonic-gate 617c478bd9Sstevel@tonic-gate #ifdef DEBUG 627c478bd9Sstevel@tonic-gate int devfs_debug = 0x0; 637c478bd9Sstevel@tonic-gate #endif 647c478bd9Sstevel@tonic-gate 657c478bd9Sstevel@tonic-gate const char dvnm[] = "devfs"; 667c478bd9Sstevel@tonic-gate kmem_cache_t *dv_node_cache; /* dv_node cache */ 677c478bd9Sstevel@tonic-gate uint_t devfs_clean_key; 687c478bd9Sstevel@tonic-gate struct dv_node *dvroot; 697c478bd9Sstevel@tonic-gate 707c478bd9Sstevel@tonic-gate /* prototype memory vattrs */ 717c478bd9Sstevel@tonic-gate vattr_t dv_vattr_dir = { 727c478bd9Sstevel@tonic-gate AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */ 737c478bd9Sstevel@tonic-gate VDIR, /* va_type */ 747c478bd9Sstevel@tonic-gate DV_DIRMODE_DEFAULT, /* va_mode */ 757c478bd9Sstevel@tonic-gate DV_UID_DEFAULT, /* va_uid */ 767c478bd9Sstevel@tonic-gate DV_GID_DEFAULT, /* va_gid */ 777c478bd9Sstevel@tonic-gate 0, /* va_fsid; */ 787c478bd9Sstevel@tonic-gate 0, /* va_nodeid; */ 797c478bd9Sstevel@tonic-gate 0, /* va_nlink; */ 807c478bd9Sstevel@tonic-gate 0, /* va_size; */ 817c478bd9Sstevel@tonic-gate 0, /* va_atime; */ 827c478bd9Sstevel@tonic-gate 0, /* va_mtime; */ 837c478bd9Sstevel@tonic-gate 0, /* va_ctime; */ 847c478bd9Sstevel@tonic-gate 0, /* va_rdev; */ 857c478bd9Sstevel@tonic-gate 0, /* va_blksize; */ 867c478bd9Sstevel@tonic-gate 0, /* va_nblocks; */ 877c478bd9Sstevel@tonic-gate 0, /* va_seq; */ 887c478bd9Sstevel@tonic-gate }; 897c478bd9Sstevel@tonic-gate 907c478bd9Sstevel@tonic-gate vattr_t dv_vattr_file = { 917c478bd9Sstevel@tonic-gate AT_TYPE|AT_MODE|AT_SIZE|AT_UID|AT_GID|AT_RDEV, /* va_mask */ 927c478bd9Sstevel@tonic-gate 0, /* va_type */ 937c478bd9Sstevel@tonic-gate DV_DEVMODE_DEFAULT, /* va_mode */ 947c478bd9Sstevel@tonic-gate DV_UID_DEFAULT, /* va_uid */ 957c478bd9Sstevel@tonic-gate DV_GID_DEFAULT, /* va_gid */ 967c478bd9Sstevel@tonic-gate 0, /* va_fsid; */ 977c478bd9Sstevel@tonic-gate 0, /* va_nodeid; */ 987c478bd9Sstevel@tonic-gate 0, /* va_nlink; */ 997c478bd9Sstevel@tonic-gate 0, /* va_size; */ 1007c478bd9Sstevel@tonic-gate 0, /* va_atime; */ 1017c478bd9Sstevel@tonic-gate 0, /* va_mtime; */ 1027c478bd9Sstevel@tonic-gate 0, /* va_ctime; */ 1037c478bd9Sstevel@tonic-gate 0, /* va_rdev; */ 1047c478bd9Sstevel@tonic-gate 0, /* va_blksize; */ 1057c478bd9Sstevel@tonic-gate 0, /* va_nblocks; */ 1067c478bd9Sstevel@tonic-gate 0, /* va_seq; */ 1077c478bd9Sstevel@tonic-gate }; 1087c478bd9Sstevel@tonic-gate 1097c478bd9Sstevel@tonic-gate vattr_t dv_vattr_priv = { 1107c478bd9Sstevel@tonic-gate AT_TYPE|AT_MODE|AT_SIZE|AT_UID|AT_GID|AT_RDEV, /* va_mask */ 1117c478bd9Sstevel@tonic-gate 0, /* va_type */ 1127c478bd9Sstevel@tonic-gate DV_DEVMODE_PRIV, /* va_mode */ 1137c478bd9Sstevel@tonic-gate DV_UID_DEFAULT, /* va_uid */ 1147c478bd9Sstevel@tonic-gate DV_GID_DEFAULT, /* va_gid */ 1157c478bd9Sstevel@tonic-gate 0, /* va_fsid; */ 1167c478bd9Sstevel@tonic-gate 0, /* va_nodeid; */ 1177c478bd9Sstevel@tonic-gate 0, /* va_nlink; */ 1187c478bd9Sstevel@tonic-gate 0, /* va_size; */ 1197c478bd9Sstevel@tonic-gate 0, /* va_atime; */ 1207c478bd9Sstevel@tonic-gate 0, /* va_mtime; */ 1217c478bd9Sstevel@tonic-gate 0, /* va_ctime; */ 1227c478bd9Sstevel@tonic-gate 0, /* va_rdev; */ 1237c478bd9Sstevel@tonic-gate 0, /* va_blksize; */ 1247c478bd9Sstevel@tonic-gate 0, /* va_nblocks; */ 1257c478bd9Sstevel@tonic-gate 0, /* va_seq; */ 1267c478bd9Sstevel@tonic-gate }; 1277c478bd9Sstevel@tonic-gate 1287c478bd9Sstevel@tonic-gate extern dev_info_t *clone_dip; 1297c478bd9Sstevel@tonic-gate extern major_t clone_major; 1307c478bd9Sstevel@tonic-gate extern struct dev_ops *ddi_hold_driver(major_t); 1317c478bd9Sstevel@tonic-gate 1327c478bd9Sstevel@tonic-gate /* 1337c478bd9Sstevel@tonic-gate * dv_node cache constructor, destructor, can cache creation 1347c478bd9Sstevel@tonic-gate */ 1357c478bd9Sstevel@tonic-gate /*ARGSUSED1*/ 1367c478bd9Sstevel@tonic-gate static int 1377c478bd9Sstevel@tonic-gate i_dv_node_ctor(void *buf, void *cfarg, int flag) 1387c478bd9Sstevel@tonic-gate { 1397c478bd9Sstevel@tonic-gate struct dv_node *dv = (struct dv_node *)buf; 1407c478bd9Sstevel@tonic-gate struct vnode *vp; 1417c478bd9Sstevel@tonic-gate 1427c478bd9Sstevel@tonic-gate bzero(buf, sizeof (struct dv_node)); 1437c478bd9Sstevel@tonic-gate 1447c478bd9Sstevel@tonic-gate /* initialize persistent parts of dv_node */ 1457c478bd9Sstevel@tonic-gate rw_init(&dv->dv_contents, NULL, RW_DEFAULT, NULL); 1467c478bd9Sstevel@tonic-gate 1477c478bd9Sstevel@tonic-gate /* allocate vnode and initialize link back to dv_node */ 1487c478bd9Sstevel@tonic-gate dv->dv_vnode = vn_alloc(KM_SLEEP); 1497c478bd9Sstevel@tonic-gate vp = DVTOV(dv); 1507c478bd9Sstevel@tonic-gate vp->v_data = (caddr_t)dv; 1517c478bd9Sstevel@tonic-gate return (0); 1527c478bd9Sstevel@tonic-gate } 1537c478bd9Sstevel@tonic-gate 1547c478bd9Sstevel@tonic-gate /* dev_info node destructor for kmem cache */ 1557c478bd9Sstevel@tonic-gate /*ARGSUSED1*/ 1567c478bd9Sstevel@tonic-gate static void 1577c478bd9Sstevel@tonic-gate i_dv_node_dtor(void *buf, void *arg) 1587c478bd9Sstevel@tonic-gate { 1597c478bd9Sstevel@tonic-gate struct dv_node *dv = (struct dv_node *)buf; 1607c478bd9Sstevel@tonic-gate struct vnode *vp = DVTOV(dv); 1617c478bd9Sstevel@tonic-gate 1627c478bd9Sstevel@tonic-gate rw_destroy(&dv->dv_contents); 1637c478bd9Sstevel@tonic-gate vn_invalid(vp); 1647c478bd9Sstevel@tonic-gate vn_free(vp); 1657c478bd9Sstevel@tonic-gate } 1667c478bd9Sstevel@tonic-gate 1677c478bd9Sstevel@tonic-gate 1687c478bd9Sstevel@tonic-gate /* initialize dev_info node cache */ 1697c478bd9Sstevel@tonic-gate void 1707c478bd9Sstevel@tonic-gate dv_node_cache_init() 1717c478bd9Sstevel@tonic-gate { 1727c478bd9Sstevel@tonic-gate ASSERT(dv_node_cache == NULL); 1737c478bd9Sstevel@tonic-gate dv_node_cache = kmem_cache_create("dv_node_cache", 1747c478bd9Sstevel@tonic-gate sizeof (struct dv_node), 0, i_dv_node_ctor, i_dv_node_dtor, 1757c478bd9Sstevel@tonic-gate NULL, NULL, NULL, 0); 1767c478bd9Sstevel@tonic-gate 1777c478bd9Sstevel@tonic-gate tsd_create(&devfs_clean_key, NULL); 1787c478bd9Sstevel@tonic-gate } 1797c478bd9Sstevel@tonic-gate 1807c478bd9Sstevel@tonic-gate /* initialize dev_info node cache */ 1817c478bd9Sstevel@tonic-gate void 1827c478bd9Sstevel@tonic-gate dv_node_cache_fini() 1837c478bd9Sstevel@tonic-gate { 1847c478bd9Sstevel@tonic-gate ASSERT(dv_node_cache != NULL); 1857c478bd9Sstevel@tonic-gate kmem_cache_destroy(dv_node_cache); 1867c478bd9Sstevel@tonic-gate dv_node_cache = NULL; 1877c478bd9Sstevel@tonic-gate 1887c478bd9Sstevel@tonic-gate tsd_destroy(&devfs_clean_key); 1897c478bd9Sstevel@tonic-gate } 1907c478bd9Sstevel@tonic-gate 1917c478bd9Sstevel@tonic-gate /* 1927c478bd9Sstevel@tonic-gate * dv_mkino - Generate a unique inode number for devfs nodes. 1937c478bd9Sstevel@tonic-gate * 1947c478bd9Sstevel@tonic-gate * Although ino_t is 64 bits, the inode number is truncated to 32 bits for 32 1957c478bd9Sstevel@tonic-gate * bit non-LARGEFILE applications. This means that there is a requirement to 1967c478bd9Sstevel@tonic-gate * maintain the inode number as a 32 bit value or applications will have 1977c478bd9Sstevel@tonic-gate * stat(2) calls fail with EOVERFLOW. We form a 32 bit inode number from the 1987c478bd9Sstevel@tonic-gate * dev_t. but if the minor number is larger than L_MAXMIN32 we fold extra minor 1997c478bd9Sstevel@tonic-gate * 2007c478bd9Sstevel@tonic-gate * To generate inode numbers for directories, we assume that we will never use 2017c478bd9Sstevel@tonic-gate * more than half the major space - this allows for ~8190 drivers. We use this 2027c478bd9Sstevel@tonic-gate * upper major number space to allocate inode numbers for directories by 2037c478bd9Sstevel@tonic-gate * encoding the major and instance into this space. 2047c478bd9Sstevel@tonic-gate * 2057c478bd9Sstevel@tonic-gate * We also skew the result so that inode 2 is reserved for the root of the file 2067c478bd9Sstevel@tonic-gate * system. 2077c478bd9Sstevel@tonic-gate * 2087c478bd9Sstevel@tonic-gate * As part of the future support for 64-bit dev_t APIs, the upper minor bits 2097c478bd9Sstevel@tonic-gate * should be folded into the high inode bits by adding the following code 2107c478bd9Sstevel@tonic-gate * after "ino |= 1": 2117c478bd9Sstevel@tonic-gate * 2127c478bd9Sstevel@tonic-gate * #if (L_BITSMINOR32 != L_BITSMINOR) 2137c478bd9Sstevel@tonic-gate * |* fold overflow minor bits into high bits of inode number *| 2147c478bd9Sstevel@tonic-gate * ino |= ((ino_t)(minor >> L_BITSMINOR32)) << L_BITSMINOR; 2157c478bd9Sstevel@tonic-gate * #endif |* (L_BITSMINOR32 != L_BITSMINOR) *| 2167c478bd9Sstevel@tonic-gate * 2177c478bd9Sstevel@tonic-gate * This way only applications that use devices that overflow their minor 2187c478bd9Sstevel@tonic-gate * space will have an application level impact. 2197c478bd9Sstevel@tonic-gate */ 2207c478bd9Sstevel@tonic-gate static ino_t 2217c478bd9Sstevel@tonic-gate dv_mkino(dev_info_t *devi, vtype_t typ, dev_t dev) 2227c478bd9Sstevel@tonic-gate { 2237c478bd9Sstevel@tonic-gate major_t major; 2247c478bd9Sstevel@tonic-gate minor_t minor; 2257c478bd9Sstevel@tonic-gate ino_t ino; 2267c478bd9Sstevel@tonic-gate static int warn; 2277c478bd9Sstevel@tonic-gate 2287c478bd9Sstevel@tonic-gate if (typ == VDIR) { 2297c478bd9Sstevel@tonic-gate major = ((L_MAXMAJ32 + 1) >> 1) + DEVI(devi)->devi_major; 2307c478bd9Sstevel@tonic-gate minor = ddi_get_instance(devi); 2317c478bd9Sstevel@tonic-gate 2327c478bd9Sstevel@tonic-gate /* makedevice32 in high half of major number space */ 2337c478bd9Sstevel@tonic-gate ino = (ino_t)((major << L_BITSMINOR32) | (minor & L_MAXMIN32)); 2347c478bd9Sstevel@tonic-gate 2357c478bd9Sstevel@tonic-gate major = DEVI(devi)->devi_major; 2367c478bd9Sstevel@tonic-gate } else { 2377c478bd9Sstevel@tonic-gate major = getmajor(dev); 2387c478bd9Sstevel@tonic-gate minor = getminor(dev); 2397c478bd9Sstevel@tonic-gate 2407c478bd9Sstevel@tonic-gate /* makedevice32 */ 2417c478bd9Sstevel@tonic-gate ino = (ino_t)((major << L_BITSMINOR32) | (minor & L_MAXMIN32)); 2427c478bd9Sstevel@tonic-gate 2437c478bd9Sstevel@tonic-gate /* make ino for VCHR different than VBLK */ 2447c478bd9Sstevel@tonic-gate ino <<= 1; 2457c478bd9Sstevel@tonic-gate if (typ == VCHR) 2467c478bd9Sstevel@tonic-gate ino |= 1; 2477c478bd9Sstevel@tonic-gate } 2487c478bd9Sstevel@tonic-gate 2497c478bd9Sstevel@tonic-gate ino += DV_ROOTINO + 1; /* skew */ 2507c478bd9Sstevel@tonic-gate 2517c478bd9Sstevel@tonic-gate /* 2527c478bd9Sstevel@tonic-gate * diagnose things a little early because adding the skew to a large 2537c478bd9Sstevel@tonic-gate * minor number could roll over the major. 2547c478bd9Sstevel@tonic-gate */ 2557c478bd9Sstevel@tonic-gate if ((major >= (L_MAXMAJ32 >> 1)) && (warn == 0)) { 2567c478bd9Sstevel@tonic-gate warn = 1; 2577c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s: inode numbers are not unique", dvnm); 2587c478bd9Sstevel@tonic-gate } 2597c478bd9Sstevel@tonic-gate 2607c478bd9Sstevel@tonic-gate return (ino); 2617c478bd9Sstevel@tonic-gate } 2627c478bd9Sstevel@tonic-gate 2637c478bd9Sstevel@tonic-gate /* 2647c478bd9Sstevel@tonic-gate * dv_mkroot 2657c478bd9Sstevel@tonic-gate * 2667c478bd9Sstevel@tonic-gate * Build the first VDIR dv_node. 2677c478bd9Sstevel@tonic-gate */ 2687c478bd9Sstevel@tonic-gate struct dv_node * 2697c478bd9Sstevel@tonic-gate dv_mkroot(struct vfs *vfsp, dev_t devfsdev) 2707c478bd9Sstevel@tonic-gate { 2717c478bd9Sstevel@tonic-gate struct dv_node *dv; 2727c478bd9Sstevel@tonic-gate struct vnode *vp; 2737c478bd9Sstevel@tonic-gate 2747c478bd9Sstevel@tonic-gate ASSERT(ddi_root_node() != NULL); 2757c478bd9Sstevel@tonic-gate ASSERT(dv_node_cache != NULL); 2767c478bd9Sstevel@tonic-gate 2777c478bd9Sstevel@tonic-gate dcmn_err3(("dv_mkroot\n")); 2787c478bd9Sstevel@tonic-gate dv = kmem_cache_alloc(dv_node_cache, KM_SLEEP); 2797c478bd9Sstevel@tonic-gate vp = DVTOV(dv); 2807c478bd9Sstevel@tonic-gate vn_reinit(vp); 2817c478bd9Sstevel@tonic-gate vp->v_flag = VROOT; 2827c478bd9Sstevel@tonic-gate vp->v_vfsp = vfsp; 2837c478bd9Sstevel@tonic-gate vp->v_type = VDIR; 2847c478bd9Sstevel@tonic-gate vp->v_rdev = devfsdev; 2857c478bd9Sstevel@tonic-gate vn_setops(vp, dv_vnodeops); 2867c478bd9Sstevel@tonic-gate vn_exists(vp); 2877c478bd9Sstevel@tonic-gate 2887c478bd9Sstevel@tonic-gate dvroot = dv; 2897c478bd9Sstevel@tonic-gate 2907c478bd9Sstevel@tonic-gate dv->dv_name = NULL; /* not needed */ 2917c478bd9Sstevel@tonic-gate dv->dv_namelen = 0; 2927c478bd9Sstevel@tonic-gate 2937c478bd9Sstevel@tonic-gate dv->dv_devi = ddi_root_node(); 2947c478bd9Sstevel@tonic-gate 2957c478bd9Sstevel@tonic-gate dv->dv_ino = DV_ROOTINO; 2967c478bd9Sstevel@tonic-gate dv->dv_nlink = 2; /* name + . (no dv_insert) */ 2977c478bd9Sstevel@tonic-gate dv->dv_dotdot = dv; /* .. == self */ 2987c478bd9Sstevel@tonic-gate dv->dv_attrvp = NULLVP; 2997c478bd9Sstevel@tonic-gate dv->dv_attr = NULL; 3007c478bd9Sstevel@tonic-gate dv->dv_flags = DV_BUILD; 3017c478bd9Sstevel@tonic-gate dv->dv_priv = NULL; 3027c478bd9Sstevel@tonic-gate dv->dv_busy = 0; 3037c478bd9Sstevel@tonic-gate dv->dv_dflt_mode = 0; 3047c478bd9Sstevel@tonic-gate 3057c478bd9Sstevel@tonic-gate return (dv); 3067c478bd9Sstevel@tonic-gate } 3077c478bd9Sstevel@tonic-gate 3087c478bd9Sstevel@tonic-gate /* 3097c478bd9Sstevel@tonic-gate * dv_mkdir 3107c478bd9Sstevel@tonic-gate * 3117c478bd9Sstevel@tonic-gate * Given an probed or attached nexus node, create a VDIR dv_node. 3127c478bd9Sstevel@tonic-gate * No dv_attrvp is created at this point. 3137c478bd9Sstevel@tonic-gate */ 3147c478bd9Sstevel@tonic-gate struct dv_node * 3157c478bd9Sstevel@tonic-gate dv_mkdir(struct dv_node *ddv, dev_info_t *devi, char *nm) 3167c478bd9Sstevel@tonic-gate { 3177c478bd9Sstevel@tonic-gate struct dv_node *dv; 3187c478bd9Sstevel@tonic-gate struct vnode *vp; 3197c478bd9Sstevel@tonic-gate size_t nmlen; 3207c478bd9Sstevel@tonic-gate 3217c478bd9Sstevel@tonic-gate ASSERT((devi)); 3227c478bd9Sstevel@tonic-gate dcmn_err4(("dv_mkdir: %s\n", nm)); 3237c478bd9Sstevel@tonic-gate 3247c478bd9Sstevel@tonic-gate dv = kmem_cache_alloc(dv_node_cache, KM_SLEEP); 3257c478bd9Sstevel@tonic-gate nmlen = strlen(nm) + 1; 3267c478bd9Sstevel@tonic-gate dv->dv_name = kmem_alloc(nmlen, KM_SLEEP); 3277c478bd9Sstevel@tonic-gate bcopy(nm, dv->dv_name, nmlen); 3287c478bd9Sstevel@tonic-gate dv->dv_namelen = nmlen - 1; /* '\0' not included */ 3297c478bd9Sstevel@tonic-gate vp = DVTOV(dv); 3307c478bd9Sstevel@tonic-gate vn_reinit(vp); 3317c478bd9Sstevel@tonic-gate vp->v_flag = 0; 3327c478bd9Sstevel@tonic-gate vp->v_vfsp = DVTOV(ddv)->v_vfsp; 3337c478bd9Sstevel@tonic-gate vp->v_type = VDIR; 3347c478bd9Sstevel@tonic-gate vp->v_rdev = DVTOV(ddv)->v_rdev; 3357c478bd9Sstevel@tonic-gate vn_setops(vp, vn_getops(DVTOV(ddv))); 3367c478bd9Sstevel@tonic-gate vn_exists(vp); 3377c478bd9Sstevel@tonic-gate 3387c478bd9Sstevel@tonic-gate dv->dv_devi = devi; 3397c478bd9Sstevel@tonic-gate ndi_hold_devi(devi); 3407c478bd9Sstevel@tonic-gate 3417c478bd9Sstevel@tonic-gate dv->dv_ino = dv_mkino(devi, VDIR, NODEV); 3427c478bd9Sstevel@tonic-gate dv->dv_nlink = 0; /* updated on insert */ 3437c478bd9Sstevel@tonic-gate dv->dv_dotdot = ddv; 3447c478bd9Sstevel@tonic-gate dv->dv_attrvp = NULLVP; 3457c478bd9Sstevel@tonic-gate dv->dv_attr = NULL; 3467c478bd9Sstevel@tonic-gate dv->dv_flags = DV_BUILD; 3477c478bd9Sstevel@tonic-gate dv->dv_priv = NULL; 3487c478bd9Sstevel@tonic-gate dv->dv_busy = 0; 3497c478bd9Sstevel@tonic-gate dv->dv_dflt_mode = 0; 3507c478bd9Sstevel@tonic-gate 3517c478bd9Sstevel@tonic-gate return (dv); 3527c478bd9Sstevel@tonic-gate } 3537c478bd9Sstevel@tonic-gate 3547c478bd9Sstevel@tonic-gate /* 3557c478bd9Sstevel@tonic-gate * dv_mknod 3567c478bd9Sstevel@tonic-gate * 3577c478bd9Sstevel@tonic-gate * Given a minor node, create a VCHR or VBLK dv_node. 3587c478bd9Sstevel@tonic-gate * No dv_attrvp is created at this point. 3597c478bd9Sstevel@tonic-gate */ 3607c478bd9Sstevel@tonic-gate static struct dv_node * 3617c478bd9Sstevel@tonic-gate dv_mknod(struct dv_node *ddv, dev_info_t *devi, char *nm, 3627c478bd9Sstevel@tonic-gate struct ddi_minor_data *dmd) 3637c478bd9Sstevel@tonic-gate { 3647c478bd9Sstevel@tonic-gate struct dv_node *dv; 3657c478bd9Sstevel@tonic-gate struct vnode *vp; 3667c478bd9Sstevel@tonic-gate size_t nmlen; 3677c478bd9Sstevel@tonic-gate 3687c478bd9Sstevel@tonic-gate dcmn_err4(("dv_mknod: %s\n", nm)); 3697c478bd9Sstevel@tonic-gate 3707c478bd9Sstevel@tonic-gate dv = kmem_cache_alloc(dv_node_cache, KM_SLEEP); 3717c478bd9Sstevel@tonic-gate nmlen = strlen(nm) + 1; 3727c478bd9Sstevel@tonic-gate dv->dv_name = kmem_alloc(nmlen, KM_SLEEP); 3737c478bd9Sstevel@tonic-gate bcopy(nm, dv->dv_name, nmlen); 3747c478bd9Sstevel@tonic-gate dv->dv_namelen = nmlen - 1; /* no '\0' */ 3757c478bd9Sstevel@tonic-gate vp = DVTOV(dv); 3767c478bd9Sstevel@tonic-gate vn_reinit(vp); 3777c478bd9Sstevel@tonic-gate vp->v_flag = 0; 3787c478bd9Sstevel@tonic-gate vp->v_vfsp = DVTOV(ddv)->v_vfsp; 3797c478bd9Sstevel@tonic-gate vp->v_type = dmd->ddm_spec_type == S_IFCHR ? VCHR : VBLK; 3807c478bd9Sstevel@tonic-gate vp->v_rdev = dmd->ddm_dev; 3817c478bd9Sstevel@tonic-gate vn_setops(vp, vn_getops(DVTOV(ddv))); 3827c478bd9Sstevel@tonic-gate vn_exists(vp); 3837c478bd9Sstevel@tonic-gate 3847c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&DEVI(devi)->devi_lock)); 3857c478bd9Sstevel@tonic-gate dv->dv_devi = devi; 3867c478bd9Sstevel@tonic-gate DEVI(devi)->devi_ref++; 3877c478bd9Sstevel@tonic-gate 3887c478bd9Sstevel@tonic-gate dv->dv_ino = dv_mkino(devi, vp->v_type, vp->v_rdev); 3897c478bd9Sstevel@tonic-gate dv->dv_nlink = 0; /* updated on insert */ 3907c478bd9Sstevel@tonic-gate dv->dv_dotdot = ddv; 3917c478bd9Sstevel@tonic-gate dv->dv_attrvp = NULLVP; 3927c478bd9Sstevel@tonic-gate dv->dv_attr = NULL; 3937c478bd9Sstevel@tonic-gate dv->dv_flags = 0; 3947c478bd9Sstevel@tonic-gate 3957c478bd9Sstevel@tonic-gate if (dmd->type == DDM_INTERNAL_PATH) 3967c478bd9Sstevel@tonic-gate dv->dv_flags |= DV_INTERNAL; 3977c478bd9Sstevel@tonic-gate if (dmd->ddm_flags & DM_NO_FSPERM) 3987c478bd9Sstevel@tonic-gate dv->dv_flags |= DV_NO_FSPERM; 3997c478bd9Sstevel@tonic-gate 4007c478bd9Sstevel@tonic-gate dv->dv_priv = dmd->ddm_node_priv; 4017c478bd9Sstevel@tonic-gate if (dv->dv_priv) 4027c478bd9Sstevel@tonic-gate dphold(dv->dv_priv); 4037c478bd9Sstevel@tonic-gate 4047c478bd9Sstevel@tonic-gate /* 4057c478bd9Sstevel@tonic-gate * Minors created with ddi_create_priv_minor_node can specify 4067c478bd9Sstevel@tonic-gate * a default mode permission other than the devfs default. 4077c478bd9Sstevel@tonic-gate */ 4087c478bd9Sstevel@tonic-gate if (dv->dv_priv || dv->dv_flags & DV_NO_FSPERM) { 4097c478bd9Sstevel@tonic-gate dcmn_err5(("%s: dv_mknod default priv mode 0%o\n", 4107c478bd9Sstevel@tonic-gate dv->dv_name, dmd->ddm_priv_mode)); 4117c478bd9Sstevel@tonic-gate dv->dv_flags |= DV_DFLT_MODE; 4127c478bd9Sstevel@tonic-gate dv->dv_dflt_mode = dmd->ddm_priv_mode & S_IAMB; 4137c478bd9Sstevel@tonic-gate } 4147c478bd9Sstevel@tonic-gate 4157c478bd9Sstevel@tonic-gate return (dv); 4167c478bd9Sstevel@tonic-gate } 4177c478bd9Sstevel@tonic-gate 4187c478bd9Sstevel@tonic-gate /* 4197c478bd9Sstevel@tonic-gate * dv_destroy 4207c478bd9Sstevel@tonic-gate * 4217c478bd9Sstevel@tonic-gate * Destroy what we created in dv_mkdir or dv_mknod. 4227c478bd9Sstevel@tonic-gate * In the case of a *referenced* directory, do nothing. 4237c478bd9Sstevel@tonic-gate */ 4247c478bd9Sstevel@tonic-gate /*ARGSUSED1*/ 4257c478bd9Sstevel@tonic-gate void 4267c478bd9Sstevel@tonic-gate dv_destroy(struct dv_node *dv, uint_t flags) 4277c478bd9Sstevel@tonic-gate { 4287c478bd9Sstevel@tonic-gate vnode_t *vp = DVTOV(dv); 4297c478bd9Sstevel@tonic-gate ASSERT(dv->dv_nlink == 0); /* no references */ 4307c478bd9Sstevel@tonic-gate ASSERT(dv->dv_next == NULL); /* unlinked from directory */ 4317c478bd9Sstevel@tonic-gate 4327c478bd9Sstevel@tonic-gate dcmn_err4(("dv_destroy: %s\n", dv->dv_name)); 4337c478bd9Sstevel@tonic-gate 4347c478bd9Sstevel@tonic-gate /* 4357c478bd9Sstevel@tonic-gate * We may be asked to unlink referenced directories. 4367c478bd9Sstevel@tonic-gate * In this case, there is nothing to be done. 4377c478bd9Sstevel@tonic-gate * The eventual memory free will be done in 4387c478bd9Sstevel@tonic-gate * devfs_inactive. 4397c478bd9Sstevel@tonic-gate */ 4407c478bd9Sstevel@tonic-gate if (vp->v_count != 0) { 4417c478bd9Sstevel@tonic-gate ASSERT(vp->v_type == VDIR); 4427c478bd9Sstevel@tonic-gate ASSERT(flags & DV_CLEAN_FORCE); 4437c478bd9Sstevel@tonic-gate ASSERT(DV_STALE(dv)); 4447c478bd9Sstevel@tonic-gate return; 4457c478bd9Sstevel@tonic-gate } 4467c478bd9Sstevel@tonic-gate 4477c478bd9Sstevel@tonic-gate if (dv->dv_attrvp != NULLVP) 4487c478bd9Sstevel@tonic-gate VN_RELE(dv->dv_attrvp); 4497c478bd9Sstevel@tonic-gate if (dv->dv_attr != NULL) 4507c478bd9Sstevel@tonic-gate kmem_free(dv->dv_attr, sizeof (struct vattr)); 4517c478bd9Sstevel@tonic-gate if (dv->dv_name != NULL) 4527c478bd9Sstevel@tonic-gate kmem_free(dv->dv_name, dv->dv_namelen + 1); 4537c478bd9Sstevel@tonic-gate if (dv->dv_devi != NULL) { 4547c478bd9Sstevel@tonic-gate ndi_rele_devi(dv->dv_devi); 4557c478bd9Sstevel@tonic-gate } 4567c478bd9Sstevel@tonic-gate if (dv->dv_priv != NULL) { 4577c478bd9Sstevel@tonic-gate dpfree(dv->dv_priv); 4587c478bd9Sstevel@tonic-gate } 4597c478bd9Sstevel@tonic-gate 4607c478bd9Sstevel@tonic-gate kmem_cache_free(dv_node_cache, dv); 4617c478bd9Sstevel@tonic-gate } 4627c478bd9Sstevel@tonic-gate 4637c478bd9Sstevel@tonic-gate /* 4647c478bd9Sstevel@tonic-gate * Find and hold dv_node by name 4657c478bd9Sstevel@tonic-gate */ 4667c478bd9Sstevel@tonic-gate struct dv_node * 4677c478bd9Sstevel@tonic-gate dv_findbyname(struct dv_node *ddv, char *nm) 4687c478bd9Sstevel@tonic-gate { 4697c478bd9Sstevel@tonic-gate struct dv_node *dv; 4707c478bd9Sstevel@tonic-gate size_t nmlen = strlen(nm); 4717c478bd9Sstevel@tonic-gate 4727c478bd9Sstevel@tonic-gate ASSERT(RW_LOCK_HELD(&ddv->dv_contents)); 4737c478bd9Sstevel@tonic-gate dcmn_err3(("dv_findbyname: %s\n", nm)); 4747c478bd9Sstevel@tonic-gate for (dv = ddv->dv_dot; dv; dv = dv->dv_next) { 4757c478bd9Sstevel@tonic-gate if (dv->dv_namelen != nmlen) 4767c478bd9Sstevel@tonic-gate continue; 4777c478bd9Sstevel@tonic-gate if (strcmp(dv->dv_name, nm) == 0) { 4787c478bd9Sstevel@tonic-gate VN_HOLD(DVTOV(dv)); 4797c478bd9Sstevel@tonic-gate return (dv); 4807c478bd9Sstevel@tonic-gate } 4817c478bd9Sstevel@tonic-gate } 4827c478bd9Sstevel@tonic-gate return (NULL); 4837c478bd9Sstevel@tonic-gate } 4847c478bd9Sstevel@tonic-gate 4857c478bd9Sstevel@tonic-gate /* 4867c478bd9Sstevel@tonic-gate * Inserts a new dv_node in a parent directory 4877c478bd9Sstevel@tonic-gate */ 4887c478bd9Sstevel@tonic-gate void 4897c478bd9Sstevel@tonic-gate dv_insert(struct dv_node *ddv, struct dv_node *dv) 4907c478bd9Sstevel@tonic-gate { 4917c478bd9Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&ddv->dv_contents)); 4927c478bd9Sstevel@tonic-gate ASSERT(DVTOV(ddv)->v_type == VDIR); 4937c478bd9Sstevel@tonic-gate ASSERT(ddv->dv_nlink >= 2); 4947c478bd9Sstevel@tonic-gate ASSERT(dv->dv_nlink == 0); 4957c478bd9Sstevel@tonic-gate 4967c478bd9Sstevel@tonic-gate dcmn_err3(("dv_insert: %s\n", dv->dv_name)); 4977c478bd9Sstevel@tonic-gate 4987c478bd9Sstevel@tonic-gate dv->dv_dotdot = ddv; 4997c478bd9Sstevel@tonic-gate dv->dv_next = ddv->dv_dot; 5007c478bd9Sstevel@tonic-gate ddv->dv_dot = dv; 5017c478bd9Sstevel@tonic-gate if (DVTOV(dv)->v_type == VDIR) { 5027c478bd9Sstevel@tonic-gate ddv->dv_nlink++; /* .. to containing directory */ 5037c478bd9Sstevel@tonic-gate dv->dv_nlink = 2; /* name + . */ 5047c478bd9Sstevel@tonic-gate } else { 5057c478bd9Sstevel@tonic-gate dv->dv_nlink = 1; /* name */ 5067c478bd9Sstevel@tonic-gate } 5077c478bd9Sstevel@tonic-gate } 5087c478bd9Sstevel@tonic-gate 5097c478bd9Sstevel@tonic-gate /* 51045a9d961Scth * Unlink a dv_node from a perent directory 51145a9d961Scth */ 51245a9d961Scth void 51345a9d961Scth dv_unlink(struct dv_node *ddv, struct dv_node *dv, struct dv_node **dv_pprev) 51445a9d961Scth { 51545a9d961Scth /* verify linkage of arguments */ 51645a9d961Scth ASSERT(ddv && dv && dv_pprev); 51745a9d961Scth ASSERT(dv->dv_dotdot == ddv); 51845a9d961Scth ASSERT(*dv_pprev == dv); 51945a9d961Scth ASSERT(RW_WRITE_HELD(&ddv->dv_contents)); 52045a9d961Scth ASSERT(DVTOV(ddv)->v_type == VDIR); 52145a9d961Scth 52245a9d961Scth dcmn_err3(("dv_unlink: %s\n", dv->dv_name)); 52345a9d961Scth 52445a9d961Scth if (DVTOV(dv)->v_type == VDIR) { 52545a9d961Scth ddv->dv_nlink--; /* .. to containing directory */ 52645a9d961Scth dv->dv_nlink -= 2; /* name + . */ 52745a9d961Scth } else { 52845a9d961Scth dv->dv_nlink -= 1; /* name */ 52945a9d961Scth } 53045a9d961Scth ASSERT(ddv->dv_nlink >= 2); 53145a9d961Scth ASSERT(dv->dv_nlink == 0); 53245a9d961Scth 53345a9d961Scth /* update ddv->dv_dot/dv_next */ 53445a9d961Scth *dv_pprev = dv->dv_next; 53545a9d961Scth 53645a9d961Scth dv->dv_dotdot = NULL; 53745a9d961Scth dv->dv_next = NULL; 53845a9d961Scth dv->dv_dot = NULL; 53945a9d961Scth } 54045a9d961Scth 54145a9d961Scth /* 5427c478bd9Sstevel@tonic-gate * Merge devfs node specific information into an attribute structure. 5437c478bd9Sstevel@tonic-gate * 5447c478bd9Sstevel@tonic-gate * NOTE: specfs provides ATIME,MTIME,CTIME,SIZE,BLKSIZE,NBLOCKS on leaf node. 5457c478bd9Sstevel@tonic-gate */ 5467c478bd9Sstevel@tonic-gate void 5477c478bd9Sstevel@tonic-gate dv_vattr_merge(struct dv_node *dv, struct vattr *vap) 5487c478bd9Sstevel@tonic-gate { 5497c478bd9Sstevel@tonic-gate struct vnode *vp = DVTOV(dv); 5507c478bd9Sstevel@tonic-gate 5517c478bd9Sstevel@tonic-gate vap->va_nodeid = dv->dv_ino; 5527c478bd9Sstevel@tonic-gate vap->va_nlink = dv->dv_nlink; 5537c478bd9Sstevel@tonic-gate 5547c478bd9Sstevel@tonic-gate if (vp->v_type == VDIR) { 5557c478bd9Sstevel@tonic-gate vap->va_rdev = 0; 5567c478bd9Sstevel@tonic-gate vap->va_fsid = vp->v_rdev; 5577c478bd9Sstevel@tonic-gate } else { 5587c478bd9Sstevel@tonic-gate vap->va_rdev = vp->v_rdev; 5597c478bd9Sstevel@tonic-gate vap->va_fsid = DVTOV(dv->dv_dotdot)->v_rdev; 5607c478bd9Sstevel@tonic-gate vap->va_type = vp->v_type; 5617c478bd9Sstevel@tonic-gate /* don't trust the shadow file type */ 5627c478bd9Sstevel@tonic-gate vap->va_mode &= ~S_IFMT; 5637c478bd9Sstevel@tonic-gate if (vap->va_type == VCHR) 5647c478bd9Sstevel@tonic-gate vap->va_mode |= S_IFCHR; 5657c478bd9Sstevel@tonic-gate else 5667c478bd9Sstevel@tonic-gate vap->va_mode |= S_IFBLK; 5677c478bd9Sstevel@tonic-gate } 5687c478bd9Sstevel@tonic-gate } 5697c478bd9Sstevel@tonic-gate 5707c478bd9Sstevel@tonic-gate /* 571*facf4a8dSllai1 * Get default device permission by consulting rules in 572*facf4a8dSllai1 * privilege specification in minor node and /etc/minor_perm. 573*facf4a8dSllai1 * 574*facf4a8dSllai1 * This function is called from the devname filesystem to get default 575*facf4a8dSllai1 * permissions for a device exported to a non-global zone. 576*facf4a8dSllai1 */ 577*facf4a8dSllai1 void 578*facf4a8dSllai1 devfs_get_defattr(struct vnode *vp, struct vattr *vap, int *no_fs_perm) 579*facf4a8dSllai1 { 580*facf4a8dSllai1 mperm_t mp; 581*facf4a8dSllai1 struct dv_node *dv; 582*facf4a8dSllai1 583*facf4a8dSllai1 /* If vp isn't a dv_node, return something sensible */ 584*facf4a8dSllai1 if (!vn_matchops(vp, dv_vnodeops)) { 585*facf4a8dSllai1 if (no_fs_perm) 586*facf4a8dSllai1 *no_fs_perm = 0; 587*facf4a8dSllai1 *vap = dv_vattr_file; 588*facf4a8dSllai1 return; 589*facf4a8dSllai1 } 590*facf4a8dSllai1 591*facf4a8dSllai1 /* 592*facf4a8dSllai1 * For minors not created by ddi_create_priv_minor_node(), 593*facf4a8dSllai1 * use devfs defaults. 594*facf4a8dSllai1 */ 595*facf4a8dSllai1 dv = VTODV(vp); 596*facf4a8dSllai1 if (vp->v_type == VDIR) { 597*facf4a8dSllai1 *vap = dv_vattr_dir; 598*facf4a8dSllai1 } else if (dv->dv_flags & DV_NO_FSPERM) { 599*facf4a8dSllai1 if (no_fs_perm) 600*facf4a8dSllai1 *no_fs_perm = 1; 601*facf4a8dSllai1 *vap = dv_vattr_priv; 602*facf4a8dSllai1 } else { 603*facf4a8dSllai1 /* 604*facf4a8dSllai1 * look up perm bits from minor_perm 605*facf4a8dSllai1 */ 606*facf4a8dSllai1 *vap = dv_vattr_file; 607*facf4a8dSllai1 if (dev_minorperm(dv->dv_devi, dv->dv_name, &mp) == 0) { 608*facf4a8dSllai1 VATTR_MP_MERGE((*vap), mp); 609*facf4a8dSllai1 dcmn_err5(("%s: minor perm mode 0%o\n", 610*facf4a8dSllai1 dv->dv_name, vap->va_mode)); 611*facf4a8dSllai1 } else if (dv->dv_flags & DV_DFLT_MODE) { 612*facf4a8dSllai1 ASSERT((dv->dv_dflt_mode & ~S_IAMB) == 0); 613*facf4a8dSllai1 vap->va_mode &= ~S_IAMB; 614*facf4a8dSllai1 vap->va_mode |= dv->dv_dflt_mode; 615*facf4a8dSllai1 dcmn_err5(("%s: priv mode 0%o\n", 616*facf4a8dSllai1 dv->dv_name, vap->va_mode)); 617*facf4a8dSllai1 } 618*facf4a8dSllai1 } 619*facf4a8dSllai1 } 620*facf4a8dSllai1 621*facf4a8dSllai1 /* 6227c478bd9Sstevel@tonic-gate * dv_shadow_node 6237c478bd9Sstevel@tonic-gate * 6247c478bd9Sstevel@tonic-gate * Given a VDIR dv_node, find/create the associated VDIR 6257c478bd9Sstevel@tonic-gate * node in the shadow attribute filesystem. 6267c478bd9Sstevel@tonic-gate * 6277c478bd9Sstevel@tonic-gate * Given a VCHR/VBLK dv_node, find the associated VREG 6287c478bd9Sstevel@tonic-gate * node in the shadow attribute filesystem. These nodes 6297c478bd9Sstevel@tonic-gate * are only created to persist non-default attributes. 6307c478bd9Sstevel@tonic-gate * Lack of such a node implies the default permissions 6317c478bd9Sstevel@tonic-gate * are sufficient. 6327c478bd9Sstevel@tonic-gate * 6337c478bd9Sstevel@tonic-gate * Managing the attribute file entries is slightly tricky (mostly 6347c478bd9Sstevel@tonic-gate * because we can't intercept VN_HOLD and VN_RELE except on the last 6357c478bd9Sstevel@tonic-gate * release). 6367c478bd9Sstevel@tonic-gate * 6377c478bd9Sstevel@tonic-gate * We assert that if the dv_attrvp pointer is non-NULL, it points 6387c478bd9Sstevel@tonic-gate * to a singly-held (by us) vnode that represents the shadow entry 6397c478bd9Sstevel@tonic-gate * in the underlying filesystem. To avoid store-ordering issues, 6407c478bd9Sstevel@tonic-gate * we assert that the pointer can only be tested under the dv_contents 6417c478bd9Sstevel@tonic-gate * READERS lock. 6427c478bd9Sstevel@tonic-gate */ 6437c478bd9Sstevel@tonic-gate 6447c478bd9Sstevel@tonic-gate void 6457c478bd9Sstevel@tonic-gate dv_shadow_node( 6467c478bd9Sstevel@tonic-gate struct vnode *dvp, /* devfs parent directory vnode */ 6477c478bd9Sstevel@tonic-gate char *nm, /* name component */ 6487c478bd9Sstevel@tonic-gate struct vnode *vp, /* devfs vnode */ 6497c478bd9Sstevel@tonic-gate struct pathname *pnp, /* the path .. */ 6507c478bd9Sstevel@tonic-gate struct vnode *rdir, /* the root .. */ 6517c478bd9Sstevel@tonic-gate struct cred *cred, /* who's asking? */ 6527c478bd9Sstevel@tonic-gate int flags) /* optionally create shadow node */ 6537c478bd9Sstevel@tonic-gate { 6547c478bd9Sstevel@tonic-gate struct dv_node *dv; /* dv_node of named directory */ 6557c478bd9Sstevel@tonic-gate struct vnode *rdvp; /* shadow parent directory vnode */ 6567c478bd9Sstevel@tonic-gate struct vnode *rvp; /* shadow vnode */ 6577c478bd9Sstevel@tonic-gate struct vnode *rrvp; /* realvp of shadow vnode */ 6587c478bd9Sstevel@tonic-gate struct vattr vattr; 6597c478bd9Sstevel@tonic-gate int create_tried; 6607c478bd9Sstevel@tonic-gate int error; 6617c478bd9Sstevel@tonic-gate 6627c478bd9Sstevel@tonic-gate ASSERT(vp->v_type == VDIR || vp->v_type == VCHR || vp->v_type == VBLK); 6637c478bd9Sstevel@tonic-gate dv = VTODV(vp); 6647c478bd9Sstevel@tonic-gate dcmn_err3(("dv_shadow_node: name %s attr %p\n", 6657c478bd9Sstevel@tonic-gate nm, (void *)dv->dv_attrvp)); 6667c478bd9Sstevel@tonic-gate 6677c478bd9Sstevel@tonic-gate if ((flags & DV_SHADOW_WRITE_HELD) == 0) { 6687c478bd9Sstevel@tonic-gate ASSERT(RW_READ_HELD(&dv->dv_contents)); 6697c478bd9Sstevel@tonic-gate if (dv->dv_attrvp != NULLVP) 6707c478bd9Sstevel@tonic-gate return; 6717c478bd9Sstevel@tonic-gate if (!rw_tryupgrade(&dv->dv_contents)) { 6727c478bd9Sstevel@tonic-gate rw_exit(&dv->dv_contents); 6737c478bd9Sstevel@tonic-gate rw_enter(&dv->dv_contents, RW_WRITER); 6747c478bd9Sstevel@tonic-gate if (dv->dv_attrvp != NULLVP) { 6757c478bd9Sstevel@tonic-gate rw_downgrade(&dv->dv_contents); 6767c478bd9Sstevel@tonic-gate return; 6777c478bd9Sstevel@tonic-gate } 6787c478bd9Sstevel@tonic-gate } 6797c478bd9Sstevel@tonic-gate } else { 6807c478bd9Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&dv->dv_contents)); 6817c478bd9Sstevel@tonic-gate if (dv->dv_attrvp != NULLVP) 6827c478bd9Sstevel@tonic-gate return; 6837c478bd9Sstevel@tonic-gate } 6847c478bd9Sstevel@tonic-gate 6857c478bd9Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&dv->dv_contents) && dv->dv_attrvp == NULL); 6867c478bd9Sstevel@tonic-gate 6877c478bd9Sstevel@tonic-gate rdvp = VTODV(dvp)->dv_attrvp; 6887c478bd9Sstevel@tonic-gate create_tried = 0; 6897c478bd9Sstevel@tonic-gate lookup: 6907c478bd9Sstevel@tonic-gate if (rdvp && (dv->dv_flags & DV_NO_FSPERM) == 0) { 6917c478bd9Sstevel@tonic-gate error = VOP_LOOKUP(rdvp, nm, &rvp, pnp, LOOKUP_DIR, rdir, cred); 6927c478bd9Sstevel@tonic-gate 6937c478bd9Sstevel@tonic-gate /* factor out the snode since we only want the attribute node */ 6947c478bd9Sstevel@tonic-gate if ((error == 0) && (VOP_REALVP(rvp, &rrvp) == 0)) { 6957c478bd9Sstevel@tonic-gate VN_HOLD(rrvp); 6967c478bd9Sstevel@tonic-gate VN_RELE(rvp); 6977c478bd9Sstevel@tonic-gate rvp = rrvp; 6987c478bd9Sstevel@tonic-gate } 6997c478bd9Sstevel@tonic-gate } else 7007c478bd9Sstevel@tonic-gate error = EROFS; /* no parent, no entry */ 7017c478bd9Sstevel@tonic-gate 7027c478bd9Sstevel@tonic-gate /* 7037c478bd9Sstevel@tonic-gate * All we want is the permissions (and maybe ACLs and 7047c478bd9Sstevel@tonic-gate * extended attributes), and we want to perform lookups 7057c478bd9Sstevel@tonic-gate * by name. Drivers occasionally change their minor 7067c478bd9Sstevel@tonic-gate * number space. If something changes, there's no 7077c478bd9Sstevel@tonic-gate * much we can do about it here. 7087c478bd9Sstevel@tonic-gate */ 7097c478bd9Sstevel@tonic-gate 7107c478bd9Sstevel@tonic-gate /* The shadow node checks out. We are done */ 7117c478bd9Sstevel@tonic-gate if (error == 0) { 7127c478bd9Sstevel@tonic-gate dv->dv_attrvp = rvp; /* with one hold */ 7137c478bd9Sstevel@tonic-gate 7147c478bd9Sstevel@tonic-gate /* 715fa9e4066Sahrens * Determine if we have non-trivial ACLs on this node. 716fa9e4066Sahrens * It is not necessary to VOP_RWLOCK since fs_acl_nontrivial 717fa9e4066Sahrens * only does VOP_GETSECATTR. 7187c478bd9Sstevel@tonic-gate */ 7197c478bd9Sstevel@tonic-gate dv->dv_flags &= ~DV_ACL; 720fa9e4066Sahrens 721fa9e4066Sahrens if (fs_acl_nontrivial(rvp, cred)) 722fa9e4066Sahrens dv->dv_flags |= DV_ACL; 7237c478bd9Sstevel@tonic-gate 7247c478bd9Sstevel@tonic-gate /* 7257c478bd9Sstevel@tonic-gate * If we have synced out the memory attributes, free 7267c478bd9Sstevel@tonic-gate * them and switch back to using the persistent store. 7277c478bd9Sstevel@tonic-gate */ 7287c478bd9Sstevel@tonic-gate if (rvp && dv->dv_attr) { 7297c478bd9Sstevel@tonic-gate kmem_free(dv->dv_attr, sizeof (struct vattr)); 7307c478bd9Sstevel@tonic-gate dv->dv_attr = NULL; 7317c478bd9Sstevel@tonic-gate } 7327c478bd9Sstevel@tonic-gate if ((flags & DV_SHADOW_WRITE_HELD) == 0) 7337c478bd9Sstevel@tonic-gate rw_downgrade(&dv->dv_contents); 7347c478bd9Sstevel@tonic-gate ASSERT(RW_LOCK_HELD(&dv->dv_contents)); 7357c478bd9Sstevel@tonic-gate return; 7367c478bd9Sstevel@tonic-gate } 7377c478bd9Sstevel@tonic-gate 7387c478bd9Sstevel@tonic-gate /* 7397c478bd9Sstevel@tonic-gate * Failed to find attribute in persistent backing store, 740*facf4a8dSllai1 * get default permission bits. 7417c478bd9Sstevel@tonic-gate */ 742*facf4a8dSllai1 devfs_get_defattr(vp, &vattr, NULL); 7437c478bd9Sstevel@tonic-gate 7447c478bd9Sstevel@tonic-gate dv_vattr_merge(dv, &vattr); 7457c478bd9Sstevel@tonic-gate gethrestime(&vattr.va_atime); 7467c478bd9Sstevel@tonic-gate vattr.va_mtime = vattr.va_atime; 7477c478bd9Sstevel@tonic-gate vattr.va_ctime = vattr.va_atime; 7487c478bd9Sstevel@tonic-gate 7497c478bd9Sstevel@tonic-gate /* 7507c478bd9Sstevel@tonic-gate * Try to create shadow dir. This is necessary in case 7517c478bd9Sstevel@tonic-gate * we need to create a shadow leaf node later, when user 7527c478bd9Sstevel@tonic-gate * executes chmod. 7537c478bd9Sstevel@tonic-gate */ 7547c478bd9Sstevel@tonic-gate if ((error == ENOENT) && !create_tried) { 7557c478bd9Sstevel@tonic-gate switch (vp->v_type) { 7567c478bd9Sstevel@tonic-gate case VDIR: 7577c478bd9Sstevel@tonic-gate error = VOP_MKDIR(rdvp, nm, &vattr, &rvp, kcred); 7587c478bd9Sstevel@tonic-gate dsysdebug(error, ("vop_mkdir %s %s %d\n", 7597c478bd9Sstevel@tonic-gate VTODV(dvp)->dv_name, nm, error)); 7607c478bd9Sstevel@tonic-gate create_tried = 1; 7617c478bd9Sstevel@tonic-gate break; 7627c478bd9Sstevel@tonic-gate 7637c478bd9Sstevel@tonic-gate case VCHR: 7647c478bd9Sstevel@tonic-gate case VBLK: 7657c478bd9Sstevel@tonic-gate /* 7667c478bd9Sstevel@tonic-gate * Shadow nodes are only created on demand 7677c478bd9Sstevel@tonic-gate */ 7687c478bd9Sstevel@tonic-gate if (flags & DV_SHADOW_CREATE) { 7697c478bd9Sstevel@tonic-gate error = VOP_CREATE(rdvp, nm, &vattr, NONEXCL, 7707c478bd9Sstevel@tonic-gate VREAD|VWRITE, &rvp, kcred, 0); 7717c478bd9Sstevel@tonic-gate dsysdebug(error, ("vop_create %s %s %d\n", 7727c478bd9Sstevel@tonic-gate VTODV(dvp)->dv_name, nm, error)); 7737c478bd9Sstevel@tonic-gate create_tried = 1; 7747c478bd9Sstevel@tonic-gate } 7757c478bd9Sstevel@tonic-gate break; 7767c478bd9Sstevel@tonic-gate 7777c478bd9Sstevel@tonic-gate default: 7787c478bd9Sstevel@tonic-gate cmn_err(CE_PANIC, "devfs: %s: create", dvnm); 7797c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 7807c478bd9Sstevel@tonic-gate } 7817c478bd9Sstevel@tonic-gate 7827c478bd9Sstevel@tonic-gate if (create_tried && 7837c478bd9Sstevel@tonic-gate (error == 0) || (error == EEXIST)) { 7847c478bd9Sstevel@tonic-gate VN_RELE(rvp); 7857c478bd9Sstevel@tonic-gate goto lookup; 7867c478bd9Sstevel@tonic-gate } 7877c478bd9Sstevel@tonic-gate } 7887c478bd9Sstevel@tonic-gate 7897c478bd9Sstevel@tonic-gate /* Store attribute in memory */ 7907c478bd9Sstevel@tonic-gate if (dv->dv_attr == NULL) { 7917c478bd9Sstevel@tonic-gate dv->dv_attr = kmem_alloc(sizeof (struct vattr), KM_SLEEP); 7927c478bd9Sstevel@tonic-gate *(dv->dv_attr) = vattr; 7937c478bd9Sstevel@tonic-gate } 7947c478bd9Sstevel@tonic-gate 7957c478bd9Sstevel@tonic-gate if ((flags & DV_SHADOW_WRITE_HELD) == 0) 7967c478bd9Sstevel@tonic-gate rw_downgrade(&dv->dv_contents); 7977c478bd9Sstevel@tonic-gate ASSERT(RW_LOCK_HELD(&dv->dv_contents)); 7987c478bd9Sstevel@tonic-gate } 7997c478bd9Sstevel@tonic-gate 8007c478bd9Sstevel@tonic-gate /* 8017c478bd9Sstevel@tonic-gate * Given a devinfo node, and a name, returns the appropriate 8027c478bd9Sstevel@tonic-gate * minor information for that named node, if it exists. 8037c478bd9Sstevel@tonic-gate */ 8047c478bd9Sstevel@tonic-gate static int 8057c478bd9Sstevel@tonic-gate dv_find_leafnode(dev_info_t *devi, char *minor_nm, struct ddi_minor_data *r_mi) 8067c478bd9Sstevel@tonic-gate { 8077c478bd9Sstevel@tonic-gate struct ddi_minor_data *dmd; 8087c478bd9Sstevel@tonic-gate 809737d277aScth ASSERT(i_ddi_devi_attached(devi)); 8107c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&DEVI(devi)->devi_lock)); 8117c478bd9Sstevel@tonic-gate 8127c478bd9Sstevel@tonic-gate dcmn_err3(("dv_find_leafnode: %s\n", minor_nm)); 8137c478bd9Sstevel@tonic-gate for (dmd = DEVI(devi)->devi_minor; dmd; dmd = dmd->next) { 8147c478bd9Sstevel@tonic-gate 8157c478bd9Sstevel@tonic-gate /* 8167c478bd9Sstevel@tonic-gate * Skip alias nodes and nodes without a name. 8177c478bd9Sstevel@tonic-gate */ 8187c478bd9Sstevel@tonic-gate if ((dmd->type == DDM_ALIAS) || (dmd->ddm_name == NULL)) 8197c478bd9Sstevel@tonic-gate continue; 8207c478bd9Sstevel@tonic-gate 8217c478bd9Sstevel@tonic-gate dcmn_err4(("dv_find_leafnode: (%s,%s)\n", 8227c478bd9Sstevel@tonic-gate minor_nm, dmd->ddm_name)); 8237c478bd9Sstevel@tonic-gate if (strcmp(minor_nm, dmd->ddm_name) == 0) { 8247c478bd9Sstevel@tonic-gate r_mi->ddm_dev = dmd->ddm_dev; 8257c478bd9Sstevel@tonic-gate r_mi->ddm_spec_type = dmd->ddm_spec_type; 8267c478bd9Sstevel@tonic-gate r_mi->type = dmd->type; 8277c478bd9Sstevel@tonic-gate r_mi->ddm_flags = dmd->ddm_flags; 8287c478bd9Sstevel@tonic-gate r_mi->ddm_node_priv = dmd->ddm_node_priv; 8297c478bd9Sstevel@tonic-gate r_mi->ddm_priv_mode = dmd->ddm_priv_mode; 8307c478bd9Sstevel@tonic-gate if (r_mi->ddm_node_priv) 8317c478bd9Sstevel@tonic-gate dphold(r_mi->ddm_node_priv); 8327c478bd9Sstevel@tonic-gate return (0); 8337c478bd9Sstevel@tonic-gate } 8347c478bd9Sstevel@tonic-gate } 8357c478bd9Sstevel@tonic-gate 8367c478bd9Sstevel@tonic-gate dcmn_err3(("dv_find_leafnode: %s: ENOENT\n", minor_nm)); 8377c478bd9Sstevel@tonic-gate return (ENOENT); 8387c478bd9Sstevel@tonic-gate } 8397c478bd9Sstevel@tonic-gate 8407c478bd9Sstevel@tonic-gate /* 8417c478bd9Sstevel@tonic-gate * Special handling for clone node: 8427c478bd9Sstevel@tonic-gate * Clone minor name is a driver name, the minor number will 8437c478bd9Sstevel@tonic-gate * be the major number of the driver. There is no minor 8447c478bd9Sstevel@tonic-gate * node under the clone driver, so we'll manufacture the 8457c478bd9Sstevel@tonic-gate * dev_t. 8467c478bd9Sstevel@tonic-gate */ 8477c478bd9Sstevel@tonic-gate static struct dv_node * 8487c478bd9Sstevel@tonic-gate dv_clone_mknod(struct dv_node *ddv, char *drvname) 8497c478bd9Sstevel@tonic-gate { 8507c478bd9Sstevel@tonic-gate major_t major; 8517c478bd9Sstevel@tonic-gate struct dv_node *dvp; 8527c478bd9Sstevel@tonic-gate char *devnm; 8537c478bd9Sstevel@tonic-gate struct ddi_minor_data *dmd; 8547c478bd9Sstevel@tonic-gate 8557c478bd9Sstevel@tonic-gate /* 8567c478bd9Sstevel@tonic-gate * Make sure drvname is a STREAMS driver. We load the driver, 8577c478bd9Sstevel@tonic-gate * but don't attach to any instances. This makes stat(2) 8587c478bd9Sstevel@tonic-gate * relatively cheap. 8597c478bd9Sstevel@tonic-gate */ 8607c478bd9Sstevel@tonic-gate major = ddi_name_to_major(drvname); 8617c478bd9Sstevel@tonic-gate if (major == (major_t)-1) 8627c478bd9Sstevel@tonic-gate return (NULL); 8637c478bd9Sstevel@tonic-gate 8647c478bd9Sstevel@tonic-gate if (ddi_hold_driver(major) == NULL) 8657c478bd9Sstevel@tonic-gate return (NULL); 8667c478bd9Sstevel@tonic-gate 8677c478bd9Sstevel@tonic-gate if (STREAMSTAB(major) == NULL) { 8687c478bd9Sstevel@tonic-gate ddi_rele_driver(major); 8697c478bd9Sstevel@tonic-gate return (NULL); 8707c478bd9Sstevel@tonic-gate } 8717c478bd9Sstevel@tonic-gate 8727c478bd9Sstevel@tonic-gate ddi_rele_driver(major); 8737c478bd9Sstevel@tonic-gate devnm = kmem_alloc(MAXNAMELEN, KM_SLEEP); 8747c478bd9Sstevel@tonic-gate (void) snprintf(devnm, MAXNAMELEN, "clone@0:%s", drvname); 8757c478bd9Sstevel@tonic-gate dmd = kmem_zalloc(sizeof (*dmd), KM_SLEEP); 8767c478bd9Sstevel@tonic-gate dmd->ddm_dev = makedevice(clone_major, (minor_t)major); 8777c478bd9Sstevel@tonic-gate dmd->ddm_spec_type = S_IFCHR; 8787c478bd9Sstevel@tonic-gate dvp = dv_mknod(ddv, clone_dip, devnm, dmd); 8797c478bd9Sstevel@tonic-gate kmem_free(dmd, sizeof (*dmd)); 8807c478bd9Sstevel@tonic-gate kmem_free(devnm, MAXNAMELEN); 8817c478bd9Sstevel@tonic-gate return (dvp); 8827c478bd9Sstevel@tonic-gate } 8837c478bd9Sstevel@tonic-gate 8847c478bd9Sstevel@tonic-gate /* 8857c478bd9Sstevel@tonic-gate * Given the parent directory node, and a name in it, returns the 8867c478bd9Sstevel@tonic-gate * named dv_node to the caller (as a vnode). 8877c478bd9Sstevel@tonic-gate * 8887c478bd9Sstevel@tonic-gate * (We need pnp and rdir for doing shadow lookups; they can be NULL) 8897c478bd9Sstevel@tonic-gate */ 8907c478bd9Sstevel@tonic-gate int 8917c478bd9Sstevel@tonic-gate dv_find(struct dv_node *ddv, char *nm, struct vnode **vpp, struct pathname *pnp, 8927c478bd9Sstevel@tonic-gate struct vnode *rdir, struct cred *cred, uint_t ndi_flags) 8937c478bd9Sstevel@tonic-gate { 8947c478bd9Sstevel@tonic-gate extern int isminiroot; /* see modctl.c */ 8957c478bd9Sstevel@tonic-gate 8967c478bd9Sstevel@tonic-gate int rv = 0, was_busy = 0, nmlen; 8977c478bd9Sstevel@tonic-gate struct vnode *vp; 8987c478bd9Sstevel@tonic-gate struct dv_node *dv, *dup; 8997c478bd9Sstevel@tonic-gate dev_info_t *pdevi, *devi = NULL; 9007c478bd9Sstevel@tonic-gate char *mnm; 9017c478bd9Sstevel@tonic-gate struct ddi_minor_data *dmd; 9027c478bd9Sstevel@tonic-gate 9037c478bd9Sstevel@tonic-gate dcmn_err3(("dv_find %s\n", nm)); 9047c478bd9Sstevel@tonic-gate 9057c478bd9Sstevel@tonic-gate rw_enter(&ddv->dv_contents, RW_READER); 9067c478bd9Sstevel@tonic-gate start: 9077c478bd9Sstevel@tonic-gate if (DV_STALE(ddv)) { 9087c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 9097c478bd9Sstevel@tonic-gate return (ESTALE); 9107c478bd9Sstevel@tonic-gate } 9117c478bd9Sstevel@tonic-gate 9127c478bd9Sstevel@tonic-gate /* 9137c478bd9Sstevel@tonic-gate * Empty name or ., return node itself. 9147c478bd9Sstevel@tonic-gate */ 9157c478bd9Sstevel@tonic-gate nmlen = strlen(nm); 9167c478bd9Sstevel@tonic-gate if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) { 9177c478bd9Sstevel@tonic-gate *vpp = DVTOV(ddv); 9187c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 9197c478bd9Sstevel@tonic-gate VN_HOLD(*vpp); 9207c478bd9Sstevel@tonic-gate return (0); 9217c478bd9Sstevel@tonic-gate } 9227c478bd9Sstevel@tonic-gate 9237c478bd9Sstevel@tonic-gate /* 9247c478bd9Sstevel@tonic-gate * .., return the parent directory 9257c478bd9Sstevel@tonic-gate */ 9267c478bd9Sstevel@tonic-gate if ((nmlen == 2) && (strcmp(nm, "..") == 0)) { 9277c478bd9Sstevel@tonic-gate *vpp = DVTOV(ddv->dv_dotdot); 9287c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 9297c478bd9Sstevel@tonic-gate VN_HOLD(*vpp); 9307c478bd9Sstevel@tonic-gate return (0); 9317c478bd9Sstevel@tonic-gate } 9327c478bd9Sstevel@tonic-gate 9337c478bd9Sstevel@tonic-gate /* 9347c478bd9Sstevel@tonic-gate * Fail anything without a valid device name component 9357c478bd9Sstevel@tonic-gate */ 9367c478bd9Sstevel@tonic-gate if (nm[0] == '@' || nm[0] == ':') { 9377c478bd9Sstevel@tonic-gate dcmn_err3(("devfs: no driver '%s'\n", nm)); 9387c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 9397c478bd9Sstevel@tonic-gate return (ENOENT); 9407c478bd9Sstevel@tonic-gate } 9417c478bd9Sstevel@tonic-gate 9427c478bd9Sstevel@tonic-gate /* 9437c478bd9Sstevel@tonic-gate * So, now we have to deal with the trickier stuff. 9447c478bd9Sstevel@tonic-gate * 9457c478bd9Sstevel@tonic-gate * (a) search the existing list of dv_nodes on this directory 9467c478bd9Sstevel@tonic-gate */ 9477c478bd9Sstevel@tonic-gate if ((dv = dv_findbyname(ddv, nm)) != NULL) { 9487c478bd9Sstevel@tonic-gate founddv: 9497c478bd9Sstevel@tonic-gate ASSERT(RW_LOCK_HELD(&ddv->dv_contents)); 9507c478bd9Sstevel@tonic-gate rw_enter(&dv->dv_contents, RW_READER); 9517c478bd9Sstevel@tonic-gate vp = DVTOV(dv); 9527c478bd9Sstevel@tonic-gate if ((dv->dv_attrvp != NULLVP) || 9537c478bd9Sstevel@tonic-gate (vp->v_type != VDIR && dv->dv_attr != NULL)) { 9547c478bd9Sstevel@tonic-gate /* 9557c478bd9Sstevel@tonic-gate * Common case - we already have attributes 9567c478bd9Sstevel@tonic-gate */ 9577c478bd9Sstevel@tonic-gate rw_exit(&dv->dv_contents); 9587c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 9597c478bd9Sstevel@tonic-gate goto found; 9607c478bd9Sstevel@tonic-gate } 9617c478bd9Sstevel@tonic-gate 9627c478bd9Sstevel@tonic-gate /* 9637c478bd9Sstevel@tonic-gate * No attribute vp, try and build one. 9647c478bd9Sstevel@tonic-gate */ 9657c478bd9Sstevel@tonic-gate dv_shadow_node(DVTOV(ddv), nm, vp, pnp, rdir, cred, 0); 9667c478bd9Sstevel@tonic-gate rw_exit(&dv->dv_contents); 9677c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 9687c478bd9Sstevel@tonic-gate goto found; 9697c478bd9Sstevel@tonic-gate } 9707c478bd9Sstevel@tonic-gate 9717c478bd9Sstevel@tonic-gate /* 9727c478bd9Sstevel@tonic-gate * (b) Search the child devinfo nodes of our parent directory, 9737c478bd9Sstevel@tonic-gate * looking for the named node. If we find it, build a new 9747c478bd9Sstevel@tonic-gate * node, then grab the writers lock, search the directory 9757c478bd9Sstevel@tonic-gate * if it's still not there, then insert it. 9767c478bd9Sstevel@tonic-gate * 9777c478bd9Sstevel@tonic-gate * We drop the devfs locks before accessing the device tree. 9787c478bd9Sstevel@tonic-gate * Take care to mark the node BUSY so that a forced devfs_clean 9797c478bd9Sstevel@tonic-gate * doesn't mark the directory node stale. 9807c478bd9Sstevel@tonic-gate * 9817c478bd9Sstevel@tonic-gate * Also, check if we are called as part of devfs_clean or 9827c478bd9Sstevel@tonic-gate * reset_perm. If so, simply return not found because there 9837c478bd9Sstevel@tonic-gate * is nothing to clean. 9847c478bd9Sstevel@tonic-gate */ 9857c478bd9Sstevel@tonic-gate if (tsd_get(devfs_clean_key)) { 9867c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 9877c478bd9Sstevel@tonic-gate return (ENOENT); 9887c478bd9Sstevel@tonic-gate } 9897c478bd9Sstevel@tonic-gate 9907c478bd9Sstevel@tonic-gate /* 9917c478bd9Sstevel@tonic-gate * We could be either READ or WRITE locked at 9927c478bd9Sstevel@tonic-gate * this point. Upgrade if we are read locked. 9937c478bd9Sstevel@tonic-gate */ 9947c478bd9Sstevel@tonic-gate ASSERT(RW_LOCK_HELD(&ddv->dv_contents)); 9957c478bd9Sstevel@tonic-gate if (rw_read_locked(&ddv->dv_contents) && 9967c478bd9Sstevel@tonic-gate !rw_tryupgrade(&ddv->dv_contents)) { 9977c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 9987c478bd9Sstevel@tonic-gate rw_enter(&ddv->dv_contents, RW_WRITER); 9997c478bd9Sstevel@tonic-gate /* 10007c478bd9Sstevel@tonic-gate * Things may have changed when we dropped 10017c478bd9Sstevel@tonic-gate * the contents lock, so start from top again 10027c478bd9Sstevel@tonic-gate */ 10037c478bd9Sstevel@tonic-gate goto start; 10047c478bd9Sstevel@tonic-gate } 10057c478bd9Sstevel@tonic-gate ddv->dv_busy++; /* mark busy before dropping lock */ 10067c478bd9Sstevel@tonic-gate was_busy++; 10077c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 10087c478bd9Sstevel@tonic-gate 10097c478bd9Sstevel@tonic-gate pdevi = ddv->dv_devi; 10107c478bd9Sstevel@tonic-gate ASSERT(pdevi != NULL); 10117c478bd9Sstevel@tonic-gate 10127c478bd9Sstevel@tonic-gate mnm = strchr(nm, ':'); 10137c478bd9Sstevel@tonic-gate if (mnm) 10147c478bd9Sstevel@tonic-gate *mnm = (char)0; 10157c478bd9Sstevel@tonic-gate 10167c478bd9Sstevel@tonic-gate /* 10177c478bd9Sstevel@tonic-gate * Configure one nexus child, will call nexus's bus_ops 10187c478bd9Sstevel@tonic-gate * If successful, devi is held upon returning. 10197c478bd9Sstevel@tonic-gate * Note: devfs lookup should not be configuring grandchildren. 10207c478bd9Sstevel@tonic-gate */ 10217c478bd9Sstevel@tonic-gate ASSERT((ndi_flags & NDI_CONFIG) == 0); 10227c478bd9Sstevel@tonic-gate 10237c478bd9Sstevel@tonic-gate rv = ndi_devi_config_one(pdevi, nm, &devi, ndi_flags | NDI_NO_EVENT); 10247c478bd9Sstevel@tonic-gate if (mnm) 10257c478bd9Sstevel@tonic-gate *mnm = ':'; 10267c478bd9Sstevel@tonic-gate if (rv != NDI_SUCCESS) { 10277c478bd9Sstevel@tonic-gate rv = ENOENT; 10287c478bd9Sstevel@tonic-gate goto notfound; 10297c478bd9Sstevel@tonic-gate } 10307c478bd9Sstevel@tonic-gate 10317c478bd9Sstevel@tonic-gate /* 10327c478bd9Sstevel@tonic-gate * Don't make vhci clients visible under phci, unless we 10337c478bd9Sstevel@tonic-gate * are in miniroot. 10347c478bd9Sstevel@tonic-gate */ 10357c478bd9Sstevel@tonic-gate if (isminiroot == 0 && ddi_get_parent(devi) != pdevi) { 10367c478bd9Sstevel@tonic-gate ndi_rele_devi(devi); 10377c478bd9Sstevel@tonic-gate rv = ENOENT; 10387c478bd9Sstevel@tonic-gate goto notfound; 10397c478bd9Sstevel@tonic-gate } 10407c478bd9Sstevel@tonic-gate 1041737d277aScth ASSERT(devi && i_ddi_devi_attached(devi)); 10427c478bd9Sstevel@tonic-gate 10437c478bd9Sstevel@tonic-gate /* 10447c478bd9Sstevel@tonic-gate * Invalidate cache to notice newly created minor nodes. 10457c478bd9Sstevel@tonic-gate */ 10467c478bd9Sstevel@tonic-gate rw_enter(&ddv->dv_contents, RW_WRITER); 10477c478bd9Sstevel@tonic-gate ddv->dv_flags |= DV_BUILD; 10487c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 10497c478bd9Sstevel@tonic-gate 10507c478bd9Sstevel@tonic-gate /* 10517c478bd9Sstevel@tonic-gate * mkdir for nexus drivers and leaf nodes as well. If we are racing 10527c478bd9Sstevel@tonic-gate * and create a duplicate, the duplicate will be destroyed below. 10537c478bd9Sstevel@tonic-gate */ 10547c478bd9Sstevel@tonic-gate if (mnm == NULL) { 10557c478bd9Sstevel@tonic-gate dv = dv_mkdir(ddv, devi, nm); 10567c478bd9Sstevel@tonic-gate } else { 10577c478bd9Sstevel@tonic-gate /* 10587c478bd9Sstevel@tonic-gate * For clone minors, load the driver indicated by minor name. 10597c478bd9Sstevel@tonic-gate */ 10607c478bd9Sstevel@tonic-gate mutex_enter(&DEVI(devi)->devi_lock); 10617c478bd9Sstevel@tonic-gate if (devi == clone_dip) { 10627c478bd9Sstevel@tonic-gate dv = dv_clone_mknod(ddv, mnm + 1); 10637c478bd9Sstevel@tonic-gate } else { 10647c478bd9Sstevel@tonic-gate /* 10657c478bd9Sstevel@tonic-gate * Find minor node and make a dv_node 10667c478bd9Sstevel@tonic-gate */ 10677c478bd9Sstevel@tonic-gate dmd = kmem_zalloc(sizeof (*dmd), KM_SLEEP); 10687c478bd9Sstevel@tonic-gate if (dv_find_leafnode(devi, mnm + 1, dmd) == 0) { 10697c478bd9Sstevel@tonic-gate dv = dv_mknod(ddv, devi, nm, dmd); 10707c478bd9Sstevel@tonic-gate if (dmd->ddm_node_priv) 10717c478bd9Sstevel@tonic-gate dpfree(dmd->ddm_node_priv); 10727c478bd9Sstevel@tonic-gate } 10737c478bd9Sstevel@tonic-gate kmem_free(dmd, sizeof (*dmd)); 10747c478bd9Sstevel@tonic-gate } 10757c478bd9Sstevel@tonic-gate mutex_exit(&DEVI(devi)->devi_lock); 10767c478bd9Sstevel@tonic-gate } 10777c478bd9Sstevel@tonic-gate /* 10787c478bd9Sstevel@tonic-gate * Release hold from ndi_devi_config_one() 10797c478bd9Sstevel@tonic-gate */ 10807c478bd9Sstevel@tonic-gate ndi_rele_devi(devi); 10817c478bd9Sstevel@tonic-gate 10827c478bd9Sstevel@tonic-gate if (dv == NULL) { 10837c478bd9Sstevel@tonic-gate rv = ENOENT; 10847c478bd9Sstevel@tonic-gate goto notfound; 10857c478bd9Sstevel@tonic-gate } 10867c478bd9Sstevel@tonic-gate 10877c478bd9Sstevel@tonic-gate /* 10887c478bd9Sstevel@tonic-gate * We have released the dv_contents lock, need to check 10897c478bd9Sstevel@tonic-gate * if another thread already created a duplicate node 10907c478bd9Sstevel@tonic-gate */ 10917c478bd9Sstevel@tonic-gate rw_enter(&ddv->dv_contents, RW_WRITER); 10927c478bd9Sstevel@tonic-gate if ((dup = dv_findbyname(ddv, nm)) == NULL) { 10937c478bd9Sstevel@tonic-gate dv_insert(ddv, dv); 10947c478bd9Sstevel@tonic-gate } else { 10957c478bd9Sstevel@tonic-gate /* 10967c478bd9Sstevel@tonic-gate * Duplicate found, use the existing node 10977c478bd9Sstevel@tonic-gate */ 10987c478bd9Sstevel@tonic-gate VN_RELE(DVTOV(dv)); 10997c478bd9Sstevel@tonic-gate dv_destroy(dv, 0); 11007c478bd9Sstevel@tonic-gate dv = dup; 11017c478bd9Sstevel@tonic-gate } 11027c478bd9Sstevel@tonic-gate goto founddv; 11037c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 11047c478bd9Sstevel@tonic-gate 11057c478bd9Sstevel@tonic-gate found: 11067c478bd9Sstevel@tonic-gate /* 11077c478bd9Sstevel@tonic-gate * Skip non-kernel lookups of internal nodes. 11087c478bd9Sstevel@tonic-gate * This use of kcred to distinguish between user and 11097c478bd9Sstevel@tonic-gate * internal kernel lookups is unfortunate. The information 11107c478bd9Sstevel@tonic-gate * provided by the seg argument to lookupnameat should 11117c478bd9Sstevel@tonic-gate * evolve into a lookup flag for filesystems that need 11127c478bd9Sstevel@tonic-gate * this distinction. 11137c478bd9Sstevel@tonic-gate */ 11147c478bd9Sstevel@tonic-gate if ((dv->dv_flags & DV_INTERNAL) && (cred != kcred)) { 11157c478bd9Sstevel@tonic-gate VN_RELE(vp); 11167c478bd9Sstevel@tonic-gate rv = ENOENT; 11177c478bd9Sstevel@tonic-gate goto notfound; 11187c478bd9Sstevel@tonic-gate } 11197c478bd9Sstevel@tonic-gate 11207c478bd9Sstevel@tonic-gate dcmn_err2(("dv_find: returning vp for nm %s\n", nm)); 11217c478bd9Sstevel@tonic-gate if (vp->v_type == VCHR || vp->v_type == VBLK) { 11227c478bd9Sstevel@tonic-gate /* 11237c478bd9Sstevel@tonic-gate * If vnode is a device, return special vnode instead 11247c478bd9Sstevel@tonic-gate * (though it knows all about -us- via sp->s_realvp, 11257c478bd9Sstevel@tonic-gate * sp->s_devvp, and sp->s_dip) 11267c478bd9Sstevel@tonic-gate */ 11277c478bd9Sstevel@tonic-gate *vpp = specvp_devfs(vp, vp->v_rdev, vp->v_type, cred, 11287c478bd9Sstevel@tonic-gate dv->dv_devi); 11297c478bd9Sstevel@tonic-gate VN_RELE(vp); 11307c478bd9Sstevel@tonic-gate if (*vpp == NULLVP) 11317c478bd9Sstevel@tonic-gate rv = ENOSYS; 11327c478bd9Sstevel@tonic-gate } else 11337c478bd9Sstevel@tonic-gate *vpp = vp; 11347c478bd9Sstevel@tonic-gate 11357c478bd9Sstevel@tonic-gate notfound: 11367c478bd9Sstevel@tonic-gate rw_enter(&ddv->dv_contents, RW_WRITER); 11377c478bd9Sstevel@tonic-gate if (was_busy) 11387c478bd9Sstevel@tonic-gate ddv->dv_busy--; 11397c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 11407c478bd9Sstevel@tonic-gate return (rv); 11417c478bd9Sstevel@tonic-gate } 11427c478bd9Sstevel@tonic-gate 11437c478bd9Sstevel@tonic-gate /* 11447c478bd9Sstevel@tonic-gate * The given directory node is out-of-date; that is, it has been 11457c478bd9Sstevel@tonic-gate * marked as needing to be rebuilt, possibly because some new devinfo 11467c478bd9Sstevel@tonic-gate * node has come into existence, or possibly because this is the first 11477c478bd9Sstevel@tonic-gate * time we've been here. 11487c478bd9Sstevel@tonic-gate */ 11497c478bd9Sstevel@tonic-gate void 11507c478bd9Sstevel@tonic-gate dv_filldir(struct dv_node *ddv) 11517c478bd9Sstevel@tonic-gate { 11527c478bd9Sstevel@tonic-gate struct dv_node *dv; 11537c478bd9Sstevel@tonic-gate dev_info_t *devi, *pdevi; 11547c478bd9Sstevel@tonic-gate struct ddi_minor_data *dmd; 11557c478bd9Sstevel@tonic-gate char devnm[MAXNAMELEN]; 11567c478bd9Sstevel@tonic-gate int circ; 11577c478bd9Sstevel@tonic-gate 11587c478bd9Sstevel@tonic-gate ASSERT(DVTOV(ddv)->v_type == VDIR); 11597c478bd9Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&ddv->dv_contents)); 11607c478bd9Sstevel@tonic-gate ASSERT(ddv->dv_flags & DV_BUILD); 11617c478bd9Sstevel@tonic-gate 11627c478bd9Sstevel@tonic-gate dcmn_err3(("dv_filldir: %s\n", ddv->dv_name)); 11637c478bd9Sstevel@tonic-gate if (DV_STALE(ddv)) 11647c478bd9Sstevel@tonic-gate return; 11657c478bd9Sstevel@tonic-gate pdevi = ddv->dv_devi; 11667c478bd9Sstevel@tonic-gate 11677c478bd9Sstevel@tonic-gate if (ndi_devi_config(pdevi, NDI_NO_EVENT) != NDI_SUCCESS) { 11687c478bd9Sstevel@tonic-gate dcmn_err3(("dv_filldir: config error %s\n", 11697c478bd9Sstevel@tonic-gate ddv->dv_name)); 11707c478bd9Sstevel@tonic-gate } 11717c478bd9Sstevel@tonic-gate 11727c478bd9Sstevel@tonic-gate ndi_devi_enter(pdevi, &circ); 11737c478bd9Sstevel@tonic-gate for (devi = ddi_get_child(pdevi); devi; 11747c478bd9Sstevel@tonic-gate devi = ddi_get_next_sibling(devi)) { 11757c478bd9Sstevel@tonic-gate if (i_ddi_node_state(devi) < DS_PROBED) 11767c478bd9Sstevel@tonic-gate continue; 11777c478bd9Sstevel@tonic-gate 11787c478bd9Sstevel@tonic-gate dcmn_err3(("dv_filldir: node %s\n", ddi_node_name(devi))); 11797c478bd9Sstevel@tonic-gate 11807c478bd9Sstevel@tonic-gate mutex_enter(&DEVI(devi)->devi_lock); 11817c478bd9Sstevel@tonic-gate for (dmd = DEVI(devi)->devi_minor; dmd; dmd = dmd->next) { 11827c478bd9Sstevel@tonic-gate char *addr; 11837c478bd9Sstevel@tonic-gate 11847c478bd9Sstevel@tonic-gate /* 11857c478bd9Sstevel@tonic-gate * Skip alias nodes, internal nodes, and nodes 11867c478bd9Sstevel@tonic-gate * without a name. We allow DDM_DEFAULT nodes 11877c478bd9Sstevel@tonic-gate * to appear in readdir. 11887c478bd9Sstevel@tonic-gate */ 11897c478bd9Sstevel@tonic-gate if ((dmd->type == DDM_ALIAS) || 11907c478bd9Sstevel@tonic-gate (dmd->type == DDM_INTERNAL_PATH) || 11917c478bd9Sstevel@tonic-gate (dmd->ddm_name == NULL)) 11927c478bd9Sstevel@tonic-gate continue; 11937c478bd9Sstevel@tonic-gate 11947c478bd9Sstevel@tonic-gate addr = ddi_get_name_addr(devi); 11957c478bd9Sstevel@tonic-gate if (addr && *addr) 11967c478bd9Sstevel@tonic-gate (void) sprintf(devnm, "%s@%s:%s", 11977c478bd9Sstevel@tonic-gate ddi_node_name(devi), addr, dmd->ddm_name); 11987c478bd9Sstevel@tonic-gate else 11997c478bd9Sstevel@tonic-gate (void) sprintf(devnm, "%s:%s", 12007c478bd9Sstevel@tonic-gate ddi_node_name(devi), dmd->ddm_name); 12017c478bd9Sstevel@tonic-gate 12027c478bd9Sstevel@tonic-gate if ((dv = dv_findbyname(ddv, devnm)) != NULL) { 12037c478bd9Sstevel@tonic-gate /* dv_node already exists */ 12047c478bd9Sstevel@tonic-gate VN_RELE(DVTOV(dv)); 12057c478bd9Sstevel@tonic-gate continue; 12067c478bd9Sstevel@tonic-gate } 12077c478bd9Sstevel@tonic-gate 12087c478bd9Sstevel@tonic-gate dv = dv_mknod(ddv, devi, devnm, dmd); 12097c478bd9Sstevel@tonic-gate dv_insert(ddv, dv); 12107c478bd9Sstevel@tonic-gate VN_RELE(DVTOV(dv)); 12117c478bd9Sstevel@tonic-gate } 12127c478bd9Sstevel@tonic-gate mutex_exit(&DEVI(devi)->devi_lock); 12137c478bd9Sstevel@tonic-gate 12147c478bd9Sstevel@tonic-gate (void) ddi_deviname(devi, devnm); 12157c478bd9Sstevel@tonic-gate if ((dv = dv_findbyname(ddv, devnm + 1)) == NULL) { 12167c478bd9Sstevel@tonic-gate /* directory doesn't exist */ 12177c478bd9Sstevel@tonic-gate dv = dv_mkdir(ddv, devi, devnm + 1); 12187c478bd9Sstevel@tonic-gate dv_insert(ddv, dv); 12197c478bd9Sstevel@tonic-gate } 12207c478bd9Sstevel@tonic-gate VN_RELE(DVTOV(dv)); 12217c478bd9Sstevel@tonic-gate } 12227c478bd9Sstevel@tonic-gate ndi_devi_exit(pdevi, circ); 12237c478bd9Sstevel@tonic-gate 12247c478bd9Sstevel@tonic-gate ddv->dv_flags &= ~DV_BUILD; 12257c478bd9Sstevel@tonic-gate } 12267c478bd9Sstevel@tonic-gate 12277c478bd9Sstevel@tonic-gate /* 12287c478bd9Sstevel@tonic-gate * Given a directory node, clean out all the nodes beneath. 12297c478bd9Sstevel@tonic-gate * 12307c478bd9Sstevel@tonic-gate * VDIR: Reinvoke to clean them, then delete the directory. 12317c478bd9Sstevel@tonic-gate * VCHR, VBLK: Just blow them away. 12327c478bd9Sstevel@tonic-gate * 12337c478bd9Sstevel@tonic-gate * Mark the directories touched as in need of a rebuild, in case 12347c478bd9Sstevel@tonic-gate * we fall over part way through. When DV_CLEAN_FORCE is specified, 12357c478bd9Sstevel@tonic-gate * we mark referenced empty directories as stale to facilitate DR. 12367c478bd9Sstevel@tonic-gate */ 12377c478bd9Sstevel@tonic-gate int 12387c478bd9Sstevel@tonic-gate dv_cleandir(struct dv_node *ddv, char *devnm, uint_t flags) 12397c478bd9Sstevel@tonic-gate { 124045a9d961Scth struct dv_node *dv; 124145a9d961Scth struct dv_node **pprev, **npprev; 12427c478bd9Sstevel@tonic-gate struct vnode *vp; 124345a9d961Scth int busy = 0; 12447c478bd9Sstevel@tonic-gate 12457c478bd9Sstevel@tonic-gate dcmn_err3(("dv_cleandir: %s\n", ddv->dv_name)); 12467c478bd9Sstevel@tonic-gate 124745a9d961Scth if (!(flags & DV_CLEANDIR_LCK)) 12487c478bd9Sstevel@tonic-gate rw_enter(&ddv->dv_contents, RW_WRITER); 124945a9d961Scth for (pprev = &ddv->dv_dot, dv = *pprev; dv; 125045a9d961Scth pprev = npprev, dv = *pprev) { 125145a9d961Scth npprev = &dv->dv_next; 12527c478bd9Sstevel@tonic-gate 125345a9d961Scth /* 125445a9d961Scth * If devnm is specified, the non-minor portion of the 125545a9d961Scth * name must match devnm. 125645a9d961Scth */ 125745a9d961Scth if (devnm && 125845a9d961Scth (strncmp(devnm, dv->dv_name, strlen(devnm)) || 12597c478bd9Sstevel@tonic-gate (dv->dv_name[strlen(devnm)] != ':' && 12607c478bd9Sstevel@tonic-gate dv->dv_name[strlen(devnm)] != '\0'))) 12617c478bd9Sstevel@tonic-gate continue; 12627c478bd9Sstevel@tonic-gate 126345a9d961Scth /* check type of what we are cleaning */ 12647c478bd9Sstevel@tonic-gate vp = DVTOV(dv); 12657c478bd9Sstevel@tonic-gate if (vp->v_type == VDIR) { 126645a9d961Scth /* recurse on directories */ 12677c478bd9Sstevel@tonic-gate rw_enter(&dv->dv_contents, RW_WRITER); 126845a9d961Scth if (dv_cleandir(dv, NULL, 126945a9d961Scth flags | DV_CLEANDIR_LCK) == EBUSY) { 12707c478bd9Sstevel@tonic-gate rw_exit(&dv->dv_contents); 127145a9d961Scth goto set_busy; 12727c478bd9Sstevel@tonic-gate } 12737c478bd9Sstevel@tonic-gate 127445a9d961Scth /* A clean directory is an empty directory... */ 127545a9d961Scth ASSERT(dv->dv_nlink == 2); 127645a9d961Scth mutex_enter(&vp->v_lock); 127745a9d961Scth if (vp->v_count > 0) { 127845a9d961Scth /* 127945a9d961Scth * ... but an empty directory can still have 128045a9d961Scth * references to it. If we have dv_busy or 128145a9d961Scth * DV_CLEAN_FORCE is *not* specified then a 128245a9d961Scth * referenced directory is considered busy. 128345a9d961Scth */ 128445a9d961Scth if (dv->dv_busy || !(flags & DV_CLEAN_FORCE)) { 128545a9d961Scth mutex_exit(&vp->v_lock); 128645a9d961Scth rw_exit(&dv->dv_contents); 128745a9d961Scth goto set_busy; 128845a9d961Scth } 128945a9d961Scth 129045a9d961Scth /* 129145a9d961Scth * Mark referenced directory stale so that DR 129245a9d961Scth * will succeed even if a shell has 129345a9d961Scth * /devices/xxx as current directory (causing 129445a9d961Scth * VN_HOLD reference to an empty directory). 129545a9d961Scth */ 129645a9d961Scth ASSERT(!DV_STALE(dv)); 129745a9d961Scth ndi_rele_devi(dv->dv_devi); 129845a9d961Scth dv->dv_devi = NULL; /* mark DV_STALE */ 129945a9d961Scth } 130045a9d961Scth } else { 130145a9d961Scth ASSERT((vp->v_type == VCHR) || (vp->v_type == VBLK)); 130245a9d961Scth ASSERT(dv->dv_nlink == 1); /* no hard links */ 130345a9d961Scth mutex_enter(&vp->v_lock); 130445a9d961Scth if (vp->v_count > 0) { 130545a9d961Scth mutex_exit(&vp->v_lock); 130645a9d961Scth goto set_busy; 130745a9d961Scth } 13087c478bd9Sstevel@tonic-gate } 13097c478bd9Sstevel@tonic-gate 13107c478bd9Sstevel@tonic-gate /* unlink from directory */ 131145a9d961Scth dv_unlink(ddv, dv, pprev); 13127c478bd9Sstevel@tonic-gate 131345a9d961Scth /* drop locks */ 131445a9d961Scth mutex_exit(&vp->v_lock); 131545a9d961Scth if (vp->v_type == VDIR) 131645a9d961Scth rw_exit(&dv->dv_contents); 131745a9d961Scth 131845a9d961Scth /* destroy vnode if ref count is zero */ 131945a9d961Scth if (vp->v_count == 0) 132045a9d961Scth dv_destroy(dv, flags); 132145a9d961Scth 132245a9d961Scth /* pointer to previous stays unchanged */ 132345a9d961Scth npprev = pprev; 132445a9d961Scth continue; 13257c478bd9Sstevel@tonic-gate 13267c478bd9Sstevel@tonic-gate /* 132745a9d961Scth * If devnm is not NULL we return immediately on busy, 132845a9d961Scth * otherwise we continue destroying unused dv_node's. 13297c478bd9Sstevel@tonic-gate */ 133045a9d961Scth set_busy: busy++; 133145a9d961Scth if (devnm) 133245a9d961Scth break; 13337c478bd9Sstevel@tonic-gate } 133445a9d961Scth 13357c478bd9Sstevel@tonic-gate /* 13367c478bd9Sstevel@tonic-gate * This code may be invoked to inform devfs that a new node has 13377c478bd9Sstevel@tonic-gate * been created in the kernel device tree. So we always set 13387c478bd9Sstevel@tonic-gate * the DV_BUILD flag to allow the next dv_filldir() to pick 13397c478bd9Sstevel@tonic-gate * the new devinfo nodes. 13407c478bd9Sstevel@tonic-gate */ 13417c478bd9Sstevel@tonic-gate ddv->dv_flags |= DV_BUILD; 13427c478bd9Sstevel@tonic-gate 134345a9d961Scth if (!(flags & DV_CLEANDIR_LCK)) 13447c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 13457c478bd9Sstevel@tonic-gate 134645a9d961Scth return (busy ? EBUSY : 0); 13477c478bd9Sstevel@tonic-gate } 13487c478bd9Sstevel@tonic-gate 13497c478bd9Sstevel@tonic-gate /* 13507c478bd9Sstevel@tonic-gate * Walk through the devfs hierarchy, correcting the permissions of 13517c478bd9Sstevel@tonic-gate * devices with default permissions that do not match those specified 13527c478bd9Sstevel@tonic-gate * by minor perm. This can only be done for all drivers for now. 13537c478bd9Sstevel@tonic-gate */ 13547c478bd9Sstevel@tonic-gate static int 13557c478bd9Sstevel@tonic-gate dv_reset_perm_dir(struct dv_node *ddv, uint_t flags) 13567c478bd9Sstevel@tonic-gate { 13577c478bd9Sstevel@tonic-gate struct dv_node *dv, *next = NULL; 13587c478bd9Sstevel@tonic-gate struct vnode *vp; 13597c478bd9Sstevel@tonic-gate int retval = 0; 13607c478bd9Sstevel@tonic-gate struct vattr *attrp; 13617c478bd9Sstevel@tonic-gate mperm_t mp; 13627c478bd9Sstevel@tonic-gate char *nm; 13637c478bd9Sstevel@tonic-gate uid_t old_uid; 13647c478bd9Sstevel@tonic-gate gid_t old_gid; 13657c478bd9Sstevel@tonic-gate mode_t old_mode; 13667c478bd9Sstevel@tonic-gate 13677c478bd9Sstevel@tonic-gate rw_enter(&ddv->dv_contents, RW_WRITER); 13687c478bd9Sstevel@tonic-gate for (dv = ddv->dv_dot; dv; dv = next) { 13697c478bd9Sstevel@tonic-gate int error = 0; 13707c478bd9Sstevel@tonic-gate next = dv->dv_next; 13717c478bd9Sstevel@tonic-gate nm = dv->dv_name; 13727c478bd9Sstevel@tonic-gate 13737c478bd9Sstevel@tonic-gate rw_enter(&dv->dv_contents, RW_READER); 13747c478bd9Sstevel@tonic-gate vp = DVTOV(dv); 13757c478bd9Sstevel@tonic-gate if (vp->v_type == VDIR) { 13767c478bd9Sstevel@tonic-gate rw_exit(&dv->dv_contents); 13777c478bd9Sstevel@tonic-gate if (dv_reset_perm_dir(dv, flags) != 0) { 13787c478bd9Sstevel@tonic-gate error = EBUSY; 13797c478bd9Sstevel@tonic-gate } 13807c478bd9Sstevel@tonic-gate } else { 13817c478bd9Sstevel@tonic-gate ASSERT(vp->v_type == VCHR || vp->v_type == VBLK); 13827c478bd9Sstevel@tonic-gate 13837c478bd9Sstevel@tonic-gate /* 13847c478bd9Sstevel@tonic-gate * Check for permissions from minor_perm 13857c478bd9Sstevel@tonic-gate * If there are none, we're done 13867c478bd9Sstevel@tonic-gate */ 13877c478bd9Sstevel@tonic-gate rw_exit(&dv->dv_contents); 13887c478bd9Sstevel@tonic-gate if (dev_minorperm(dv->dv_devi, nm, &mp) != 0) 13897c478bd9Sstevel@tonic-gate continue; 13907c478bd9Sstevel@tonic-gate 13917c478bd9Sstevel@tonic-gate rw_enter(&dv->dv_contents, RW_READER); 13927c478bd9Sstevel@tonic-gate 13937c478bd9Sstevel@tonic-gate /* 13947c478bd9Sstevel@tonic-gate * Allow a node's permissions to be altered 13957c478bd9Sstevel@tonic-gate * permanently from the defaults by chmod, 13967c478bd9Sstevel@tonic-gate * using the shadow node as backing store. 13977c478bd9Sstevel@tonic-gate * Otherwise, update node to minor_perm permissions. 13987c478bd9Sstevel@tonic-gate */ 13997c478bd9Sstevel@tonic-gate if (dv->dv_attrvp == NULLVP) { 14007c478bd9Sstevel@tonic-gate /* 14017c478bd9Sstevel@tonic-gate * No attribute vp, try to find one. 14027c478bd9Sstevel@tonic-gate */ 14037c478bd9Sstevel@tonic-gate dv_shadow_node(DVTOV(ddv), nm, vp, 14047c478bd9Sstevel@tonic-gate NULL, NULLVP, kcred, 0); 14057c478bd9Sstevel@tonic-gate } 14067c478bd9Sstevel@tonic-gate if (dv->dv_attrvp != NULLVP || dv->dv_attr == NULL) { 14077c478bd9Sstevel@tonic-gate rw_exit(&dv->dv_contents); 14087c478bd9Sstevel@tonic-gate continue; 14097c478bd9Sstevel@tonic-gate } 14107c478bd9Sstevel@tonic-gate 14117c478bd9Sstevel@tonic-gate attrp = dv->dv_attr; 14127c478bd9Sstevel@tonic-gate 14137c478bd9Sstevel@tonic-gate if (VATTRP_MP_CMP(attrp, mp) == 0) { 14147c478bd9Sstevel@tonic-gate dcmn_err5(("%s: no perm change: " 14157c478bd9Sstevel@tonic-gate "%d %d 0%o\n", nm, attrp->va_uid, 14167c478bd9Sstevel@tonic-gate attrp->va_gid, attrp->va_mode)); 14177c478bd9Sstevel@tonic-gate rw_exit(&dv->dv_contents); 14187c478bd9Sstevel@tonic-gate continue; 14197c478bd9Sstevel@tonic-gate } 14207c478bd9Sstevel@tonic-gate 14217c478bd9Sstevel@tonic-gate old_uid = attrp->va_uid; 14227c478bd9Sstevel@tonic-gate old_gid = attrp->va_gid; 14237c478bd9Sstevel@tonic-gate old_mode = attrp->va_mode; 14247c478bd9Sstevel@tonic-gate 14257c478bd9Sstevel@tonic-gate VATTRP_MP_MERGE(attrp, mp); 14267c478bd9Sstevel@tonic-gate mutex_enter(&vp->v_lock); 14277c478bd9Sstevel@tonic-gate if (vp->v_count > 0) { 14287c478bd9Sstevel@tonic-gate error = EBUSY; 14297c478bd9Sstevel@tonic-gate } 14307c478bd9Sstevel@tonic-gate mutex_exit(&vp->v_lock); 14317c478bd9Sstevel@tonic-gate 14327c478bd9Sstevel@tonic-gate dcmn_err5(("%s: perm %d/%d/0%o -> %d/%d/0%o (%d)\n", 14337c478bd9Sstevel@tonic-gate nm, old_uid, old_gid, old_mode, attrp->va_uid, 14347c478bd9Sstevel@tonic-gate attrp->va_gid, attrp->va_mode, error)); 14357c478bd9Sstevel@tonic-gate 14367c478bd9Sstevel@tonic-gate rw_exit(&dv->dv_contents); 14377c478bd9Sstevel@tonic-gate } 14387c478bd9Sstevel@tonic-gate 14397c478bd9Sstevel@tonic-gate if (error != 0) { 14407c478bd9Sstevel@tonic-gate retval = error; 14417c478bd9Sstevel@tonic-gate } 14427c478bd9Sstevel@tonic-gate } 14437c478bd9Sstevel@tonic-gate 14447c478bd9Sstevel@tonic-gate ddv->dv_flags |= DV_BUILD; 14457c478bd9Sstevel@tonic-gate 14467c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 14477c478bd9Sstevel@tonic-gate 14487c478bd9Sstevel@tonic-gate return (retval); 14497c478bd9Sstevel@tonic-gate } 14507c478bd9Sstevel@tonic-gate 14517c478bd9Sstevel@tonic-gate int 14527c478bd9Sstevel@tonic-gate devfs_reset_perm(uint_t flags) 14537c478bd9Sstevel@tonic-gate { 14547c478bd9Sstevel@tonic-gate struct dv_node *dvp; 14557c478bd9Sstevel@tonic-gate int rval; 14567c478bd9Sstevel@tonic-gate 14577c478bd9Sstevel@tonic-gate if ((dvp = devfs_dip_to_dvnode(ddi_root_node())) == NULL) 14587c478bd9Sstevel@tonic-gate return (0); 14597c478bd9Sstevel@tonic-gate 14607c478bd9Sstevel@tonic-gate VN_HOLD(DVTOV(dvp)); 14617c478bd9Sstevel@tonic-gate rval = dv_reset_perm_dir(dvp, flags); 14627c478bd9Sstevel@tonic-gate VN_RELE(DVTOV(dvp)); 14637c478bd9Sstevel@tonic-gate return (rval); 14647c478bd9Sstevel@tonic-gate } 14657c478bd9Sstevel@tonic-gate 14667c478bd9Sstevel@tonic-gate /* 14677c478bd9Sstevel@tonic-gate * Clean up dangling devfs shadow nodes for removed 14687c478bd9Sstevel@tonic-gate * drivers so that, in the event the driver is re-added 14697c478bd9Sstevel@tonic-gate * to the system, newly created nodes won't incorrectly 14707c478bd9Sstevel@tonic-gate * pick up these stale shadow node permissions. 14717c478bd9Sstevel@tonic-gate * 14727c478bd9Sstevel@tonic-gate * This is accomplished by walking down the pathname 14737c478bd9Sstevel@tonic-gate * to the directory, starting at the root's attribute 14747c478bd9Sstevel@tonic-gate * node, then removing all minors matching the specified 14757c478bd9Sstevel@tonic-gate * node name. Care must be taken to remove all entries 14767c478bd9Sstevel@tonic-gate * in a directory before the directory itself, so that 14777c478bd9Sstevel@tonic-gate * the clean-up associated with rem_drv'ing a nexus driver 14787c478bd9Sstevel@tonic-gate * does not inadvertently result in an inconsistent 14797c478bd9Sstevel@tonic-gate * filesystem underlying devfs. 14807c478bd9Sstevel@tonic-gate */ 14817c478bd9Sstevel@tonic-gate 14827c478bd9Sstevel@tonic-gate static int 14830466df59Sjg devfs_remdrv_rmdir(vnode_t *dirvp, const char *dir, vnode_t *rvp) 14847c478bd9Sstevel@tonic-gate { 14857c478bd9Sstevel@tonic-gate int error; 14867c478bd9Sstevel@tonic-gate vnode_t *vp; 14877c478bd9Sstevel@tonic-gate int eof; 14887c478bd9Sstevel@tonic-gate struct iovec iov; 14897c478bd9Sstevel@tonic-gate struct uio uio; 14907c478bd9Sstevel@tonic-gate struct dirent64 *dp; 14917c478bd9Sstevel@tonic-gate dirent64_t *dbuf; 14927c478bd9Sstevel@tonic-gate size_t dlen; 14937c478bd9Sstevel@tonic-gate size_t dbuflen; 14947c478bd9Sstevel@tonic-gate int ndirents = 64; 14957c478bd9Sstevel@tonic-gate char *nm; 14967c478bd9Sstevel@tonic-gate 14977c478bd9Sstevel@tonic-gate VN_HOLD(dirvp); 14987c478bd9Sstevel@tonic-gate 14997c478bd9Sstevel@tonic-gate dlen = ndirents * (sizeof (*dbuf)); 15007c478bd9Sstevel@tonic-gate dbuf = kmem_alloc(dlen, KM_SLEEP); 15017c478bd9Sstevel@tonic-gate 15027c478bd9Sstevel@tonic-gate uio.uio_iov = &iov; 15037c478bd9Sstevel@tonic-gate uio.uio_iovcnt = 1; 15047c478bd9Sstevel@tonic-gate uio.uio_segflg = UIO_SYSSPACE; 15057c478bd9Sstevel@tonic-gate uio.uio_fmode = 0; 15067c478bd9Sstevel@tonic-gate uio.uio_extflg = UIO_COPY_CACHED; 15077c478bd9Sstevel@tonic-gate uio.uio_loffset = 0; 15087c478bd9Sstevel@tonic-gate uio.uio_llimit = MAXOFFSET_T; 15097c478bd9Sstevel@tonic-gate 15107c478bd9Sstevel@tonic-gate eof = 0; 15117c478bd9Sstevel@tonic-gate error = 0; 15127c478bd9Sstevel@tonic-gate while (!error && !eof) { 15137c478bd9Sstevel@tonic-gate uio.uio_resid = dlen; 15147c478bd9Sstevel@tonic-gate iov.iov_base = (char *)dbuf; 15157c478bd9Sstevel@tonic-gate iov.iov_len = dlen; 15167c478bd9Sstevel@tonic-gate 15177c478bd9Sstevel@tonic-gate (void) VOP_RWLOCK(dirvp, V_WRITELOCK_FALSE, NULL); 15187c478bd9Sstevel@tonic-gate error = VOP_READDIR(dirvp, &uio, kcred, &eof); 15197c478bd9Sstevel@tonic-gate VOP_RWUNLOCK(dirvp, V_WRITELOCK_FALSE, NULL); 15207c478bd9Sstevel@tonic-gate 15217c478bd9Sstevel@tonic-gate dbuflen = dlen - uio.uio_resid; 15227c478bd9Sstevel@tonic-gate 15237c478bd9Sstevel@tonic-gate if (error || dbuflen == 0) 15247c478bd9Sstevel@tonic-gate break; 15257c478bd9Sstevel@tonic-gate 15267c478bd9Sstevel@tonic-gate for (dp = dbuf; ((intptr_t)dp < (intptr_t)dbuf + dbuflen); 15277c478bd9Sstevel@tonic-gate dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) { 15287c478bd9Sstevel@tonic-gate 15297c478bd9Sstevel@tonic-gate nm = dp->d_name; 15307c478bd9Sstevel@tonic-gate 15317c478bd9Sstevel@tonic-gate if (strcmp(nm, ".") == 0 || strcmp(nm, "..") == 0) 15327c478bd9Sstevel@tonic-gate continue; 15337c478bd9Sstevel@tonic-gate 15347c478bd9Sstevel@tonic-gate error = VOP_LOOKUP(dirvp, 15357c478bd9Sstevel@tonic-gate nm, &vp, NULL, 0, NULL, kcred); 15367c478bd9Sstevel@tonic-gate 15377c478bd9Sstevel@tonic-gate dsysdebug(error, 15387c478bd9Sstevel@tonic-gate ("rem_drv %s/%s lookup (%d)\n", 15397c478bd9Sstevel@tonic-gate dir, nm, error)); 15407c478bd9Sstevel@tonic-gate 15417c478bd9Sstevel@tonic-gate if (error) 15427c478bd9Sstevel@tonic-gate continue; 15437c478bd9Sstevel@tonic-gate 15447c478bd9Sstevel@tonic-gate ASSERT(vp->v_type == VDIR || 15457c478bd9Sstevel@tonic-gate vp->v_type == VCHR || vp->v_type == VBLK); 15467c478bd9Sstevel@tonic-gate 15477c478bd9Sstevel@tonic-gate if (vp->v_type == VDIR) { 15480466df59Sjg error = devfs_remdrv_rmdir(vp, nm, rvp); 15497c478bd9Sstevel@tonic-gate if (error == 0) { 15507c478bd9Sstevel@tonic-gate error = VOP_RMDIR(dirvp, 15510466df59Sjg (char *)nm, rvp, kcred); 15527c478bd9Sstevel@tonic-gate dsysdebug(error, 15537c478bd9Sstevel@tonic-gate ("rem_drv %s/%s rmdir (%d)\n", 15547c478bd9Sstevel@tonic-gate dir, nm, error)); 15557c478bd9Sstevel@tonic-gate } 15567c478bd9Sstevel@tonic-gate } else { 15577c478bd9Sstevel@tonic-gate error = VOP_REMOVE(dirvp, (char *)nm, kcred); 15587c478bd9Sstevel@tonic-gate dsysdebug(error, 15597c478bd9Sstevel@tonic-gate ("rem_drv %s/%s remove (%d)\n", 15607c478bd9Sstevel@tonic-gate dir, nm, error)); 15617c478bd9Sstevel@tonic-gate } 15627c478bd9Sstevel@tonic-gate 15637c478bd9Sstevel@tonic-gate VN_RELE(vp); 15647c478bd9Sstevel@tonic-gate if (error) { 15657c478bd9Sstevel@tonic-gate goto exit; 15667c478bd9Sstevel@tonic-gate } 15677c478bd9Sstevel@tonic-gate } 15687c478bd9Sstevel@tonic-gate } 15697c478bd9Sstevel@tonic-gate 15707c478bd9Sstevel@tonic-gate exit: 15717c478bd9Sstevel@tonic-gate VN_RELE(dirvp); 15727c478bd9Sstevel@tonic-gate kmem_free(dbuf, dlen); 15737c478bd9Sstevel@tonic-gate 15747c478bd9Sstevel@tonic-gate return (error); 15757c478bd9Sstevel@tonic-gate } 15767c478bd9Sstevel@tonic-gate 15777c478bd9Sstevel@tonic-gate int 15787c478bd9Sstevel@tonic-gate devfs_remdrv_cleanup(const char *dir, const char *nodename) 15797c478bd9Sstevel@tonic-gate { 15807c478bd9Sstevel@tonic-gate int error; 15817c478bd9Sstevel@tonic-gate vnode_t *vp; 15827c478bd9Sstevel@tonic-gate vnode_t *dirvp; 15837c478bd9Sstevel@tonic-gate int eof; 15847c478bd9Sstevel@tonic-gate struct iovec iov; 15857c478bd9Sstevel@tonic-gate struct uio uio; 15867c478bd9Sstevel@tonic-gate struct dirent64 *dp; 15877c478bd9Sstevel@tonic-gate dirent64_t *dbuf; 15887c478bd9Sstevel@tonic-gate size_t dlen; 15897c478bd9Sstevel@tonic-gate size_t dbuflen; 15907c478bd9Sstevel@tonic-gate int ndirents = 64; 15917c478bd9Sstevel@tonic-gate int nodenamelen = strlen(nodename); 15927c478bd9Sstevel@tonic-gate char *nm; 15937c478bd9Sstevel@tonic-gate struct pathname pn; 15940466df59Sjg vnode_t *rvp; /* root node of the underlying attribute fs */ 15957c478bd9Sstevel@tonic-gate 15967c478bd9Sstevel@tonic-gate dcmn_err5(("devfs_remdrv_cleanup: %s %s\n", dir, nodename)); 15977c478bd9Sstevel@tonic-gate 15987c478bd9Sstevel@tonic-gate if (error = pn_get((char *)dir, UIO_SYSSPACE, &pn)) 15997c478bd9Sstevel@tonic-gate return (0); 16007c478bd9Sstevel@tonic-gate 16010466df59Sjg rvp = dvroot->dv_attrvp; 16020466df59Sjg ASSERT(rvp != NULL); 16030466df59Sjg VN_HOLD(rvp); 16047c478bd9Sstevel@tonic-gate 16057c478bd9Sstevel@tonic-gate pn_skipslash(&pn); 16060466df59Sjg dirvp = rvp; 16077c478bd9Sstevel@tonic-gate VN_HOLD(dirvp); 16087c478bd9Sstevel@tonic-gate 16097c478bd9Sstevel@tonic-gate nm = kmem_alloc(MAXNAMELEN, KM_SLEEP); 16107c478bd9Sstevel@tonic-gate 16117c478bd9Sstevel@tonic-gate while (pn_pathleft(&pn)) { 16127c478bd9Sstevel@tonic-gate ASSERT(dirvp->v_type == VDIR); 16137c478bd9Sstevel@tonic-gate (void) pn_getcomponent(&pn, nm); 16147c478bd9Sstevel@tonic-gate ASSERT((strcmp(nm, ".") != 0) && (strcmp(nm, "..") != 0)); 16150466df59Sjg error = VOP_LOOKUP(dirvp, nm, &vp, NULL, 0, rvp, kcred); 16167c478bd9Sstevel@tonic-gate if (error) { 16177c478bd9Sstevel@tonic-gate dcmn_err5(("remdrv_cleanup %s lookup error %d\n", 16187c478bd9Sstevel@tonic-gate nm, error)); 16197c478bd9Sstevel@tonic-gate VN_RELE(dirvp); 16200466df59Sjg if (dirvp != rvp) 16210466df59Sjg VN_RELE(rvp); 16227c478bd9Sstevel@tonic-gate pn_free(&pn); 16237c478bd9Sstevel@tonic-gate kmem_free(nm, MAXNAMELEN); 16247c478bd9Sstevel@tonic-gate return (0); 16257c478bd9Sstevel@tonic-gate } 16267c478bd9Sstevel@tonic-gate VN_RELE(dirvp); 16277c478bd9Sstevel@tonic-gate dirvp = vp; 16287c478bd9Sstevel@tonic-gate pn_skipslash(&pn); 16297c478bd9Sstevel@tonic-gate } 16307c478bd9Sstevel@tonic-gate 16317c478bd9Sstevel@tonic-gate ASSERT(dirvp->v_type == VDIR); 16320466df59Sjg if (dirvp != rvp) 16330466df59Sjg VN_RELE(rvp); 16347c478bd9Sstevel@tonic-gate pn_free(&pn); 16357c478bd9Sstevel@tonic-gate kmem_free(nm, MAXNAMELEN); 16367c478bd9Sstevel@tonic-gate 16377c478bd9Sstevel@tonic-gate dlen = ndirents * (sizeof (*dbuf)); 16387c478bd9Sstevel@tonic-gate dbuf = kmem_alloc(dlen, KM_SLEEP); 16397c478bd9Sstevel@tonic-gate 16407c478bd9Sstevel@tonic-gate uio.uio_iov = &iov; 16417c478bd9Sstevel@tonic-gate uio.uio_iovcnt = 1; 16427c478bd9Sstevel@tonic-gate uio.uio_segflg = UIO_SYSSPACE; 16437c478bd9Sstevel@tonic-gate uio.uio_fmode = 0; 16447c478bd9Sstevel@tonic-gate uio.uio_extflg = UIO_COPY_CACHED; 16457c478bd9Sstevel@tonic-gate uio.uio_loffset = 0; 16467c478bd9Sstevel@tonic-gate uio.uio_llimit = MAXOFFSET_T; 16477c478bd9Sstevel@tonic-gate 16487c478bd9Sstevel@tonic-gate eof = 0; 16497c478bd9Sstevel@tonic-gate error = 0; 16507c478bd9Sstevel@tonic-gate while (!error && !eof) { 16517c478bd9Sstevel@tonic-gate uio.uio_resid = dlen; 16527c478bd9Sstevel@tonic-gate iov.iov_base = (char *)dbuf; 16537c478bd9Sstevel@tonic-gate iov.iov_len = dlen; 16547c478bd9Sstevel@tonic-gate 16557c478bd9Sstevel@tonic-gate (void) VOP_RWLOCK(dirvp, V_WRITELOCK_FALSE, NULL); 16567c478bd9Sstevel@tonic-gate error = VOP_READDIR(dirvp, &uio, kcred, &eof); 16577c478bd9Sstevel@tonic-gate VOP_RWUNLOCK(dirvp, V_WRITELOCK_FALSE, NULL); 16587c478bd9Sstevel@tonic-gate 16597c478bd9Sstevel@tonic-gate dbuflen = dlen - uio.uio_resid; 16607c478bd9Sstevel@tonic-gate 16617c478bd9Sstevel@tonic-gate if (error || dbuflen == 0) 16627c478bd9Sstevel@tonic-gate break; 16637c478bd9Sstevel@tonic-gate 16647c478bd9Sstevel@tonic-gate for (dp = dbuf; ((intptr_t)dp < (intptr_t)dbuf + dbuflen); 16657c478bd9Sstevel@tonic-gate dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) { 16667c478bd9Sstevel@tonic-gate 16677c478bd9Sstevel@tonic-gate nm = dp->d_name; 16687c478bd9Sstevel@tonic-gate 16697c478bd9Sstevel@tonic-gate if (strcmp(nm, ".") == 0 || strcmp(nm, "..") == 0) 16707c478bd9Sstevel@tonic-gate continue; 16717c478bd9Sstevel@tonic-gate 16727c478bd9Sstevel@tonic-gate if (strncmp(nm, nodename, nodenamelen) != 0) 16737c478bd9Sstevel@tonic-gate continue; 16747c478bd9Sstevel@tonic-gate 16757c478bd9Sstevel@tonic-gate error = VOP_LOOKUP(dirvp, nm, &vp, 16767c478bd9Sstevel@tonic-gate NULL, 0, NULL, kcred); 16777c478bd9Sstevel@tonic-gate 16787c478bd9Sstevel@tonic-gate dsysdebug(error, 16797c478bd9Sstevel@tonic-gate ("rem_drv %s/%s lookup (%d)\n", 16807c478bd9Sstevel@tonic-gate dir, nm, error)); 16817c478bd9Sstevel@tonic-gate 16827c478bd9Sstevel@tonic-gate if (error) 16837c478bd9Sstevel@tonic-gate continue; 16847c478bd9Sstevel@tonic-gate 16857c478bd9Sstevel@tonic-gate ASSERT(vp->v_type == VDIR || 16867c478bd9Sstevel@tonic-gate vp->v_type == VCHR || vp->v_type == VBLK); 16877c478bd9Sstevel@tonic-gate 16887c478bd9Sstevel@tonic-gate if (vp->v_type == VDIR) { 16890466df59Sjg error = devfs_remdrv_rmdir(vp, nm, rvp); 16907c478bd9Sstevel@tonic-gate if (error == 0) { 16917c478bd9Sstevel@tonic-gate error = VOP_RMDIR(dirvp, 16920466df59Sjg (char *)nm, rvp, kcred); 16937c478bd9Sstevel@tonic-gate dsysdebug(error, 16947c478bd9Sstevel@tonic-gate ("rem_drv %s/%s rmdir (%d)\n", 16957c478bd9Sstevel@tonic-gate dir, nm, error)); 16967c478bd9Sstevel@tonic-gate } 16977c478bd9Sstevel@tonic-gate } else { 16987c478bd9Sstevel@tonic-gate error = VOP_REMOVE(dirvp, (char *)nm, kcred); 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 exit: 17117c478bd9Sstevel@tonic-gate VN_RELE(dirvp); 17127c478bd9Sstevel@tonic-gate 17137c478bd9Sstevel@tonic-gate kmem_free(dbuf, dlen); 17147c478bd9Sstevel@tonic-gate 17157c478bd9Sstevel@tonic-gate return (0); 17167c478bd9Sstevel@tonic-gate } 17177c478bd9Sstevel@tonic-gate 17187c478bd9Sstevel@tonic-gate struct dv_list { 17197c478bd9Sstevel@tonic-gate struct dv_node *dv; 17207c478bd9Sstevel@tonic-gate struct dv_list *next; 17217c478bd9Sstevel@tonic-gate }; 17227c478bd9Sstevel@tonic-gate 17237c478bd9Sstevel@tonic-gate void 17247c478bd9Sstevel@tonic-gate dv_walk( 17257c478bd9Sstevel@tonic-gate struct dv_node *ddv, 17267c478bd9Sstevel@tonic-gate char *devnm, 17277c478bd9Sstevel@tonic-gate void (*callback)(struct dv_node *, void *), 17287c478bd9Sstevel@tonic-gate void *arg) 17297c478bd9Sstevel@tonic-gate { 17307c478bd9Sstevel@tonic-gate struct vnode *dvp; 17317c478bd9Sstevel@tonic-gate struct dv_node *dv; 17327c478bd9Sstevel@tonic-gate struct dv_list *head, *tail, *next; 173345a9d961Scth int len; 17347c478bd9Sstevel@tonic-gate 17357c478bd9Sstevel@tonic-gate dcmn_err3(("dv_walk: ddv = %s, devnm = %s\n", 17367c478bd9Sstevel@tonic-gate ddv->dv_name, devnm ? devnm : "<null>")); 17377c478bd9Sstevel@tonic-gate 17387c478bd9Sstevel@tonic-gate dvp = DVTOV(ddv); 17397c478bd9Sstevel@tonic-gate 17407c478bd9Sstevel@tonic-gate ASSERT(dvp->v_type == VDIR); 17417c478bd9Sstevel@tonic-gate 17427c478bd9Sstevel@tonic-gate head = tail = next = NULL; 17437c478bd9Sstevel@tonic-gate 17447c478bd9Sstevel@tonic-gate rw_enter(&ddv->dv_contents, RW_READER); 174545a9d961Scth mutex_enter(&dvp->v_lock); 17467c478bd9Sstevel@tonic-gate for (dv = ddv->dv_dot; dv; dv = dv->dv_next) { 17477c478bd9Sstevel@tonic-gate /* 17487c478bd9Sstevel@tonic-gate * If devnm is not NULL and is not the empty string, 17497c478bd9Sstevel@tonic-gate * select only dv_nodes with matching non-minor name 17507c478bd9Sstevel@tonic-gate */ 17517c478bd9Sstevel@tonic-gate if (devnm && (len = strlen(devnm)) && 17527c478bd9Sstevel@tonic-gate (strncmp(devnm, dv->dv_name, len) || 17537c478bd9Sstevel@tonic-gate (dv->dv_name[len] != ':' && dv->dv_name[len] != '\0'))) 17547c478bd9Sstevel@tonic-gate continue; 17557c478bd9Sstevel@tonic-gate 17567c478bd9Sstevel@tonic-gate callback(dv, arg); 17577c478bd9Sstevel@tonic-gate 17587c478bd9Sstevel@tonic-gate if (DVTOV(dv)->v_type != VDIR) 17597c478bd9Sstevel@tonic-gate continue; 17607c478bd9Sstevel@tonic-gate 17617c478bd9Sstevel@tonic-gate next = kmem_zalloc(sizeof (*next), KM_SLEEP); 17627c478bd9Sstevel@tonic-gate next->dv = dv; 17637c478bd9Sstevel@tonic-gate 17647c478bd9Sstevel@tonic-gate if (tail) 17657c478bd9Sstevel@tonic-gate tail->next = next; 17667c478bd9Sstevel@tonic-gate else 17677c478bd9Sstevel@tonic-gate head = next; 17687c478bd9Sstevel@tonic-gate 17697c478bd9Sstevel@tonic-gate tail = next; 17707c478bd9Sstevel@tonic-gate } 17717c478bd9Sstevel@tonic-gate 17727c478bd9Sstevel@tonic-gate while (head) { 17737c478bd9Sstevel@tonic-gate dv_walk(head->dv, NULL, callback, arg); 17747c478bd9Sstevel@tonic-gate next = head->next; 17757c478bd9Sstevel@tonic-gate kmem_free(head, sizeof (*head)); 17767c478bd9Sstevel@tonic-gate head = next; 17777c478bd9Sstevel@tonic-gate } 17787c478bd9Sstevel@tonic-gate rw_exit(&ddv->dv_contents); 17797c478bd9Sstevel@tonic-gate mutex_exit(&dvp->v_lock); 17807c478bd9Sstevel@tonic-gate } 1781