sdev_subr.c revision b774fca88bff9e11d96e3b40f4d259d278048a3a
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* utility routines for the /dev fs
*/
#include <sys/sysmacros.h>
#include <sys/pathname.h>
#ifdef DEBUG
int sdev_debug = 0x00000001;
int sdev_debug_cache_flags = 0;
#endif
/*
* globals
*/
/* prototype memory vattrs */
VDIR, /* va_type */
SDEV_DIRMODE_DEFAULT, /* va_mode */
SDEV_UID_DEFAULT, /* va_uid */
SDEV_GID_DEFAULT, /* va_gid */
0, /* va_fsid */
0, /* va_nodeid */
0, /* va_nlink */
0, /* va_size */
0, /* va_atime */
0, /* va_mtime */
0, /* va_ctime */
0, /* va_rdev */
0, /* va_blksize */
0, /* va_nblocks */
0 /* va_vcode */
};
VLNK, /* va_type */
SDEV_LNKMODE_DEFAULT, /* va_mode */
SDEV_UID_DEFAULT, /* va_uid */
SDEV_GID_DEFAULT, /* va_gid */
0, /* va_fsid */
0, /* va_nodeid */
0, /* va_nlink */
0, /* va_size */
0, /* va_atime */
0, /* va_mtime */
0, /* va_ctime */
0, /* va_rdev */
0, /* va_blksize */
0, /* va_nblocks */
0 /* va_vcode */
};
VBLK, /* va_type */
SDEV_UID_DEFAULT, /* va_uid */
SDEV_GID_DEFAULT, /* va_gid */
0, /* va_fsid */
0, /* va_nodeid */
0, /* va_nlink */
0, /* va_size */
0, /* va_atime */
0, /* va_mtime */
0, /* va_ctime */
0, /* va_rdev */
0, /* va_blksize */
0, /* va_nblocks */
0 /* va_vcode */
};
VCHR, /* va_type */
SDEV_UID_DEFAULT, /* va_uid */
SDEV_GID_DEFAULT, /* va_gid */
0, /* va_fsid */
0, /* va_nodeid */
0, /* va_nlink */
0, /* va_size */
0, /* va_atime */
0, /* va_mtime */
0, /* va_ctime */
0, /* va_rdev */
0, /* va_blksize */
0, /* va_nblocks */
0 /* va_vcode */
};
int devtype; /* fstype */
/* static */
/* contents from /etc/dev/devname_master */
static int devname_nsmaps_invalidated = 0; /* "devfsadm -m" has run */
static void sdev_set_no_nocache(struct sdev_node *);
static int sdev_get_moduleops(struct sdev_node *);
static void sdev_handle_alloc(struct sdev_node *);
static void sdev_free_vtab(fs_operation_def_t *);
static void
{
}
/*
* sdev_node cache constructor
*/
/*ARGSUSED1*/
static int
{
return (0);
}
/* sdev_node destructor for kmem cache */
/*ARGSUSED1*/
static void
{
}
/* initialize sdev_node cache */
void
{
int flags = 0;
#ifdef DEBUG
if (flags)
#endif /* DEBUG */
}
/* destroy sdev_node cache */
void
{
}
void
{
}
static void
{
gethrestime(&now);
}
/* alloc and initialize a sdev_node */
int
{
if (nmlen > MAXNAMELEN) {
sdcmn_err9(("sdev_nodeinit: node name %s"
" too long\n", nm));
return (ENAMETOOLONG);
}
/* overwritten for VLNK nodes */
if (vap)
/*
* initialized to the parent's vnodeops.
* maybe overwriten for a VDIR
*/
if (vap) {
} else {
}
if (SDEV_IS_GLOBAL(ddv)) {
dv->sdev_gdir_gen = 0;
} else {
dv->sdev_ldir_gen = 0;
dv->sdev_devtree_gen = 0;
}
return (0);
}
/*
* transition a sdev_node into SDEV_READY state
*/
int
{
int error = 0;
} else {
}
if (!(SDEV_IS_GLOBAL(dv))) {
}
/*
* shadow node is created here OR
* if failed (indicated by dv->sdev_attrvp == NULL),
* created later in sdev_setattr
*/
if (avp) {
} else {
else
}
/* transition to READY state */
return (error);
}
/*
* setting ZOMBIE state
*/
static int
{
return (0);
}
/*
* Build the VROOT sdev_node.
*/
/*ARGSUSED*/
struct sdev_node *
{
char devdir[] = "/dev";
else
/* vfs_mountdev1 set mount point later */
dv->sdev_gdir_gen = 0;
} else {
dv->sdev_ldir_gen = 0;
dv->sdev_devtree_gen = 0;
}
return (dv);
}
/*
* 1. load the module
* 2. modload invokes sdev_module_register, which in turn sets
* the dv->sdev_mapinfo->dir_ops
*
* note: locking order:
* dv->sdev_contents -> map->dir_lock
*/
static int
{
int error = 0;
char *module;
char *path;
int load = 1;
if (devname_nsmaps == NULL)
return (0);
if (!sdev_nsmaps_loaded() && !sdev_nsmaps_reloaded())
return (0);
if (map->dir_invalid) {
map->dir_newmodule) == 0)) {
load = 0;
}
map->dir_newmap);
}
sdcmn_err6(("sdev_get_moduleops: "
"load module %s", module));
if (error < 0) {
return (-1);
}
/*
* loading the module ops for name services
*/
if (devname_ns_ops == NULL) {
"sdev_get_moduleops: modload default\n"));
"sdev_get_moduleops: error %d\n", error));
if (error < 0) {
return (-1);
}
}
}
}
}
return (0);
}
/* directory dependent vop table */
struct sdev_vop_table {
char *vt_name; /* subdirectory name */
int vt_flags;
};
/*
* A nice improvement would be to provide a plug-in mechanism
* for this table instead of a const table.
*/
static struct sdev_vop_table vtab[] =
{
SDEV_DYNAMIC | SDEV_VTOR },
};
/*
* sets a directory's vnodeops if the directory is in the vtab;
*/
static struct vnodeops *
{
int i;
char *path;
/* gets the relative path to /dev/ */
path += 5;
/* gets the vtab entry if matches */
continue;
if (vtab[i].vt_global_vops)
}
if (vtab[i].vt_service) {
(const fs_operation_def_t *)templ,
/*NOTREACHED*/
}
if (vtab[i].vt_global_vops) {
}
}
return (sdev_vnodeops);
}
/* child inherits the persistence of the parent */
return (sdev_vnodeops);
}
static void
{
int i;
char *path;
break;
}
}
}
void *
{
int i;
continue;
}
return (NULL);
}
/*
* Build the base root inode
*/
{
/*
* for now, follow the lead of tmpfs here
* need to someday understand the requirements here
*/
return (ino);
}
static int
{
int err;
char *buf;
return (ENOENT);
if (err) {
return (ENOENT);
}
/* mission complete */
return (0);
}
/*
* A convenient wrapper to get the devfs node vnode for a device
* minor functionality: readlink() of a /dev symlink
* Place the link into dv->sdev_symlink
*/
static int
{
int err;
return (ENOENT);
if (err) {
(void) sdev_nodezombied(dv);
return (ENOENT);
}
return (0);
}
static int
{
/*
* existing sdev_node has a different type.
*/
sdcmn_err9(("sdev_node_check: existing node "
" %s type %d does not match new node type %d\n",
return (EEXIST);
}
/*
* For a symlink, the target should be the same.
*/
sdcmn_err9(("sdev_node_check: existing node "
" %s has different symlink %s as new node "
(char *)nargs));
return (EEXIST);
}
}
return (0);
}
/*
* sdev_mknode - a wrapper for sdev_nodeinit(), sdev_nodeready()
*
* arguments:
* - ddv (parent)
* - nm (child name)
* - newdv (sdev_node for nm is returned here)
* - vap (vattr for the node to be created, va_type should be set.
* the defaults should be used if unknown)
* - cred
* - args
* . tnm (for VLNK)
* . global sdev_node (for !SDEV_GLOBAL)
* - state: SDEV_INIT, SDEV_READY
*
* only ddv, nm, newddv, vap, cred are required for sdev_mknode(SDEV_INIT)
*
* NOTE: directory contents writers lock needs to be held before
* calling this routine.
*/
int
{
int error = 0;
if (*newdv) {
} else {
/* allocate and initialize a sdev_node */
sdcmn_err9(("sdev_mknode: parent %s ZOMBIEd\n",
return (ENOENT);
}
if (error != 0) {
sdcmn_err9(("sdev_mknode: error %d,"
" name %s can not be initialized\n",
return (ENOENT);
}
/* insert into the directory cache */
if (error) {
sdcmn_err9(("sdev_mknode: node %s can not"
" be added into directory cache\n", nm));
return (ENOENT);
}
}
if (state == SDEV_READY) {
switch (node_state) {
case SDEV_INIT:
/*
* masking the errors with ENOENT
*/
if (error) {
sdcmn_err9(("sdev_mknode: node %s can NOT"
" be transitioned into READY state, "
}
break;
case SDEV_READY:
/*
* Do some sanity checking to make sure
* the existing sdev_node is what has been
* asked for.
*/
break;
default:
break;
}
}
if (!error) {
} else {
}
return (error);
}
/*
* convenient wrapper to change vp's ATIME, CTIME and ATIME
*/
void
{
int err;
gethrestime(&now);
}
}
/*
* the backing store vnode is released here
*/
/*ARGSUSED1*/
void
{
/* no references */
/*
* reset the attrvp so that no more
* references can be made on this already
* vn_rele() vnode
*/
}
}
}
}
}
if (!SDEV_IS_GLOBAL(dv))
/* return node to initial state as per constructor */
sizeof (dv->sdev_instance_data));
}
/*
* DIRECTORY CACHE lookup
*/
struct sdev_node *
{
continue;
}
/*
* Can't lookup stale nodes
*/
"sdev_findbyname: skipped stale node: %s\n",
continue;
}
return (dv);
}
}
return (NULL);
}
/*
* Inserts a new sdev_node in a parent directory
*/
void
{
ddv->sdev_nlink++;
}
/*
* The following check is needed because while sdev_nodes are linked
* in SDEV_INIT state, they have their link counts incremented only
* in SDEV_READY state.
*/
static void
{
dv->sdev_nlink--;
else
}
/*
* Delete an existing dv from directory cache
*
* In the case of a node is still held by non-zero reference count,
* the node is put into ZOMBIE state. Once the reference count
* reaches "0", the node is unlinked and destroyed,
* in sdev_inactive().
*/
static int
{
/* dv is held still */
"sdev_delete: node %s busy with count %d\n",
}
return (EBUSY);
}
/* unlink from the memory cache */
}
;
else
/* destroy the node */
sdev_nodedestroy(dv, 0);
return (0);
}
/*
* check if the source is in the path of the target
*
* source and target are different
*/
/*ARGSUSED2*/
static int
{
int error = 0;
/* fs root */
return (0);
}
for (;;) {
/*
* avoid error cases like
* mv a a/b
* mv a a/b/c
* etc.
*/
break;
}
/* done checking because root is reached */
break;
}
}
return (error);
}
/*
* Renaming a directory to a different parent
* requires modifying the ".." reference.
*/
static void
{
nparent->sdev_nlink++;
oparent->sdev_nlink--;
}
int
{
int error = 0;
/*
* If renaming a directory, and the parents are different (".." must be
* changed) then the source dir must not be in the dir hierarchy above
* the target since it would orphan everything below the source dir.
*/
if (error)
return (error);
}
if (error)
return (error);
if (*ndvp) {
/* destination existing */
/* handling renaming to itself */
return (0);
/* special handling directory renaming */
if (doingdir) {
return (ENOTDIR);
/*
* Renaming a directory with the parent different
* requires that ".." be re-written.
*/
}
}
} else {
/* creating the destination node with the source attr */
if (error)
return (error);
}
/* fix the source for a symlink */
if (error)
return (ENOENT);
}
}
cred, SDEV_READY);
if (link)
/* update timestamps */
return (0);
}
/*
* Merge sdev_node specific information into an attribute structure.
*
* note: sdev_node is not locked here
*/
void
{
else
} else {
}
}
static struct vattr *
{
return (&sdev_vattr_dir);
return (&sdev_vattr_chr);
return (&sdev_vattr_blk);
return (&sdev_vattr_lnk);
else
return (NULL);
}
int
{
int rv = 0;
case VCHR:
case VBLK:
/*
* If vnode is a device, return special vnode instead
* (though it knows all about -us- via sp->s_realvp)
*/
break;
default: /* most types are returned as is */
break;
}
return (rv);
}
/*
* loopback into sdev_lookup()
*/
static struct vnode *
{
int error = 0;
if (error) {
return (NULL);
}
if (vattr)
return (vp);
}
/*
* the junction between devname and devfs
*/
static struct vnode *
{
int error = 0;
== 0);
if (error != 0) {
}
return (NULL);
}
if (vattr)
return (vp);
}
/*
* junction between devname and root file system, e.g. ufs
*/
int
{
int rval = 0;
return (rval);
}
static int
{
char *nm;
int error;
int eof;
return (0);
return (0);
uio.uio_loffset = 0;
eof = 0;
error = 0;
break;
error = 0;
break;
}
continue;
if (dv) {
} else {
/*
* A ZOMBIE node may not have been
* cleaned up from the backing store,
* bypass this entry in this case,
* and clean it up from the directory
* cache if this is the last call.
*/
}
continue;
}
/* refill the cache if not already */
if (error)
continue;
if (error)
continue;
if (error) {
continue;
}
}
}
cred, SDEV_READY);
}
if (!error) {
}
}
}
done:
return (error);
}
static int
{
int error;
int i;
return (0);
}
}
if (error)
continue;
}
return (0);
}
/*
* Creating a backing store entry based on sdev_attr.
* This is called either as part of node creation in a persistent directory
* or from setattr/setsecattr to persist access attributes across reboot.
*/
int
{
int error = 0;
/* try to find it in the backing store */
if (error == 0) {
}
return (0);
}
/* let's try to persist the node */
case VDIR:
sdcmn_err9(("sdev_shadow_node: mkdir vp %p error %d\n",
break;
case VCHR:
case VBLK:
case VREG:
case VDOOR:
sdcmn_err9(("sdev_shadow_node: create vp %p, error %d\n",
if (!error)
break;
case VLNK:
sdcmn_err9(("sdev_shadow_node: create symlink error %d\n",
error));
break;
default:
"create\n", nm);
/*NOTREACHED*/
}
/* go back to lookup to factor out spec node and set attrvp */
if (error == 0)
goto lookup;
return (error);
}
static int
{
int error = 0;
} else {
/*
* The ZOMBIE node is still hanging
* around with more than one reference counts.
* Fail the new node creation so that
* the directory cache won't have
* duplicate entries for the same named node
*/
sdev_nodedestroy(*dv, 0);
return (error);
}
} else {
sdev_nodedestroy(*dv, 0);
}
}
return (0);
}
static int
{
}
/*
* update the in-core directory cache
*/
int
{
int error = 0;
switch (ops) {
case SDEV_CACHE_ADD:
break;
case SDEV_CACHE_DELETE:
break;
default:
break;
}
return (error);
}
/*
* retrive the named entry from the directory cache
*/
struct sdev_node *
{
return (dv);
}
/*
* Implicit reconfig for nodes constructed by a link generator
* Start devfsadm if needed, or if devfsadm is in progress,
* prepare to block on devfsadm either completing or
* constructing the desired node. As devfsadmd is global
* in scope, constructing all necessary nodes, we only
* need to initiate it once.
*/
static int
{
int error = 0;
sdcmn_err6(("lookup: waiting for %s/%s, 0x%x\n",
error = 0;
} else if (!DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state)) {
sdcmn_err6(("lookup %s/%s starting devfsadm, 0x%x\n",
(SDEV_LOOKUP | SDEV_LGWAITING));
error = 0;
} else {
error = -1;
}
return (error);
}
static int
{
int error = 0;
if (error) {
return (error);
}
switch (spec) {
case DEVNAME_NS_PATH:
/*
* symlink of:
*/
break;
case DEVNAME_NS_DEV:
/*
* symlink of:
*/
break;
default:
if (args)
return (ENOENT);
}
if (args)
return (ENOENT);
} else {
/*
* Could sdev_mknode return a different dv_node
* once the lock is dropped?
*/
}
if (error) {
if (args)
return (error);
} else {
error = 0;
}
}
if (args)
return (0);
}
/*
* Support for specialized device naming construction mechanisms
*/
static int
{
int rv = 0;
/* for non-devfsadm devices */
NULL);
if (rv) {
return (-1);
}
sdcmn_err3(("devname_configure_by_path: "
"failed for /dev/%s/%s\n",
rv = -1;
} else {
/*
* Sdev_mknode may return back a different sdev_node
* that was created by another thread that
* raced to the directroy cache before this thread.
*
* With current directory cache mechanism
* (linked list with the sdev_node name as
* the entity key), this is a way to make sure
* only one entry exists for the same name
* in the same directory. The outcome is
* the winner wins.
*/
}
if (rv) {
return (rv);
} else {
return (0);
}
}
} else if (flags & SDEV_VNODE) {
/*
* DBNR has its own way to create the device
* and return a backing store vnode in rvp
*/
sdcmn_err3(("devname_lookup_func: SDEV_VNODE "
"callback failed \n"));
return (-1);
}
return (-1);
}
cred, SDEV_READY);
if (rv)
return (rv);
return (0);
} else if (flags & SDEV_VATTR) {
/*
*
* DBNR has its own way to create the device
* "0" is returned upon success.
*
* callback is responsible to set the basic attributes,
* dev_t if VCHR or VBLK/
*/
if (rv) {
sdcmn_err3(("devname_lookup_func: SDEV_NONE "
"callback failed \n"));
return (-1);
}
}
cred, SDEV_READY);
if (rv)
return (rv);
return (0);
} else {
impossible(("lookup: %s/%s by %s not supported (%d)\n",
__LINE__));
rv = -1;
}
return (rv);
}
static int
is_devfsadm_thread(char *exec_name)
{
/*
* it is safe to use "devfsadm" to capture the lookups
* from devfsadm and its daemon version.
*/
return (1);
return (0);
}
/*
* Lookup Order:
* sdev_node cache;
* backing store (SDEV_PERSIST);
* DBNR: a. dir_ops implemented in the loadable modules;
* b. vnode ops in vtab.
*/
int
{
int retried = 0;
int error = 0;
int failed_flags = 0;
int state;
int parent_state;
return (ENOTDIR);
/*
* Empty name or ., return node itself.
*/
return (0);
}
/*
* .., return the parent directory
*/
return (0);
}
}
/*
* (a) directory cache lookup:
*/
if (dv) {
switch (state) {
case SDEV_INIT:
break;
/* ZOMBIED parent won't allow node creation */
if (parent_state == SDEV_ZOMBIE) {
retried);
goto nolock_notfound;
}
/* compensate the threads started after devfsadm */
!(SDEV_IS_LOOKUP(dv)))
(SDEV_LOOKUP | SDEV_LGWAITING));
if (SDEV_IS_LOOKUP(dv)) {
if (error != 0) {
retried);
goto nolock_notfound;
}
retried);
goto nolock_notfound;
} else if (state == SDEV_READY) {
goto found;
} else if (state == SDEV_ZOMBIE) {
retried);
goto lookup_failed;
}
} else {
}
break;
case SDEV_READY:
goto found;
case SDEV_ZOMBIE:
goto lookup_failed;
default:
return (ENOENT);
}
}
/*
* ZOMBIED parent does not allow new node creation.
* bail out early
*/
if (parent_state == SDEV_ZOMBIE) {
return (ENOENT);
}
/*
* (b0): backing store lookup
* SDEV_PERSIST is default except:
* 1) pts nodes
* 2) non-chmod'ed local nodes
*/
if (SDEV_IS_PERSIST(ddv)) {
if (!error) {
sdcmn_err3(("devname_backstore_lookup: "
if (error) {
if (dv)
return (ENOENT);
}
if (error) {
if (dv)
retried);
return (ENOENT);
}
}
}
}
if (error) {
if (dv)
goto lookup_failed;
} else {
goto found;
}
} else if (retried) {
sdcmn_err3(("retry of lookup of %s/%s: failed\n",
if (dv)
return (ENOENT);
}
}
/* first thread that is doing the lookup on this node */
if (!dv) {
}
if (!dv) {
return (ENOENT);
}
}
if (SDEV_IS_NO_NCACHE(dv)) {
}
if (SDEV_IS_GLOBAL(ddv)) {
}
/*
* (b1) invoking devfsadm once per life time for devfsadm nodes
*/
if (sdev_reconfig_boot || !i_ddi_io_initialized() ||
((moddebug & MODDEBUG_FINI_EBUSY) != 0)) {
goto nolock_notfound;
}
/*
* filter out known non-existent devices recorded
* during initial reconfiguration boot for which
* reconfig should not be done and lookup may
* be short-circuited now.
*/
goto nolock_notfound;
}
/* bypassing devfsadm internal nodes */
if (is_devfsadm_thread(lookup_thread)) {
goto nolock_notfound;
}
if (sdev_reconfig_disable) {
goto nolock_notfound;
}
if (error == 0) {
sdcmn_err8(("lookup of %s/%s by %s: reconfig\n",
if (sdev_reconfig_verbose) {
"?lookup of %s/%s by %s: reconfig\n",
}
retried = 1;
goto tryagain;
} else {
goto nolock_notfound;
}
}
/*
* (b2) Directory Based Name Resolution (DBNR):
* ddv - parent
* nm - /dev/(ddv->sdev_name)/nm
*
* note: module vnode ops take precedence than the build-in ones
*/
if (fn) {
if (error) {
goto notfound;
} else {
goto found;
}
} else if (callback) {
if (error == 0) {
goto found;
} else {
goto notfound;
}
}
if (vtor) {
/*
* Check validity of returned node
*/
case SDEV_VTOR_VALID:
break;
case SDEV_VTOR_INVALID:
sdcmn_err7(("lookup: destroy invalid "
goto nolock_notfound;
case SDEV_VTOR_SKIP:
sdcmn_err7(("lookup: node not applicable - "
goto lookup_failed;
default:
"dev fs: validator failed: %s(%p)\n",
break;
/*NOTREACHED*/
}
}
}
sdcmn_err3(("devname_lookup_func: returning vp %p v_count %d state %d "
return (rv);
/*
* Destroy the node that is created for synchronization purposes.
*/
sdcmn_err3(("devname_lookup_func: %s with state %d\n",
}
/*
* Node state may have changed during the lock
* changes. Re-check.
*/
return (ENOENT);
}
}
return (ENOENT);
}
/*
* Given a directory node, mark all nodes beneath as
* STALE, i.e. nodes that don't exist as far as new
* consumers are concerned
*/
void
{
sdev_stale(dv);
sdcmn_err9(("sdev_stale: setting stale %s\n",
}
}
/*
* Given a directory node, clean out all the nodes beneath.
* If expr is specified, clean node with names matching expr.
* If SDEV_ENFORCE is specified in flags, busy nodes are made stale,
* so they are excluded from future lookups.
*/
int
{
int error = 0;
int busy = 0;
int bkstore = 0;
int len = 0;
/*
* We try our best to destroy all unused sdev_node's
*/
continue;
sdcmn_err9(("sdev_cleandir: dir %s busy\n",
busy++;
continue;
}
sdcmn_err9(("sdev_cleandir: dir %s busy\n",
busy++;
continue;
}
/*
* at this point, either dv is not held or SDEV_ENFORCE
* is specified. In either case, dv needs to be deleted
*/
bkstore += 1;
if (bkstore) {
}
sdcmn_err9(("sdev_cleandir: dir busy\n"));
busy++;
}
/* take care the backing store clean up */
if (bkstore == 1) {
} else if (bkstore == 2) {
}
/* do not propagate the backing store errors */
if (error) {
sdcmn_err9(("sdev_cleandir: backing store"
"not cleaned\n"));
error = 0;
}
bkstore = 0;
len = 0;
}
}
if (busy) {
}
return (error);
}
/*
* a convenient wrapper for readdir() funcs
*/
{
return (0);
return (reclen);
}
/*
* sdev_mount service routines
*/
int
{
int error;
return (EINVAL);
"get user data. error %d\n", error);
return (EFAULT);
}
return (0);
}
#ifdef nextdp
#endif
/*
* readdir helper func
*/
int
int flags)
{
dirent64_t *dp;
void *outbuf;
int error = 0;
int this_reclen;
if (eofp)
*eofp = 1;
return (0);
}
return (EINVAL);
return (ENOTDIR);
}
*eofp = 0;
outcount = 0;
goto get_cache;
if (SDEV_IS_GLOBAL(ddv)) {
/*
* load the name mapping rule database
* through invoking devfsadm and symlink
* all the entries in the map
*/
int do_thread = 0;
if (do_thread) {
}
} else if ((sdev_boot_state == SDEV_BOOT_STATE_COMPLETE) &&
((moddebug & MODDEBUG_FINI_EBUSY) == 0) &&
/*
* invoking "devfsadm" to do system device reconfig
*/
sdcmn_err8(("readdir of %s by %s: reconfig\n",
if (sdev_reconfig_verbose) {
"?readdir of %s by %s: reconfig\n",
}
} else if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) {
/*
* compensate the "ls" started later than "devfsadm"
*/
}
/*
* release the contents lock so that
* the cache maybe updated by devfsadmd
*/
if (SDEV_IS_READDIR(ddv))
sdcmn_err4(("readdir of directory %s by %s\n",
if (SDEV_IS_PERSIST(ddv)) {
alloc_count, cred);
}
/*
* pre-creating the directories
* defined in vtab
*/
}
if (!error)
}
}
/* handle "." and ".." */
diroff = 0;
if (soff == 0) {
/* first time */
if (alloc_count < this_reclen) {
goto done;
}
}
diroff++;
if (soff <= 1) {
goto done;
}
}
/* gets the cache */
diroff++;
sdcmn_err3(("sdev_readdir: diroff %lld soff %lld for '%s' \n",
/* bypassing pre-matured nodes */
sdcmn_err3(("sdev_readdir: pre-mature node "
continue;
}
/* don't list stale nodes */
sdcmn_err4(("sdev_readdir: STALE node "
continue;
}
/*
* Check validity of node
*/
if (vtor) {
case SDEV_VTOR_VALID:
break;
case SDEV_VTOR_INVALID:
case SDEV_VTOR_SKIP:
continue;
default:
"dev fs: validator failed: %s(%p)\n",
break;
/*NOTREACHED*/
}
}
/*
* call back into the module for the validity/bookkeeping
* of this entry
*/
if (fn) {
if (error) {
sdcmn_err4(("sdev_readdir: module did not "
continue;
}
}
goto full;
}
}
full:
sdcmn_err4(("sdev_readdir: moving %lu bytes: "
(void *)dv));
if (outcount)
if (!error) {
if (eofp)
}
if (ddv->sdev_attrvp) {
gethrestime(&now);
}
done:
return (error);
}
static int
{
char *nm;
int error;
int persisted = 0;
return (error);
while (pn_pathleft(&pn)) {
if (error)
break;
/* traverse mount points encountered on our journey */
break;
}
/*
* Direct the operation to the persisting filesystem
* underlying /dev. Bail if we encounter a
* non-persistent dev entity here.
*/
break;
}
break;
}
break;
}
persisted = 1;
}
pn_skipslash(&pn);
}
if (error)
return (error);
/*
* Only return persisted nodes in the filesystem underlying /dev.
*/
if (!persisted) {
return (ENOENT);
}
return (0);
}
int
int *npathsp, int *npathsp_alloc)
{
int npaths = 0;
int npaths_alloc = 0;
int n;
char *s;
int error;
int eof;
int ndirents = 64;
char *nm;
sdcmn_err11(("modctl readdir: %s by %s: %s\n",
if (error)
return (error);
uio.uio_loffset = 0;
eof = 0;
error = 0;
break;
continue;
if (npaths == npaths_alloc) {
npaths_alloc += 64;
newlist = (char **)
sizeof (char *), KM_SLEEP);
if (pathlist) {
npaths * sizeof (char *));
(npaths + 1) * sizeof (char *));
}
}
s = kmem_alloc(n, KM_SLEEP);
}
}
exit:
if (dbuf)
if (error)
return (error);
return (0);
}
void
{
int i, n;
for (i = 0; i < npaths; i++) {
}
}
int
sdev_modctl_devexists(const char *path)
{
int error;
sdcmn_err11(("modctl dev exists: %s by %s: %s\n",
if (error == 0)
return (error);
}
void
{
if (module) {
}
if (mapname) {
}
}
void
{
char *old_module = NULL;
}
if (module) {
}
}
if (mapname) {
}
}
map->dir_maploaded = 0;
map->dir_invalid = 0;
}
/*
* dir_name should have at least one attribute,
* dir_module
* or dir_map
* or both
* caller holds the devname_nsmaps_lock
*/
void
{
struct devname_nsmap *map;
int len = 0;
return;
}
if (dir_module) {
}
if (dir_map) {
if (dir_map[0] != '/') {
dir_map);
} else {
}
}
map->dir_maploaded = 0;
map->dir_invalid = 0;
if (devname_nsmaps) {
}
}
struct devname_nsmap *
{
if (!locked)
if (!locked)
return (map);
}
}
if (!locked)
return (NULL);
}
struct devname_nsmap *
sdev_get_nsmap_by_module(char *mod_name)
{
sdcmn_err7(("sdev_get_nsmap_by_module: module %s\n",
map->dir_module));
return (map);
}
}
return (NULL);
}
void
{
if (devname_nsmaps == NULL)
return;
}
}
int
{
int ret = 0;
ret = 1;
return (ret);
}
int
{
int ret = 0;
ret = 1;
return (ret);
}
static void
{
if (map->dir_module)
}
void
{
while (map) {
if (map == devname_nsmaps)
if (map)
} else {
}
}
}
static int
{
int ret = 0;
if (map->dir_invalid)
ret = 1;
return (ret);
}
static int
{
struct devname_nsmap *mapp;
if (devname_nsmaps == NULL) {
return (1);
}
return (0);
}
}
return (1);
}
struct devname_nsmap *
{
struct devname_nsmap *map;
int error;
}
return (NULL);
}
}
if (!error)
}
return (map);
}
void
{
}
extern int sdev_vnodeops_tbl_size;
/*
* construct a new template with overrides from vtab
*/
static fs_operation_def_t *
{
const fs_operation_def_t *tab_entry;
/* make a copy of standard vnode ops table */
/* replace the overrides from tab */
break;
}
std_entry++;
}
}
return (new);
}
/* free memory allocated by sdev_merge_vtab */
static void
{
}
void
{
}
int
{
return (0);
}
int
{
return (0);
}
void
{
}
int
{
return (0);
}
int
{
return (0);
}
int
{
return (0);
}
int
{
return (0);
}
void
{
}
/*
* a generic setattr() function
*
* note: flags only supports AT_UID and AT_GID.
* Future enhancements can be done for other types, e.g. AT_MODE
*/
int
int), int protocol)
{
int error;
/* some sanity checks */
return (EINVAL);
return (EISDIR);
}
}
/* no need to set attribute, but do not fail either */
return (0);
}
/* If backing store exists, just set it. */
if (dv->sdev_attrvp) {
}
/*
* Otherwise, for nodes with the persistence attribute, create it.
*/
if (SDEV_IS_PERSIST(dv) ||
if (error)
return (error);
}
/*
* sdev_attr was allocated in sdev_mknode
*/
if (error) {
return (error);
}
}
/*
* a callback must be provided if the protocol is set
*/
if (error) {
return (error);
}
}
}
}
return (0);
}