diff options
Diffstat (limited to 'sys/security/lomac/lomacfs_vnops.c')
-rw-r--r-- | sys/security/lomac/lomacfs_vnops.c | 1150 |
1 files changed, 1150 insertions, 0 deletions
diff --git a/sys/security/lomac/lomacfs_vnops.c b/sys/security/lomac/lomacfs_vnops.c new file mode 100644 index 0000000..4a2e3c1 --- /dev/null +++ b/sys/security/lomac/lomacfs_vnops.c @@ -0,0 +1,1150 @@ +/*- + * Copyright (c) 2001 Networks Associates Technologies, Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project by NAI Labs, the + * Security Research Division of Network Associates, Inc. under + * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA + * CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/proc.h> +#include <sys/mount.h> +#include <sys/namei.h> +#include <sys/sysctl.h> +#include <sys/vnode.h> + +#include <vm/vm.h> +#include <vm/vm_object.h> +#include <vm/vnode_pager.h> + +#include <machine/limits.h> + +#include "lomacfs.h" +#include "kernel_mediate.h" +#include "kernel_monitor.h" + +#if defined(LOMAC_DEBUG_LOOKUPSTATS) +static unsigned int lomacfs_successful_lookups, lomacfs_failed_lookups, + lomacfs_successful_cachedlookups, lomacfs_failed_cachedlookups, + lomacfs_node_alloc_clashes, lomacfs_node_alloc_failures; + +SYSCTL_NODE(_vfs, OID_AUTO, lomacfs, CTLFLAG_RW, 0, "LOMACFS filesystem"); +SYSCTL_NODE(_vfs_lomacfs, OID_AUTO, debug, CTLFLAG_RW, 0, "debug stats"); +SYSCTL_UINT(_vfs_lomacfs_debug, OID_AUTO, successful_lookups, + CTLFLAG_RW, &lomacfs_successful_lookups, 0, ""); +SYSCTL_UINT(_vfs_lomacfs_debug, OID_AUTO, failed_lookups, + CTLFLAG_RW, &lomacfs_failed_lookups, 0, ""); +SYSCTL_UINT(_vfs_lomacfs_debug, OID_AUTO, successful_cachedlookups, + CTLFLAG_RW, &lomacfs_successful_cachedlookups, 0, ""); +SYSCTL_UINT(_vfs_lomacfs_debug, OID_AUTO, failed_cachedlookups, + CTLFLAG_RW, &lomacfs_failed_cachedlookups, 0, ""); +SYSCTL_UINT(_vfs_lomacfs_debug, OID_AUTO, node_alloc_clashes, + CTLFLAG_RW, &lomacfs_node_alloc_clashes, 0, ""); +SYSCTL_UINT(_vfs_lomacfs_debug, OID_AUTO, node_alloc_failures, + CTLFLAG_RW, &lomacfs_node_alloc_failures, 0, ""); +#endif + +static int +lomacfs_defaultop( + struct vop_generic_args /* { + struct vnodeop_desc *a_desc; + } */ *ap +) { + + printf("lomacfs: %s unsupported\n", ap->a_desc->vdesc_name); + return (EOPNOTSUPP); +} + +static int +lomacfs_inactive( + struct vop_inactive_args /* { + struct vnode *a_vp; + struct thread *a_td; + } */ *ap +) { + struct vnode *vp = ap->a_vp; + struct vnode *lvp = VTOLVP(vp); + struct thread *td = ap->a_td; + + KASSERT(lvp != NULL, ("inactive with NULL lowervp")); + VOP_UNLOCK(ap->a_vp, 0, td); + /* + * Temporarily drop our reference to the lower vnode, while keeping + * it held, to possibly call VOP_INACTIVE() on the lower layer. + */ + vrele(lvp); +#if defined(LOMAC_DEBUG_INACTIVE) + do { +#if defined(LOMAC_DEBUG_INCNAME) + const char *name = VTOLOMAC(vp)->ln_name; +#else + const char *name = "[unknown]"; +#endif + printf("lomacfs: inactive(%p \"%s\"), lvp usecount down to %u\n", + vp, name, lvp->v_usecount); + } while (0); +#endif + /* + * Since the lower fs may actually remove the vnode on last + * release, destroy ourselves mostly here if that occurs. + * + * Additionally, devices should be totally freed + * on last close, not lazily. + */ + if (lvp->v_usecount == 0 && + (lvp->v_type != VREG && lvp->v_type != VDIR)) { + vdrop(lvp); + VTOLVP(vp) = NULL; + cache_purge(vp); + } else + vref(lvp); + return (0); +} + +static int +lomacfs_reclaim( + struct vop_reclaim_args /* { + struct vnode *a_vp; + struct thread *a_td; + } */ *ap +) { + struct vnode *vp = ap->a_vp; + struct lomac_node *ln = VTOLOMAC(vp); + struct vnode *lvp = VTOLVP(vp); + + if (lvp != NULL) + vrele(lvp); +#if defined(LOMAC_DEBUG_RECLAIM) + if (lvp != NULL) { +#if defined(LOMAC_DEBUG_INCNAME) + const char *name = ln->ln_name; +#else + const char *name = "[unknown]"; +#endif + printf("lomacfs: reclaim(%p \"%s\"), lvp usecount down to %u\n", + vp, name, lvp->v_usecount); + } +#endif + if (lvp != NULL) + vdrop(lvp); + vp->v_data = NULL; + vp->v_rdev = NULL; + free(ln, M_LOMACFS); + + return (0); +} + +static int +lomacfs_print( + struct vop_print_args /* { + struct vnode *a_vp; + } */ *ap +) { + struct vnode *vp = ap->a_vp; + + printf ("\ttag VT_LOMACFS, vp=%p, lowervp=%p\n", vp, + VTOLVP(vp)); + return (0); +} + +static int +lomacfs_lock( + struct vop_lock_args /* { + struct vnode *a_vp; + int a_flags; + struct thread *a_td; + } */ *ap +) { + struct vnode *vp = ap->a_vp; + int flags = ap->a_flags; + struct thread *td = ap->a_td; + struct vnode *lvp; + int lflags = flags & ~(LK_INTERLOCK | LK_THISLAYER); + int error; + + /* + * To prevent race conditions involving doing a lookup + * on "..", we have to lock the lower node, then lock our + * node. Most of the time it won't matter that we lock our + * node (as any locking would need the lower one locked + * first). But we can LK_DRAIN the upper lock as a step + * towards decomissioning it. + */ + lvp = VTOLVP(vp); + if (lvp == NULL || flags & LK_THISLAYER) + return (lockmgr(&vp->v_lock, flags, &vp->v_interlock, td)); + if (flags & LK_INTERLOCK) { + mtx_unlock(&vp->v_interlock); + flags &= ~LK_INTERLOCK; + } + if ((flags & LK_TYPE_MASK) == LK_DRAIN) { + error = vn_lock(lvp, + (lflags & ~LK_TYPE_MASK) | LK_EXCLUSIVE | LK_CANRECURSE, + td); + } else + error = vn_lock(lvp, lflags | LK_CANRECURSE, td); + if (error) + return (error); + error = lockmgr(&vp->v_lock, flags, &vp->v_interlock, td); + if (error) + VOP_UNLOCK(lvp, 0, td); + return (error); +} + +/* + * We need to process our own vnode unlock and then clear the + * interlock flag as it applies only to our vnode, not the + * vnodes below us on the stack. + */ +static int +lomacfs_unlock( + struct vop_unlock_args /* { + struct vnode *a_vp; + int a_flags; + struct thread *a_td; + } */ *ap +) { + struct vnode *vp = ap->a_vp; + int flags = ap->a_flags; + int lflags = (ap->a_flags | LK_RELEASE) & + ~(LK_THISLAYER | LK_INTERLOCK); + struct thread *td = ap->a_td; + struct vnode *lvp = VTOLVP(vp); + int error; + + error = lockmgr(&vp->v_lock, flags | LK_RELEASE, &vp->v_interlock, td); + if (lvp == NULL || flags & LK_THISLAYER || error) + return (error); + /* + * Hmm... in a vput(), this means we'll grab the lomacfs interlock, + * then the lower interlock. I don't think this matters, though, + * since both won't be held at the same time. + */ + if (lvp != NULL) + error = VOP_UNLOCK(lvp, lflags, td); + return (error); +} + +static int +lomacfs_islocked( + struct vop_islocked_args /* { + struct vnode *a_vp; + struct thread *a_td; + } */ *ap +) { + + struct vnode *vp = ap->a_vp; + struct thread *td = ap->a_td; + + return (lockstatus(&vp->v_lock, td)); +} + +static int +lomacfs_lookup( + struct vop_lookup_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + } */ *ap +) { + int error; + + error = vfs_cache_lookup(ap); +#if defined(LOMAC_DEBUG_LOOKUPSTATS) + if (error == 0) + lomacfs_successful_lookups++; + else + lomacfs_failed_lookups++; +#endif +#if defined(LOMAC_DEBUG_LOOKUP) + if (error == 0 && (*ap->a_vpp)->v_mount == dvp->v_mount) { + struct vnode *vp = *ap->a_vpp; +#if defined(LOMAC_DEBUG_INCNAME) + const char *name = VTOLOMAC(vp)->ln_name; +#else + const char *name = "[unknown]"; +#endif + printf("lomacfs: lookup(%p \"%s\"), lvp usecount up to %u\n", + vp, name, VTOLVP(vp)->v_usecount); + } +#endif + return (error); +} + +static int +lomacfs_cachedlookup( + struct vop_lookup_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + } */ *ap +) { + struct vnode *dvp = ap->a_dvp; + struct componentname *cnp = ap->a_cnp; + struct vnode *ldvp = VTOLVP(dvp); + struct vnode *lvp; + int makeentry; + int error; + + if (cnp->cn_flags & ISLASTCN && cnp->cn_nameiop != LOOKUP && + cnp->cn_nameiop != CREATE) { + lomac_object_t lobj = { LO_TYPE_LVNODE, { dvp } }; + const char *op; + + if (cnp->cn_nameiop == DELETE) + op = "delete"; + else + op = "rename"; + + if (!mediate_subject_object(op, curthread->td_proc, &lobj)) + return (EPERM); + } + makeentry = cnp->cn_flags & MAKEENTRY; + cnp->cn_flags &= ~makeentry; + error = VOP_LOOKUP(ldvp, &lvp, cnp); + cnp->cn_flags |= makeentry; + if ((error == 0 || error == EJUSTRETURN) && + cnp->cn_flags != (cnp->cn_flags | LOCKPARENT | ISLASTCN)) + (void)VOP_UNLOCK(dvp, LK_THISLAYER, curthread); + if (error == 0 && lvp->v_type != VSOCK) { + struct mount *mp; + + /* + * Check to see if the vnode has been mounted on; + * if so find the root of the mounted file system. + */ + if (lvp->v_type == VDIR && (mp = lvp->v_mountedhere) && + (cnp->cn_flags & NOCROSSMOUNT) == 0) { + struct vnode *tdp; + + if (vfs_busy(mp, 0, 0, curthread)) + goto forget_it; + VOP_UNLOCK(lvp, 0, curthread); + error = VFS_ROOT(mp, &tdp); + vfs_unbusy(mp, curthread); + if (error) { + vrele(lvp); + return (error); + } + vrele(lvp); + lvp = tdp; + } +forget_it: + /* + * For a create or for devices (dynamic things, aren't they), + * don't enter the vnode into the cache. + */ + if (cnp->cn_nameiop == CREATE || lvp->v_type == VCHR) + cnp->cn_flags &= ~makeentry; + /* + * The top half of dvp is locked, but ldvp is unlocked. + * Additionally, lvp is locked already, and + * lomacfs_node_alloc() always returns it locked. + */ + error = lomacfs_node_alloc(dvp->v_mount, cnp, + dvp, lvp, ap->a_vpp); + if (cnp->cn_nameiop == CREATE) + cnp->cn_flags |= makeentry; +#if defined(LOMAC_DEBUG_LOOKUPSTATS) + if (error) { + if (error != EEXIST) { + lomacfs_node_alloc_failures++; + } else { + lomacfs_node_alloc_clashes++; + error = 0; + } + } +#else + if (error == EEXIST) + error = 0; +#endif + } else if (error == 0) { + /* + * For sockets, just return the "real" thing + * after entering it into the cache. + */ + *ap->a_vpp = lvp; + if (cnp->cn_nameiop != CREATE && cnp->cn_flags & MAKEENTRY) + cache_enter(dvp, lvp, cnp); + } + +#if defined(LOMAC_DEBUG_LOOKUPSTATS) + if (error == 0) + lomacfs_successful_cachedlookups++; + else + lomacfs_failed_cachedlookups++; +#endif + return (error); +} + +static int +lomacfs_getattr( + struct vop_getattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct thread *a_td; + */ *ap +) { + struct vnode *vp = ap->a_vp; + struct vattr *vap = ap->a_vap; + int error; + + error = VOP_GETATTR(VTOLVP(vp), vap, ap->a_cred, ap->a_td); + if (error == 0 && vap->va_fsid == VNOVAL) + vap->va_fsid = VTOLVP(vp)->v_mount->mnt_stat.f_fsid.val[0]; + return (error); +} + +static int +lomacfs_setattr( + struct vop_getattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct thread *a_td; + */ *ap +) { + lomac_object_t lobj = { LO_TYPE_LVNODE, { ap->a_vp } }; + int error; + + if (mediate_subject_object(ap->a_desc->vdesc_name, curthread->td_proc, + &lobj)) + error = VOP_SETATTR(VTOLVP(ap->a_vp), ap->a_vap, ap->a_cred, + ap->a_td); + else + error = EPERM; + return (error); +} + +static int +lomacfs_readdir( + struct vop_readdir_args /* { + struct vnode *a_vp; + struct uio *a_uio; + struct ucred *a_cred; + int *a_eofflag; + int *a_ncookies; + u_long **a_cookies; + } */ *ap +) { + + return (VOP_READDIR(VTOLVP(ap->a_vp), ap->a_uio, ap->a_cred, + ap->a_eofflag, ap->a_ncookies, ap->a_cookies)); +} + +static int +lomacfs_open( + struct vop_open_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap +) { + lomac_object_t lobj; + int error; + + lobj.lo_type = LO_TYPE_LVNODE; + lobj.lo_object.vnode = ap->a_vp; + if (!mediate_subject_object_open(ap->a_td->td_proc, &lobj)) + error = EPERM; + else + error = VOP_OPEN(VTOLVP(ap->a_vp), ap->a_mode, ap->a_cred, + ap->a_td); + return (error); +} + +static int +lomacfs_close( + struct vop_close_args /* { + struct vnode *a_vp; + int a_fflag; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap +) { + struct vnode *vp = ap->a_vp; + struct vnode *lvp = VTOLVP(vp); + int error; + + /* + * XXX + * Try to cope with the horrible semantics introduced here... + */ + vref(lvp); + error = VOP_CLOSE(lvp, ap->a_fflag, ap->a_cred, ap->a_td); + if (error == EAGAIN) + error = 0; + else + vrele(lvp); + return (error); +} + +static int +lomacfs_access( + struct vop_access_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap +) { + + return (VOP_ACCESS(VTOLVP(ap->a_vp), ap->a_mode, ap->a_cred, ap->a_td)); +} + +static int +lomacfs_readlink( + struct vop_readlink_args /* { + struct vnode *a_vp; + struct uio *a_uio; + struct ucred *a_cred; + } */ *ap +) { + struct vnode *lvp = VTOLVP(ap->a_vp); + + if (lvp == NULL) + return (EPERM); + return (VOP_READLINK(lvp, ap->a_uio, ap->a_cred)); +} + +static int +lomacfs_lease( + struct vop_lease_args /* { + struct vnode *a_vp; + struct thread *a_td; + struct ucred *a_cred; + int a_flag; + } */ *ap +) { + struct vnode *lvp = VTOLVP(ap->a_vp); + + return (VOP_LEASE(lvp, ap->a_td, ap->a_cred, ap->a_flag)); +} + +static int +lomacfs_read( + struct vop_read_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap +) { + struct vnode *lvp = VTOLVP(ap->a_vp); + lomac_object_t lobj = { LO_TYPE_LVNODE, { ap->a_vp } }; + int error; + + error = monitor_read_object(curthread->td_proc, &lobj); + if (error == 0) + error = VOP_READ(lvp, ap->a_uio, ap->a_ioflag, ap->a_cred); + return (error); +} + +static int +lomacfs_write( + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap +) { + struct vnode *lvp = VTOLVP(ap->a_vp); + lomac_object_t lobj = { LO_TYPE_LVNODE, { ap->a_vp } }; + int error; + + if (mediate_subject_object(ap->a_desc->vdesc_name, curthread->td_proc, + &lobj)) + error = VOP_WRITE(lvp, ap->a_uio, ap->a_ioflag, ap->a_cred); + else + error = EPERM; + return (error); +} + +static int +lomacfs_ioctl( + struct vop_ioctl_args /* { + struct vnode *a_vp; + u_long a_command; + caddr_t a_data; + int a_fflag; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap +) { + struct vnode *lvp = VTOLVP(ap->a_vp); + + return (VOP_IOCTL(lvp, ap->a_command, ap->a_data, ap->a_fflag, + ap->a_cred, ap->a_td)); +} + +static int +lomacfs_muxcreate( + struct vop_create_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap +) { + struct vnode *dvp = ap->a_dvp; + struct vnode *ldvp = VTOLVP(dvp); + struct componentname *cnp = ap->a_cnp; + struct vattr *vap = ap->a_vap; + int makeentry = cnp->cn_flags & MAKEENTRY; + lomac_object_t lobj = { LO_TYPE_LVNODE, { dvp } }; + struct thread *td = curthread; + int error; + + if (!mediate_subject_object(ap->a_desc->vdesc_name, td->td_proc, + &lobj) || (vap->va_type == VCHR && + !mediate_subject_at_level("mknod", curthread->td_proc, + LOMAC_HIGHEST_LEVEL))) + return (EPERM); + ap->a_dvp = ldvp; + cnp->cn_flags &= ~makeentry; + error = VCALL(ldvp, ap->a_desc->vdesc_offset, ap); + if (error == 0) { + struct vnode *vp; + int issock; + + issock = vap->va_type == VSOCK; + vp = *ap->a_vpp; + *ap->a_vpp = NULL; + if (!issock) + cnp->cn_flags |= makeentry; + error = lomacfs_node_alloc(dvp->v_mount, cnp, dvp, vp, + ap->a_vpp); + if (error) + vput(vp); + else if (issock) { + /* + * I should really find a nicer way to do this. + */ + vref(vp); + vput(*ap->a_vpp); + *ap->a_vpp = vp; + (void)VOP_LOCK(vp, LK_EXCLUSIVE | LK_RETRY, td); + } + } + return (error); +} + +static int +lomacfs_muxremove( + struct vop_remove_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap +) { + struct vnode *dvp = ap->a_dvp; + struct vnode *vp = ap->a_vp; + int error; + + ap->a_dvp = VTOLVP(dvp); + if (VISLOMAC(vp)) + ap->a_vp = VTOLVP(vp); + error = VCALL(ap->a_dvp, ap->a_desc->vdesc_offset, ap); + if (error == 0) + cache_purge(vp); + return (error); +} + +static int +lomacfs_fsync( + struct vop_fsync_args /* { + struct vnode *a_vp; + struct ucred *a_cred; + int a_waitfor; + struct thread *a_td; + } */ *ap +) { + + return (VOP_FSYNC(VTOLVP(ap->a_vp), ap->a_cred, ap->a_waitfor, + ap->a_td)); +} + +static int +lomacfs_advlock( + struct vop_advlock_args /* { + struct vnode *a_vp; + caddr_t a_id; + int a_op; + struct flock *a_fl; + int a_flags; + } */ *ap +) { + + return (VOP_ADVLOCK(VTOLVP(ap->a_vp), ap->a_id, ap->a_op, ap->a_fl, + ap->a_flags)); +} + +static int +lomacfs_whiteout( + struct vop_whiteout_args /* { + struct vnode *a_dvp; + struct componentname *a_cnp; + int a_flags; + } */ *ap +) { + + return (VOP_WHITEOUT(VTOLVP(ap->a_dvp), ap->a_cnp, ap->a_flags)); +} + +static int +lomacfs_poll( + struct vop_poll_args /* { + struct vnode *a_vp; + int a_events; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap +) { + + return (VOP_POLL(VTOLVP(ap->a_vp), ap->a_events, ap->a_cred, ap->a_td)); +} + +static int +lomacfs_revoke( + struct vop_revoke_args /* { + struct vnode *a_vp; + int a_flags; + } */ *ap +) { + + return (VOP_REVOKE(VTOLVP(ap->a_vp), ap->a_flags)); +} + +static int +lomacfs_link( + struct vop_link_args /* { + struct vnode *a_tdvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap +) { + struct vnode *tdvp = ap->a_tdvp; + struct vnode *vp = ap->a_vp; + struct vnode *lvp = VISLOMAC(vp) ? VTOLVP(vp) : vp; + struct componentname *cnp = ap->a_cnp; + int error; + + error = VOP_LINK(VTOLVP(tdvp), lvp, cnp); + if (error == 0 && vp->v_type == VSOCK) { + cache_enter(tdvp, vp, cnp); +#if defined(LOMAC_DEBUG_LINK) + do { + struct vnode *nvp; + int nerror; + + nerror = cache_lookup(tdvp, &nvp, cnp); + printf("lomacfs: link(%p), cache_lookup() = %d (%p)\n", + vp, nerror, nvp); + } while (0); +#endif + } + return (error); +} + +static int +lomacfs_rename( + struct vop_rename_args /* { + struct vnode *a_fdvp; + struct vnode *a_fvp; + struct componentname *a_fcnp; + struct vnode *a_tdvp; + struct vnode *a_tvp; + struct componentname *a_tcnp; + } */ *ap +) { + struct vnode *fdvp = ap->a_fdvp; + struct vnode *fvp = ap->a_fvp; + struct componentname *fcnp = ap->a_fcnp; + struct vnode *tdvp = ap->a_tdvp; + struct vnode *tvp = ap->a_tvp; + struct componentname *tcnp = ap->a_tcnp; + int fvp_is_lomac = VISLOMAC(fvp); + int error; + + vref(VTOLVP(fdvp)); + /* + * Handle the case when LOMAC returns a real vnode for + * VSOCK, rather than the LOMAC covering vnode. + */ + if (fvp_is_lomac) + vref(VTOLVP(fvp)); + vref(VTOLVP(tdvp)); + if (tvp != NULL) + vref(VTOLVP(tvp)); + error = VOP_RENAME(VTOLVP(fdvp), fvp_is_lomac ? VTOLVP(fvp) : fvp, fcnp, + VTOLVP(tdvp), tvp != NULL ? VTOLVP(tvp) : NULL, tcnp); + if (fvp->v_type == VDIR) { + if (tvp != NULL && tvp->v_type == VDIR) + cache_purge(tdvp); + cache_purge(fdvp); + } + cache_purge(fvp); + if (tvp != NULL) + cache_purge(tvp); + (void)VOP_UNLOCK(tdvp, LK_THISLAYER, curthread); + vrele(fdvp); + if (fvp_is_lomac) + vrele(fvp); + vrele(tdvp); + if (tvp != NULL) { + (void)VOP_UNLOCK(tvp, LK_THISLAYER, curthread); + vrele(tvp); + } else if (tcnp->cn_nameiop == RENAME /* NOCACHE unsets MAKEENTRY */ + && fvp->v_type == VSOCK) + cache_enter(tdvp, fvp, tcnp); + return (error); +} + +static int +lomacfs_strategy( + struct vop_strategy_args /* { + struct vnode *a_vp; + struct buf *a_bp; + } */ *ap +) { + + return (VOP_STRATEGY(VTOLVP(ap->a_vp), ap->a_bp)); +} + +/* + * Let an underlying filesystem do the work of creating the "actual" + * vm_object_t, and we will reference it. + */ +static int +lomacfs_createvobject( + struct vop_createvobject_args /* { + struct vnode *vp; + struct ucred *cred; + struct proc *p; + } */ *ap +) { + struct vnode *vp = ap->a_vp; + struct vnode *lowervp = VTOLOMAC(vp) != NULL ? VTOLVP(vp) : NULL; + int error; + + if (vp->v_type == VNON || lowervp == NULL) + return (EINVAL); + error = VOP_CREATEVOBJECT(lowervp, ap->a_cred, ap->a_td); + if (error) + return (error); + vp->v_flag |= VOBJBUF; + return (error); +} + +/* + * We need to destroy the lower vnode object only if we created it. + * XXX - I am very unsure about all of this. + */ +static int +lomacfs_destroyvobject( + struct vop_destroyvobject_args /* { + struct vnode *vp; + } */ *ap +) { + struct vnode *vp = ap->a_vp; + + vp->v_flag &= ~VOBJBUF; + return (0); +} + +static int +lomacfs_bmap( + struct vop_bmap_args /* { + struct vnode *a_vp; + daddr_t a_bn; + struct vnode **a_vpp; + daddr_t *a_bnp; + int *a_runp; + int *a_runb; + } */ *ap +) { + + return (VOP_BMAP(VTOLVP(ap->a_vp), ap->a_bn, ap->a_vpp, ap->a_bnp, + ap->a_runp, ap->a_runb)); +} + +static int +lomacfs_getpages( + struct vop_getpages_args /* { + struct vnode *a_vp; + vm_page_t *a_m; + int a_count; + int a_reqpage; + vm_ooffset_t a_offset; + } */ *ap +) { + + return (VOP_GETPAGES(VTOLVP(ap->a_vp), ap->a_m, ap->a_count, + ap->a_reqpage, ap->a_offset)); +} + +static int +lomacfs_putpages( + struct vop_putpages_args /* { + struct vnode *a_vp; + vm_page_t *a_m; + int a_count; + int a_sync; + int *a_rtvals; + vm_ooffset_t a_offset; + } */ *ap +) { + + return (VOP_PUTPAGES(VTOLVP(ap->a_vp), ap->a_m, ap->a_count, + ap->a_sync, ap->a_rtvals, ap->a_offset)); +} + +static int +lomacfs_getvobject( + struct vop_getvobject_args /* { + struct vnode *a_vp; + struct vm_object **a_objpp; + } */ *ap +) { + struct vnode *lvp = VTOLVP(ap->a_vp); + + if (lvp == NULL) + return EINVAL; + return (VOP_GETVOBJECT(lvp, ap->a_objpp)); +} + +static int +lomacfs_kqfilter( + struct vop_kqfilter_args /* { + struct vnode *a_vp; + struct knote *a_kn; + } */ *ap +) { + + return (VOP_KQFILTER(VTOLVP(ap->a_vp), ap->a_kn)); +} + +static int +lomacfs_pathconf( + struct vop_pathconf_args /* { + struct vnode *a_vp; + int a_name; + register_t *a_retval; + } */ *ap +) { + + return (VOP_PATHCONF(VTOLVP(ap->a_vp), ap->a_name, ap->a_retval)); +} + +static int +lomacfs_reallocblks( + struct vop_reallocblks_args /* { + struct vnode *a_vp; + struct cluster_save *a_buflist; + } */ *ap +) { + + return (VOP_REALLOCBLKS(VTOLVP(ap->a_vp), ap->a_buflist)); +} + +static int +lomacfs_freeblks( + struct vop_freeblks_args /* { + struct vnode *a_vp; + daddr_t a_addr; + daddr_t a_length; + } */ *ap +) { + + return (VOP_FREEBLKS(VTOLVP(ap->a_vp), ap->a_addr, ap->a_length)); +} + +static int +lomacfs_getacl( + struct vop_getacl_args /* { + struct vnode *a_vp; + acl_type_t a_type; + struct acl *a_aclp; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap +) { + + return (VOP_GETACL(VTOLVP(ap->a_vp), ap->a_type, ap->a_aclp, ap->a_cred, + ap->a_td)); +} + +static int +lomacfs_setacl( + struct vop_setacl_args /* { + struct vnode *a_vp; + acl_type_t a_type; + struct acl *a_aclp; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap +) { + lomac_object_t lobj; + + lobj.lo_type = LO_TYPE_LVNODE; + lobj.lo_object.vnode = ap->a_vp; + if (!mediate_subject_object("setacl", ap->a_td->td_proc, &lobj)) + return (EPERM); + else + return (VOP_SETACL(VTOLVP(ap->a_vp), ap->a_type, ap->a_aclp, + ap->a_cred, ap->a_td)); +} + +static int +lomacfs_aclcheck( + struct vop_aclcheck_args /* { + struct vnode *a_vp; + acl_type_t a_type; + struct acl *a_aclp; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap +) { + + return (VOP_ACLCHECK(VTOLVP(ap->a_vp), ap->a_type, ap->a_aclp, + ap->a_cred, ap->a_td)); +} + +static int +lomacfs_getextattr( + struct vop_getextattr_args /* { + struct vnode *a_vp; + int a_attrnamespace; + const char *a_name; + struct uio *a_uio; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap +) { + lomac_object_t lobj; + + lobj.lo_type = LO_TYPE_LVNODE; + lobj.lo_object.vnode = ap->a_vp; + if (monitor_read_object(ap->a_td->td_proc, &lobj)) + return (EPERM); + else + return (VOP_GETEXTATTR(VTOLVP(ap->a_vp), ap->a_attrnamespace, + ap->a_name, ap->a_uio, ap->a_cred, ap->a_td)); +} + +static int +lomacfs_setextattr( + struct vop_setextattr_args /* { + struct vnode *a_vp; + int a_attrnamespace; + const char *a_name; + struct uio *a_uio; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap +) { + lomac_object_t lobj; + + lobj.lo_type = LO_TYPE_LVNODE; + lobj.lo_object.vnode = ap->a_vp; + if (!mediate_subject_object("setextattr", ap->a_td->td_proc, &lobj)) + return (EPERM); + else + return (VOP_SETEXTATTR(VTOLVP(ap->a_vp), ap->a_attrnamespace, + ap->a_name, ap->a_uio, ap->a_cred, ap->a_td)); +} + +vop_t **lomacfs_vnodeop_p; +static struct vnodeopv_entry_desc lomacfs_vnodeop_entries[] = { + { &vop_default_desc, (vop_t *)lomacfs_defaultop }, + { &vop_inactive_desc, (vop_t *)lomacfs_inactive }, + { &vop_reclaim_desc, (vop_t *)lomacfs_reclaim }, + { &vop_print_desc, (vop_t *)lomacfs_print }, + { &vop_lock_desc, (vop_t *)lomacfs_lock }, + { &vop_unlock_desc, (vop_t *)lomacfs_unlock }, + { &vop_islocked_desc, (vop_t *)lomacfs_islocked }, + { &vop_lookup_desc, (vop_t *)lomacfs_lookup }, + { &vop_setattr_desc, (vop_t *)lomacfs_setattr }, + { &vop_getattr_desc, (vop_t *)lomacfs_getattr }, + { &vop_readdir_desc, (vop_t *)lomacfs_readdir }, + { &vop_open_desc, (vop_t *)lomacfs_open }, + { &vop_close_desc, (vop_t *)lomacfs_close }, + { &vop_access_desc, (vop_t *)lomacfs_access }, + { &vop_readlink_desc, (vop_t *)lomacfs_readlink }, + { &vop_lease_desc, (vop_t *)lomacfs_lease }, + { &vop_read_desc, (vop_t *)lomacfs_read }, + { &vop_write_desc, (vop_t *)lomacfs_write }, + { &vop_ioctl_desc, (vop_t *)lomacfs_ioctl }, + { &vop_create_desc, (vop_t *)lomacfs_muxcreate }, + { &vop_mkdir_desc, (vop_t *)lomacfs_muxcreate }, + { &vop_mknod_desc, (vop_t *)lomacfs_muxcreate }, + { &vop_symlink_desc, (vop_t *)lomacfs_muxcreate }, + { &vop_remove_desc, (vop_t *)lomacfs_muxremove }, + { &vop_rmdir_desc, (vop_t *)lomacfs_muxremove }, + { &vop_fsync_desc, (vop_t *)lomacfs_fsync }, + { &vop_advlock_desc, (vop_t *)lomacfs_advlock }, + { &vop_whiteout_desc, (vop_t *)lomacfs_whiteout }, + { &vop_poll_desc, (vop_t *)lomacfs_poll }, + { &vop_link_desc, (vop_t *)lomacfs_link }, + { &vop_rename_desc, (vop_t *)lomacfs_rename }, + { &vop_revoke_desc, (vop_t *)lomacfs_revoke }, + { &vop_cachedlookup_desc, (vop_t *)lomacfs_cachedlookup }, + { &vop_lookup_desc, (vop_t *)lomacfs_lookup }, + { &vop_bmap_desc, (vop_t *)lomacfs_bmap }, + { &vop_getpages_desc, (vop_t *)lomacfs_getpages }, + { &vop_putpages_desc, (vop_t *)lomacfs_putpages }, + { &vop_strategy_desc, (vop_t *)lomacfs_strategy }, + { &vop_createvobject_desc, (vop_t *)lomacfs_createvobject }, + { &vop_destroyvobject_desc, (vop_t *)lomacfs_destroyvobject }, + { &vop_getvobject_desc, (vop_t *)lomacfs_getvobject }, + { &vop_getwritemount_desc, (vop_t *)vop_stdgetwritemount }, + { &vop_kqfilter_desc, (vop_t *)lomacfs_kqfilter }, + { &vop_pathconf_desc, (vop_t *)lomacfs_pathconf }, + { &vop_reallocblks_desc, (vop_t *)lomacfs_reallocblks }, + { &vop_freeblks_desc, (vop_t *)lomacfs_freeblks }, + { &vop_getacl_desc, (vop_t *)lomacfs_getacl }, + { &vop_setacl_desc, (vop_t *)lomacfs_setacl }, + { &vop_aclcheck_desc, (vop_t *)lomacfs_aclcheck }, + { &vop_getextattr_desc, (vop_t *)lomacfs_getextattr }, + { &vop_setextattr_desc, (vop_t *)lomacfs_setextattr }, + { NULL, NULL } +}; +static struct vnodeopv_desc lomacfs_vnodeopv_opv_desc = + { &lomacfs_vnodeop_p, lomacfs_vnodeop_entries }; +VNODEOP_SET(lomacfs_vnodeopv_opv_desc); |