sdev_profile.c revision 45b1747515a17db45e8971501ee84a26bdff37b2
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2016, Joyent Inc.
*/
/*
* This file implements /dev filesystem operations for non-global
* instances. Three major entry points:
* devname_profile_update()
* Update matching rules determining which names to export
* prof_readdir()
* Return the list of exported names
* prof_lookup()
* Implements lookup
*/
#include <sys/sysmacros.h>
#include <sys/pathname.h>
enum {
};
enum {
WALK_DIR_CONTINUE = 0,
};
static const char *sdev_nvp_val_err = "nvpair_value error %d, %s\n";
char *, char *, int);
static void
{
/* get attribute from shadow, if present; else get default */
/* always create shadow directory */
*vap = sdev_vattr_dir;
sdcmn_err10(("prof_getattr: failed to create "
}
} else {
/*
* get default permission from devfs
* Before calling devfs_get_defattr, we need to get
* the realvp (the dv_node). If realvp is not a dv_node,
* devfs_get_defattr() will return a system-wide default
* attr for device nodes.
*/
}
/* ignore dev_t and vtype from backing store */
if (gdv) {
}
}
static void
{
char *name;
int rv = 0;
return;
char *pathleft;
continue;
if (rv != 0) {
break;
}
}
}
/*
* Some commonality here with sdev_mknode(), could be simplified.
* NOTE: prof_mknode returns with *newdv held once, if success.
*/
static int
{
int rv;
/* check cache first */
return (0);
}
/* allocate node and insert into cache */
if (rv != 0) {
return (rv);
}
/* put it in ready state */
/* handle glob pattern in the middle of a path */
if (rv == 0) {
sdcmn_err10(("sdev_origin for %s set to 0x%p\n",
} else {
}
return (rv);
}
/*
* 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
{
int error;
/* see if name already exists */
return (0);
}
/* find corresponding dir node in global dev */
if (gdir) {
if (error == 0) {
} else { /* it's ok if there no global dir */
}
}
/* get attribute from shadow, also create shadow dir */
/* create dev directory vnode */
kcred);
if (error == 0) {
}
return (error);
}
/*
* 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
*/
static void
{
int error;
/* check if node already exists */
if (newdv) {
return;
}
/* sanity check arguments */
return;
/* perform a relative lookup of the global /dev instance */
if (error) {
return;
}
/*
* 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
*/
} else
}
}
static void
{
}
}
/*
* Create symlinks in the current directory based on profile
*/
static void
{
int rv;
return;
if (rv != 0) {
break;
}
}
}
static void
{
int rv;
return;
char *name;
if (rv != 0) {
break;
}
}
}
struct match_arg {
char *expr;
int match;
};
static int
{
return (WALK_DIR_TERMINATE);
}
return (WALK_DIR_CONTINUE);
}
static int
{
return (0);
return (0);
}
return (0);
}
}
/* Check if name passes matching rules */
int
{
char *expr;
int rv;
if (rv != 0) {
break;
}
if (type == PROFILE_TYPE_EXCLUDE) {
return (0); /* excluded */
} else if (!match) {
}
}
if (match) {
return (match);
}
/* check for match against directory globbing pattern */
char *pathleft;
continue;
if (rv != 0) {
break;
}
return (1);
}
}
return (0);
}
static void
{
char *nm;
dlen = 4096;
uio.uio_loffset = 0;
eof = 0;
error = 0;
break;
continue;
goto end;
}
}
end:
}
/*
* 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
{
char zonename[ZONENAME_MAX];
int znlen = ZONENAME_MAX;
int ret;
sdcmn_err10(("sdev_node %p is zoned, looking for %s\n",
return (0);
if (ret != 0) {
return (0);
}
/*
* VBLK doesn't matter, and the property name is in fact treated
* as a const char *.
*/
if (ret == DDI_PROP_NOT_FOUND) {
return (0);
} else if (ret != DDI_PROP_SUCCESS) {
sdcmn_err10(("vnode %p: zone prop error: %d\n",
return (0);
}
}
static int
{
return (WALK_DIR_CONTINUE);
}
static int
{
return (WALK_DIR_CONTINUE);
}
static void
{
return;
}
static void
{
char *name;
int rv;
return;
return;
}
/* Walk nvlist and lookup corresponding device in global inst */
int type;
if (rv != 0) {
break;
}
if (type == PROFILE_TYPE_EXCLUDE)
continue;
}
}
/*
* Return True if directory cache is out of date and should be updated.
*/
static boolean_t
{
/*
* Caller can have either reader or writer lock
*/
/*
* We need to rebuild the directory content if
* - ddv is not in a SDEV_ZOMBIE state
* - SDEV_BUILD is set OR
* - The device tree generation number has changed OR
* - The corresponding /dev namespace has been updated
*/
}
/*
* Build directory vnodes based on the profile and the global
* dev instance.
*/
void
{
if (!prof_dev_needupdate(ddv)) {
return;
}
/*
* Upgrade to writer lock
*/
/*
* We need to drop the read lock and re-acquire it as a
* write lock. While we do this the condition may change so we
* need to re-check condition
*/
if (!prof_dev_needupdate(ddv)) {
/* Downgrade back to the read lock before returning */
return;
}
}
/* At this point we should have a write lock */
sdcmn_err10(("devtree_gen (%s): %ld -> %ld\n",
sdcmn_err10(("sdev_dir_gen (%s): %ld -> %ld\n",
gdir->sdev_gdir_gen));
/* update flags and generation number so next filldir is quick */
}
}
static void
{
/* leaf pattern */
if (type == PROFILE_TYPE_INCLUDE)
return; /* nothing to do for include */
return;
}
/* directory pattern */
continue;
}
}
/*
* Add a profile rule.
* tgt represents a device name matching expression,
* matching device names are to be either included or excluded.
*/
static void
{
int error;
int rv;
switch (type) {
case PROFILE_TYPE_INCLUDE:
if (tgt)
else
break;
case PROFILE_TYPE_EXCLUDE:
if (tgt)
else
break;
case PROFILE_TYPE_MAP:
break;
case PROFILE_TYPE_SYMLINK:
break;
};
/* initialize nvlist */
}
if (tgt) {
} else {
}
/* rebuild directory content */
if ((type == PROFILE_TYPE_INCLUDE) &&
}
/* additional details for glob pattern and exclusion */
switch (type) {
case PROFILE_TYPE_INCLUDE:
case PROFILE_TYPE_EXCLUDE:
break;
};
}
/*
* Parse path components and apply requested matching rule at
* directory level.
*/
static void
{
char *name;
int rv = 0;
path += 5;
}
return;
pn_skipslash(&pn);
while (pn_pathleft(&pn)) {
/* If this is pattern, just add the pattern */
(type == PROFILE_TYPE_INCLUDE ||
type == PROFILE_TYPE_EXCLUDE)) {
break;
}
break;
}
pn_skipslash(&pn);
}
/* process the leaf component */
if (rv == 0) {
}
}
static int
{
int err = 0;
char *packed;
/* simple sanity check */
return (NULL);
/* copyin packed profile nvlist */
return (ENOMEM);
/* unpack packed profile nvlist */
if (err)
"err %d\n", err);
"failed with err %d\n", err);
if (err == 0)
return (err);
}
/*
* Process profile passed down from libdevinfo. There are four types
* of matching rules:
* include: export a name or names matching a pattern
* exclude: exclude a name or names matching a pattern
* symlink: create a local symlink
* map: export a device with a name different from the global zone
* Note: We may consider supporting VOP_SYMLINK in non-global instances,
* because it does not present any security risk. For now, the fs
* instance is read only.
*/
static void
{
char **pair; /* for symlinks and maps */
int rv;
/* process nvpairs in the list */
if (rv != 0) {
break;
}
if (rv != 0) {
break;
}
if (rv != 0) {
break;
}
if (rv != 0) {
break;
}
"nvpair %s\n", nvname);
}
}
}
/*ARGSUSED*/
int
{
int nmlen;
/*
* Empty name or ., return node itself.
*/
return (0);
}
/*
* .., return the parent directory
*/
return (0);
}
}
return (ENOENT);
}
}
/*
* This is invoked after a new filesystem is mounted to define the
* name space. It is also invoked during normal system operation
* to update the name space.
*
* Applications call di_prof_commit() in libdevinfo, which invokes
* modctl(). modctl calls this function. The input is a packed nvlist.
*/
int
{
char *mntpt;
int err;
int rv;
return (err);
/* The first nvpair must be the mount point */
"devname_profile_update: mount point not specified");
return (EINVAL);
}
/* find the matching filesystem instance */
if (rv != 0) {
} else {
" mount point %s not found", mntpt);
return (EINVAL);
}
/* now do the hardwork to process the profile */
}
return (0);
}