Home | History | Annotate | Download | only in dev
      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 
    166 	for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) {
    167 		next = SDEV_NEXT_ENTRY(ddv, dv);
    168 
    169 		/* validate and prune only ready nodes */
    170 		if (dv->sdev_state != SDEV_READY)
    171 			continue;
    172 		switch (devipnet_validate(dv)) {
    173 		case SDEV_VTOR_VALID:
    174 		case SDEV_VTOR_SKIP:
    175 			continue;
    176 		case SDEV_VTOR_INVALID:
    177 		case SDEV_VTOR_STALE:
    178 			sdcmn_err12(("devipnet_filldir: destroy invalid "
    179 			    "node: %s(%p)\n", dv->sdev_name, (void *)dv));
    180 			break;
    181 		}
    182 
    183 		if (SDEVTOV(dv)->v_count > 0)
    184 			continue;
    185 		SDEV_HOLD(dv);
    186 		/* remove the cache node */
    187 		(void) sdev_cache_update(ddv, &dv, dv->sdev_name,
    188 		    SDEV_CACHE_DELETE);
    189 	}
    190 
    191 	ipnet_walk_if(devipnet_filldir_entry, ddv, getzoneid());
    192 
    193 	rw_downgrade(&ddv->sdev_contents);
    194 }
    195 
    196 /*
    197  * Display all instantiated ipnet device nodes.
    198  */
    199 /* ARGSUSED */
    200 static int
    201 devipnet_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
    202     int *eofp, caller_context_t *ct, int flags)
    203 {
    204 	struct sdev_node *sdvp = VTOSDEV(dvp);
    205 
    206 	if (uiop->uio_offset == 0)
    207 		devipnet_filldir(sdvp);
    208 
    209 	return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
    210 }
    211 
    212 /*
    213  * We override lookup and readdir to build entries based on the
    214  * in kernel ipnet table.
    215  */
    216 const fs_operation_def_t devipnet_vnodeops_tbl[] = {
    217 	VOPNAME_READDIR,	{ .vop_readdir = devipnet_readdir },
    218 	VOPNAME_LOOKUP,		{ .vop_lookup = devipnet_lookup },
    219 	VOPNAME_CREATE,		{ .error = fs_nosys },
    220 	VOPNAME_REMOVE,		{ .error = fs_nosys },
    221 	VOPNAME_MKDIR,		{ .error = fs_nosys },
    222 	VOPNAME_RMDIR,		{ .error = fs_nosys },
    223 	VOPNAME_SYMLINK,	{ .error = fs_nosys },
    224 	VOPNAME_SETSECATTR,	{ .error = fs_nosys },
    225 	NULL,			NULL
    226 };
    227