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