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/net directory 28 * 29 * The lookup is based on the internal vanity naming node table. We also 30 * override readdir in order to delete net 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 <sys/zone.h> 42 #include <sys/dls.h> 43 44 struct vnodeops *devnet_vnodeops; 45 46 /* 47 * Called by zone_walk_datalink() to see if the given link name belongs to the 48 * given zone. Returns 0 to continue the walk, -1 if the link name is found. 49 */ 50 static int 51 devnet_validate_name(const char *link, void *arg) 52 { 53 return ((strcmp(link, arg) == 0) ? -1 : 0); 54 } 55 56 /* 57 * Check if a net sdev_node is still valid - i.e. it represents a current 58 * network link. 59 * This serves two purposes 60 * - only valid net nodes are returned during lookup() and readdir(). 61 * - since net sdev_nodes are not actively destroyed when a network link 62 * goes away, we use the validator to do deferred cleanup i.e. when such 63 * nodes are encountered during subsequent lookup() and readdir(). 64 */ 65 int 66 devnet_validate(struct sdev_node *dv) 67 { 68 char *nm = dv->sdev_name; 69 datalink_id_t linkid; 70 71 ASSERT(!(dv->sdev_flags & SDEV_STALE)); 72 ASSERT(dv->sdev_state == SDEV_READY); 73 74 if (SDEV_IS_GLOBAL(dv)) { 75 return ((dls_mgmt_get_linkid(nm, &linkid) != 0) ? 76 SDEV_VTOR_INVALID : SDEV_VTOR_VALID); 77 } else { 78 return ((zone_datalink_walk(getzoneid(), devnet_validate_name, 79 nm) == -1) ? SDEV_VTOR_VALID : SDEV_VTOR_INVALID); 80 } 81 } 82 83 /* 84 * This callback is invoked from devname_lookup_func() to create 85 * a net entry when the node is not found in the cache. 86 */ 87 static int 88 devnet_create_rvp(const char *nm, struct vattr *vap, dls_dl_handle_t *ddhp) 89 { 90 timestruc_t now; 91 dev_t dev; 92 int error; 93 94 if ((error = dls_devnet_open(nm, ddhp, &dev)) != 0) { 95 sdcmn_err12(("devnet_create_rvp: not a valid vanity name " 96 "network node: %s\n", nm)); 97 return (error); 98 } 99 100 /* 101 * This is a valid network device (at least at this point in time). 102 * Create the node by setting the attribute; the rest is taken care 103 * of by devname_lookup_func(). 104 */ 105 *vap = sdev_vattr_chr; 106 vap->va_mode |= 0666; 107 vap->va_rdev = dev; 108 109 gethrestime(&now); 110 vap->va_atime = now; 111 vap->va_mtime = now; 112 vap->va_ctime = now; 113 return (0); 114 } 115 116 /* 117 * Lookup for /dev/net directory 118 * If the entry does not exist, the devnet_create_rvp() callback 119 * is invoked to create it. Nodes do not persist across reboot. 120 */ 121 /*ARGSUSED3*/ 122 static int 123 devnet_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, 124 struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred, 125 caller_context_t *ct, int *direntflags, pathname_t *realpnp) 126 { 127 struct sdev_node *ddv = VTOSDEV(dvp); 128 struct sdev_node *dv = NULL; 129 dls_dl_handle_t ddh = NULL; 130 struct vattr vattr; 131 int nmlen; 132 int error = ENOENT; 133 134 if (SDEVTOV(ddv)->v_type != VDIR) 135 return (ENOTDIR); 136 137 /* 138 * Empty name or ., return node itself. 139 */ 140 nmlen = strlen(nm); 141 if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) { 142 *vpp = SDEVTOV(ddv); 143 VN_HOLD(*vpp); 144 return (0); 145 } 146 147 /* 148 * .., return the parent directory 149 */ 150 if ((nmlen == 2) && (strcmp(nm, "..") == 0)) { 151 *vpp = SDEVTOV(ddv->sdev_dotdot); 152 VN_HOLD(*vpp); 153 return (0); 154 } 155 156 rw_enter(&ddv->sdev_contents, RW_WRITER); 157 158 /* 159 * directory cache lookup: 160 */ 161 if ((dv = sdev_cache_lookup(ddv, nm)) != NULL) { 162 if (dv->sdev_state == SDEV_READY) { 163 if (!(dv->sdev_flags & SDEV_ATTR_INVALID)) 164 goto found; 165 } else { 166 ASSERT(dv->sdev_state == SDEV_ZOMBIE); 167 goto failed; 168 } 169 } 170 171 /* 172 * ZOMBIED parent does not allow new node creation, bail out early. 173 */ 174 if (ddv->sdev_state == SDEV_ZOMBIE) 175 goto failed; 176 177 error = devnet_create_rvp(nm, &vattr, &ddh); 178 if (error != 0) 179 goto failed; 180 181 error = sdev_mknode(ddv, nm, &dv, &vattr, NULL, NULL, cred, SDEV_READY); 182 if (error != 0) { 183 ASSERT(dv == NULL); 184 dls_devnet_close(ddh); 185 goto failed; 186 } 187 188 ASSERT(dv != NULL); 189 190 rw_enter(&dv->sdev_contents, RW_WRITER); 191 if (dv->sdev_flags & SDEV_ATTR_INVALID) { 192 /* 193 * SDEV_ATTR_INVALID means that this device has been 194 * detached, and its dev_t might've been changed too. 195 * Therefore, sdev_node's 'vattr' needs to be updated. 196 */ 197 SDEVTOV(dv)->v_rdev = vattr.va_rdev; 198 ASSERT(dv->sdev_attr != NULL); 199 dv->sdev_attr->va_rdev = vattr.va_rdev; 200 dv->sdev_flags &= ~SDEV_ATTR_INVALID; 201 } 202 ASSERT(dv->sdev_private == NULL); 203 dv->sdev_private = ddh; 204 rw_exit(&dv->sdev_contents); 205 206 found: 207 ASSERT(SDEV_HELD(dv)); 208 rw_exit(&ddv->sdev_contents); 209 return (sdev_to_vp(dv, vpp)); 210 211 failed: 212 rw_exit(&ddv->sdev_contents); 213 214 if (dv != NULL) 215 SDEV_RELE(dv); 216 217 *vpp = NULL; 218 return (error); 219 } 220 221 static int 222 devnet_filldir_datalink(const char *link, void *arg) 223 { 224 struct sdev_node *ddv = arg; 225 struct vattr vattr; 226 struct sdev_node *dv; 227 dls_dl_handle_t ddh = NULL; 228 229 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 230 if ((dv = sdev_cache_lookup(ddv, (char *)link)) != NULL) 231 goto found; 232 233 if (devnet_create_rvp(link, &vattr, &ddh) != 0) 234 return (0); 235 236 ASSERT(ddh != NULL); 237 dls_devnet_close(ddh); 238 239 if (sdev_mknode(ddv, (char *)link, &dv, &vattr, NULL, NULL, kcred, 240 SDEV_READY) != 0) { 241 return (0); 242 } 243 244 /* 245 * As there is no reference holding the network device, it could be 246 * detached. Set SDEV_ATTR_INVALID so that the 'vattr' will be updated 247 * later. 248 */ 249 rw_enter(&dv->sdev_contents, RW_WRITER); 250 dv->sdev_flags |= SDEV_ATTR_INVALID; 251 rw_exit(&dv->sdev_contents); 252 253 found: 254 SDEV_SIMPLE_RELE(dv); 255 return (0); 256 } 257 258 static void 259 devnet_filldir(struct sdev_node *ddv) 260 { 261 sdev_node_t *dv, *next; 262 char link[MAXLINKNAMELEN]; 263 datalink_id_t linkid; 264 265 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 266 if (rw_tryupgrade(&ddv->sdev_contents) == NULL) { 267 rw_exit(&ddv->sdev_contents); 268 rw_enter(&ddv->sdev_contents, RW_WRITER); 269 } 270 271 for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) { 272 next = SDEV_NEXT_ENTRY(ddv, dv); 273 274 /* validate and prune only ready nodes */ 275 if (dv->sdev_state != SDEV_READY) 276 continue; 277 278 switch (devnet_validate(dv)) { 279 case SDEV_VTOR_VALID: 280 case SDEV_VTOR_SKIP: 281 continue; 282 case SDEV_VTOR_INVALID: 283 case SDEV_VTOR_STALE: 284 sdcmn_err12(("devnet_filldir: destroy invalid " 285 "node: %s(%p)\n", dv->sdev_name, (void *)dv)); 286 break; 287 } 288 289 if (SDEVTOV(dv)->v_count > 0) 290 continue; 291 SDEV_HOLD(dv); 292 /* remove the cache node */ 293 (void) sdev_cache_update(ddv, &dv, dv->sdev_name, 294 SDEV_CACHE_DELETE); 295 } 296 297 if (((ddv->sdev_flags & SDEV_BUILD) == 0) && !dls_devnet_rebuild()) 298 goto done; 299 300 if (SDEV_IS_GLOBAL(ddv)) { 301 linkid = DATALINK_INVALID_LINKID; 302 do { 303 linkid = dls_mgmt_get_next(linkid, DATALINK_CLASS_ALL, 304 DATALINK_ANY_MEDIATYPE, DLMGMT_ACTIVE); 305 306 if ((linkid != DATALINK_INVALID_LINKID) && 307 (dls_mgmt_get_linkinfo(linkid, link, 308 NULL, NULL, NULL) == 0)) { 309 (void) devnet_filldir_datalink(link, ddv); 310 } 311 } while (linkid != DATALINK_INVALID_LINKID); 312 } else { 313 (void) zone_datalink_walk(getzoneid(), 314 devnet_filldir_datalink, ddv); 315 } 316 317 ddv->sdev_flags &= ~SDEV_BUILD; 318 319 done: 320 rw_downgrade(&ddv->sdev_contents); 321 } 322 323 /* 324 * Display all instantiated network datalink device nodes. 325 * A /dev/net entry will be created only after the first lookup of 326 * the network datalink device succeeds. 327 */ 328 /*ARGSUSED4*/ 329 static int 330 devnet_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, 331 int *eofp, caller_context_t *ct, int flags) 332 { 333 struct sdev_node *sdvp = VTOSDEV(dvp); 334 335 ASSERT(sdvp); 336 337 if (uiop->uio_offset == 0) 338 devnet_filldir(sdvp); 339 340 return (devname_readdir_func(dvp, uiop, cred, eofp, 0)); 341 } 342 343 /* 344 * This callback is invoked from devname_inactive_func() to release 345 * the net entry which was held in devnet_create_rvp(). 346 */ 347 static void 348 devnet_inactive_callback(struct vnode *dvp) 349 { 350 struct sdev_node *sdvp = VTOSDEV(dvp); 351 dls_dl_handle_t ddh; 352 353 if (dvp->v_type == VDIR) 354 return; 355 356 ASSERT(dvp->v_type == VCHR); 357 rw_enter(&sdvp->sdev_contents, RW_WRITER); 358 ddh = sdvp->sdev_private; 359 sdvp->sdev_private = NULL; 360 sdvp->sdev_flags |= SDEV_ATTR_INVALID; 361 rw_exit(&sdvp->sdev_contents); 362 363 /* 364 * "ddh" (sdev_private) could be NULL if devnet_lookup fails. 365 */ 366 if (ddh != NULL) 367 dls_devnet_close(ddh); 368 } 369 370 /*ARGSUSED*/ 371 static void 372 devnet_inactive(struct vnode *dvp, struct cred *cred, caller_context_t *ct) 373 { 374 devname_inactive_func(dvp, cred, devnet_inactive_callback); 375 } 376 377 /* 378 * We override lookup and readdir to build entries based on the 379 * in kernel vanity naming node table. 380 */ 381 const fs_operation_def_t devnet_vnodeops_tbl[] = { 382 VOPNAME_READDIR, { .vop_readdir = devnet_readdir }, 383 VOPNAME_LOOKUP, { .vop_lookup = devnet_lookup }, 384 VOPNAME_INACTIVE, { .vop_inactive = devnet_inactive }, 385 VOPNAME_CREATE, { .error = fs_nosys }, 386 VOPNAME_REMOVE, { .error = fs_nosys }, 387 VOPNAME_MKDIR, { .error = fs_nosys }, 388 VOPNAME_RMDIR, { .error = fs_nosys }, 389 VOPNAME_SYMLINK, { .error = fs_nosys }, 390 VOPNAME_SETSECATTR, { .error = fs_nosys }, 391 NULL, NULL 392 }; 393