#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/cred.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/fcntl.h>
#include <sys/flock.h>
#include <sys/kmem.h>
#include <sys/uio.h>
#include <sys/vfs.h>
#include <sys/vfs_opreg.h>
#include <sys/vnode.h>
#include <sys/pcb.h>
#include <sys/signal.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/conf.h>
#include <sys/debug.h>
#include <vm/seg.h>
#include <sys/fs/namenode.h>
#include <sys/stream.h>
#include <fs/fs_subr.h>
#include <sys/policy.h>
int
nm_open(vnode_t **vpp, int flag, cred_t *crp, caller_context_t *ct)
{
struct namenode *nodep = VTONM(*vpp);
int error = 0;
struct namenode *newnamep;
struct vnode *newvp;
struct vnode *infilevp;
struct vnode *outfilevp;
infilevp = outfilevp = nodep->nm_filevp;
VN_HOLD(outfilevp);
if ((error = VOP_OPEN(&outfilevp, flag, crp, ct)) != 0) {
VN_RELE(outfilevp);
return (error);
}
if (infilevp != outfilevp) {
mutex_enter(&ntable_lock);
if ((newnamep =
namefind(outfilevp, nodep->nm_mountpt)) != NULL) {
struct vnode *vp = NMTOV(newnamep);
VN_HOLD(vp);
goto gotit;
}
newnamep = kmem_zalloc(sizeof (struct namenode), KM_SLEEP);
newvp = vn_alloc(KM_SLEEP);
newnamep->nm_vnode = newvp;
mutex_init(&newnamep->nm_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_enter(&nodep->nm_lock);
newvp->v_flag = ((*vpp)->v_flag | VNOMAP | VNOSWAP) & ~VROOT;
vn_setops(newvp, vn_getops(*vpp));
newvp->v_vfsp = &namevfs;
newvp->v_stream = outfilevp->v_stream;
newvp->v_type = outfilevp->v_type;
newvp->v_rdev = outfilevp->v_rdev;
newvp->v_data = (caddr_t)newnamep;
vn_exists(newvp);
bcopy(&nodep->nm_vattr, &newnamep->nm_vattr, sizeof (vattr_t));
newnamep->nm_vattr.va_type = outfilevp->v_type;
newnamep->nm_vattr.va_nodeid = namenodeno_alloc();
newnamep->nm_vattr.va_size = (u_offset_t)0;
newnamep->nm_vattr.va_rdev = outfilevp->v_rdev;
newnamep->nm_flag = NMNMNT;
newnamep->nm_filevp = outfilevp;
newnamep->nm_filep = nodep->nm_filep;
newnamep->nm_mountpt = nodep->nm_mountpt;
mutex_exit(&nodep->nm_lock);
nameinsert(newnamep);
gotit:
mutex_exit(&ntable_lock);
VN_RELE(*vpp);
*vpp = NMTOV(newnamep);
}
return (0);
}
static int
nm_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *crp,
caller_context_t *ct)
{
struct namenode *nodep = VTONM(vp);
int error = 0;
(void) cleanlocks(vp, ttoproc(curthread)->p_pid, 0);
cleanshares(vp, ttoproc(curthread)->p_pid);
error = VOP_CLOSE(nodep->nm_filevp, flag, count, offset, crp, ct);
if (count == 1) {
(void) VOP_FSYNC(nodep->nm_filevp, FSYNC, crp, ct);
if ((nodep->nm_flag & NMNMNT) != 0) {
mutex_enter(&ntable_lock);
nameremove(nodep);
mutex_exit(&ntable_lock);
}
VN_RELE(nodep->nm_filevp);
}
return (error);
}
static int
nm_read(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *crp,
caller_context_t *ct)
{
return (VOP_READ(VTONM(vp)->nm_filevp, uiop, ioflag, crp, ct));
}
static int
nm_write(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *crp,
caller_context_t *ct)
{
return (VOP_WRITE(VTONM(vp)->nm_filevp, uiop, ioflag, crp, ct));
}
static int
nm_ioctl(vnode_t *vp, int cmd, intptr_t arg, int mode, cred_t *cr, int *rvalp,
caller_context_t *ct)
{
return (VOP_IOCTL(VTONM(vp)->nm_filevp, cmd, arg, mode, cr, rvalp, ct));
}
static int
nm_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *crp,
caller_context_t *ct)
{
struct namenode *nodep = VTONM(vp);
struct vattr va;
int error;
mutex_enter(&nodep->nm_lock);
bcopy(&nodep->nm_vattr, vap, sizeof (vattr_t));
mutex_exit(&nodep->nm_lock);
if ((va.va_mask = vap->va_mask & AT_SIZE) != 0) {
if (error = VOP_GETATTR(nodep->nm_filevp, &va, flags, crp, ct))
return (error);
vap->va_size = va.va_size;
}
return (0);
}
static int
nm_access_unlocked(void *vnp, int mode, cred_t *crp)
{
struct namenode *nodep = vnp;
int shift = 0;
if (crgetuid(crp) != nodep->nm_vattr.va_uid) {
shift += 3;
if (!groupmember(nodep->nm_vattr.va_gid, crp))
shift += 3;
}
return (secpolicy_vnode_access2(crp, NMTOV(nodep),
nodep->nm_vattr.va_uid, nodep->nm_vattr.va_mode << shift,
mode));
}
static int
nm_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *crp,
caller_context_t *ctp)
{
struct namenode *nodep = VTONM(vp);
struct vattr *nmvap = &nodep->nm_vattr;
long mask = vap->va_mask;
int error = 0;
if (mask & (AT_NOSET|AT_SIZE))
return (EINVAL);
(void) VOP_RWLOCK(nodep->nm_filevp, V_WRITELOCK_TRUE, ctp);
mutex_enter(&nodep->nm_lock);
error = secpolicy_vnode_setattr(crp, vp, vap, nmvap, flags,
nm_access_unlocked, nodep);
if (error)
goto out;
mask = vap->va_mask;
if (mask & AT_MODE)
nmvap->va_mode = vap->va_mode & ~VSVTX;
if (mask & AT_UID)
nmvap->va_uid = vap->va_uid;
if (mask & AT_GID)
nmvap->va_gid = vap->va_gid;
if (mask & AT_ATIME)
nmvap->va_atime = vap->va_atime;
if (mask & AT_MTIME) {
nmvap->va_mtime = vap->va_mtime;
gethrestime(&nmvap->va_ctime);
}
out:
mutex_exit(&nodep->nm_lock);
VOP_RWUNLOCK(nodep->nm_filevp, V_WRITELOCK_TRUE, ctp);
return (error);
}
static int
nm_access(vnode_t *vp, int mode, int flags, cred_t *crp, caller_context_t *ct)
{
struct namenode *nodep = VTONM(vp);
int error;
mutex_enter(&nodep->nm_lock);
error = nm_access_unlocked(nodep, mode, crp);
mutex_exit(&nodep->nm_lock);
if (error == 0)
return (VOP_ACCESS(nodep->nm_filevp, mode, flags, crp, ct));
else
return (error);
}
static int
nm_create(vnode_t *dvp, char *name, vattr_t *vap, enum vcexcl excl, int mode,
vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct,
vsecattr_t *vsecp)
{
int error;
ASSERT(dvp && *name == '\0');
if (excl == NONEXCL) {
if (mode && (error = nm_access(dvp, mode, 0, cr, ct)) != 0)
return (error);
VN_HOLD(dvp);
return (0);
}
return (EEXIST);
}
static int
nm_link(vnode_t *tdvp, vnode_t *vp, char *tnm, cred_t *crp,
caller_context_t *ct, int flags)
{
return (EXDEV);
}
static int
nm_fsync(vnode_t *vp, int syncflag, cred_t *crp, caller_context_t *ct)
{
return (VOP_FSYNC(VTONM(vp)->nm_filevp, syncflag, crp, ct));
}
static void
nm_inactive(vnode_t *vp, cred_t *crp, caller_context_t *ct)
{
struct namenode *nodep = VTONM(vp);
vfs_t *vfsp = vp->v_vfsp;
mutex_enter(&vp->v_lock);
ASSERT(vp->v_count >= 1);
VN_RELE_LOCKED(vp);
if (vp->v_count != 0) {
mutex_exit(&vp->v_lock);
return;
}
mutex_exit(&vp->v_lock);
if (!(nodep->nm_flag & NMNMNT)) {
ASSERT(nodep->nm_filep->f_vnode == nodep->nm_filevp);
(void) closef(nodep->nm_filep);
}
vn_invalid(vp);
vn_free(vp);
if (vfsp != &namevfs)
VFS_RELE(vfsp);
namenodeno_free(nodep->nm_vattr.va_nodeid);
kmem_free(nodep, sizeof (struct namenode));
}
static int
nm_fid(vnode_t *vp, struct fid *fidnodep, caller_context_t *ct)
{
return (VOP_FID(VTONM(vp)->nm_filevp, fidnodep, ct));
}
static int
nm_rwlock(vnode_t *vp, int write, caller_context_t *ctp)
{
return (VOP_RWLOCK(VTONM(vp)->nm_filevp, write, ctp));
}
static void
nm_rwunlock(vnode_t *vp, int write, caller_context_t *ctp)
{
VOP_RWUNLOCK(VTONM(vp)->nm_filevp, write, ctp);
}
static int
nm_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct)
{
return (VOP_SEEK(VTONM(vp)->nm_filevp, ooff, noffp, ct));
}
static int
nm_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct)
{
struct vnode *rvp;
vp = VTONM(vp)->nm_filevp;
if (VOP_REALVP(vp, &rvp, ct) == 0)
vp = rvp;
*vpp = vp;
return (0);
}
static int
nm_poll(vnode_t *vp, short events, int anyyet, short *reventsp,
pollhead_t **phpp, caller_context_t *ct)
{
return (VOP_POLL(VTONM(vp)->nm_filevp, events, anyyet, reventsp,
phpp, ct));
}
struct vnodeops *nm_vnodeops;
const fs_operation_def_t nm_vnodeops_template[] = {
VOPNAME_OPEN, { .vop_open = nm_open },
VOPNAME_CLOSE, { .vop_close = nm_close },
VOPNAME_READ, { .vop_read = nm_read },
VOPNAME_WRITE, { .vop_write = nm_write },
VOPNAME_IOCTL, { .vop_ioctl = nm_ioctl },
VOPNAME_GETATTR, { .vop_getattr = nm_getattr },
VOPNAME_SETATTR, { .vop_setattr = nm_setattr },
VOPNAME_ACCESS, { .vop_access = nm_access },
VOPNAME_CREATE, { .vop_create = nm_create },
VOPNAME_LINK, { .vop_link = nm_link },
VOPNAME_FSYNC, { .vop_fsync = nm_fsync },
VOPNAME_INACTIVE, { .vop_inactive = nm_inactive },
VOPNAME_FID, { .vop_fid = nm_fid },
VOPNAME_RWLOCK, { .vop_rwlock = nm_rwlock },
VOPNAME_RWUNLOCK, { .vop_rwunlock = nm_rwunlock },
VOPNAME_SEEK, { .vop_seek = nm_seek },
VOPNAME_REALVP, { .vop_realvp = nm_realvp },
VOPNAME_POLL, { .vop_poll = nm_poll },
VOPNAME_DISPOSE, { .error = fs_error },
NULL, NULL
};