sdev_subr.c revision dd9c3b29f8e9f6b99b80e1fd8fc03241abd67311
/*
* 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 (c) 2012, Joyent, Inc. All rights reserved.
*/
/*
* 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 */
static void sdev_set_no_negcache(struct sdev_node *);
static void sdev_free_vtab(fs_operation_def_t *);
static void
{
}
/* sdev_node cache constructor */
/*ARGSUSED1*/
static int
{
return (-1);
}
return (0);
}
/* sdev_node cache destructor */
/*ARGSUSED1*/
static void
{
}
/* initialize sdev_node cache */
void
{
int flags = 0;
#ifdef DEBUG
if (flags)
#endif /* DEBUG */
}
/* destroy sdev_node cache */
void
{
}
/*
* Compare two nodes lexographically to balance avl tree
*/
static int
{
int rv;
return (0);
}
void
{
}
static void
{
gethrestime(&now);
}
static void
{
}
/* 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;
(int (*)(const void *, const void *))sdev_compare_nodes,
sizeof (struct sdev_node),
} 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 {
}
}
if (error == 0) {
/* transition to READY state */
} else {
}
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;
}
(int (*)(const void *, const void *))sdev_compare_nodes,
sizeof (struct sdev_node),
return (dv);
}
/* 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 },
SDEV_DYNAMIC | SDEV_VTOR },
SDEV_DYNAMIC | SDEV_VTOR },
/*
* SDEV_DYNAMIC: prevent calling out to devfsadm, since only the
* lofi driver controls child nodes.
*
* SDEV_PERSIST: ensure devfsadm knows to clean up any persisted
* stale nodes (e.g. from devfsadm -R).
*
* In addition, devfsadm knows not to attempt a rmdir: a zone
* may hold a reference, which would zombify the node,
* preventing a mkdir.
*/
};
struct sdev_vop_table *
{
int vlen;
int i;
return (&vtab[i]);
char *ptr;
return (&vtab[i]);
}
}
return (NULL);
}
/*
* sets a directory's vnodeops if the directory is in the vtab;
*/
static struct vnodeops *
{
struct sdev_vop_table *vtp;
char *path;
/* gets the relative path to /dev/ */
path += 5;
/* gets the vtab entry it matches */
if (vtp->vt_global_vops)
}
if (vtp->vt_service) {
(const fs_operation_def_t *)templ,
/*NOTREACHED*/
}
if (vtp->vt_global_vops) {
}
}
return (sdev_vnodeops);
}
/* child inherits the persistence of the parent */
return (sdev_vnodeops);
}
static void
{
int i;
char *path;
break;
}
}
}
void *
{
struct sdev_vop_table *vtp;
if (vtp)
else
return (NULL);
}
/*
* Build the base root inode
*/
{
/*
* for now, follow the lead of tmpfs here
* need to someday understand the requirements here
*/
return (ino);
}
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.
* - avp (attribute vnode)
* 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 (error);
}
/* 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:
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 MTIME
*/
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 *
{
if (dv) {
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_dirdelete: node %s busy with count %d\n",
}
return (EBUSY);
}
/* unlink from the memory cache */
}
/* 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);
}
int
{
int error = 0;
int bkstore = 0;
if (error)
return (error);
if (!samedir)
/*
* the source may have been deleted by another thread before
* we gets here.
*/
goto err_out;
}
goto err_out;
}
/*
* 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)
goto err_out;
}
/* destination existing */
if (*ndvp) {
/* handling renaming to itself */
error = 0;
goto err_out;
}
if (!doingdir) {
goto err_out;
}
if (vn_vfswlock(nvp)) {
goto err_out;
}
goto err_out;
}
/* in case dir1 exists in dir2 and "mv dir1 dir2" */
goto err_out;
}
if (error)
goto err_out;
} else {
if (doingdir) {
goto err_out;
}
if (SDEV_IS_PERSIST((*ndvp))) {
bkstore = 1;
}
/*
* get rid of the node from the directory cache
* note, in case EBUSY is returned, the ZOMBIE
* node is taken care in sdev_mknode.
*/
if (bkstore) {
if (error)
goto err_out;
}
}
}
/* fix the source for a symlink */
if (error) {
goto err_out;
}
}
}
/*
* make a fresh node from the source attrs
*/
if (link)
if (error)
goto err_out;
/* move dir contents */
if (doingdir) {
if (error)
goto err_out;
}
}
if ((*ndvp)->sdev_attrvp) {
} else {
gethrestime(&now);
}
if (nddv->sdev_attrvp) {
} else {
gethrestime(&now);
}
if (!samedir)
return (error);
if (!samedir)
return (error);
}
/*
* Merge sdev_node specific information into an attribute structure.
*
* note: sdev_node is not locked here
*/
void
{
else
} else {
}
}
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);
}
/*
* junction between devname and root file system, e.g. ufs
*/
int
{
int rval = 0;
NULL);
return (rval);
}
static int
{
char *nm;
int error;
int eof;
return (0);
return (0);
uio.uio_loffset = 0;
eof = 0;
error = 0;
break;
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);
}
void
{
int error;
int i;
/*
* This early, we may be in a read-only /dev
* environment: leave the creation of any nodes we'd
* attempt to persist to devfsadm.
*/
continue;
if (error) {
} else {
}
}
}
/*
* 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 */
NULL);
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:
NULL, 0);
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);
}
/*
* retrieve 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);
}
/*
* Support for specialized device naming construction mechanisms
*/
static int
{
int rv = 0;
if (flags & SDEV_VLINK) {
NULL);
if (rv) {
return (-1);
}
if (rv)
return (rv);
} else if (flags & SDEV_VATTR) {
/*
*
* 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);
} 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
* 3) zvol nodes
*/
if (SDEV_IS_PERSIST(ddv)) {
if (!error) {
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 (callback) {
}
if (error == 0) {
goto found;
} else {
goto lookup_failed;
}
}
if (!dv) {
}
if (!dv) {
return (ENOENT);
}
}
/*
* (b1) invoking devfsadm once per life time for devfsadm nodes
*/
if (SDEV_IS_NO_NCACHE(dv))
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;
}
if (vtor) {
/*
* Check validity of returned node
*/
case SDEV_VTOR_VALID:
break;
case SDEV_VTOR_STALE:
/*
* The name exists, but the cache entry is
* stale and needs to be re-created.
*/
}
if (error == 0) {
goto lookup_create_node;
}
/* FALLTHRU */
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;
}
}
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. Remove them from the
* list of directory entries so that no lookup or
* directory traversal will find them. The node
* not deallocated so existing holds are not affected.
*/
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)) {
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 may be 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);
}
}
}
/* 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;
}
/*
* Check validity of node
* Drop invalid and nodes to be skipped.
* A node the validator indicates as stale needs
* to be returned as presumably the node name itself
* is valid and the node data itself will be refreshed
* on lookup. An application performing a readdir then
* stat on each entry should thus always see consistent
* data. In any case, it is not possible to synchronize
* with dynamic kernel state, and any view we return can
* never be anything more than a snapshot at a point in time.
*/
if (vtor) {
case SDEV_VTOR_VALID:
break;
case SDEV_VTOR_INVALID:
case SDEV_VTOR_SKIP:
continue;
case SDEV_VTOR_STALE:
sdcmn_err3(("sdev_readir: %s stale\n",
break;
default:
"dev fs: validator failed: %s(%p)\n",
break;
/*NOTREACHED*/
}
}
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)) {
/*
* Deal with the .. special case where we may be
* traversing up across a mount point, to the
* root of this filesystem or global root.
*/
nm[1] = 0;
break;
}
goto checkforroot;
}
}
if (error) {
break;
}
/* traverse mount points encountered on our journey */
break;
}
/*
* symbolic link, can be either relative and absolute
*/
break;
}
if (pn_pathleft(&linkpath) == 0)
if (pn.pn_pathlen == 0) {
return (ENOENT);
}
pn_skipslash(&pn);
} else {
}
continue;
}
/*
* 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 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);
/* if checking empty, one entry is as good as many */
if (checking_empty) {
eof = 1;
break;
}
}
}
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);
}
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
{
}
/*
* 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);
}
/*
* a generic inactive() function
*/
/*ARGSUSED*/
void
{
int clean;
int state;
/*
* last ref count on the ZOMBIE node is released.
* clean up the sdev_node, and
* release the hold on the backing store node so that
* the ZOMBIE backing stores also cleaned out.
*/
if (clean) {
ddv->sdev_nlink--;
dv->sdev_nlink--;
}
dv->sdev_nlink--;
sdev_nodedestroy(dv, 0);
} else {
}
}