#include <sys/types.h>
#include <sys/vnode.h>
#include <sys/vfs_opreg.h>
#include <sys/door.h>
#include <sys/proc.h>
#include <sys/kmem.h>
#include <sys/debug.h>
#include <sys/cmn_err.h>
#include <fs/fs_subr.h>
#include <sys/zone.h>
#include <sys/tsol/label.h>
kmutex_t door_knob;
static int door_open(struct vnode **vpp, int flag, struct cred *cr,
caller_context_t *ct);
static int door_close(struct vnode *vp, int flag, int count,
offset_t offset, struct cred *cr, caller_context_t *ct);
static int door_getattr(struct vnode *vp, struct vattr *vap,
int flags, struct cred *cr, caller_context_t *ct);
static void door_inactive(struct vnode *vp, struct cred *cr,
caller_context_t *ct);
static int door_access(struct vnode *vp, int mode, int flags,
struct cred *cr, caller_context_t *ct);
static int door_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct);
struct vfs door_vfs;
struct vnodeops *door_vnodeops;
const fs_operation_def_t door_vnodeops_template[] = {
VOPNAME_OPEN, { .vop_open = door_open },
VOPNAME_CLOSE, { .vop_close = door_close },
VOPNAME_GETATTR, { .vop_getattr = door_getattr },
VOPNAME_ACCESS, { .vop_access = door_access },
VOPNAME_INACTIVE, { .vop_inactive = door_inactive },
VOPNAME_FRLOCK, { .error = fs_error },
VOPNAME_REALVP, { .vop_realvp = door_realvp },
VOPNAME_POLL, { .error = fs_error },
VOPNAME_PATHCONF, { .error = fs_error },
VOPNAME_DISPOSE, { .error = fs_error },
VOPNAME_GETSECATTR, { .error = fs_error },
VOPNAME_SHRLOCK, { .error = fs_error },
NULL, NULL
};
static int
door_open(struct vnode **vpp, int flag, struct cred *cr, caller_context_t *ct)
{
if (is_system_labeled()) {
zone_t *server_zone, *client_zone;
door_node_t *dp = VTOD((*vpp));
mutex_enter(&door_knob);
if (DOOR_INVALID(dp)) {
mutex_exit(&door_knob);
return (0);
}
client_zone = curproc->p_zone;
server_zone = dp->door_target->p_zone;
mutex_exit(&door_knob);
if (server_zone != global_zone &&
server_zone != client_zone)
return (EACCES);
}
return (0);
}
static int
door_close(struct vnode *vp, int flag, int count, offset_t offset,
struct cred *cr, caller_context_t *ct)
{
door_node_t *dp = VTOD(vp);
ASSERT(dp->door_target != curproc ||
((curthread->t_proc_flag & TP_LWPEXIT) == 0));
if (count == 2 && vp->v_count == 1 &&
(dp->door_flags & (DOOR_UNREF | DOOR_UNREF_MULTI))) {
mutex_enter(&door_knob);
if (dp->door_active == 0) {
door_deliver_unref(dp);
} else {
dp->door_flags |= DOOR_DELAY;
}
mutex_exit(&door_knob);
}
return (0);
}
static int
door_getattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cr,
caller_context_t *ct)
{
static timestruc_t tzero = {0, 0};
extern dev_t doordev;
vap->va_mask = 0;
vap->va_type = vp->v_type;
vap->va_mode = 0777;
vap->va_uid = 0;
vap->va_gid = 0;
vap->va_fsid = doordev;
vap->va_nodeid = (ino64_t)0;
vap->va_nlink = vp->v_count;
vap->va_size = (u_offset_t)0;
vap->va_atime = tzero;
vap->va_mtime = tzero;
vap->va_ctime = tzero;
vap->va_rdev = doordev;
vap->va_blksize = 0;
vap->va_nblocks = (fsblkcnt64_t)0;
vap->va_seq = 0;
return (0);
}
static void
door_inactive(struct vnode *vp, struct cred *cr, caller_context_t *ct)
{
door_node_t *dp = VTOD(vp);
mutex_enter(&vp->v_lock);
ASSERT(vp->v_count == 1);
if (dp->door_bound_threads > 0) {
VN_RELE_LOCKED(vp);
mutex_exit(&vp->v_lock);
return;
}
mutex_exit(&vp->v_lock);
if (dp->door_target) {
mutex_enter(&door_knob);
if (dp->door_target)
door_list_delete(dp);
mutex_exit(&door_knob);
}
vn_invalid(vp);
vn_free(vp);
kmem_free(dp, sizeof (door_node_t));
}
void
door_bind_thread(door_node_t *dp)
{
vnode_t *vp = DTOV(dp);
mutex_enter(&vp->v_lock);
dp->door_bound_threads++;
ASSERT(dp->door_bound_threads > 0 && vp->v_count > 0);
mutex_exit(&vp->v_lock);
}
void
door_unbind_thread(door_node_t *dp)
{
vnode_t *vp = DTOV(dp);
int do_inactive = 0;
mutex_enter(&vp->v_lock);
ASSERT(dp->door_bound_threads > 0);
if (--dp->door_bound_threads == 0 && vp->v_count == 0) {
VN_HOLD_LOCKED(vp);
do_inactive = 1;
}
mutex_exit(&vp->v_lock);
if (do_inactive)
door_inactive(vp, NULL, NULL);
}
static int
door_access(struct vnode *vp, int mode, int flags, struct cred *cr,
caller_context_t *ct)
{
return (0);
}
static int
door_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct)
{
*vpp = vp;
return (0);
}