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 /*
23 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2016, Joyent Inc.
25 */
26
27 /*
28 * This file implements /dev filesystem operations for non-global
29 * instances. Three major entry points:
30 * devname_profile_update()
31 * Update matching rules determining which names to export
32 * prof_readdir()
33 * Return the list of exported names
34 * prof_lookup()
35 * Implements lookup
36 */
37
38 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/sysmacros.h>
41 #include <sys/vnode.h>
42 #include <sys/uio.h>
43 #include <sys/dirent.h>
44 #include <sys/pathname.h>
45 #include <sys/fs/dv_node.h>
46 #include <sys/fs/sdev_impl.h>
47 #include <sys/sunndi.h>
48 #include <sys/modctl.h>
49
50 enum {
51 PROFILE_TYPE_INCLUDE,
52 PROFILE_TYPE_EXCLUDE,
53 PROFILE_TYPE_MAP,
54 PROFILE_TYPE_SYMLINK
55 };
56
57 enum {
58 WALK_DIR_CONTINUE = 0,
59 WALK_DIR_TERMINATE
60 };
61
62 static const char *sdev_nvp_val_err = "nvpair_value error %d, %s\n";
63
64 static void process_rule(struct sdev_node *, struct sdev_node *,
65 char *, char *, int);
66 static void walk_dir(struct vnode *, void *, int (*)(char *, void *));
67
68 static void
prof_getattr(struct sdev_node * dir,char * name,struct vnode * gdv,struct vattr * vap,struct vnode ** avpp,int * no_fs_perm)69 prof_getattr(struct sdev_node *dir, char *name, struct vnode *gdv,
70 struct vattr *vap, struct vnode **avpp, int *no_fs_perm)
71 {
72 struct vnode *advp;
73
74 /* get attribute from shadow, if present; else get default */
75 advp = dir->sdev_attrvp;
76 if (advp && VOP_LOOKUP(advp, name, avpp, NULL, 0, NULL, kcred,
77 NULL, NULL, NULL) == 0) {
78 (void) VOP_GETATTR(*avpp, vap, 0, kcred, NULL);
79 } else if (gdv == NULL || gdv->v_type == VDIR) {
80 /* always create shadow directory */
81 *vap = sdev_vattr_dir;
82 if (advp && VOP_MKDIR(advp, name, &sdev_vattr_dir,
83 avpp, kcred, NULL, 0, NULL) != 0) {
84 *avpp = NULLVP;
85 sdcmn_err10(("prof_getattr: failed to create "
86 "shadow directory %s/%s\n", dir->sdev_path, name));
87 }
88 } else {
89 /*
90 * get default permission from devfs
91 * Before calling devfs_get_defattr, we need to get
92 * the realvp (the dv_node). If realvp is not a dv_node,
93 * devfs_get_defattr() will return a system-wide default
94 * attr for device nodes.
95 */
96 struct vnode *rvp;
97 if (VOP_REALVP(gdv, &rvp, NULL) != 0)
98 rvp = gdv;
99 devfs_get_defattr(rvp, vap, no_fs_perm);
100 *avpp = NULLVP;
101 }
102
103 /* ignore dev_t and vtype from backing store */
104 if (gdv) {
105 vap->va_type = gdv->v_type;
106 vap->va_rdev = gdv->v_rdev;
107 }
108 }
109
110 static void
apply_glob_pattern(struct sdev_node * pdir,struct sdev_node * cdir)111 apply_glob_pattern(struct sdev_node *pdir, struct sdev_node *cdir)
112 {
113 char *name;
114 nvpair_t *nvp = NULL;
115 nvlist_t *nvl;
116 struct vnode *vp = SDEVTOV(cdir);
117 int rv = 0;
118
119 if (vp->v_type != VDIR)
120 return;
121 name = cdir->sdev_name;
122 nvl = pdir->sdev_prof.dev_glob_incdir;
123 while (nvp = nvlist_next_nvpair(nvl, nvp)) {
124 char *pathleft;
125 char *expr = nvpair_name(nvp);
126 if (!gmatch(name, expr))
127 continue;
128 rv = nvpair_value_string(nvp, &pathleft);
129 if (rv != 0) {
130 cmn_err(CE_WARN, sdev_nvp_val_err,
131 rv, nvpair_name(nvp));
132 break;
133 }
134 process_rule(cdir, cdir->sdev_origin,
135 pathleft, NULL, PROFILE_TYPE_INCLUDE);
136 }
137 }
138
139 /*
140 * Some commonality here with sdev_mknode(), could be simplified.
141 * NOTE: prof_mknode returns with *newdv held once, if success.
142 */
143 static int
prof_mknode(struct sdev_node * dir,char * name,struct sdev_node ** newdv,vattr_t * vap,vnode_t * avp,void * arg,cred_t * cred)144 prof_mknode(struct sdev_node *dir, char *name, struct sdev_node **newdv,
145 vattr_t *vap, vnode_t *avp, void *arg, cred_t *cred)
146 {
147 struct sdev_node *dv;
148 int rv;
149
150 ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
151
152 /* check cache first */
153 if (dv = sdev_cache_lookup(dir, name)) {
154 *newdv = dv;
155 return (0);
156 }
157
158 /* allocate node and insert into cache */
159 rv = sdev_nodeinit(dir, name, &dv, NULL);
160 if (rv != 0) {
161 *newdv = NULL;
162 return (rv);
163 }
164
165 sdev_cache_update(dir, &dv, name, SDEV_CACHE_ADD);
166 *newdv = dv;
167
168 /* put it in ready state */
169 rv = sdev_nodeready(*newdv, vap, avp, arg, cred);
170
171 /* handle glob pattern in the middle of a path */
172 if (rv == 0) {
173 if (SDEVTOV(*newdv)->v_type == VDIR)
174 sdcmn_err10(("sdev_origin for %s set to 0x%p\n",
175 name, arg));
176 apply_glob_pattern(dir, *newdv);
177 } else {
178 sdev_cache_update(dir, &dv, name, SDEV_CACHE_DELETE);
179 SDEV_RELE(dv);
180 }
181 return (rv);
182 }
183
184 /*
185 * Create a directory node in a non-global dev instance.
186 * Always create shadow vnode. Set sdev_origin to the corresponding
187 * global directory sdev_node if it exists. This facilitates the
188 * lookup operation.
189 */
190 static int
prof_make_dir(char * name,struct sdev_node ** gdirp,struct sdev_node ** dirp)191 prof_make_dir(char *name, struct sdev_node **gdirp, struct sdev_node **dirp)
192 {
193 struct sdev_node *dir = *dirp;
194 struct sdev_node *gdir = *gdirp;
195 struct sdev_node *newdv;
196 struct vnode *avp, *gnewdir = NULL;
197 struct vattr vattr;
198 int error;
199
200 /* see if name already exists */
201 rw_enter(&dir->sdev_contents, RW_READER);
202 if (newdv = sdev_cache_lookup(dir, name)) {
203 *dirp = newdv;
204 *gdirp = newdv->sdev_origin;
205 rw_exit(&dir->sdev_contents);
206 SDEV_RELE(dir);
207 return (0);
208 }
209 rw_exit(&dir->sdev_contents);
210
211 /* find corresponding dir node in global dev */
212 if (gdir) {
213 error = VOP_LOOKUP(SDEVTOV(gdir), name, &gnewdir,
214 NULL, 0, NULL, kcred, NULL, NULL, NULL);
215 if (error == 0) {
216 *gdirp = VTOSDEV(gnewdir);
217 } else { /* it's ok if there no global dir */
218 *gdirp = NULL;
219 }
220 }
221
222 /* get attribute from shadow, also create shadow dir */
223 prof_getattr(dir, name, gnewdir, &vattr, &avp, NULL);
224
225 /* create dev directory vnode */
226 rw_enter(&dir->sdev_contents, RW_WRITER);
227 error = prof_mknode(dir, name, &newdv, &vattr, avp, (void *)*gdirp,
228 kcred);
229 rw_exit(&dir->sdev_contents);
230 if (error == 0) {
231 ASSERT(newdv);
232 *dirp = newdv;
233 }
234 SDEV_RELE(dir);
235 return (error);
236 }
237
238 /*
239 * Look up a logical name in the global zone.
240 * Provides the ability to map the global zone's device name
241 * to an alternate name within a zone. The primary example
242 * is the virtual console device /dev/zcons/[zonename]/zconsole
243 * mapped to /[zonename]/root/dev/zconsole.
244 */
245 static void
prof_lookup_globaldev(struct sdev_node * dir,struct sdev_node * gdir,char * name,char * rename)246 prof_lookup_globaldev(struct sdev_node *dir, struct sdev_node *gdir,
247 char *name, char *rename)
248 {
249 int error;
250 struct vnode *avp, *gdv, *gddv;
251 struct sdev_node *newdv;
252 struct vattr vattr = {0};
253 struct pathname pn;
254
255 /* check if node already exists */
256 newdv = sdev_cache_lookup(dir, rename);
257 if (newdv) {
258 ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
259 SDEV_SIMPLE_RELE(newdv);
260 return;
261 }
262
263 /* sanity check arguments */
264 if (!gdir || pn_get(name, UIO_SYSSPACE, &pn))
265 return;
266
267 /* perform a relative lookup of the global /dev instance */
268 gddv = SDEVTOV(gdir);
269 VN_HOLD(gddv);
270 error = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &gdv,
271 rootdir, gddv, kcred);
272 pn_free(&pn);
273 if (error) {
274 sdcmn_err10(("prof_lookup_globaldev: %s not found\n", name));
275 return;
276 }
277 ASSERT(gdv && gdv->v_type != VLNK);
278
279 /*
280 * Found the entry in global /dev, figure out attributes
281 * by looking at backing store. Call into devfs for default.
282 * Note, mapped device is persisted under the new name
283 */
284 prof_getattr(dir, rename, gdv, &vattr, &avp, NULL);
285
286 if (gdv->v_type != VDIR) {
287 VN_RELE(gdv);
288 gdir = NULL;
289 } else
290 gdir = VTOSDEV(gdv);
291
292 if (prof_mknode(dir, rename, &newdv, &vattr, avp,
293 (void *)gdir, kcred) == 0) {
294 ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
295 SDEV_SIMPLE_RELE(newdv);
296 }
297 }
298
299 static void
prof_make_sym(struct sdev_node * dir,char * lnm,char * tgt)300 prof_make_sym(struct sdev_node *dir, char *lnm, char *tgt)
301 {
302 struct sdev_node *newdv;
303
304 if (prof_mknode(dir, lnm, &newdv, &sdev_vattr_lnk, NULL,
305 (void *)tgt, kcred) == 0) {
306 ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
307 SDEV_SIMPLE_RELE(newdv);
308 }
309 }
310
311 /*
312 * Create symlinks in the current directory based on profile
313 */
314 static void
prof_make_symlinks(struct sdev_node * dir)315 prof_make_symlinks(struct sdev_node *dir)
316 {
317 char *tgt, *lnm;
318 nvpair_t *nvp = NULL;
319 nvlist_t *nvl = dir->sdev_prof.dev_symlink;
320 int rv;
321
322 ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
323
324 if (nvl == NULL)
325 return;
326
327 while (nvp = nvlist_next_nvpair(nvl, nvp)) {
328 lnm = nvpair_name(nvp);
329 rv = nvpair_value_string(nvp, &tgt);
330 if (rv != 0) {
331 cmn_err(CE_WARN, sdev_nvp_val_err,
332 rv, nvpair_name(nvp));
333 break;
334 }
335 prof_make_sym(dir, lnm, tgt);
336 }
337 }
338
339 static void
prof_make_maps(struct sdev_node * dir)340 prof_make_maps(struct sdev_node *dir)
341 {
342 nvpair_t *nvp = NULL;
343 nvlist_t *nvl = dir->sdev_prof.dev_map;
344 int rv;
345
346 ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
347
348 if (nvl == NULL)
349 return;
350
351 while (nvp = nvlist_next_nvpair(nvl, nvp)) {
352 char *name;
353 char *rename = nvpair_name(nvp);
354 rv = nvpair_value_string(nvp, &name);
355 if (rv != 0) {
356 cmn_err(CE_WARN, sdev_nvp_val_err,
357 rv, nvpair_name(nvp));
358 break;
359 }
360 sdcmn_err10(("map %s -> %s\n", name, rename));
361 (void) prof_lookup_globaldev(dir, sdev_origins->sdev_root,
362 name, rename);
363 }
364 }
365
366 struct match_arg {
367 char *expr;
368 int match;
369 };
370
371 static int
match_name(char * name,void * arg)372 match_name(char *name, void *arg)
373 {
374 struct match_arg *margp = (struct match_arg *)arg;
375
376 if (gmatch(name, margp->expr)) {
377 margp->match = 1;
378 return (WALK_DIR_TERMINATE);
379 }
380 return (WALK_DIR_CONTINUE);
381 }
382
383 static int
is_nonempty_dir(char * name,char * pathleft,struct sdev_node * dir)384 is_nonempty_dir(char *name, char *pathleft, struct sdev_node *dir)
385 {
386 struct match_arg marg;
387 struct pathname pn;
388 struct vnode *gvp;
389 struct sdev_node *gdir = dir->sdev_origin;
390
391 if (VOP_LOOKUP(SDEVTOV(gdir), name, &gvp, NULL, 0, NULL, kcred,
392 NULL, NULL, NULL) != 0)
393 return (0);
394
395 if (gvp->v_type != VDIR) {
396 VN_RELE(gvp);
397 return (0);
398 }
399
400 if (pn_get(pathleft, UIO_SYSSPACE, &pn) != 0) {
401 VN_RELE(gvp);
402 return (0);
403 }
404
405 marg.expr = kmem_alloc(MAXNAMELEN, KM_SLEEP);
406 (void) pn_getcomponent(&pn, marg.expr);
407 marg.match = 0;
408
409 walk_dir(gvp, &marg, match_name);
410 VN_RELE(gvp);
411 kmem_free(marg.expr, MAXNAMELEN);
412 pn_free(&pn);
413
414 return (marg.match);
415 }
416
417
418 /* Check if name passes matching rules */
419 int
prof_name_matched(char * name,struct sdev_node * dir)420 prof_name_matched(char *name, struct sdev_node *dir)
421 {
422 int type, match = 0;
423 char *expr;
424 nvlist_t *nvl;
425 nvpair_t *nvp = NULL;
426 int rv;
427
428 /* check against nvlist for leaf include/exclude */
429 nvl = dir->sdev_prof.dev_name;
430 while (nvp = nvlist_next_nvpair(nvl, nvp)) {
431 expr = nvpair_name(nvp);
432 rv = nvpair_value_int32(nvp, &type);
433 if (rv != 0) {
434 cmn_err(CE_WARN, sdev_nvp_val_err,
435 rv, nvpair_name(nvp));
436 break;
437 }
438
439 if (type == PROFILE_TYPE_EXCLUDE) {
440 if (gmatch(name, expr))
441 return (0); /* excluded */
442 } else if (!match) {
443 match = gmatch(name, expr);
444 }
445 }
446 if (match) {
447 sdcmn_err10(("prof_name_matched: %s\n", name));
448 return (match);
449 }
450
451 /* check for match against directory globbing pattern */
452 nvl = dir->sdev_prof.dev_glob_incdir;
453 while (nvp = nvlist_next_nvpair(nvl, nvp)) {
454 char *pathleft;
455 expr = nvpair_name(nvp);
456 if (gmatch(name, expr) == 0)
457 continue;
458 rv = nvpair_value_string(nvp, &pathleft);
459 if (rv != 0) {
460 cmn_err(CE_WARN, sdev_nvp_val_err,
461 rv, nvpair_name(nvp));
462 break;
463 }
464 if (is_nonempty_dir(name, pathleft, dir)) {
465 sdcmn_err10(("prof_name_matched: dir %s\n", name));
466 return (1);
467 }
468 }
469
470 return (0);
471 }
472
473 static void
walk_dir(struct vnode * dvp,void * arg,int (* callback)(char *,void *))474 walk_dir(struct vnode *dvp, void *arg, int (*callback)(char *, void *))
475 {
476 char *nm;
477 int eof, error;
478 struct iovec iov;
479 struct uio uio;
480 struct dirent64 *dp;
481 dirent64_t *dbuf;
482 size_t dbuflen, dlen;
483
484 ASSERT(dvp);
485
486 dlen = 4096;
487 dbuf = kmem_zalloc(dlen, KM_SLEEP);
488
489 uio.uio_iov = &iov;
490 uio.uio_iovcnt = 1;
491 uio.uio_segflg = UIO_SYSSPACE;
492 uio.uio_fmode = 0;
493 uio.uio_extflg = UIO_COPY_CACHED;
494 uio.uio_loffset = 0;
495 uio.uio_llimit = MAXOFFSET_T;
496
497 eof = 0;
498 error = 0;
499 while (!error && !eof) {
500 uio.uio_resid = dlen;
501 iov.iov_base = (char *)dbuf;
502 iov.iov_len = dlen;
503 (void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL);
504 error = VOP_READDIR(dvp, &uio, kcred, &eof, NULL, 0);
505 VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL);
506
507 dbuflen = dlen - uio.uio_resid;
508 if (error || dbuflen == 0)
509 break;
510 for (dp = dbuf; ((intptr_t)dp <
511 (intptr_t)dbuf + dbuflen);
512 dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
513 nm = dp->d_name;
514
515 if (strcmp(nm, ".") == 0 ||
516 strcmp(nm, "..") == 0)
517 continue;
518
519 if (callback(nm, arg) == WALK_DIR_TERMINATE)
520 goto end;
521 }
522 }
523
524 end:
525 kmem_free(dbuf, dlen);
526 }
527
528 /*
529 * Last chance for a zone to see a node. If our parent dir is
530 * SDEV_ZONED, then we look up the "zone" property for the node. If the
531 * property is found and matches the current zone name, we allow it.
532 * Note that this isn't quite correct for the global zone peeking inside
533 * a zone's /dev - for that to work, we'd have to have a per-dev-mount
534 * zone ref squirreled away.
535 */
536 static int
prof_zone_matched(char * name,struct sdev_node * dir)537 prof_zone_matched(char *name, struct sdev_node *dir)
538 {
539 vnode_t *gvn = SDEVTOV(dir->sdev_origin);
540 struct pathname pn;
541 vnode_t *vn = NULL;
542 char zonename[ZONENAME_MAX];
543 int znlen = ZONENAME_MAX;
544 int ret;
545
546 ASSERT((dir->sdev_flags & SDEV_ZONED) != 0);
547
548 sdcmn_err10(("sdev_node %p is zoned, looking for %s\n",
549 (void *)dir, name));
550
551 if (pn_get(name, UIO_SYSSPACE, &pn))
552 return (0);
553
554 VN_HOLD(gvn);
555
556 ret = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &vn, rootdir, gvn, kcred);
557
558 pn_free(&pn);
559
560 if (ret != 0) {
561 sdcmn_err10(("prof_zone_matched: %s not found\n", name));
562 return (0);
563 }
564
565 /*
566 * VBLK doesn't matter, and the property name is in fact treated
567 * as a const char *.
568 */
569 ret = e_ddi_getlongprop_buf(vn->v_rdev, VBLK, (char *)"zone",
570 DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, (caddr_t)zonename, &znlen);
571
572 VN_RELE(vn);
573
574 if (ret == DDI_PROP_NOT_FOUND) {
575 sdcmn_err10(("vnode %p: no zone prop\n", (void *)vn));
576 return (0);
577 } else if (ret != DDI_PROP_SUCCESS) {
578 sdcmn_err10(("vnode %p: zone prop error: %d\n",
579 (void *)vn, ret));
580 return (0);
581 }
582
583 sdcmn_err10(("vnode %p zone prop: %s\n", (void *)vn, zonename));
584 return (strcmp(zonename, curproc->p_zone->zone_name) == 0);
585 }
586
587 static int
prof_make_name_glob(char * nm,void * arg)588 prof_make_name_glob(char *nm, void *arg)
589 {
590 struct sdev_node *ddv = (struct sdev_node *)arg;
591
592 if (prof_name_matched(nm, ddv))
593 prof_lookup_globaldev(ddv, ddv->sdev_origin, nm, nm);
594
595 return (WALK_DIR_CONTINUE);
596 }
597
598 static int
prof_make_name_zone(char * nm,void * arg)599 prof_make_name_zone(char *nm, void *arg)
600 {
601 struct sdev_node *ddv = (struct sdev_node *)arg;
602
603 if (prof_zone_matched(nm, ddv))
604 prof_lookup_globaldev(ddv, ddv->sdev_origin, nm, nm);
605
606 return (WALK_DIR_CONTINUE);
607 }
608
609 static void
prof_make_names_walk(struct sdev_node * ddv,int (* cb)(char *,void *))610 prof_make_names_walk(struct sdev_node *ddv, int (*cb)(char *, void *))
611 {
612 struct sdev_node *gdir;
613
614 gdir = ddv->sdev_origin;
615 if (gdir == NULL)
616 return;
617 walk_dir(SDEVTOV(gdir), (void *)ddv, cb);
618 }
619
620 static void
prof_make_names(struct sdev_node * dir)621 prof_make_names(struct sdev_node *dir)
622 {
623 char *name;
624 nvpair_t *nvp = NULL;
625 nvlist_t *nvl = dir->sdev_prof.dev_name;
626 int rv;
627
628 ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
629
630 if ((dir->sdev_flags & SDEV_ZONED) != 0)
631 prof_make_names_walk(dir, prof_make_name_zone);
632
633 if (nvl == NULL)
634 return;
635
636 if (dir->sdev_prof.has_glob) {
637 prof_make_names_walk(dir, prof_make_name_glob);
638 return;
639 }
640
641 /* Walk nvlist and lookup corresponding device in global inst */
642 while (nvp = nvlist_next_nvpair(nvl, nvp)) {
643 int type;
644 rv = nvpair_value_int32(nvp, &type);
645 if (rv != 0) {
646 cmn_err(CE_WARN, sdev_nvp_val_err,
647 rv, nvpair_name(nvp));
648 break;
649 }
650 if (type == PROFILE_TYPE_EXCLUDE)
651 continue;
652 name = nvpair_name(nvp);
653 (void) prof_lookup_globaldev(dir, dir->sdev_origin,
654 name, name);
655 }
656 }
657
658 /*
659 * Return True if directory cache is out of date and should be updated.
660 */
661 static boolean_t
prof_dev_needupdate(sdev_node_t * ddv)662 prof_dev_needupdate(sdev_node_t *ddv)
663 {
664 sdev_node_t *gdir = ddv->sdev_origin;
665
666 /*
667 * Caller can have either reader or writer lock
668 */
669 ASSERT(RW_LOCK_HELD(&ddv->sdev_contents));
670
671 /*
672 * We need to rebuild the directory content if
673 * - ddv is not in a SDEV_ZOMBIE state
674 * - SDEV_BUILD is set OR
675 * - The device tree generation number has changed OR
676 * - The corresponding /dev namespace has been updated
677 */
678 return ((ddv->sdev_state != SDEV_ZOMBIE) &&
679 (((ddv->sdev_flags & SDEV_BUILD) != 0) ||
680 (ddv->sdev_devtree_gen != devtree_gen) ||
681 ((gdir != NULL) &&
682 (ddv->sdev_ldir_gen != gdir->sdev_gdir_gen))));
683 }
684
685 /*
686 * Build directory vnodes based on the profile and the global
687 * dev instance.
688 */
689 void
prof_filldir(sdev_node_t * ddv)690 prof_filldir(sdev_node_t *ddv)
691 {
692 sdev_node_t *gdir;
693
694 ASSERT(RW_READ_HELD(&ddv->sdev_contents));
695
696 if (!prof_dev_needupdate(ddv)) {
697 ASSERT(RW_READ_HELD(&ddv->sdev_contents));
698 return;
699 }
700 /*
701 * Upgrade to writer lock
702 */
703 if (rw_tryupgrade(&ddv->sdev_contents) == 0) {
704 /*
705 * We need to drop the read lock and re-acquire it as a
706 * write lock. While we do this the condition may change so we
707 * need to re-check condition
708 */
709 rw_exit(&ddv->sdev_contents);
710 rw_enter(&ddv->sdev_contents, RW_WRITER);
711 if (!prof_dev_needupdate(ddv)) {
712 /* Downgrade back to the read lock before returning */
713 rw_downgrade(&ddv->sdev_contents);
714 return;
715 }
716 }
717 /* At this point we should have a write lock */
718 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
719
720 sdcmn_err10(("devtree_gen (%s): %ld -> %ld\n",
721 ddv->sdev_path, ddv->sdev_devtree_gen, devtree_gen));
722
723 gdir = ddv->sdev_origin;
724
725 if (gdir != NULL)
726 sdcmn_err10(("sdev_dir_gen (%s): %ld -> %ld\n",
727 ddv->sdev_path, ddv->sdev_ldir_gen,
728 gdir->sdev_gdir_gen));
729
730 /* update flags and generation number so next filldir is quick */
731 if ((ddv->sdev_flags & SDEV_BUILD) == SDEV_BUILD) {
732 ddv->sdev_flags &= ~SDEV_BUILD;
733 }
734 ddv->sdev_devtree_gen = devtree_gen;
735 if (gdir != NULL)
736 ddv->sdev_ldir_gen = gdir->sdev_gdir_gen;
737
738 prof_make_symlinks(ddv);
739 prof_make_maps(ddv);
740 prof_make_names(ddv);
741 rw_downgrade(&ddv->sdev_contents);
742 }
743
744 /* apply include/exclude pattern to existing directory content */
745 static void
apply_dir_pattern(struct sdev_node * dir,char * expr,char * pathleft,int type)746 apply_dir_pattern(struct sdev_node *dir, char *expr, char *pathleft, int type)
747 {
748 struct sdev_node *dv;
749
750 /* leaf pattern */
751 if (pathleft == NULL) {
752 if (type == PROFILE_TYPE_INCLUDE)
753 return; /* nothing to do for include */
754 (void) sdev_cleandir(dir, expr, SDEV_ENFORCE);
755 return;
756 }
757
758 /* directory pattern */
759 rw_enter(&dir->sdev_contents, RW_WRITER);
760
761 for (dv = SDEV_FIRST_ENTRY(dir); dv; dv = SDEV_NEXT_ENTRY(dir, dv)) {
762 if (gmatch(dv->sdev_name, expr) == 0 ||
763 SDEVTOV(dv)->v_type != VDIR)
764 continue;
765 process_rule(dv, dv->sdev_origin,
766 pathleft, NULL, type);
767 }
768 rw_exit(&dir->sdev_contents);
769 }
770
771 /*
772 * Add a profile rule.
773 * tgt represents a device name matching expression,
774 * matching device names are to be either included or excluded.
775 */
776 static void
prof_add_rule(char * name,char * tgt,struct sdev_node * dir,int type)777 prof_add_rule(char *name, char *tgt, struct sdev_node *dir, int type)
778 {
779 int error;
780 nvlist_t **nvlp = NULL;
781 int rv;
782
783 ASSERT(SDEVTOV(dir)->v_type == VDIR);
784
785 rw_enter(&dir->sdev_contents, RW_WRITER);
786
787 switch (type) {
788 case PROFILE_TYPE_INCLUDE:
789 if (tgt)
790 nvlp = &(dir->sdev_prof.dev_glob_incdir);
791 else
792 nvlp = &(dir->sdev_prof.dev_name);
793 break;
794 case PROFILE_TYPE_EXCLUDE:
795 if (tgt)
796 nvlp = &(dir->sdev_prof.dev_glob_excdir);
797 else
798 nvlp = &(dir->sdev_prof.dev_name);
799 break;
800 case PROFILE_TYPE_MAP:
801 nvlp = &(dir->sdev_prof.dev_map);
802 break;
803 case PROFILE_TYPE_SYMLINK:
804 nvlp = &(dir->sdev_prof.dev_symlink);
805 break;
806 };
807
808 /* initialize nvlist */
809 if (*nvlp == NULL) {
810 error = nvlist_alloc(nvlp, NV_UNIQUE_NAME, KM_SLEEP);
811 ASSERT(error == 0);
812 }
813
814 if (tgt) {
815 rv = nvlist_add_string(*nvlp, name, tgt);
816 } else {
817 rv = nvlist_add_int32(*nvlp, name, type);
818 }
819 ASSERT(rv == 0);
820 /* rebuild directory content */
821 dir->sdev_flags |= SDEV_BUILD;
822
823 if ((type == PROFILE_TYPE_INCLUDE) &&
824 (strpbrk(name, "*?[]") != NULL)) {
825 dir->sdev_prof.has_glob = 1;
826 }
827
828 rw_exit(&dir->sdev_contents);
829
830 /* additional details for glob pattern and exclusion */
831 switch (type) {
832 case PROFILE_TYPE_INCLUDE:
833 case PROFILE_TYPE_EXCLUDE:
834 apply_dir_pattern(dir, name, tgt, type);
835 break;
836 };
837 }
838
839 /*
840 * Parse path components and apply requested matching rule at
841 * directory level.
842 */
843 static void
process_rule(struct sdev_node * dir,struct sdev_node * gdir,char * path,char * tgt,int type)844 process_rule(struct sdev_node *dir, struct sdev_node *gdir,
845 char *path, char *tgt, int type)
846 {
847 char *name;
848 struct pathname pn;
849 int rv = 0;
850
851 if ((strlen(path) > 5) && (strncmp(path, "/dev/", 5) == 0)) {
852 path += 5;
853 }
854
855 if (pn_get(path, UIO_SYSSPACE, &pn) != 0)
856 return;
857
858 name = kmem_alloc(MAXPATHLEN, KM_SLEEP);
859 (void) pn_getcomponent(&pn, name);
860 pn_skipslash(&pn);
861 SDEV_HOLD(dir);
862
863 while (pn_pathleft(&pn)) {
864 /* If this is pattern, just add the pattern */
865 if (strpbrk(name, "*?[]") != NULL &&
866 (type == PROFILE_TYPE_INCLUDE ||
867 type == PROFILE_TYPE_EXCLUDE)) {
868 ASSERT(tgt == NULL);
869 tgt = pn.pn_path;
870 break;
871 }
872 if ((rv = prof_make_dir(name, &gdir, &dir)) != 0) {
873 cmn_err(CE_CONT, "process_rule: %s error %d\n",
874 path, rv);
875 break;
876 }
877 (void) pn_getcomponent(&pn, name);
878 pn_skipslash(&pn);
879 }
880
881 /* process the leaf component */
882 if (rv == 0) {
883 prof_add_rule(name, tgt, dir, type);
884 SDEV_SIMPLE_RELE(dir);
885 }
886
887 kmem_free(name, MAXPATHLEN);
888 pn_free(&pn);
889 }
890
891 static int
copyin_nvlist(char * packed_usr,size_t packed_sz,nvlist_t ** nvlp)892 copyin_nvlist(char *packed_usr, size_t packed_sz, nvlist_t **nvlp)
893 {
894 int err = 0;
895 char *packed;
896 nvlist_t *profile = NULL;
897
898 /* simple sanity check */
899 if (packed_usr == NULL || packed_sz == 0)
900 return (err);
901
902 /* copyin packed profile nvlist */
903 packed = kmem_alloc(packed_sz, KM_NOSLEEP);
904 if (packed == NULL)
905 return (ENOMEM);
906 err = copyin(packed_usr, packed, packed_sz);
907
908 /* unpack packed profile nvlist */
909 if (err)
910 cmn_err(CE_WARN, "copyin_nvlist: copyin failed with "
911 "err %d\n", err);
912 else if (err = nvlist_unpack(packed, packed_sz, &profile, KM_NOSLEEP))
913 cmn_err(CE_WARN, "copyin_nvlist: nvlist_unpack "
914 "failed with err %d\n", err);
915
916 kmem_free(packed, packed_sz);
917 if (err == 0)
918 *nvlp = profile;
919 return (err);
920 }
921
922 /*
923 * Process profile passed down from libdevinfo. There are four types
924 * of matching rules:
925 * include: export a name or names matching a pattern
926 * exclude: exclude a name or names matching a pattern
927 * symlink: create a local symlink
928 * map: export a device with a name different from the global zone
929 * Note: We may consider supporting VOP_SYMLINK in non-global instances,
930 * because it does not present any security risk. For now, the fs
931 * instance is read only.
932 */
933 static void
sdev_process_profile(struct sdev_data * sdev_data,nvlist_t * profile)934 sdev_process_profile(struct sdev_data *sdev_data, nvlist_t *profile)
935 {
936 nvpair_t *nvpair;
937 char *nvname, *dname;
938 struct sdev_node *dir, *gdir;
939 char **pair; /* for symlinks and maps */
940 uint_t nelem;
941 int rv;
942
943 gdir = sdev_origins->sdev_root; /* root of global /dev */
944 dir = sdev_data->sdev_root; /* root of current instance */
945
946 ASSERT(profile);
947
948 /* process nvpairs in the list */
949 nvpair = NULL;
950 while (nvpair = nvlist_next_nvpair(profile, nvpair)) {
951 nvname = nvpair_name(nvpair);
952 ASSERT(nvname != NULL);
953
954 if (strcmp(nvname, SDEV_NVNAME_INCLUDE) == 0) {
955 rv = nvpair_value_string(nvpair, &dname);
956 if (rv != 0) {
957 cmn_err(CE_WARN, sdev_nvp_val_err,
958 rv, nvpair_name(nvpair));
959 break;
960 }
961 process_rule(dir, gdir, dname, NULL,
962 PROFILE_TYPE_INCLUDE);
963 } else if (strcmp(nvname, SDEV_NVNAME_EXCLUDE) == 0) {
964 rv = nvpair_value_string(nvpair, &dname);
965 if (rv != 0) {
966 cmn_err(CE_WARN, sdev_nvp_val_err,
967 rv, nvpair_name(nvpair));
968 break;
969 }
970 process_rule(dir, gdir, dname, NULL,
971 PROFILE_TYPE_EXCLUDE);
972 } else if (strcmp(nvname, SDEV_NVNAME_SYMLINK) == 0) {
973 rv = nvpair_value_string_array(nvpair, &pair, &nelem);
974 if (rv != 0) {
975 cmn_err(CE_WARN, sdev_nvp_val_err,
976 rv, nvpair_name(nvpair));
977 break;
978 }
979 ASSERT(nelem == 2);
980 process_rule(dir, gdir, pair[0], pair[1],
981 PROFILE_TYPE_SYMLINK);
982 } else if (strcmp(nvname, SDEV_NVNAME_MAP) == 0) {
983 rv = nvpair_value_string_array(nvpair, &pair, &nelem);
984 if (rv != 0) {
985 cmn_err(CE_WARN, sdev_nvp_val_err,
986 rv, nvpair_name(nvpair));
987 break;
988 }
989 process_rule(dir, gdir, pair[1], pair[0],
990 PROFILE_TYPE_MAP);
991 } else if (strcmp(nvname, SDEV_NVNAME_MOUNTPT) != 0) {
992 cmn_err(CE_WARN, "sdev_process_profile: invalid "
993 "nvpair %s\n", nvname);
994 }
995 }
996 }
997
998 /*ARGSUSED*/
999 int
prof_lookup(vnode_t * dvp,char * nm,struct vnode ** vpp,struct cred * cred)1000 prof_lookup(vnode_t *dvp, char *nm, struct vnode **vpp, struct cred *cred)
1001 {
1002 struct sdev_node *ddv = VTOSDEV(dvp);
1003 struct sdev_node *dv;
1004 int nmlen;
1005
1006 /*
1007 * Empty name or ., return node itself.
1008 */
1009 nmlen = strlen(nm);
1010 if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
1011 *vpp = SDEVTOV(ddv);
1012 VN_HOLD(*vpp);
1013 return (0);
1014 }
1015
1016 /*
1017 * .., return the parent directory
1018 */
1019 if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
1020 *vpp = SDEVTOV(ddv->sdev_dotdot);
1021 VN_HOLD(*vpp);
1022 return (0);
1023 }
1024
1025 rw_enter(&ddv->sdev_contents, RW_READER);
1026 dv = sdev_cache_lookup(ddv, nm);
1027 if (dv == NULL) {
1028 prof_filldir(ddv);
1029 dv = sdev_cache_lookup(ddv, nm);
1030 }
1031 rw_exit(&ddv->sdev_contents);
1032 if (dv == NULL) {
1033 sdcmn_err10(("prof_lookup: %s not found\n", nm));
1034 return (ENOENT);
1035 }
1036
1037 return (sdev_to_vp(dv, vpp));
1038 }
1039
1040 /*
1041 * This is invoked after a new filesystem is mounted to define the
1042 * name space. It is also invoked during normal system operation
1043 * to update the name space.
1044 *
1045 * Applications call di_prof_commit() in libdevinfo, which invokes
1046 * modctl(). modctl calls this function. The input is a packed nvlist.
1047 */
1048 int
devname_profile_update(char * packed,size_t packed_sz)1049 devname_profile_update(char *packed, size_t packed_sz)
1050 {
1051 char *mntpt;
1052 nvlist_t *nvl;
1053 nvpair_t *nvp;
1054 struct sdev_data *mntinfo;
1055 int err;
1056 int rv;
1057
1058 nvl = NULL;
1059 if ((err = copyin_nvlist(packed, packed_sz, &nvl)) != 0)
1060 return (err);
1061 ASSERT(nvl);
1062
1063 /* The first nvpair must be the mount point */
1064 nvp = nvlist_next_nvpair(nvl, NULL);
1065 if (strcmp(nvpair_name(nvp), SDEV_NVNAME_MOUNTPT) != 0) {
1066 cmn_err(CE_NOTE,
1067 "devname_profile_update: mount point not specified");
1068 nvlist_free(nvl);
1069 return (EINVAL);
1070 }
1071
1072 /* find the matching filesystem instance */
1073 rv = nvpair_value_string(nvp, &mntpt);
1074 if (rv != 0) {
1075 cmn_err(CE_WARN, sdev_nvp_val_err,
1076 rv, nvpair_name(nvp));
1077 } else {
1078 mntinfo = sdev_find_mntinfo(mntpt);
1079 if (mntinfo == NULL) {
1080 cmn_err(CE_NOTE, "devname_profile_update: "
1081 " mount point %s not found", mntpt);
1082 nvlist_free(nvl);
1083 return (EINVAL);
1084 }
1085
1086 /* now do the hardwork to process the profile */
1087 sdev_process_profile(mntinfo, nvl);
1088
1089 sdev_mntinfo_rele(mntinfo);
1090 }
1091
1092 nvlist_free(nvl);
1093 return (0);
1094 }
1095