1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * vnode ops for the /dev/ipnet directory 28 * The lookup is based on the ipnetif nodes held 29 * in the ipnet module. We also override readdir 30 * in order to delete ipnet nodes no longer in use. 31 */ 32 33 #include <sys/types.h> 34 #include <sys/param.h> 35 #include <sys/sysmacros.h> 36 #include <sys/sunndi.h> 37 #include <fs/fs_subr.h> 38 #include <sys/fs/dv_node.h> 39 #include <sys/fs/sdev_impl.h> 40 #include <sys/policy.h> 41 #include <inet/ipnet.h> 42 #include <sys/zone.h> 43 44 struct vnodeops *devipnet_vnodeops; 45 46 static void 47 devipnet_fill_vattr(struct vattr *vap, dev_t dev) 48 { 49 timestruc_t now; 50 51 *vap = sdev_vattr_chr; 52 vap->va_rdev = dev; 53 vap->va_mode |= 0666; 54 55 gethrestime(&now); 56 vap->va_atime = now; 57 vap->va_mtime = now; 58 vap->va_ctime = now; 59 } 60 61 /* 62 * Check if an ipnet sdev_node is still valid. 63 */ 64 int 65 devipnet_validate(struct sdev_node *dv) 66 { 67 dev_t dev; 68 69 dev = ipnet_if_getdev(dv->sdev_name, getzoneid()); 70 if (dev == (dev_t)-1) 71 return (SDEV_VTOR_INVALID); 72 if (getminor(SDEVTOV(dv)->v_rdev) != getminor(dev)) 73 return (SDEV_VTOR_STALE); 74 return (SDEV_VTOR_VALID); 75 } 76 77 /* 78 * This callback is invoked from devname_lookup_func() to create 79 * an ipnet entry when the node is not found in the cache. 80 */ 81 /*ARGSUSED*/ 82 static int 83 devipnet_create_rvp(struct sdev_node *ddv, char *nm, 84 void **arg, cred_t *cred, void *whatever, char *whichever) 85 { 86 dev_t dev; 87 struct vattr *vap = (struct vattr *)arg; 88 int err = 0; 89 90 if ((dev = ipnet_if_getdev(nm, getzoneid())) == (dev_t)-1) 91 err = ENOENT; 92 else 93 devipnet_fill_vattr(vap, dev); 94 95 return (err); 96 } 97 98 /* 99 * Lookup for /dev/ipnet directory 100 * If the entry does not exist, the devipnet_create_rvp() callback 101 * is invoked to create it. Nodes do not persist across reboot. 102 */ 103 /*ARGSUSED3*/ 104 static int 105 devipnet_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, 106 struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred, 107 caller_context_t *ct, int *direntflags, pathname_t *realpnp) 108 { 109 struct sdev_node *sdvp = VTOSDEV(dvp); 110 struct sdev_node *dv; 111 struct vnode *rvp = NULL; 112 int error; 113 114 error = devname_lookup_func(sdvp, nm, vpp, cred, devipnet_create_rvp, 115 SDEV_VATTR); 116 117 if (error == 0) { 118 switch ((*vpp)->v_type) { 119 case VCHR: 120 dv = VTOSDEV(VTOS(*vpp)->s_realvp); 121 ASSERT(VOP_REALVP(SDEVTOV(dv), &rvp, NULL) == ENOSYS); 122 break; 123 case VDIR: 124 dv = VTOSDEV(*vpp); 125 break; 126 default: 127 cmn_err(CE_PANIC, "devipnet_lookup: Unsupported node " 128 "type: %p: %d", (void *)(*vpp), (*vpp)->v_type); 129 break; 130 } 131 ASSERT(SDEV_HELD(dv)); 132 } 133 134 return (error); 135 } 136 137 static void 138 devipnet_filldir_entry(const char *name, void *arg, dev_t dev) 139 { 140 struct sdev_node *ddv = arg; 141 struct vattr vattr; 142 struct sdev_node *dv; 143 144 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 145 146 if ((dv = sdev_cache_lookup(ddv, (char *)name)) == NULL) { 147 devipnet_fill_vattr(&vattr, dev); 148 if (sdev_mknode(ddv, (char *)name, &dv, &vattr, NULL, NULL, 149 kcred, SDEV_READY) != 0) 150 return; 151 } 152 SDEV_SIMPLE_RELE(dv); 153 } 154 155 static void 156 devipnet_filldir(struct sdev_node *ddv) 157 { 158 sdev_node_t *dv, *next; 159 160 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 161 if (rw_tryupgrade(&ddv->sdev_contents) == NULL) { 162 rw_exit(&ddv->sdev_contents); 163 rw_enter(&ddv->sdev_contents, RW_WRITER); 164 /* 165 * We've been made a zombie while we weren't looking. We'll bail 166 * if that's the case. 167 */ 168 if (ddv->sdev_state == SDEV_ZOMBIE) { 169 rw_exit(&ddv->sdev_contents); 170 return; 171 } 172 } 173 174 for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) { 175 next = SDEV_NEXT_ENTRY(ddv, dv); 176 177 /* validate and prune only ready nodes */ 178 if (dv->sdev_state != SDEV_READY) 179 continue; 180 switch (devipnet_validate(dv)) { 181 case SDEV_VTOR_VALID: 182 case SDEV_VTOR_SKIP: 183 continue; 184 case SDEV_VTOR_INVALID: 185 case SDEV_VTOR_STALE: 186 sdcmn_err12(("devipnet_filldir: destroy invalid " 187 "node: %s(%p)\n", dv->sdev_name, (void *)dv)); 188 break; 189 } 190 191 if (SDEVTOV(dv)->v_count > 0) 192 continue; 193 SDEV_HOLD(dv); 194 /* remove the cache node */ 195 (void) sdev_cache_update(ddv, &dv, dv->sdev_name, 196 SDEV_CACHE_DELETE); 197 SDEV_RELE(dv); 198 } 199 200 ipnet_walk_if(devipnet_filldir_entry, ddv, getzoneid()); 201 202 rw_downgrade(&ddv->sdev_contents); 203 } 204 205 /* 206 * Display all instantiated ipnet device nodes. 207 */ 208 /* ARGSUSED */ 209 static int 210 devipnet_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, 211 int *eofp, caller_context_t *ct, int flags) 212 { 213 struct sdev_node *sdvp = VTOSDEV(dvp); 214 215 if (uiop->uio_offset == 0) 216 devipnet_filldir(sdvp); 217 218 return (devname_readdir_func(dvp, uiop, cred, eofp, 0)); 219 } 220 221 /* 222 * We override lookup and readdir to build entries based on the 223 * in kernel ipnet table. 224 */ 225 const fs_operation_def_t devipnet_vnodeops_tbl[] = { 226 { VOPNAME_READDIR, { .vop_readdir = devipnet_readdir } }, 227 { VOPNAME_LOOKUP, { .vop_lookup = devipnet_lookup } }, 228 { VOPNAME_CREATE, { .error = fs_nosys } }, 229 { VOPNAME_REMOVE, { .error = fs_nosys } }, 230 { VOPNAME_MKDIR, { .error = fs_nosys } }, 231 { VOPNAME_RMDIR, { .error = fs_nosys } }, 232 { VOPNAME_SYMLINK, { .error = fs_nosys } }, 233 { VOPNAME_SETSECATTR, { .error = fs_nosys } }, 234 { NULL, { NULL } } 235 };