Example #1
0
/* apply include/exclude pattern to existing directory content */
static void
apply_dir_pattern(struct sdev_node *dir, char *expr, char *pathleft, int type)
{
	struct sdev_node *dv;

	/* leaf pattern */
	if (pathleft == NULL) {
		if (type == PROFILE_TYPE_INCLUDE)
			return;	/* nothing to do for include */
		(void) sdev_cleandir(dir, expr, SDEV_ENFORCE);
		return;
	}

	/* directory pattern */
	rw_enter(&dir->sdev_contents, RW_WRITER);

	for (dv = SDEV_FIRST_ENTRY(dir); dv; dv = SDEV_NEXT_ENTRY(dir, dv)) {
		if (gmatch(dv->sdev_name, expr) == 0 ||
		    SDEVTOV(dv)->v_type != VDIR)
			continue;
		process_rule(dv, dv->sdev_origin,
		    pathleft, NULL, type);
	}
	rw_exit(&dir->sdev_contents);
}
Example #2
0
/*ARGSUSED3*/
static int
devpts_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)
{
	struct sdev_node *sdvp = VTOSDEV(dvp);
	struct sdev_node *dv;
	struct vnode *rvp = NULL;
	int error;

	error = devname_lookup_func(sdvp, nm, vpp, cred, devpts_create_rvp,
	    SDEV_VATTR);

	if (error == 0) {
		switch ((*vpp)->v_type) {
		case VCHR:
			dv = VTOSDEV(VTOS(*vpp)->s_realvp);
			ASSERT(VOP_REALVP(SDEVTOV(dv), &rvp, NULL) == ENOSYS);
			break;
		case VDIR:
			dv = VTOSDEV(*vpp);
			break;
		default:
			cmn_err(CE_PANIC, "devpts_lookup: Unsupported node "
			    "type: %p: %d", (void *)(*vpp), (*vpp)->v_type);
			break;
		}
		ASSERT(SDEV_HELD(dv));
	}

	return (error);
}
Example #3
0
static int
is_nonempty_dir(char *name, char *pathleft, struct sdev_node *dir)
{
	struct match_arg marg;
	struct pathname pn;
	struct vnode *gvp;
	struct sdev_node *gdir = dir->sdev_origin;

	if (VOP_LOOKUP(SDEVTOV(gdir), name, &gvp, NULL, 0, NULL, kcred,
	    NULL, NULL, NULL) != 0)
		return (0);

	if (gvp->v_type != VDIR) {
		VN_RELE(gvp);
		return (0);
	}

	if (pn_get(pathleft, UIO_SYSSPACE, &pn) != 0) {
		VN_RELE(gvp);
		return (0);
	}

	marg.expr = kmem_alloc(MAXNAMELEN, KM_SLEEP);
	(void) pn_getcomponent(&pn, marg.expr);
	marg.match = 0;

	walk_dir(gvp, &marg, match_name);
	VN_RELE(gvp);
	kmem_free(marg.expr, MAXNAMELEN);
	pn_free(&pn);

	return (marg.match);
}
Example #4
0
static void
apply_glob_pattern(struct sdev_node *pdir, struct sdev_node *cdir)
{
	char *name;
	nvpair_t *nvp = NULL;
	nvlist_t *nvl;
	struct vnode *vp = SDEVTOV(cdir);
	int rv = 0;

	if (vp->v_type != VDIR)
		return;
	name = cdir->sdev_name;
	nvl = pdir->sdev_prof.dev_glob_incdir;
	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
		char *pathleft;
		char *expr = nvpair_name(nvp);
		if (!gmatch(name, expr))
			continue;
		rv = nvpair_value_string(nvp, &pathleft);
		if (rv != 0) {
			cmn_err(CE_WARN, sdev_nvp_val_err,
			    rv, nvpair_name(nvp));
			break;
		}
		process_rule(cdir, cdir->sdev_origin,
		    pathleft, NULL, PROFILE_TYPE_INCLUDE);
	}
}
Example #5
0
static int
devpts_set_id(struct sdev_node *dv, struct vattr *vap, int protocol)
{
	ASSERT((protocol & AT_UID) || (protocol & AT_GID));
	ptms_set_owner(getminor(SDEVTOV(dv)->v_rdev),
	    vap->va_uid, vap->va_gid);
	return (0);

}
Example #6
0
static void
prof_make_names_walk(struct sdev_node *ddv, int (*cb)(char *, void *))
{
	struct sdev_node *gdir;

	gdir = ddv->sdev_origin;
	if (gdir == NULL)
		return;
	walk_dir(SDEVTOV(gdir), (void *)ddv, cb);
}
Example #7
0
/*
 * Look up a logical name in the global zone.
 * Provides the ability to map the global zone's device name
 * to an alternate name within a zone.  The primary example
 * is the virtual console device /dev/zcons/[zonename]/zconsole
 * mapped to /[zonename]/root/dev/zconsole.
 */
static void
prof_lookup_globaldev(struct sdev_node *dir, struct sdev_node *gdir,
    char *name, char *rename)
{
	int error;
	struct vnode *avp, *gdv, *gddv;
	struct sdev_node *newdv;
	struct vattr vattr = {0};
	struct pathname pn;

	/* check if node already exists */
	newdv = sdev_cache_lookup(dir, rename);
	if (newdv) {
		ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
		SDEV_SIMPLE_RELE(newdv);
		return;
	}

	/* sanity check arguments */
	if (!gdir || pn_get(name, UIO_SYSSPACE, &pn))
		return;

	/* perform a relative lookup of the global /dev instance */
	gddv = SDEVTOV(gdir);
	VN_HOLD(gddv);
	error = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &gdv,
	    rootdir, gddv, kcred);
	pn_free(&pn);
	if (error) {
		sdcmn_err10(("prof_lookup_globaldev: %s not found\n", name));
		return;
	}
	ASSERT(gdv && gdv->v_type != VLNK);

	/*
	 * Found the entry in global /dev, figure out attributes
	 * by looking at backing store. Call into devfs for default.
	 * Note, mapped device is persisted under the new name
	 */
	prof_getattr(dir, rename, gdv, &vattr, &avp, NULL);

	if (gdv->v_type != VDIR) {
		VN_RELE(gdv);
		gdir = NULL;
	} else
		gdir = VTOSDEV(gdv);

	if (prof_mknode(dir, rename, &newdv, &vattr, avp,
	    (void *)gdir, kcred) == 0) {
		ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
		SDEV_SIMPLE_RELE(newdv);
	}
}
Example #8
0
/*ARGSUSED*/
int
prof_lookup(vnode_t *dvp, char *nm, struct vnode **vpp, struct cred *cred)
{
	struct sdev_node *ddv = VTOSDEV(dvp);
	struct sdev_node *dv;
	int nmlen;

	/*
	 * Empty name or ., return node itself.
	 */
	nmlen = strlen(nm);
	if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
		*vpp = SDEVTOV(ddv);
		VN_HOLD(*vpp);
		return (0);
	}

	/*
	 * .., return the parent directory
	 */
	if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
		*vpp = SDEVTOV(ddv->sdev_dotdot);
		VN_HOLD(*vpp);
		return (0);
	}

	rw_enter(&ddv->sdev_contents, RW_READER);
	dv = sdev_cache_lookup(ddv, nm);
	if (dv == NULL) {
		prof_filldir(ddv);
		dv = sdev_cache_lookup(ddv, nm);
	}
	rw_exit(&ddv->sdev_contents);
	if (dv == NULL) {
		sdcmn_err10(("prof_lookup: %s not found\n", nm));
		return (ENOENT);
	}

	return (sdev_to_vp(dv, vpp));
}
Example #9
0
/*
 * Last chance for a zone to see a node.  If our parent dir is
 * SDEV_ZONED, then we look up the "zone" property for the node.  If the
 * property is found and matches the current zone name, we allow it.
 * Note that this isn't quite correct for the global zone peeking inside
 * a zone's /dev - for that to work, we'd have to have a per-dev-mount
 * zone ref squirreled away.
 */
static int
prof_zone_matched(char *name, struct sdev_node *dir)
{
	vnode_t *gvn = SDEVTOV(dir->sdev_origin);
	struct pathname pn;
	vnode_t *vn = NULL;
	char zonename[ZONENAME_MAX];
	int znlen = ZONENAME_MAX;
	int ret;

	ASSERT((dir->sdev_flags & SDEV_ZONED) != 0);

	sdcmn_err10(("sdev_node %p is zoned, looking for %s\n",
	    (void *)dir, name));

	if (pn_get(name, UIO_SYSSPACE, &pn))
		return (0);

	VN_HOLD(gvn);

	ret = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &vn, rootdir, gvn, kcred);

	pn_free(&pn);

	if (ret != 0) {
		sdcmn_err10(("prof_zone_matched: %s not found\n", name));
		return (0);
	}

	/*
	 * VBLK doesn't matter, and the property name is in fact treated
	 * as a const char *.
	 */
	ret = e_ddi_getlongprop_buf(vn->v_rdev, VBLK, (char *)"zone",
	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, (caddr_t)zonename, &znlen);

	VN_RELE(vn);

	if (ret == DDI_PROP_NOT_FOUND) {
		sdcmn_err10(("vnode %p: no zone prop\n", (void *)vn));
		return (0);
	} else if (ret != DDI_PROP_SUCCESS) {
		sdcmn_err10(("vnode %p: zone prop error: %d\n",
		    (void *)vn, ret));
		return (0);
	}

	sdcmn_err10(("vnode %p zone prop: %s\n", (void *)vn, zonename));
	return (strcmp(zonename, curproc->p_zone->zone_name) == 0);
}
Example #10
0
/*
 * Create a directory node in a non-global dev instance.
 * Always create shadow vnode. Set sdev_origin to the corresponding
 * global directory sdev_node if it exists. This facilitates the
 * lookup operation.
 */
static int
prof_make_dir(char *name, struct sdev_node **gdirp, struct sdev_node **dirp)
{
	struct sdev_node *dir = *dirp;
	struct sdev_node *gdir = *gdirp;
	struct sdev_node *newdv;
	struct vnode *avp, *gnewdir = NULL;
	struct vattr vattr;
	int error;

	/* see if name already exists */
	rw_enter(&dir->sdev_contents, RW_READER);
	if (newdv = sdev_cache_lookup(dir, name)) {
		*dirp = newdv;
		*gdirp = newdv->sdev_origin;
		rw_exit(&dir->sdev_contents);
		SDEV_RELE(dir);
		return (0);
	}
	rw_exit(&dir->sdev_contents);

	/* find corresponding dir node in global dev */
	if (gdir) {
		error = VOP_LOOKUP(SDEVTOV(gdir), name, &gnewdir,
		    NULL, 0, NULL, kcred, NULL, NULL, NULL);
		if (error == 0) {
			*gdirp = VTOSDEV(gnewdir);
		} else { 	/* it's ok if there no global dir */
			*gdirp = NULL;
		}
	}

	/* get attribute from shadow, also create shadow dir */
	prof_getattr(dir, name, gnewdir, &vattr, &avp, NULL);

	/* create dev directory vnode */
	rw_enter(&dir->sdev_contents, RW_WRITER);
	error = prof_mknode(dir, name, &newdv, &vattr, avp, (void *)*gdirp,
	    kcred);
	rw_exit(&dir->sdev_contents);
	if (error == 0) {
		ASSERT(newdv);
		*dirp = newdv;
	}
	SDEV_RELE(dir);
	return (error);
}
Example #11
0
/*
 * Clean pts sdev_nodes that are no longer valid.
 */
static void
devpts_prunedir(struct sdev_node *ddv)
{
	struct vnode *vp;
	struct sdev_node *dv, *next = NULL;
	int (*vtor)(struct sdev_node *) = NULL;

	ASSERT(ddv->sdev_flags & SDEV_VTOR);

	vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv);
	ASSERT(vtor);

	if (rw_tryupgrade(&ddv->sdev_contents) == NULL) {
		rw_exit(&ddv->sdev_contents);
		rw_enter(&ddv->sdev_contents, RW_WRITER);
	}

	for (dv = ddv->sdev_dot; dv; dv = next) {
		next = dv->sdev_next;

		/* skip stale nodes */
		if (dv->sdev_flags & SDEV_STALE)
			continue;

		/* validate and prune only ready nodes */
		if (dv->sdev_state != SDEV_READY)
			continue;

		switch (vtor(dv)) {
		case SDEV_VTOR_VALID:
		case SDEV_VTOR_SKIP:
			continue;
		case SDEV_VTOR_INVALID:
			sdcmn_err7(("prunedir: destroy invalid "
			    "node: %s(%p)\n", dv->sdev_name, (void *)dv));
			break;
		}
		vp = SDEVTOV(dv);
		if (vp->v_count > 0)
			continue;
		SDEV_HOLD(dv);
		/* remove the cache node */
		(void) sdev_cache_update(ddv, &dv, dv->sdev_name,
		    SDEV_CACHE_DELETE);
	}
	rw_downgrade(&ddv->sdev_contents);
}
Example #12
0
/*
 * Lookup: filter out entries in the negative cache
 * Return 1 if the lookup should not cause a reconfig.
 */
int
sdev_lookup_filter(sdev_node_t *dv, char *nm)
{
	int n;
	sdev_nc_list_t *ncl = sdev_ncache;
	sdev_nc_node_t *lp;
	char *path;
	int rval = 0;
	int changed = 0;

	ASSERT(i_ddi_io_initialized());
	ASSERT(SDEVTOV(dv)->v_type == VDIR);

	if (sdev_nc_disable)
		return (0);

	n = strlen(dv->sdev_path) + strlen(nm) + 2;
	path = kmem_alloc(n, KM_SLEEP);
	(void) sprintf(path, "%s/%s", dv->sdev_path, nm);

	rw_enter(&ncl->ncl_lock, RW_READER);
	if ((lp = sdev_nc_findpath(ncl, path)) != NULL) {
		sdcmn_err5(("%s/%s: lookup by %s cached, no reconfig\n",
		    dv->sdev_name, nm, curproc->p_user.u_comm));
		if (sdev_nc_verbose) {
			cmn_err(CE_CONT,
			    "?%s/%s: lookup by %s cached, no reconfig\n",
			    dv->sdev_name, nm, curproc->p_user.u_comm);
		}
		mutex_enter(&ncl->ncl_mutex);
		lp->ncn_flags |= NCN_ACTIVE;
		if (sdev_nc_expirecnt > 0 && lp->ncn_expirecnt > 0 &&
		    lp->ncn_expirecnt < sdev_nc_expirecnt) {
			lp->ncn_expirecnt = sdev_nc_expirecnt;
			ncl->ncl_flags |= NCL_LIST_DIRTY;
			changed = 1;
		}
		mutex_exit(&ncl->ncl_mutex);
		rval = 1;
	}
	rw_exit(&ncl->ncl_lock);
	kmem_free(path, n);
	if (changed)
		sdev_nc_flush_boot_update();
	return (rval);
}
Example #13
0
/*ARGSUSED3*/
static int
devvt_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)
{
	struct sdev_node *sdvp = VTOSDEV(dvp);
	struct sdev_node *dv;
	struct vnode *rvp = NULL;
	int type, error;

	if ((strcmp(nm, DEVVT_ACTIVE_NAME) == 0) ||
	    (strcmp(nm, DEVVT_CONSUSER_NAME) == 0)) {
		type = SDEV_VLINK;
	} else {
		type = SDEV_VATTR;
	}

/* Give warlock a more clear call graph */
#ifndef __lock_lint
	error = devname_lookup_func(sdvp, nm, vpp, cred,
	    devvt_create_rvp, type);
#else
	devvt_create_rvp(0, 0, 0, 0, 0, 0);
#endif

	if (error == 0) {
		switch ((*vpp)->v_type) {
		case VCHR:
			dv = VTOSDEV(VTOS(*vpp)->s_realvp);
			ASSERT(VOP_REALVP(SDEVTOV(dv), &rvp, NULL) == ENOSYS);
			break;
		case VDIR:
		case VLNK:
			dv = VTOSDEV(*vpp);
			break;
		default:
			cmn_err(CE_PANIC, "devvt_lookup: Unsupported node "
			    "type: %p: %d", (void *)(*vpp), (*vpp)->v_type);
			break;
		}
		ASSERT(SDEV_HELD(dv));
	}

	return (error);
}
Example #14
0
/*
 * Some commonality here with sdev_mknode(), could be simplified.
 * NOTE: prof_mknode returns with *newdv held once, if success.
 */
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)
{
	struct sdev_node *dv;
	int rv;

	ASSERT(RW_WRITE_HELD(&dir->sdev_contents));

	/* check cache first */
	if (dv = sdev_cache_lookup(dir, name)) {
		*newdv = dv;
		return (0);
	}

	/* allocate node and insert into cache */
	rv = sdev_nodeinit(dir, name, &dv, NULL);
	if (rv != 0) {
		*newdv = NULL;
		return (rv);
	}

	sdev_cache_update(dir, &dv, name, SDEV_CACHE_ADD);
	*newdv = dv;

	/* put it in ready state */
	rv = sdev_nodeready(*newdv, vap, avp, arg, cred);

	/* handle glob pattern in the middle of a path */
	if (rv == 0) {
		if (SDEVTOV(*newdv)->v_type == VDIR)
			sdcmn_err10(("sdev_origin for %s set to 0x%p\n",
			    name, arg));
		apply_glob_pattern(dir, *newdv);
	} else {
		sdev_cache_update(dir, &dv, name, SDEV_CACHE_DELETE);
		SDEV_RELE(dv);
	}
	return (rv);
}
Example #15
0
/*
 * First step in refreshing directory contents.
 * Remove each invalid entry and rebuild the link
 * reference for each stale entry.
 */
static void
devvt_prunedir(struct sdev_node *ddv)
{
	struct vnode *vp;
	struct sdev_node *dv, *next = NULL;
	int (*vtor)(struct sdev_node *) = NULL;

	ASSERT(ddv->sdev_flags & SDEV_VTOR);

	vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv);
	ASSERT(vtor);

	for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) {
		next = SDEV_NEXT_ENTRY(ddv, dv);

		switch (vtor(dv)) {
		case SDEV_VTOR_VALID:
			break;
		case SDEV_VTOR_SKIP:
			break;
		case SDEV_VTOR_INVALID:
			vp = SDEVTOV(dv);
			if (vp->v_count != 0)
				break;
			/* remove the cached node */
			SDEV_HOLD(dv);
			(void) sdev_cache_update(ddv, &dv,
			    dv->sdev_name, SDEV_CACHE_DELETE);
			SDEV_RELE(dv);
			break;
		case SDEV_VTOR_STALE:
			devvt_rebuild_stale_link(ddv, dv);
			break;
		}
	}
}
Example #16
0
/*
 * Add a profile rule.
 * tgt represents a device name matching expression,
 * matching device names are to be either included or excluded.
 */
static void
prof_add_rule(char *name, char *tgt, struct sdev_node *dir, int type)
{
	int error;
	nvlist_t **nvlp = NULL;
	int rv;

	ASSERT(SDEVTOV(dir)->v_type == VDIR);

	rw_enter(&dir->sdev_contents, RW_WRITER);

	switch (type) {
	case PROFILE_TYPE_INCLUDE:
		if (tgt)
			nvlp = &(dir->sdev_prof.dev_glob_incdir);
		else
			nvlp = &(dir->sdev_prof.dev_name);
		break;
	case PROFILE_TYPE_EXCLUDE:
		if (tgt)
			nvlp = &(dir->sdev_prof.dev_glob_excdir);
		else
			nvlp = &(dir->sdev_prof.dev_name);
		break;
	case PROFILE_TYPE_MAP:
		nvlp = &(dir->sdev_prof.dev_map);
		break;
	case PROFILE_TYPE_SYMLINK:
		nvlp = &(dir->sdev_prof.dev_symlink);
		break;
	};

	/* initialize nvlist */
	if (*nvlp == NULL) {
		error = nvlist_alloc(nvlp, NV_UNIQUE_NAME, KM_SLEEP);
		ASSERT(error == 0);
	}

	if (tgt) {
		rv = nvlist_add_string(*nvlp, name, tgt);
	} else {
		rv = nvlist_add_int32(*nvlp, name, type);
	}
	ASSERT(rv == 0);
	/* rebuild directory content */
	dir->sdev_flags |= SDEV_BUILD;

	if ((type == PROFILE_TYPE_INCLUDE) &&
	    (strpbrk(name, "*?[]") != NULL)) {
			dir->sdev_prof.has_glob = 1;
	}

	rw_exit(&dir->sdev_contents);

	/* additional details for glob pattern and exclusion */
	switch (type) {
	case PROFILE_TYPE_INCLUDE:
	case PROFILE_TYPE_EXCLUDE:
		apply_dir_pattern(dir, name, tgt, type);
		break;
	};
}