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
44struct vnodeops		*devipnet_vnodeops;
45
46static void
47devipnet_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 */
64int
65devipnet_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*/
82static int
83devipnet_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*/
104static int
105devipnet_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
137static void
138devipnet_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
155static void
156devipnet_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 */
200static int
201devipnet_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 */
216const 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