/* * 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 * or http://www.opensolaris.org/os/licensing. * 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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NNODES_DEFAULT 8 /* Default number of nodes in a fem_list */ /* * fl_ntob(n) - Fem_list: number of nodes to bytes * Given the number of nodes in a fem_list return the size, in bytes, * of the fem_list structure. */ #define fl_ntob(n) (sizeof (struct fem_list) + \ ((n) - 1) * sizeof (struct fem_node)) typedef enum { FEMTYPE_NULL, /* Uninitialized */ FEMTYPE_VNODE, FEMTYPE_VFS, FEMTYPE_NTYPES } femtype_t; #define FEM_HEAD(_t) femtype[(_t)].head.fn_op.anon #define FEM_GUARD(_t) femtype[(_t)].guard static struct fem_type_info { struct fem_node head; struct fem_node guard; femop_t *errf; } femtype[FEMTYPE_NTYPES]; /* * For each type, two tables - the translation offset definition, which * is used by fs_build_vector to layout the operation(s) vector; and the * guard_operation_vector which protects from stack under-run. */ int fem_err(); int fsem_err(); #define _FEMOPDEF(name, member) \ { VOPNAME_##name, offsetof(fem_t, femop_##member), NULL, fem_err } static fs_operation_trans_def_t fem_opdef[] = { _FEMOPDEF(OPEN, open), _FEMOPDEF(CLOSE, close), _FEMOPDEF(READ, read), _FEMOPDEF(WRITE, write), _FEMOPDEF(IOCTL, ioctl), _FEMOPDEF(SETFL, setfl), _FEMOPDEF(GETATTR, getattr), _FEMOPDEF(SETATTR, setattr), _FEMOPDEF(ACCESS, access), _FEMOPDEF(LOOKUP, lookup), _FEMOPDEF(CREATE, create), _FEMOPDEF(REMOVE, remove), _FEMOPDEF(LINK, link), _FEMOPDEF(RENAME, rename), _FEMOPDEF(MKDIR, mkdir), _FEMOPDEF(RMDIR, rmdir), _FEMOPDEF(READDIR, readdir), _FEMOPDEF(SYMLINK, symlink), _FEMOPDEF(READLINK, readlink), _FEMOPDEF(FSYNC, fsync), _FEMOPDEF(INACTIVE, inactive), _FEMOPDEF(FID, fid), _FEMOPDEF(RWLOCK, rwlock), _FEMOPDEF(RWUNLOCK, rwunlock), _FEMOPDEF(SEEK, seek), _FEMOPDEF(CMP, cmp), _FEMOPDEF(FRLOCK, frlock), _FEMOPDEF(SPACE, space), _FEMOPDEF(REALVP, realvp), _FEMOPDEF(GETPAGE, getpage), _FEMOPDEF(PUTPAGE, putpage), _FEMOPDEF(MAP, map), _FEMOPDEF(ADDMAP, addmap), _FEMOPDEF(DELMAP, delmap), _FEMOPDEF(POLL, poll), _FEMOPDEF(DUMP, dump), _FEMOPDEF(PATHCONF, pathconf), _FEMOPDEF(PAGEIO, pageio), _FEMOPDEF(DUMPCTL, dumpctl), _FEMOPDEF(DISPOSE, dispose), _FEMOPDEF(SETSECATTR, setsecattr), _FEMOPDEF(GETSECATTR, getsecattr), _FEMOPDEF(SHRLOCK, shrlock), _FEMOPDEF(VNEVENT, vnevent), { NULL, 0, NULL, NULL } }; #define _FEMGUARD(name, ignore) \ { VOPNAME_##name, (femop_t *)fem_err } static struct fs_operation_def fem_guard_ops[] = { _FEMGUARD(OPEN, open), _FEMGUARD(CLOSE, close), _FEMGUARD(READ, read), _FEMGUARD(WRITE, write), _FEMGUARD(IOCTL, ioctl), _FEMGUARD(SETFL, setfl), _FEMGUARD(GETATTR, getattr), _FEMGUARD(SETATTR, setattr), _FEMGUARD(ACCESS, access), _FEMGUARD(LOOKUP, lookup), _FEMGUARD(CREATE, create), _FEMGUARD(REMOVE, remove), _FEMGUARD(LINK, link), _FEMGUARD(RENAME, rename), _FEMGUARD(MKDIR, mkdir), _FEMGUARD(RMDIR, rmdir), _FEMGUARD(READDIR, readdir), _FEMGUARD(SYMLINK, symlink), _FEMGUARD(READLINK, readlink), _FEMGUARD(FSYNC, fsync), _FEMGUARD(INACTIVE, inactive), _FEMGUARD(FID, fid), _FEMGUARD(RWLOCK, rwlock), _FEMGUARD(RWUNLOCK, rwunlock), _FEMGUARD(SEEK, seek), _FEMGUARD(CMP, cmp), _FEMGUARD(FRLOCK, frlock), _FEMGUARD(SPACE, space), _FEMGUARD(REALVP, realvp), _FEMGUARD(GETPAGE, getpage), _FEMGUARD(PUTPAGE, putpage), _FEMGUARD(MAP, map), _FEMGUARD(ADDMAP, addmap), _FEMGUARD(DELMAP, delmap), _FEMGUARD(POLL, poll), _FEMGUARD(DUMP, dump), _FEMGUARD(PATHCONF, pathconf), _FEMGUARD(PAGEIO, pageio), _FEMGUARD(DUMPCTL, dumpctl), _FEMGUARD(DISPOSE, dispose), _FEMGUARD(SETSECATTR, setsecattr), _FEMGUARD(GETSECATTR, getsecattr), _FEMGUARD(SHRLOCK, shrlock), _FEMGUARD(VNEVENT, vnevent), { NULL, NULL } }; #define _FSEMOPDEF(name, member) \ { VFSNAME_##name, offsetof(fsem_t, fsemop_##member), NULL, fsem_err } static fs_operation_trans_def_t fsem_opdef[] = { _FSEMOPDEF(MOUNT, mount), _FSEMOPDEF(UNMOUNT, unmount), _FSEMOPDEF(ROOT, root), _FSEMOPDEF(STATVFS, statvfs), _FSEMOPDEF(SYNC, sync), _FSEMOPDEF(VGET, vget), _FSEMOPDEF(MOUNTROOT, mountroot), _FSEMOPDEF(FREEVFS, freevfs), _FSEMOPDEF(VNSTATE, vnstate), { NULL, 0, NULL, NULL } }; #define _FSEMGUARD(name, ignore) \ { VFSNAME_##name, (femop_t *)fsem_err } static struct fs_operation_def fsem_guard_ops[] = { _FSEMGUARD(MOUNT, mount), _FSEMGUARD(UNMOUNT, unmount), _FSEMGUARD(ROOT, root), _FSEMGUARD(STATVFS, statvfs), _FSEMGUARD(SYNC, sync), _FSEMGUARD(VGET, vget), _FSEMGUARD(MOUNTROOT, mountroot), _FSEMGUARD(FREEVFS, freevfs), _FSEMGUARD(VNSTATE, vnstate), { NULL, NULL} }; /* * vsop_find, vfsop_find - * * These macros descend the stack until they find either a basic * vnode/vfs operation [ indicated by a null fn_available ] or a * stacked item where this method is non-null [_vsop]. * * The DEBUG one is written with a single function which manually applies * the structure offsets. It can have additional debugging support. */ #ifndef DEBUG #define vsop_find(ap, func, funct, arg0, _vop, _vsop) \ for (;;) { \ if ((ap)->fa_fnode->fn_available == NULL) { \ *(func) = (funct (*)())((ap)->fa_fnode->fn_op.vnode->_vop); \ *(arg0) = (void *)(ap)->fa_vnode.vp; \ break; \ } else if ((*(func) = (funct (*)())((ap)->fa_fnode->fn_op.fem->_vsop))\ != NULL) { \ *(arg0) = (void *) (ap); \ break; \ } else { \ (ap)->fa_fnode--; \ } \ } \ #define vfsop_find(ap, func, funct, arg0, _vop, _vsop) \ for (;;) { \ if ((ap)->fa_fnode->fn_available == NULL) { \ *(func) = (funct (*)())((ap)->fa_fnode->fn_op.vfs->_vop); \ *(arg0) = (void *)(ap)->fa_vnode.vp; \ break; \ } else if ((*(func) = (funct (*)())((ap)->fa_fnode->fn_op.fsem->_vsop))\ != NULL) { \ *(arg0) = (void *) (ap); \ break; \ } else { \ (ap)->fa_fnode--; \ } \ } \ #else #define vsop_find(ap, func, funct, arg0, _vop, _vsop) \ *(arg0) = _op_find((ap), (void **)(func), \ offsetof(vnodeops_t, _vop), offsetof(fem_t, _vsop)) #define vfsop_find(ap, func, funct, arg0, _fop, _fsop) \ *(arg0) = _op_find((ap), (void **)(func), \ offsetof(vfsops_t, _fop), offsetof(fsem_t, _fsop)) static void * _op_find(femarg_t *ap, void **fp, int offs0, int offs1) { void *ptr; for (;;) { struct fem_node *fnod = ap->fa_fnode; if (fnod->fn_available == NULL) { *fp = *(void **)((char *)fnod->fn_op.anon + offs0); ptr = (void *)(ap->fa_vnode.anon); break; } else if ((*fp = *(void **)((char *)fnod->fn_op.anon+offs1)) != NULL) { ptr = (void *)(ap); break; } else { ap->fa_fnode--; } } return (ptr); } #endif static fem_t * fem_alloc() { fem_t *p; p = (fem_t *)kmem_alloc(sizeof (*p), KM_SLEEP); return (p); } void fem_free(fem_t *p) { kmem_free(p, sizeof (*p)); } static fsem_t * fsem_alloc() { fsem_t *p; p = (fsem_t *)kmem_alloc(sizeof (*p), KM_SLEEP); return (p); } void fsem_free(fsem_t *p) { kmem_free(p, sizeof (*p)); } /* * fem_get, fem_release - manage reference counts on the stack. * * The list of monitors can be updated while operations are in * progress on the object. * * The reference count facilitates this by counting the number of * current accessors, and deconstructing the list when it is exhausted. * * fem_lock() is required to: * look at femh_list * update what femh_list points to * update femh_list * increase femh_list->feml_refc. * * the feml_refc can decrement without holding the lock; * when feml_refc becomes zero, the list is destroyed. * */ static struct fem_list * fem_lock(struct fem_head *fp) { struct fem_list *sp = NULL; ASSERT(fp != NULL); mutex_enter(&fp->femh_lock); sp = fp->femh_list; return (sp); } static void fem_unlock(struct fem_head *fp) { ASSERT(fp != NULL); mutex_exit(&fp->femh_lock); } /* * Addref can only be called while its head->lock is held. */ static void fem_addref(struct fem_list *sp) { atomic_add_32(&sp->feml_refc, 1); } static uint32_t fem_delref(struct fem_list *sp) { return (atomic_add_32_nv(&sp->feml_refc, -1)); } static struct fem_list * fem_get(struct fem_head *fp) { struct fem_list *sp = NULL; if (fp != NULL) { if ((sp = fem_lock(fp)) != NULL) { fem_addref(sp); } fem_unlock(fp); } return (sp); } static void fem_release(struct fem_list *sp) { int i; ASSERT(sp->feml_refc != 0); if (fem_delref(sp) == 0) { /* * Before freeing the list, we need to release the * caller-provided data. */ for (i = sp->feml_tos; i > 0; i--) { struct fem_node *fnp = &sp->feml_nodes[i]; if (fnp->fn_av_rele) (*(fnp->fn_av_rele))(fnp->fn_available); } kmem_free(sp, fl_ntob(sp->feml_ssize)); } } /* * These are the 'head' operations which perform the interposition. * * This set must be 1:1, onto with the (vnodeops, vfsos). * * If there is a desire to globally disable interposition for a particular * method, the corresponding 'head' routine should unearth the base method * and invoke it directly rather than bypassing the function. * * All the functions are virtually the same, save for names, types & args. * 1. get a reference to the monitor stack for this object. * 2. store the top of stack into the femarg structure. * 3. store the basic object (vnode *, vnode **, vfs *) in the femarg struc. * 4. invoke the "top" method for this object. * 5. release the reference to the monitor stack. * */ static int vhead_open(vnode_t **vpp, int mode, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock((*vpp)->v_femhead)) == NULL) { func = (int (*)()) ((*vpp)->v_op->vop_open); arg0 = (void *)vpp; fem_unlock((*vpp)->v_femhead); errc = (*func)(arg0, mode, cr); } else { fem_addref(femsp); fem_unlock((*vpp)->v_femhead); farg.fa_vnode.vpp = vpp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_open, femop_open); errc = (*func)(arg0, mode, cr); fem_release(femsp); } return (errc); } static int vhead_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_close); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, flag, count, offset, cr); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_close, femop_close); errc = (*func)(arg0, flag, count, offset, cr); fem_release(femsp); } return (errc); } static int vhead_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, struct caller_context *ct) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_read); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, uiop, ioflag, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_read, femop_read); errc = (*func)(arg0, uiop, ioflag, cr, ct); fem_release(femsp); } return (errc); } static int vhead_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, struct caller_context *ct) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_write); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, uiop, ioflag, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_write, femop_write); errc = (*func)(arg0, uiop, ioflag, cr, ct); fem_release(femsp); } return (errc); } static int vhead_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_ioctl); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, cmd, arg, flag, cr, rvalp); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_ioctl, femop_ioctl); errc = (*func)(arg0, cmd, arg, flag, cr, rvalp); fem_release(femsp); } return (errc); } static int vhead_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_setfl); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, oflags, nflags, cr); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_setfl, femop_setfl); errc = (*func)(arg0, oflags, nflags, cr); fem_release(femsp); } return (errc); } static int vhead_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_getattr); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, vap, flags, cr); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_getattr, femop_getattr); errc = (*func)(arg0, vap, flags, cr); fem_release(femsp); } return (errc); } static int vhead_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_setattr); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, vap, flags, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_setattr, femop_setattr); errc = (*func)(arg0, vap, flags, cr, ct); fem_release(femsp); } return (errc); } static int vhead_access(vnode_t *vp, int mode, int flags, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_access); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, mode, flags, cr); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_access, femop_access); errc = (*func)(arg0, mode, flags, cr); fem_release(femsp); } return (errc); } static int vhead_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp, int flags, vnode_t *rdir, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(dvp->v_femhead)) == NULL) { func = (int (*)()) (dvp->v_op->vop_lookup); arg0 = dvp; fem_unlock(dvp->v_femhead); errc = (*func)(arg0, nm, vpp, pnp, flags, rdir, cr); } else { fem_addref(femsp); fem_unlock(dvp->v_femhead); farg.fa_vnode.vp = dvp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_lookup, femop_lookup); errc = (*func)(arg0, nm, vpp, pnp, flags, rdir, cr); fem_release(femsp); } return (errc); } static int vhead_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl, int mode, vnode_t **vpp, cred_t *cr, int flag) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(dvp->v_femhead)) == NULL) { func = (int (*)()) (dvp->v_op->vop_create); arg0 = dvp; fem_unlock(dvp->v_femhead); errc = (*func)(arg0, name, vap, excl, mode, vpp, cr, flag); } else { fem_addref(femsp); fem_unlock(dvp->v_femhead); farg.fa_vnode.vp = dvp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_create, femop_create); errc = (*func)(arg0, name, vap, excl, mode, vpp, cr, flag); fem_release(femsp); } return (errc); } static int vhead_remove(vnode_t *dvp, char *nm, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(dvp->v_femhead)) == NULL) { func = (int (*)()) (dvp->v_op->vop_remove); arg0 = dvp; fem_unlock(dvp->v_femhead); errc = (*func)(arg0, nm, cr); } else { fem_addref(femsp); fem_unlock(dvp->v_femhead); farg.fa_vnode.vp = dvp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_remove, femop_remove); errc = (*func)(arg0, nm, cr); fem_release(femsp); } return (errc); } static int vhead_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(tdvp->v_femhead)) == NULL) { func = (int (*)()) (tdvp->v_op->vop_link); arg0 = tdvp; fem_unlock(tdvp->v_femhead); errc = (*func)(arg0, svp, tnm, cr); } else { fem_addref(femsp); fem_unlock(tdvp->v_femhead); farg.fa_vnode.vp = tdvp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_link, femop_link); errc = (*func)(arg0, svp, tnm, cr); fem_release(femsp); } return (errc); } static int vhead_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(sdvp->v_femhead)) == NULL) { func = (int (*)()) (sdvp->v_op->vop_rename); arg0 = sdvp; fem_unlock(sdvp->v_femhead); errc = (*func)(arg0, snm, tdvp, tnm, cr); } else { fem_addref(femsp); fem_unlock(sdvp->v_femhead); farg.fa_vnode.vp = sdvp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_rename, femop_rename); errc = (*func)(arg0, snm, tdvp, tnm, cr); fem_release(femsp); } return (errc); } static int vhead_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(dvp->v_femhead)) == NULL) { func = (int (*)()) (dvp->v_op->vop_mkdir); arg0 = dvp; fem_unlock(dvp->v_femhead); errc = (*func)(arg0, dirname, vap, vpp, cr); } else { fem_addref(femsp); fem_unlock(dvp->v_femhead); farg.fa_vnode.vp = dvp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_mkdir, femop_mkdir); errc = (*func)(arg0, dirname, vap, vpp, cr); fem_release(femsp); } return (errc); } static int vhead_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(dvp->v_femhead)) == NULL) { func = (int (*)()) (dvp->v_op->vop_rmdir); arg0 = dvp; fem_unlock(dvp->v_femhead); errc = (*func)(arg0, nm, cdir, cr); } else { fem_addref(femsp); fem_unlock(dvp->v_femhead); farg.fa_vnode.vp = dvp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_rmdir, femop_rmdir); errc = (*func)(arg0, nm, cdir, cr); fem_release(femsp); } return (errc); } static int vhead_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_readdir); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, uiop, cr, eofp); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_readdir, femop_readdir); errc = (*func)(arg0, uiop, cr, eofp); fem_release(femsp); } return (errc); } static int vhead_symlink(vnode_t *dvp, char *linkname, vattr_t *vap, char *target, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(dvp->v_femhead)) == NULL) { func = (int (*)()) (dvp->v_op->vop_symlink); arg0 = dvp; fem_unlock(dvp->v_femhead); errc = (*func)(arg0, linkname, vap, target, cr); } else { fem_addref(femsp); fem_unlock(dvp->v_femhead); farg.fa_vnode.vp = dvp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_symlink, femop_symlink); errc = (*func)(arg0, linkname, vap, target, cr); fem_release(femsp); } return (errc); } static int vhead_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_readlink); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, uiop, cr); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_readlink, femop_readlink); errc = (*func)(arg0, uiop, cr); fem_release(femsp); } return (errc); } static int vhead_fsync(vnode_t *vp, int syncflag, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_fsync); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, syncflag, cr); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_fsync, femop_fsync); errc = (*func)(arg0, syncflag, cr); fem_release(femsp); } return (errc); } static void vhead_inactive(vnode_t *vp, cred_t *cr) { femarg_t farg; struct fem_list *femsp; void (*func)(); void *arg0; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (void (*)()) (vp->v_op->vop_inactive); arg0 = vp; fem_unlock(vp->v_femhead); (*func)(arg0, cr); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, void, &arg0, vop_inactive, femop_inactive); (*func)(arg0, cr); fem_release(femsp); } } static int vhead_fid(vnode_t *vp, fid_t *fidp) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_fid); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, fidp); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_fid, femop_fid); errc = (*func)(arg0, fidp); fem_release(femsp); } return (errc); } static int vhead_rwlock(vnode_t *vp, int write_lock, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_rwlock); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, write_lock, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_rwlock, femop_rwlock); errc = (*func)(arg0, write_lock, ct); fem_release(femsp); } return (errc); } static void vhead_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; void (*func)(); void *arg0; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (void (*)()) (vp->v_op->vop_rwunlock); arg0 = vp; fem_unlock(vp->v_femhead); (*func)(arg0, write_lock, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, void, &arg0, vop_rwunlock, femop_rwunlock); (*func)(arg0, write_lock, ct); fem_release(femsp); } } static int vhead_seek(vnode_t *vp, offset_t ooff, offset_t *noffp) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_seek); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, ooff, noffp); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_seek, femop_seek); errc = (*func)(arg0, ooff, noffp); fem_release(femsp); } return (errc); } static int vhead_cmp(vnode_t *vp1, vnode_t *vp2) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp1->v_femhead)) == NULL) { func = (int (*)()) (vp1->v_op->vop_cmp); arg0 = vp1; fem_unlock(vp1->v_femhead); errc = (*func)(arg0, vp2); } else { fem_addref(femsp); fem_unlock(vp1->v_femhead); farg.fa_vnode.vp = vp1; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_cmp, femop_cmp); errc = (*func)(arg0, vp2); fem_release(femsp); } return (errc); } static int vhead_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, offset_t offset, struct flk_callback *flk_cbp, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_frlock); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, cmd, bfp, flag, offset, flk_cbp, cr); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_frlock, femop_frlock); errc = (*func)(arg0, cmd, bfp, flag, offset, flk_cbp, cr); fem_release(femsp); } return (errc); } static int vhead_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, offset_t offset, cred_t *cr, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_space); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, cmd, bfp, flag, offset, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_space, femop_space); errc = (*func)(arg0, cmd, bfp, flag, offset, cr, ct); fem_release(femsp); } return (errc); } static int vhead_realvp(vnode_t *vp, vnode_t **vpp) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_realvp); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, vpp); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_realvp, femop_realvp); errc = (*func)(arg0, vpp); fem_release(femsp); } return (errc); } static int vhead_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *protp, struct page **plarr, size_t plsz, struct seg *seg, caddr_t addr, enum seg_rw rw, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_getpage); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, off, len, protp, plarr, plsz, seg, addr, rw, cr); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_getpage, femop_getpage); errc = (*func)(arg0, off, len, protp, plarr, plsz, seg, addr, rw, cr); fem_release(femsp); } return (errc); } static int vhead_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_putpage); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, off, len, flags, cr); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_putpage, femop_putpage); errc = (*func)(arg0, off, len, flags, cr); fem_release(femsp); } return (errc); } static int vhead_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_map); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, off, as, addrp, len, prot, maxprot, flags, cr); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_map, femop_map); errc = (*func)(arg0, off, as, addrp, len, prot, maxprot, flags, cr); fem_release(femsp); } return (errc); } static int vhead_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_addmap); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, off, as, addr, len, prot, maxprot, flags, cr); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_addmap, femop_addmap); errc = (*func)(arg0, off, as, addr, len, prot, maxprot, flags, cr); fem_release(femsp); } return (errc); } static int vhead_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_delmap); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, off, as, addr, len, prot, maxprot, flags, cr); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_delmap, femop_delmap); errc = (*func)(arg0, off, as, addr, len, prot, maxprot, flags, cr); fem_release(femsp); } return (errc); } static int vhead_poll(vnode_t *vp, short events, int anyyet, short *reventsp, struct pollhead **phpp) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_poll); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, events, anyyet, reventsp, phpp); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_poll, femop_poll); errc = (*func)(arg0, events, anyyet, reventsp, phpp); fem_release(femsp); } return (errc); } static int vhead_dump(vnode_t *vp, caddr_t addr, int lbdn, int dblks) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_dump); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, addr, lbdn, dblks); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_dump, femop_dump); errc = (*func)(arg0, addr, lbdn, dblks); fem_release(femsp); } return (errc); } static int vhead_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_pathconf); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, cmd, valp, cr); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_pathconf, femop_pathconf); errc = (*func)(arg0, cmd, valp, cr); fem_release(femsp); } return (errc); } static int vhead_pageio(vnode_t *vp, struct page *pp, u_offset_t io_off, size_t io_len, int flags, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_pageio); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, pp, io_off, io_len, flags, cr); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_pageio, femop_pageio); errc = (*func)(arg0, pp, io_off, io_len, flags, cr); fem_release(femsp); } return (errc); } static int vhead_dumpctl(vnode_t *vp, int action, int *blkp) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_dumpctl); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, action, blkp); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_dumpctl, femop_dumpctl); errc = (*func)(arg0, action, blkp); fem_release(femsp); } return (errc); } static void vhead_dispose(vnode_t *vp, struct page *pp, int flag, int dn, cred_t *cr) { femarg_t farg; struct fem_list *femsp; void (*func)(); void *arg0; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (void (*)()) (vp->v_op->vop_dispose); arg0 = vp; fem_unlock(vp->v_femhead); (*func)(arg0, pp, flag, dn, cr); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, void, &arg0, vop_dispose, femop_dispose); (*func)(arg0, pp, flag, dn, cr); fem_release(femsp); } } static int vhead_setsecattr(vnode_t *vp, vsecattr_t *vsap, int flag, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_setsecattr); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, vsap, flag, cr); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_setsecattr, femop_setsecattr); errc = (*func)(arg0, vsap, flag, cr); fem_release(femsp); } return (errc); } static int vhead_getsecattr(vnode_t *vp, vsecattr_t *vsap, int flag, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_getsecattr); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, vsap, flag, cr); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_getsecattr, femop_getsecattr); errc = (*func)(arg0, vsap, flag, cr); fem_release(femsp); } return (errc); } static int vhead_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_shrlock); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, cmd, shr, flag, cr); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_shrlock, femop_shrlock); errc = (*func)(arg0, cmd, shr, flag, cr); fem_release(femsp); } return (errc); } static int vhead_vnevent(vnode_t *vp, vnevent_t vnevent) { femarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; if ((femsp = fem_lock(vp->v_femhead)) == NULL) { func = (int (*)()) (vp->v_op->vop_vnevent); arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, vnevent); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_vnevent, femop_vnevent); errc = (*func)(arg0, vnevent); fem_release(femsp); } return (errc); } static int fshead_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) { fsemarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; ASSERT(vfsp->vfs_implp); if ((femsp = fem_lock(vfsp->vfs_femhead)) == NULL) { func = (int (*)()) vfsp->vfs_op->vfs_mount; fem_unlock(vfsp->vfs_femhead); errc = (*func)(vfsp, mvp, uap, cr); } else { fem_addref(femsp); fem_unlock(vfsp->vfs_femhead); farg.fa_vnode.vfsp = vfsp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vfsop_find(&farg, &func, int, &arg0, vfs_mount, fsemop_mount); errc = (*func)(arg0, mvp, uap, cr); fem_release(femsp); } return (errc); } static int fshead_unmount(vfs_t *vfsp, int flag, cred_t *cr) { fsemarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; ASSERT(vfsp->vfs_implp); if ((femsp = fem_lock(vfsp->vfs_femhead)) == NULL) { func = (int (*)()) vfsp->vfs_op->vfs_unmount; fem_unlock(vfsp->vfs_femhead); errc = (*func)(vfsp, flag, cr); } else { fem_addref(femsp); fem_unlock(vfsp->vfs_femhead); farg.fa_vnode.vfsp = vfsp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vfsop_find(&farg, &func, int, &arg0, vfs_unmount, fsemop_unmount); errc = (*func)(arg0, flag, cr); fem_release(femsp); } return (errc); } static int fshead_root(vfs_t *vfsp, vnode_t **vpp) { fsemarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; ASSERT(vfsp->vfs_implp); if ((femsp = fem_lock(vfsp->vfs_femhead)) == NULL) { func = (int (*)()) vfsp->vfs_op->vfs_root; fem_unlock(vfsp->vfs_femhead); errc = (*func)(vfsp, vpp); } else { fem_addref(femsp); fem_unlock(vfsp->vfs_femhead); farg.fa_vnode.vfsp = vfsp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vfsop_find(&farg, &func, int, &arg0, vfs_root, fsemop_root); errc = (*func)(arg0, vpp); fem_release(femsp); } return (errc); } static int fshead_statvfs(vfs_t *vfsp, statvfs64_t *sp) { fsemarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; ASSERT(vfsp->vfs_implp); if ((femsp = fem_lock(vfsp->vfs_femhead)) == NULL) { func = (int (*)()) vfsp->vfs_op->vfs_statvfs; fem_unlock(vfsp->vfs_femhead); errc = (*func)(vfsp, sp); } else { fem_addref(femsp); fem_unlock(vfsp->vfs_femhead); farg.fa_vnode.vfsp = vfsp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vfsop_find(&farg, &func, int, &arg0, vfs_statvfs, fsemop_statvfs); errc = (*func)(arg0, sp); fem_release(femsp); } return (errc); } static int fshead_sync(vfs_t *vfsp, short flag, cred_t *cr) { fsemarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; ASSERT(vfsp->vfs_implp); if ((femsp = fem_lock(vfsp->vfs_femhead)) == NULL) { func = (int (*)()) vfsp->vfs_op->vfs_sync; fem_unlock(vfsp->vfs_femhead); errc = (*func)(vfsp, flag, cr); } else { fem_addref(femsp); fem_unlock(vfsp->vfs_femhead); farg.fa_vnode.vfsp = vfsp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vfsop_find(&farg, &func, int, &arg0, vfs_sync, fsemop_sync); errc = (*func)(arg0, flag, cr); fem_release(femsp); } return (errc); } static int fshead_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp) { fsemarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; ASSERT(vfsp->vfs_implp); if ((femsp = fem_lock(vfsp->vfs_femhead)) == NULL) { func = (int (*)()) vfsp->vfs_op->vfs_vget; fem_unlock(vfsp->vfs_femhead); errc = (*func)(vfsp, vpp, fidp); } else { fem_addref(femsp); fem_unlock(vfsp->vfs_femhead); farg.fa_vnode.vfsp = vfsp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vfsop_find(&farg, &func, int, &arg0, vfs_vget, fsemop_vget); errc = (*func)(arg0, vpp, fidp); fem_release(femsp); } return (errc); } static int fshead_mountroot(vfs_t *vfsp, enum whymountroot reason) { fsemarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; ASSERT(vfsp->vfs_implp); if ((femsp = fem_lock(vfsp->vfs_femhead)) == NULL) { func = (int (*)()) vfsp->vfs_op->vfs_mountroot; fem_unlock(vfsp->vfs_femhead); errc = (*func)(vfsp, reason); } else { fem_addref(femsp); fem_unlock(vfsp->vfs_femhead); farg.fa_vnode.vfsp = vfsp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vfsop_find(&farg, &func, int, &arg0, vfs_mountroot, fsemop_mountroot); errc = (*func)(arg0, reason); fem_release(femsp); } return (errc); } static void fshead_freevfs(vfs_t *vfsp) { fsemarg_t farg; struct fem_list *femsp; void (*func)(); void *arg0; ASSERT(vfsp->vfs_implp); if ((femsp = fem_lock(vfsp->vfs_femhead)) == NULL) { func = (void (*)()) vfsp->vfs_op->vfs_freevfs; fem_unlock(vfsp->vfs_femhead); (*func)(vfsp); } else { fem_addref(femsp); fem_unlock(vfsp->vfs_femhead); farg.fa_vnode.vfsp = vfsp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vfsop_find(&farg, &func, void, &arg0, vfs_freevfs, fsemop_freevfs); (*func)(arg0); fem_release(femsp); } } static int fshead_vnstate(vfs_t *vfsp, vnode_t *vp, vntrans_t nstate) { fsemarg_t farg; struct fem_list *femsp; int (*func)(); void *arg0; int errc; ASSERT(vfsp->vfs_implp); if ((femsp = fem_lock(vfsp->vfs_femhead)) == NULL) { func = (int (*)()) vfsp->vfs_op->vfs_vnstate; fem_unlock(vfsp->vfs_femhead); errc = (*func)(vfsp, vp, nstate); } else { fem_addref(femsp); fem_unlock(vfsp->vfs_femhead); farg.fa_vnode.vfsp = vfsp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vfsop_find(&farg, &func, int, &arg0, vfs_vnstate, fsemop_vnstate); errc = (*func)(arg0, vp, nstate); fem_release(femsp); } return (errc); } /* * specification table for the vhead vnode operations. * It is an error for any operations to be missing. */ static struct fs_operation_def fhead_vn_spec[] = { { VOPNAME_OPEN, (femop_t *)vhead_open }, { VOPNAME_CLOSE, (femop_t *)vhead_close }, { VOPNAME_READ, (femop_t *)vhead_read }, { VOPNAME_WRITE, (femop_t *)vhead_write }, { VOPNAME_IOCTL, (femop_t *)vhead_ioctl }, { VOPNAME_SETFL, (femop_t *)vhead_setfl }, { VOPNAME_GETATTR, (femop_t *)vhead_getattr }, { VOPNAME_SETATTR, (femop_t *)vhead_setattr }, { VOPNAME_ACCESS, (femop_t *)vhead_access }, { VOPNAME_LOOKUP, (femop_t *)vhead_lookup }, { VOPNAME_CREATE, (femop_t *)vhead_create }, { VOPNAME_REMOVE, (femop_t *)vhead_remove }, { VOPNAME_LINK, (femop_t *)vhead_link }, { VOPNAME_RENAME, (femop_t *)vhead_rename }, { VOPNAME_MKDIR, (femop_t *)vhead_mkdir }, { VOPNAME_RMDIR, (femop_t *)vhead_rmdir }, { VOPNAME_READDIR, (femop_t *)vhead_readdir }, { VOPNAME_SYMLINK, (femop_t *)vhead_symlink }, { VOPNAME_READLINK, (femop_t *)vhead_readlink }, { VOPNAME_FSYNC, (femop_t *)vhead_fsync }, { VOPNAME_INACTIVE, (femop_t *)vhead_inactive }, { VOPNAME_FID, (femop_t *)vhead_fid }, { VOPNAME_RWLOCK, (femop_t *)vhead_rwlock }, { VOPNAME_RWUNLOCK, (femop_t *)vhead_rwunlock }, { VOPNAME_SEEK, (femop_t *)vhead_seek }, { VOPNAME_CMP, (femop_t *)vhead_cmp }, { VOPNAME_FRLOCK, (femop_t *)vhead_frlock }, { VOPNAME_SPACE, (femop_t *)vhead_space }, { VOPNAME_REALVP, (femop_t *)vhead_realvp }, { VOPNAME_GETPAGE, (femop_t *)vhead_getpage }, { VOPNAME_PUTPAGE, (femop_t *)vhead_putpage }, { VOPNAME_MAP, (femop_t *)vhead_map }, { VOPNAME_ADDMAP, (femop_t *)vhead_addmap }, { VOPNAME_DELMAP, (femop_t *)vhead_delmap }, { VOPNAME_POLL, (femop_t *)vhead_poll }, { VOPNAME_DUMP, (femop_t *)vhead_dump }, { VOPNAME_PATHCONF, (femop_t *)vhead_pathconf }, { VOPNAME_PAGEIO, (femop_t *)vhead_pageio }, { VOPNAME_DUMPCTL, (femop_t *)vhead_dumpctl }, { VOPNAME_DISPOSE, (femop_t *)vhead_dispose }, { VOPNAME_SETSECATTR, (femop_t *)vhead_setsecattr }, { VOPNAME_GETSECATTR, (femop_t *)vhead_getsecattr }, { VOPNAME_SHRLOCK, (femop_t *)vhead_shrlock }, { VOPNAME_VNEVENT, (femop_t *)vhead_vnevent }, { NULL, NULL } }; /* * specification table for the vfshead vnode operations. * It is an error for any operations to be missing. */ static struct fs_operation_def fshead_vfs_spec[] = { { VFSNAME_MOUNT, (femop_t *)fshead_mount }, { VFSNAME_UNMOUNT, (femop_t *)fshead_unmount }, { VFSNAME_ROOT, (femop_t *)fshead_root }, { VFSNAME_STATVFS, (femop_t *)fshead_statvfs }, { VFSNAME_SYNC, (femop_t *)fshead_sync }, { VFSNAME_VGET, (femop_t *)fshead_vget }, { VFSNAME_MOUNTROOT, (femop_t *)fshead_mountroot }, { VFSNAME_FREEVFS, (femop_t *)fshead_freevfs }, { VFSNAME_VNSTATE, (femop_t *)fshead_vnstate }, { NULL, NULL } }; /* * This set of routines transfer control to the next stacked monitor. * * Each routine is identical except for naming, types and arguments. * * The basic steps are: * 1. Decrease the stack pointer by one. * 2. If the current item is a base operation (vnode, vfs), goto 5. * 3. If the current item does not have a corresponding operation, goto 1 * 4. Return by invoking the current item with the argument handle. * 5. Return by invoking the base operation with the base object. * * for each classification, there needs to be at least one "next" operation * for each "head"operation. * */ int vnext_open(femarg_t *vf, int mode, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_open, femop_open); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, mode, cr)); } int vnext_close(femarg_t *vf, int flag, int count, offset_t offset, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_close, femop_close); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, flag, count, offset, cr)); } int vnext_read(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr, struct caller_context *ct) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_read, femop_read); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, uiop, ioflag, cr, ct)); } int vnext_write(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr, struct caller_context *ct) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_write, femop_write); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, uiop, ioflag, cr, ct)); } int vnext_ioctl(femarg_t *vf, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_ioctl, femop_ioctl); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, cmd, arg, flag, cr, rvalp)); } int vnext_setfl(femarg_t *vf, int oflags, int nflags, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_setfl, femop_setfl); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, oflags, nflags, cr)); } int vnext_getattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_getattr, femop_getattr); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, vap, flags, cr)); } int vnext_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr, caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_setattr, femop_setattr); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, vap, flags, cr, ct)); } int vnext_access(femarg_t *vf, int mode, int flags, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_access, femop_access); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, mode, flags, cr)); } int vnext_lookup(femarg_t *vf, char *nm, vnode_t **vpp, pathname_t *pnp, int flags, vnode_t *rdir, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_lookup, femop_lookup); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, nm, vpp, pnp, flags, rdir, cr)); } int vnext_create(femarg_t *vf, char *name, vattr_t *vap, vcexcl_t excl, int mode, vnode_t **vpp, cred_t *cr, int flag) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_create, femop_create); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, name, vap, excl, mode, vpp, cr, flag)); } int vnext_remove(femarg_t *vf, char *nm, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_remove, femop_remove); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, nm, cr)); } int vnext_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_link, femop_link); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, svp, tnm, cr)); } int vnext_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_rename, femop_rename); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, snm, tdvp, tnm, cr)); } int vnext_mkdir(femarg_t *vf, char *dirname, vattr_t *vap, vnode_t **vpp, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_mkdir, femop_mkdir); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, dirname, vap, vpp, cr)); } int vnext_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_rmdir, femop_rmdir); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, nm, cdir, cr)); } int vnext_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_readdir, femop_readdir); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, uiop, cr, eofp)); } int vnext_symlink(femarg_t *vf, char *linkname, vattr_t *vap, char *target, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_symlink, femop_symlink); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, linkname, vap, target, cr)); } int vnext_readlink(femarg_t *vf, uio_t *uiop, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_readlink, femop_readlink); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, uiop, cr)); } int vnext_fsync(femarg_t *vf, int syncflag, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_fsync, femop_fsync); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, syncflag, cr)); } void vnext_inactive(femarg_t *vf, cred_t *cr) { void (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, void, &arg0, vop_inactive, femop_inactive); ASSERT(func != NULL); ASSERT(arg0 != NULL); (*func)(arg0, cr); } int vnext_fid(femarg_t *vf, fid_t *fidp) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_fid, femop_fid); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, fidp)); } int vnext_rwlock(femarg_t *vf, int write_lock, caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_rwlock, femop_rwlock); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, write_lock, ct)); } void vnext_rwunlock(femarg_t *vf, int write_lock, caller_context_t *ct) { void (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, void, &arg0, vop_rwunlock, femop_rwunlock); ASSERT(func != NULL); ASSERT(arg0 != NULL); (*func)(arg0, write_lock, ct); } int vnext_seek(femarg_t *vf, offset_t ooff, offset_t *noffp) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_seek, femop_seek); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, ooff, noffp)); } int vnext_cmp(femarg_t *vf, vnode_t *vp2) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_cmp, femop_cmp); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, vp2)); } int vnext_frlock(femarg_t *vf, int cmd, struct flock64 *bfp, int flag, offset_t offset, struct flk_callback *flk_cbp, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_frlock, femop_frlock); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, cmd, bfp, flag, offset, flk_cbp, cr)); } int vnext_space(femarg_t *vf, int cmd, struct flock64 *bfp, int flag, offset_t offset, cred_t *cr, caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_space, femop_space); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, cmd, bfp, flag, offset, cr, ct)); } int vnext_realvp(femarg_t *vf, vnode_t **vpp) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_realvp, femop_realvp); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, vpp)); } int vnext_getpage(femarg_t *vf, offset_t off, size_t len, uint_t *protp, struct page **plarr, size_t plsz, struct seg *seg, caddr_t addr, enum seg_rw rw, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_getpage, femop_getpage); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, off, len, protp, plarr, plsz, seg, addr, rw, cr)); } int vnext_putpage(femarg_t *vf, offset_t off, size_t len, int flags, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_putpage, femop_putpage); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, off, len, flags, cr)); } int vnext_map(femarg_t *vf, offset_t off, struct as *as, caddr_t *addrp, size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_map, femop_map); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, off, as, addrp, len, prot, maxprot, flags, cr)); } int vnext_addmap(femarg_t *vf, offset_t off, struct as *as, caddr_t addr, size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_addmap, femop_addmap); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, off, as, addr, len, prot, maxprot, flags, cr)); } int vnext_delmap(femarg_t *vf, offset_t off, struct as *as, caddr_t addr, size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_delmap, femop_delmap); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, off, as, addr, len, prot, maxprot, flags, cr)); } int vnext_poll(femarg_t *vf, short events, int anyyet, short *reventsp, struct pollhead **phpp) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_poll, femop_poll); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, events, anyyet, reventsp, phpp)); } int vnext_dump(femarg_t *vf, caddr_t addr, int lbdn, int dblks) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_dump, femop_dump); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, addr, lbdn, dblks)); } int vnext_pathconf(femarg_t *vf, int cmd, ulong_t *valp, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_pathconf, femop_pathconf); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, cmd, valp, cr)); } int vnext_pageio(femarg_t *vf, struct page *pp, u_offset_t io_off, size_t io_len, int flags, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_pageio, femop_pageio); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, pp, io_off, io_len, flags, cr)); } int vnext_dumpctl(femarg_t *vf, int action, int *blkp) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_dumpctl, femop_dumpctl); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, action, blkp)); } void vnext_dispose(femarg_t *vf, struct page *pp, int flag, int dn, cred_t *cr) { void (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, void, &arg0, vop_dispose, femop_dispose); ASSERT(func != NULL); ASSERT(arg0 != NULL); (*func)(arg0, pp, flag, dn, cr); } int vnext_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flag, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_setsecattr, femop_setsecattr); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, vsap, flag, cr)); } int vnext_getsecattr(femarg_t *vf, vsecattr_t *vsap, int flag, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_getsecattr, femop_getsecattr); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, vsap, flag, cr)); } int vnext_shrlock(femarg_t *vf, int cmd, struct shrlock *shr, int flag, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_shrlock, femop_shrlock); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, cmd, shr, flag, cr)); } int vnext_vnevent(femarg_t *vf, vnevent_t vnevent) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vsop_find(vf, &func, int, &arg0, vop_vnevent, femop_vnevent); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, vnevent)); } int vfsnext_mount(fsemarg_t *vf, vnode_t *mvp, struct mounta *uap, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vfsop_find(vf, &func, int, &arg0, vfs_mount, fsemop_mount); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, mvp, uap, cr)); } int vfsnext_unmount(fsemarg_t *vf, int flag, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vfsop_find(vf, &func, int, &arg0, vfs_unmount, fsemop_unmount); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, flag, cr)); } int vfsnext_root(fsemarg_t *vf, vnode_t **vpp) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vfsop_find(vf, &func, int, &arg0, vfs_root, fsemop_root); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, vpp)); } int vfsnext_statvfs(fsemarg_t *vf, statvfs64_t *sp) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vfsop_find(vf, &func, int, &arg0, vfs_statvfs, fsemop_statvfs); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, sp)); } int vfsnext_sync(fsemarg_t *vf, short flag, cred_t *cr) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vfsop_find(vf, &func, int, &arg0, vfs_sync, fsemop_sync); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, flag, cr)); } int vfsnext_vget(fsemarg_t *vf, vnode_t **vpp, fid_t *fidp) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vfsop_find(vf, &func, int, &arg0, vfs_vget, fsemop_vget); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, vpp, fidp)); } int vfsnext_mountroot(fsemarg_t *vf, enum whymountroot reason) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vfsop_find(vf, &func, int, &arg0, vfs_mountroot, fsemop_mountroot); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, reason)); } void vfsnext_freevfs(fsemarg_t *vf) { void (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vfsop_find(vf, &func, void, &arg0, vfs_freevfs, fsemop_freevfs); ASSERT(func != NULL); ASSERT(arg0 != NULL); (*func)(arg0); } int vfsnext_vnstate(fsemarg_t *vf, vnode_t *vp, vntrans_t nstate) { int (*func)() = NULL; void *arg0 = NULL; ASSERT(vf != NULL); vf->fa_fnode--; vfsop_find(vf, &func, int, &arg0, vfs_vnstate, fsemop_vnstate); ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, vp, nstate)); } /* * Create a new fem_head and associate with the vnode. * To keep the unaugmented vnode access path lock free, we spin * update this - create a new one, then try and install it. If * we fail to install, release the old one and pretend we succeeded. */ static struct fem_head * new_femhead(struct fem_head **hp) { struct fem_head *head; head = kmem_alloc(sizeof (*head), KM_SLEEP); mutex_init(&head->femh_lock, NULL, MUTEX_DEFAULT, NULL); head->femh_list = NULL; if (casptr(hp, NULL, head) != NULL) { kmem_free(head, sizeof (*head)); head = *hp; } return (head); } /* * Create a fem_list. The fem_list that gets returned is in a * very rudimentary state and MUST NOT be used until it's initialized * (usually by femlist_construct() or fem_dup_list()). The refcount * and size is set properly and top-of-stack is set to the "guard" node * just to be consistent. * * If anyone were to accidentally trying to run on this fem_list before * it's initialized then the system would likely panic trying to defererence * the (NULL) fn_op pointer. * */ static struct fem_list * femlist_create(int numnodes) { struct fem_list *sp; sp = kmem_alloc(fl_ntob(numnodes), KM_SLEEP); sp->feml_refc = 1; sp->feml_ssize = numnodes; sp->feml_nodes[0] = FEM_GUARD(FEMTYPE_NULL); sp->feml_tos = 0; return (sp); } /* * Construct a new femlist. * The list is constructed with the appropriate type of guard to * anchor it, and inserts the original ops. */ static struct fem_list * femlist_construct(void *baseops, int type, int numnodes) { struct fem_list *sp; sp = femlist_create(numnodes); sp->feml_nodes[0] = FEM_GUARD(type); sp->feml_nodes[1].fn_op.anon = baseops; sp->feml_nodes[1].fn_available = NULL; sp->feml_nodes[1].fn_av_hold = NULL; sp->feml_nodes[1].fn_av_rele = NULL; sp->feml_tos = 1; return (sp); } /* * Duplicate a list. Copy the original list to the clone. * * NOTE: The caller must have the fem_head for the lists locked. * Assuming the appropriate lock is held and the caller has done the * math right, the clone list should be big enough to old the original. */ static void fem_dup_list(struct fem_list *orig, struct fem_list *clone) { int i; ASSERT(clone->feml_ssize >= orig->feml_ssize); bcopy(orig->feml_nodes, clone->feml_nodes, sizeof (orig->feml_nodes[0]) * orig->feml_ssize); clone->feml_tos = orig->feml_tos; /* * Now that we've copied the old list (orig) to the new list (clone), * we need to walk the new list and put another hold on fn_available. */ for (i = clone->feml_tos; i > 0; i--) { struct fem_node *fnp = &clone->feml_nodes[i]; if (fnp->fn_av_hold) (*(fnp->fn_av_hold))(fnp->fn_available); } } static int fem_push_node( struct fem_head **hp, void **baseops, int type, struct fem_node *nnode, femhow_t how) { struct fem_head *hd; struct fem_list *list; void *oldops; int retry; int error = 0; int i; /* Validate the node */ if ((nnode->fn_op.anon == NULL) || (nnode->fn_available == NULL)) { return (EINVAL); } if ((hd = *hp) == NULL) { /* construct a proto-list */ hd = new_femhead(hp); } /* * RULE: once a femhead has been pushed onto a object, it cannot be * removed until the object is destroyed. It can be deactivated by * placing the original 'object operations' onto the object, which * will ignore the femhead. * The loop will exist when the femh_list has space to push a monitor * onto it. */ do { retry = 1; list = fem_lock(hd); oldops = *baseops; if (list != NULL) { if (list->feml_tos+1 < list->feml_ssize) { retry = 0; } else { struct fem_list *olist = list; fem_addref(olist); fem_unlock(hd); list = femlist_create(olist->feml_ssize * 2); (void) fem_lock(hd); if (hd->femh_list == olist) { if (list->feml_ssize <= olist->feml_ssize) { /* * We have a new list, but it * is too small to hold the * original contents plus the * one to push. Release the * new list and start over. */ fem_release(list); fem_unlock(hd); } else { /* * Life is good: Our new list * is big enough to hold the * original list (olist) + 1. */ fem_dup_list(olist, list); /* orphan this list */ hd->femh_list = list; (void) fem_delref(olist); retry = 0; } } else { /* concurrent update, retry */ fem_release(list); fem_unlock(hd); } /* remove the reference we added above */ fem_release(olist); } } else { fem_unlock(hd); list = femlist_construct(oldops, type, NNODES_DEFAULT); (void) fem_lock(hd); if (hd->femh_list != NULL || *baseops != oldops) { /* concurrent update, retry */ fem_release(list); fem_unlock(hd); } else { hd->femh_list = list; *baseops = FEM_HEAD(type); retry = 0; } } } while (retry); ASSERT(mutex_owner(&hd->femh_lock) == curthread); ASSERT(list->feml_tos+1 < list->feml_ssize); /* * The presence of "how" will modify the behavior of how/if * nodes are pushed. If it's FORCE, then we can skip * all the checks and push it on. */ if (how != FORCE) { /* Start at the top and work our way down */ for (i = list->feml_tos; i > 0; i--) { void *fn_av = list->feml_nodes[i].fn_available; void *fn_op = list->feml_nodes[i].fn_op.anon; /* * OPARGUNIQ means that this node should not * be pushed on if a node with the same op/avail * combination exists. This situation returns * EBUSY. * * OPUNIQ means that this node should not be * pushed on if a node with the same op exists. * This situation also returns EBUSY. */ switch (how) { case OPUNIQ: if (fn_op == nnode->fn_op.anon) { error = EBUSY; } break; case OPARGUNIQ: if ((fn_op == nnode->fn_op.anon) && (fn_av == nnode->fn_available)) { error = EBUSY; } break; default: error = EINVAL; /* Unexpected value */ break; } if (error) break; } } if (error == 0) { /* * If no errors, slap the node on the list. * Note: The following is a structure copy. */ list->feml_nodes[++(list->feml_tos)] = *nnode; } fem_unlock(hd); return (error); } /* * Remove a node by copying the list above it down a notch. * If the list is busy, replace it with an idle one and work * upon it. * A node matches if the opset matches and the datap matches or is * null. */ static int remove_node(struct fem_list *sp, void **baseops, void *opset, void *datap) { int i; struct fem_node *fn; for (i = sp->feml_tos; i > 0; i--) { fn = sp->feml_nodes+i; if (fn->fn_op.anon == opset && (fn->fn_available == datap || datap == NULL)) { break; } } if (i == 0) { return (EINVAL); } /* * At this point we have a node in-hand (*fn) that we are about * to remove by overwriting it and adjusting the stack. This is * our last chance to do anything with this node so we do the * release on the arg. */ if (fn->fn_av_rele) (*(fn->fn_av_rele))(fn->fn_available); while (i++ < sp->feml_tos) { sp->feml_nodes[i-1] = sp->feml_nodes[i]; } if (--(sp->feml_tos) == 1) { /* Empty, restore ops */ *baseops = sp->feml_nodes[1].fn_op.anon; } return (0); } static int fem_remove_node(struct fem_head *fh, void **baseops, void *opset, void *datap) { struct fem_list *sp; int error = 0; int retry; if (fh == NULL) { return (EINVAL); } do { retry = 0; if ((sp = fem_lock(fh)) == NULL) { fem_unlock(fh); error = EINVAL; } else if (sp->feml_refc == 1) { error = remove_node(sp, baseops, opset, datap); if (sp->feml_tos == 1) { /* * The top-of-stack was decremented by * remove_node(). If it got down to 1, * then the base ops were replaced and we * call fem_release() which will free the * fem_list. */ fem_release(sp); fh->femh_list = NULL; /* XXX - Do we need a membar_producer() call? */ } fem_unlock(fh); } else { /* busy - install a new one without this monitor */ struct fem_list *nsp; /* New fem_list being cloned */ fem_addref(sp); fem_unlock(fh); nsp = femlist_create(sp->feml_ssize); if (fem_lock(fh) == sp) { /* * We popped out of the lock, created a * list, then relocked. If we're in here * then the fem_head points to the same list * it started with. */ fem_dup_list(sp, nsp); error = remove_node(nsp, baseops, opset, datap); if (error != 0) { fem_release(nsp); } else if (nsp->feml_tos == 1) { /* New list now empty, tear it down */ fem_release(nsp); fh->femh_list = NULL; } else { fh->femh_list = nsp; } (void) fem_delref(sp); } else { /* List changed while locked, try again... */ fem_release(nsp); retry = 1; } /* * If error is set, then we tried to remove a node * from the list, but failed. This means that we * will still be using this list so don't release it. */ if (error == 0) fem_release(sp); fem_unlock(fh); } } while (retry); return (error); } /* * perform operation on each element until one returns non zero */ static int fem_walk_list( struct fem_list *sp, int (*f)(struct fem_node *, void *, void *), void *mon, void *arg) { int i; ASSERT(sp != NULL); for (i = sp->feml_tos; i > 0; i--) { if ((*f)(sp->feml_nodes+i, mon, arg) != 0) { break; } } return (i); } /* * companion comparison functions. */ static int fem_compare_mon(struct fem_node *n, void *mon, void *arg) { return ((n->fn_op.anon == mon) && (n->fn_available == arg)); } /* * VNODE interposition. */ int fem_create(char *name, const struct fs_operation_def *templ, fem_t **actual) { int unused_ops = 0; int e; fem_t *newf; newf = fem_alloc(); newf->name = name; newf->templ = templ; e = fs_build_vector(newf, &unused_ops, fem_opdef, templ); if (e != 0) { #ifdef DEBUG cmn_err(CE_WARN, "fem_create: error %d building vector", e); #endif fem_free(newf); } else { *actual = newf; } return (e); } int fem_install( vnode_t *vp, /* Vnode on which monitor is being installed */ fem_t *mon, /* Monitor operations being installed */ void *arg, /* Opaque data used by monitor */ femhow_t how, /* Installation control */ void (*arg_hold)(void *), /* Hold routine for "arg" */ void (*arg_rele)(void *)) /* Release routine for "arg" */ { int error; struct fem_node nnode; nnode.fn_available = arg; nnode.fn_op.fem = mon; nnode.fn_av_hold = arg_hold; nnode.fn_av_rele = arg_rele; /* * If we have a non-NULL hold function, do the hold right away. * The release is done in remove_node(). */ if (arg_hold) (*arg_hold)(arg); error = fem_push_node(&vp->v_femhead, (void **)&vp->v_op, FEMTYPE_VNODE, &nnode, how); /* If there was an error then the monitor wasn't pushed */ if (error && arg_rele) (*arg_rele)(arg); return (error); } int fem_is_installed(vnode_t *v, fem_t *mon, void *arg) { int e; struct fem_list *fl; fl = fem_get(v->v_femhead); if (fl != NULL) { e = fem_walk_list(fl, fem_compare_mon, (void *)mon, arg); fem_release(fl); return (e); } return (0); } int fem_uninstall(vnode_t *v, fem_t *mon, void *arg) { int e; e = fem_remove_node(v->v_femhead, (void **)&v->v_op, (void *)mon, arg); return (e); } void fem_setvnops(vnode_t *v, vnodeops_t *newops) { vnodeops_t *r; ASSERT(v != NULL); ASSERT(newops != NULL); do { r = v->v_op; membar_consumer(); if (v->v_femhead != NULL) { struct fem_list *fl; if ((fl = fem_lock(v->v_femhead)) != NULL) { fl->feml_nodes[1].fn_op.vnode = newops; fem_unlock(v->v_femhead); return; } fem_unlock(v->v_femhead); } } while (casptr(&v->v_op, r, newops) != r); } vnodeops_t * fem_getvnops(vnode_t *v) { vnodeops_t *r; ASSERT(v != NULL); r = v->v_op; membar_consumer(); if (v->v_femhead != NULL) { struct fem_list *fl; if ((fl = fem_lock(v->v_femhead)) != NULL) { r = fl->feml_nodes[1].fn_op.vnode; } fem_unlock(v->v_femhead); } return (r); } /* * VFS interposition */ int fsem_create(char *name, const struct fs_operation_def *templ, fsem_t **actual) { int unused_ops = 0; int e; fsem_t *newv; newv = fsem_alloc(); newv->name = (const char *)name; newv->templ = templ; e = fs_build_vector(newv, &unused_ops, fsem_opdef, templ); if (e != 0) { #ifdef DEBUG cmn_err(CE_WARN, "fsem_create: error %d building vector", e); #endif fsem_free(newv); } else { *actual = newv; } return (e); } /* * These need to be re-written, but there should be more common bits. */ int fsem_is_installed(struct vfs *v, fsem_t *mon, void *arg) { struct fem_list *fl; if (v->vfs_implp == NULL) return (0); fl = fem_get(v->vfs_femhead); if (fl != NULL) { int e; e = fem_walk_list(fl, fem_compare_mon, (void *)mon, arg); fem_release(fl); return (e); } return (0); } int fsem_install( struct vfs *vfsp, /* VFS on which monitor is being installed */ fsem_t *mon, /* Monitor operations being installed */ void *arg, /* Opaque data used by monitor */ femhow_t how, /* Installation control */ void (*arg_hold)(void *), /* Hold routine for "arg" */ void (*arg_rele)(void *)) /* Release routine for "arg" */ { int error; struct fem_node nnode; /* If this vfs hasn't been properly initialized, fail the install */ if (vfsp->vfs_implp == NULL) return (EINVAL); nnode.fn_available = arg; nnode.fn_op.fsem = mon; nnode.fn_av_hold = arg_hold; nnode.fn_av_rele = arg_rele; /* * If we have a non-NULL hold function, do the hold right away. * The release is done in remove_node(). */ if (arg_hold) (*arg_hold)(arg); error = fem_push_node(&vfsp->vfs_femhead, (void **)&vfsp->vfs_op, FEMTYPE_VFS, &nnode, how); /* If there was an error then the monitor wasn't pushed */ if (error && arg_rele) (*arg_rele)(arg); return (error); } int fsem_uninstall(struct vfs *v, fsem_t *mon, void *arg) { int e; if (v->vfs_implp == NULL) return (EINVAL); e = fem_remove_node(v->vfs_femhead, (void **)&v->vfs_op, (void *)mon, arg); return (e); } void fsem_setvfsops(vfs_t *v, vfsops_t *newops) { vfsops_t *r; ASSERT(v != NULL); ASSERT(newops != NULL); ASSERT(v->vfs_implp); do { r = v->vfs_op; membar_consumer(); if (v->vfs_femhead != NULL) { struct fem_list *fl; if ((fl = fem_lock(v->vfs_femhead)) != NULL) { fl->feml_nodes[1].fn_op.vfs = newops; fem_unlock(v->vfs_femhead); return; } fem_unlock(v->vfs_femhead); } } while (casptr(&v->vfs_op, r, newops) != r); } vfsops_t * fsem_getvfsops(vfs_t *v) { vfsops_t *r; ASSERT(v != NULL); ASSERT(v->vfs_implp); r = v->vfs_op; membar_consumer(); if (v->vfs_femhead != NULL) { struct fem_list *fl; if ((fl = fem_lock(v->vfs_femhead)) != NULL) { r = fl->feml_nodes[1].fn_op.vfs; } fem_unlock(v->vfs_femhead); } return (r); } /* * Setup FEM. */ void fem_init() { struct fem_type_info *fi; /* * This femtype is only used for fem_list creation so we only * need the "guard" to be initialized so that feml_tos has * some rudimentary meaning. A fem_list must not be used until * it has been initialized (either via femlist_construct() or * fem_dup_list()). Anything that tries to use this fem_list * before it's actually initialized would panic the system as * soon as "fn_op" (NULL) is dereferenced. */ fi = femtype + FEMTYPE_NULL; fi->errf = fem_err; fi->guard.fn_available = (void *)&fi->guard; fi->guard.fn_av_hold = NULL; fi->guard.fn_av_rele = NULL; fi->guard.fn_op.anon = NULL; fi = femtype + FEMTYPE_VNODE; fi->errf = fem_err; fi->head.fn_available = NULL; fi->head.fn_av_hold = NULL; fi->head.fn_av_rele = NULL; (void) vn_make_ops("fem-head", fhead_vn_spec, &fi->head.fn_op.vnode); fi->guard.fn_available = (void *)&fi->guard; fi->guard.fn_av_hold = NULL; fi->guard.fn_av_rele = NULL; (void) fem_create("fem-guard", fem_guard_ops, &fi->guard.fn_op.fem); fi = femtype + FEMTYPE_VFS; fi->errf = fsem_err; fi->head.fn_available = NULL; fi->head.fn_av_hold = NULL; fi->head.fn_av_rele = NULL; (void) vfs_makefsops(fshead_vfs_spec, &fi->head.fn_op.vfs); fi->guard.fn_available = (void *)&fi->guard; fi->guard.fn_av_hold = NULL; fi->guard.fn_av_rele = NULL; (void) fsem_create("fem-guard", fsem_guard_ops, &fi->guard.fn_op.fsem); } int fem_err() { cmn_err(CE_PANIC, "fem/vnode operations corrupt"); return (0); } int fsem_err() { cmn_err(CE_PANIC, "fem/vfs operations corrupt"); return (0); }