e6e370a7fe
- v_vflag is protected by the vnode lock and is used when synchronization with VOP calls is needed. - v_iflag is protected by interlock and is used for dealing with vnode management issues. These flags include X/O LOCK, FREE, DOOMED, etc. - All accesses to v_iflag and v_vflag have either been locked or marked with mp_fixme's. - Many ASSERT_VOP_LOCKED calls have been added where the locking was not clear. - Many functions in vfs_subr.c were restructured to provide for stronger locking. Idea stolen from: BSD/OS
1152 lines
27 KiB
C
1152 lines
27 KiB
C
/*-
|
|
* Copyright (c) 2001 Networks Associates Technology, 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 filesystem.
|
|
*/
|
|
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 = EIO;
|
|
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_vflag |= VV_OBJBUF;
|
|
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_vflag &= ~VV_OBJBUF;
|
|
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;
|
|
size_t *a_size;
|
|
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_size, 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);
|