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
devipnet_fill_vattr(struct vattr * vap,dev_t dev)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
devipnet_validate(struct sdev_node * dv)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
devipnet_create_rvp(struct sdev_node * ddv,char * nm,void ** arg,cred_t * cred,void * whatever,char * whichever)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
devipnet_lookup(struct vnode * dvp,char * nm,struct vnode ** vpp,struct pathname * pnp,int flags,struct vnode * rdir,struct cred * cred,caller_context_t * ct,int * direntflags,pathname_t * realpnp)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
devipnet_filldir_entry(const char * name,void * arg,dev_t dev)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
devipnet_filldir(struct sdev_node * ddv)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) == 0) {
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
devipnet_readdir(struct vnode * dvp,struct uio * uiop,struct cred * cred,int * eofp,caller_context_t * ct,int flags)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 };
236