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