#include <sys/types.h>
#include <sys/t_lock.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bitmap.h>
#include <sys/debug.h>
#include <sys/errno.h>
#include <sys/strsubr.h>
#include <sys/cmn_err.h>
#include <sys/sysmacros.h>
#include <sys/filio.h>
#include <sys/flock.h>
#include <sys/stat.h>
#include <sys/share.h>
#include <sys/vfs.h>
#include <sys/vfs_opreg.h>
#include <sys/sockio.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/strsun.h>
#include <fs/sockfs/sockcommon.h>
#include <fs/sockfs/socktpi.h>
static int socket_vop_open(struct vnode **, int, struct cred *,
caller_context_t *);
static int socket_vop_close(struct vnode *, int, int, offset_t,
struct cred *, caller_context_t *);
static int socket_vop_read(struct vnode *, struct uio *, int,
struct cred *, caller_context_t *);
static int socket_vop_write(struct vnode *, struct uio *, int,
struct cred *, caller_context_t *);
static int socket_vop_ioctl(struct vnode *, int, intptr_t, int,
struct cred *, int32_t *, caller_context_t *);
static int socket_vop_setfl(struct vnode *, int, int, cred_t *,
caller_context_t *);
static int socket_vop_getattr(struct vnode *, struct vattr *, int,
struct cred *, caller_context_t *);
static int socket_vop_setattr(struct vnode *, struct vattr *, int,
struct cred *, caller_context_t *);
static int socket_vop_access(struct vnode *, int, int, struct cred *,
caller_context_t *);
static int socket_vop_fsync(struct vnode *, int, struct cred *,
caller_context_t *);
static void socket_vop_inactive(struct vnode *, struct cred *,
caller_context_t *);
static int socket_vop_fid(struct vnode *, struct fid *,
caller_context_t *);
static int socket_vop_seek(struct vnode *, offset_t, offset_t *,
caller_context_t *);
static int socket_vop_poll(struct vnode *, short, int, short *,
struct pollhead **, caller_context_t *);
extern int socket_close_internal(struct sonode *, int, cred_t *);
extern void socket_destroy_internal(struct sonode *, cred_t *);
struct vnodeops *socket_vnodeops;
const fs_operation_def_t socket_vnodeops_template[] = {
VOPNAME_OPEN, { .vop_open = socket_vop_open },
VOPNAME_CLOSE, { .vop_close = socket_vop_close },
VOPNAME_READ, { .vop_read = socket_vop_read },
VOPNAME_WRITE, { .vop_write = socket_vop_write },
VOPNAME_IOCTL, { .vop_ioctl = socket_vop_ioctl },
VOPNAME_SETFL, { .vop_setfl = socket_vop_setfl },
VOPNAME_GETATTR, { .vop_getattr = socket_vop_getattr },
VOPNAME_SETATTR, { .vop_setattr = socket_vop_setattr },
VOPNAME_ACCESS, { .vop_access = socket_vop_access },
VOPNAME_FSYNC, { .vop_fsync = socket_vop_fsync },
VOPNAME_INACTIVE, { .vop_inactive = socket_vop_inactive },
VOPNAME_FID, { .vop_fid = socket_vop_fid },
VOPNAME_SEEK, { .vop_seek = socket_vop_seek },
VOPNAME_POLL, { .vop_poll = socket_vop_poll },
VOPNAME_DISPOSE, { .error = fs_error },
NULL, NULL
};
static int
socket_vop_open(struct vnode **vpp, int flag, struct cred *cr,
caller_context_t *ct)
{
struct vnode *vp = *vpp;
struct sonode *so = VTOSO(vp);
flag &= ~FCREAT;
mutex_enter(&so->so_lock);
so->so_count++;
mutex_exit(&so->so_lock);
ASSERT(so->so_count != 0);
ASSERT(vp->v_type == VSOCK);
return (0);
}
static int
socket_vop_close(struct vnode *vp, int flag, int count, offset_t offset,
struct cred *cr, caller_context_t *ct)
{
struct sonode *so;
int error = 0;
so = VTOSO(vp);
ASSERT(vp->v_type == VSOCK);
cleanlocks(vp, ttoproc(curthread)->p_pid, 0);
cleanshares(vp, ttoproc(curthread)->p_pid);
if (vp->v_stream)
strclean(vp);
if (count > 1) {
dprint(2, ("socket_vop_close: count %d\n", count));
return (0);
}
mutex_enter(&so->so_lock);
if (--so->so_count == 0) {
mutex_exit(&so->so_lock);
error = socket_close_internal(so, flag, cr);
} else {
mutex_exit(&so->so_lock);
}
return (error);
}
static int
socket_vop_read(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *cr,
caller_context_t *ct)
{
struct sonode *so = VTOSO(vp);
struct nmsghdr lmsg;
ASSERT(vp->v_type == VSOCK);
bzero((void *)&lmsg, sizeof (lmsg));
return (socket_recvmsg(so, &lmsg, uiop, cr));
}
static int
socket_vop_write(struct vnode *vp, struct uio *uiop, int ioflag,
struct cred *cr, caller_context_t *ct)
{
struct sonode *so = VTOSO(vp);
struct nmsghdr lmsg;
ASSERT(vp->v_type == VSOCK);
bzero((void *)&lmsg, sizeof (lmsg));
if (!(so->so_mode & SM_BYTESTREAM)) {
lmsg.msg_flags = MSG_EOR;
}
return (socket_sendmsg(so, &lmsg, uiop, cr));
}
static int
socket_vop_ioctl(struct vnode *vp, int cmd, intptr_t arg, int mode,
struct cred *cr, int32_t *rvalp, caller_context_t *ct)
{
struct sonode *so = VTOSO(vp);
ASSERT(vp->v_type == VSOCK);
return (socket_ioctl(so, cmd, arg, mode, cr, rvalp));
}
static int
socket_vop_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr,
caller_context_t *ct)
{
struct sonode *so = VTOSO(vp);
int error = 0;
ASSERT(vp->v_type == VSOCK);
mutex_enter(&so->so_lock);
if (nflags & FNDELAY)
so->so_state |= SS_NDELAY;
else
so->so_state &= ~SS_NDELAY;
if (nflags & FNONBLOCK)
so->so_state |= SS_NONBLOCK;
else
so->so_state &= ~SS_NONBLOCK;
mutex_exit(&so->so_lock);
if (so->so_state & SS_ASYNC)
oflags |= FASYNC;
if ((oflags ^ nflags) & FASYNC && so->so_version != SOV_STREAM) {
int async = nflags & FASYNC;
int32_t rv;
error = socket_ioctl(so, FIOASYNC, (intptr_t)&async, FKIOCTL,
cr, &rv);
}
return (error);
}
int
socket_vop_getattr(struct vnode *vp, struct vattr *vap, int flags,
struct cred *cr, caller_context_t *ct)
{
dev_t fsid;
struct sonode *so;
static int sonode_shift = 0;
if (sonode_shift == 0) {
int bit = highbit(sizeof (struct sonode));
VERIFY3S(bit, >, 0);
sonode_shift = bit - 1;
}
so = VTOSO(vp);
fsid = sockdev;
if (so->so_version == SOV_STREAM) {
vap->va_type = VCHR;
vap->va_mode = 0;
} else {
vap->va_type = vp->v_type;
vap->va_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|
S_IROTH|S_IWOTH;
}
vap->va_uid = vap->va_gid = 0;
vap->va_fsid = fsid;
vap->va_nodeid = ((ino_t)so >> sonode_shift) & 0xFFFFFFFF;
vap->va_nlink = 0;
vap->va_size = 0;
vap->va_rdev = (dev_t)0;
vap->va_blksize = MAXBSIZE;
vap->va_nblocks = btod(vap->va_size);
if (!SOCK_IS_NONSTR(so)) {
sotpi_info_t *sti = SOTOTPI(so);
mutex_enter(&so->so_lock);
vap->va_atime.tv_sec = sti->sti_atime;
vap->va_mtime.tv_sec = sti->sti_mtime;
vap->va_ctime.tv_sec = sti->sti_ctime;
mutex_exit(&so->so_lock);
} else {
vap->va_atime.tv_sec = 0;
vap->va_mtime.tv_sec = 0;
vap->va_ctime.tv_sec = 0;
}
vap->va_atime.tv_nsec = 0;
vap->va_mtime.tv_nsec = 0;
vap->va_ctime.tv_nsec = 0;
vap->va_seq = 0;
return (0);
}
int
socket_vop_setattr(struct vnode *vp, struct vattr *vap, int flags,
struct cred *cr, caller_context_t *ct)
{
struct sonode *so = VTOSO(vp);
if (!SOCK_IS_NONSTR(so)) {
sotpi_info_t *sti = SOTOTPI(so);
mutex_enter(&so->so_lock);
if (vap->va_mask & AT_ATIME)
sti->sti_atime = vap->va_atime.tv_sec;
if (vap->va_mask & AT_MTIME) {
sti->sti_mtime = vap->va_mtime.tv_sec;
sti->sti_ctime = gethrestime_sec();
}
mutex_exit(&so->so_lock);
}
return (0);
}
int
socket_vop_access(struct vnode *vp, int mode, int flags, struct cred *cr,
caller_context_t *ct)
{
struct sonode *so = VTOSO(vp);
if (!SOCK_IS_NONSTR(so)) {
ASSERT(so->so_sockparams->sp_sdev_info.sd_vnode != NULL);
return (VOP_ACCESS(so->so_sockparams->sp_sdev_info.sd_vnode,
mode, flags, cr, NULL));
}
return (0);
}
int
socket_vop_fsync(struct vnode *vp, int syncflag, struct cred *cr,
caller_context_t *ct)
{
return (EINVAL);
}
static void
socket_vop_inactive(struct vnode *vp, struct cred *cr, caller_context_t *ct)
{
struct sonode *so = VTOSO(vp);
ASSERT(vp->v_type == VSOCK);
mutex_enter(&vp->v_lock);
if (vp->v_count < 1)
cmn_err(CE_PANIC, "socket_inactive: Bad v_count");
VN_RELE_LOCKED(vp);
if (vp->v_count != 0) {
mutex_exit(&vp->v_lock);
return;
}
mutex_exit(&vp->v_lock);
ASSERT(!vn_has_cached_data(vp));
socket_destroy_internal(so, cr);
}
int
socket_vop_fid(struct vnode *vp, struct fid *fidp, caller_context_t *ct)
{
return (EINVAL);
}
int
socket_vop_seek(struct vnode *vp, offset_t ooff, offset_t *noffp,
caller_context_t *ct)
{
return (ESPIPE);
}
static int
socket_vop_poll(struct vnode *vp, short events, int anyyet, short *reventsp,
struct pollhead **phpp, caller_context_t *ct)
{
struct sonode *so = VTOSO(vp);
ASSERT(vp->v_type == VSOCK);
return (socket_poll(so, events, anyyet, reventsp, phpp));
}