#include <sys/param.h>
#include <sys/proc.h>
#include <sys/time.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/stat.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <miscfs/fifofs/fifo.h>
#include <vfs/vfs_support.h>
struct fifoinfo {
struct socket *fi_readsock;
struct socket *fi_writesock;
long fi_readers;
long fi_writers;
};
#define VOPFUNC int (*)(void *)
int (**fifo_vnodeop_p)(void *);
struct vnodeopv_entry_desc fifo_vnodeop_entries[] = {
{ &vop_default_desc, (VOPFUNC)vn_default_error },
{ &vop_lookup_desc, (VOPFUNC)fifo_lookup },
{ &vop_create_desc, (VOPFUNC)err_create },
{ &vop_mknod_desc, (VOPFUNC)err_mknod },
{ &vop_open_desc, (VOPFUNC)fifo_open },
{ &vop_close_desc, (VOPFUNC)fifo_close },
{ &vop_access_desc, (VOPFUNC)fifo_access },
{ &vop_getattr_desc, (VOPFUNC)fifo_getattr },
{ &vop_setattr_desc, (VOPFUNC)fifo_setattr },
{ &vop_read_desc, (VOPFUNC)fifo_read },
{ &vop_write_desc, (VOPFUNC)fifo_write },
{ &vop_lease_desc, (VOPFUNC)fifo_lease_check },
{ &vop_ioctl_desc, (VOPFUNC)fifo_ioctl },
{ &vop_select_desc, (VOPFUNC)fifo_select },
{ &vop_revoke_desc, (VOPFUNC)fifo_revoke },
{ &vop_mmap_desc, (VOPFUNC)err_mmap },
{ &vop_fsync_desc, (VOPFUNC)fifo_fsync },
{ &vop_seek_desc, (VOPFUNC)err_seek },
{ &vop_remove_desc, (VOPFUNC)err_remove },
{ &vop_link_desc, (VOPFUNC)err_link },
{ &vop_rename_desc, (VOPFUNC)err_rename },
{ &vop_mkdir_desc, (VOPFUNC)err_mkdir },
{ &vop_rmdir_desc, (VOPFUNC)err_rmdir },
{ &vop_symlink_desc, (VOPFUNC)err_symlink },
{ &vop_readdir_desc, (VOPFUNC)err_readdir },
{ &vop_readlink_desc, (VOPFUNC)err_readlink },
{ &vop_abortop_desc, (VOPFUNC)err_abortop },
{ &vop_inactive_desc, (VOPFUNC)fifo_inactive },
{ &vop_reclaim_desc, (VOPFUNC)fifo_reclaim },
{ &vop_lock_desc, (VOPFUNC)fifo_lock },
{ &vop_unlock_desc, (VOPFUNC)fifo_unlock },
{ &vop_bmap_desc, (VOPFUNC)fifo_bmap },
{ &vop_strategy_desc, (VOPFUNC)err_strategy },
{ &vop_print_desc, (VOPFUNC)fifo_print },
{ &vop_islocked_desc, (VOPFUNC)fifo_islocked },
{ &vop_pathconf_desc, (VOPFUNC)fifo_pathconf },
{ &vop_advlock_desc, (VOPFUNC)fifo_advlock },
{ &vop_blkatoff_desc, (VOPFUNC)err_blkatoff },
{ &vop_valloc_desc, (VOPFUNC)err_valloc },
{ &vop_vfree_desc, (VOPFUNC)err_vfree },
{ &vop_truncate_desc, (VOPFUNC)fifo_truncate },
{ &vop_update_desc, (VOPFUNC)fifo_update },
{ &vop_bwrite_desc, (VOPFUNC)fifo_bwrite },
{ &vop_pagein_desc, (VOPFUNC)err_pagein },
{ &vop_pageout_desc, (VOPFUNC)err_pageout },
{ &vop_copyfile_desc, (VOPFUNC)err_copyfile },
{ &vop_blktooff_desc, (VOPFUNC)err_blktooff },
{ &vop_offtoblk_desc, (VOPFUNC)err_offtoblk },
{ &vop_cmap_desc, (VOPFUNC)err_cmap },
{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
};
struct vnodeopv_desc fifo_vnodeop_opv_desc =
{ &fifo_vnodeop_p, fifo_vnodeop_entries };
fifo_lookup(ap)
struct vop_lookup_args *ap;
{
*ap->a_vpp = NULL;
return (ENOTDIR);
}
fifo_open(ap)
struct vop_open_args *ap;
{
struct vnode *vp = ap->a_vp;
struct fifoinfo *fip;
struct proc *p = ap->a_p;
struct socket *rso, *wso;
int error;
if ((fip = vp->v_fifoinfo) == NULL) {
MALLOC_ZONE(fip, struct fifoinfo *,
sizeof(*fip), M_VNODE, M_WAITOK);
vp->v_fifoinfo = fip;
thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
if (error = socreate(AF_LOCAL, &rso, SOCK_STREAM, 0)) {
thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
_FREE_ZONE(fip, sizeof *fip, M_VNODE);
vp->v_fifoinfo = NULL;
return (error);
}
fip->fi_readsock = rso;
if (error = socreate(AF_LOCAL, &wso, SOCK_STREAM, 0)) {
(void)soclose(rso);
thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
_FREE_ZONE(fip, sizeof *fip, M_VNODE);
vp->v_fifoinfo = NULL;
return (error);
}
fip->fi_writesock = wso;
if (error = unp_connect2(wso, rso)) {
(void)soclose(wso);
(void)soclose(rso);
thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
_FREE_ZONE(fip, sizeof *fip, M_VNODE);
vp->v_fifoinfo = NULL;
return (error);
}
wso->so_state |= SS_CANTRCVMORE;
wso->so_snd.sb_lowat = PIPE_BUF;
rso->so_state |= SS_CANTSENDMORE;
thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
fip->fi_readers = fip->fi_writers = 0;
}
if (ap->a_mode & FREAD) {
fip->fi_readers++;
if (fip->fi_readers == 1) {
fip->fi_writesock->so_state &= ~SS_CANTSENDMORE;
if (fip->fi_writers > 0)
wakeup((caddr_t)&fip->fi_writers);
}
}
if (ap->a_mode & FWRITE) {
fip->fi_writers++;
if (fip->fi_writers == 1) {
fip->fi_readsock->so_state &= ~SS_CANTRCVMORE;
if (fip->fi_readers > 0)
wakeup((caddr_t)&fip->fi_readers);
}
}
if ((ap->a_mode & FREAD) && (ap->a_mode & O_NONBLOCK) == 0) {
if (fip->fi_writers == 0) {
VOP_UNLOCK(vp, 0, p);
error = tsleep((caddr_t)&fip->fi_readers,
PCATCH | PSOCK, "fifoor", 0);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
if (error)
goto bad;
if (fip->fi_readers == 1) {
if (fip->fi_writers > 0)
wakeup((caddr_t)&fip->fi_writers);
}
}
}
if (ap->a_mode & FWRITE) {
if (ap->a_mode & O_NONBLOCK) {
if (fip->fi_readers == 0) {
error = ENXIO;
goto bad;
}
} else {
if (fip->fi_readers == 0) {
VOP_UNLOCK(vp, 0, p);
error = tsleep((caddr_t)&fip->fi_writers,
PCATCH | PSOCK, "fifoow", 0);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
if (error)
goto bad;
if (fip->fi_writers == 1) {
if (fip->fi_readers > 0)
wakeup((caddr_t)&fip->fi_readers);
}
}
}
}
return (0);
bad:
if (error)
VOP_CLOSE(vp, ap->a_mode, ap->a_cred, p);
return (error);
}
fifo_read(ap)
struct vop_read_args *ap;
{
struct uio *uio = ap->a_uio;
struct socket *rso = ap->a_vp->v_fifoinfo->fi_readsock;
struct proc *p = uio->uio_procp;
int error, startresid;
#if DIAGNOSTIC
if (uio->uio_rw != UIO_READ)
panic("fifo_read mode");
#endif
if (uio->uio_resid == 0)
return (0);
if (ap->a_ioflag & IO_NDELAY)
rso->so_state |= SS_NBIO;
startresid = uio->uio_resid;
VOP_UNLOCK(ap->a_vp, 0, p);
thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
error = soreceive(rso, (struct sockaddr **)0, uio, (struct mbuf **)0,
(struct mbuf **)0, (int *)0);
thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY, p);
if (uio->uio_resid == startresid)
rso->so_state &= ~SS_CANTRCVMORE;
if (ap->a_ioflag & IO_NDELAY)
rso->so_state &= ~SS_NBIO;
return (error);
}
fifo_write(ap)
struct vop_write_args *ap;
{
struct socket *wso = ap->a_vp->v_fifoinfo->fi_writesock;
struct proc *p = ap->a_uio->uio_procp;
int error;
#if DIAGNOSTIC
if (ap->a_uio->uio_rw != UIO_WRITE)
panic("fifo_write mode");
#endif
if (ap->a_ioflag & IO_NDELAY)
wso->so_state |= SS_NBIO;
VOP_UNLOCK(ap->a_vp, 0, p);
thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
error = sosend(wso, (struct sockaddr *)0, ap->a_uio, 0, (struct mbuf *)0, 0);
thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY, p);
if (ap->a_ioflag & IO_NDELAY)
wso->so_state &= ~SS_NBIO;
return (error);
}
fifo_ioctl(ap)
struct vop_ioctl_args *ap;
{
struct file filetmp;
int error;
if (ap->a_command == FIONBIO)
return (0);
if (ap->a_fflag & FREAD) {
filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock;
error = soo_ioctl(&filetmp, ap->a_command, ap->a_data, ap->a_p);
if (error)
return (error);
}
if (ap->a_fflag & FWRITE) {
filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock;
error = soo_ioctl(&filetmp, ap->a_command, ap->a_data, ap->a_p);
if (error)
return (error);
}
return (0);
}
fifo_select(ap)
struct vop_select_args *ap;
{
struct file filetmp;
int ready;
if (ap->a_which & FREAD) {
filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock;
ready = soo_select(&filetmp, ap->a_which, ap->a_wql, ap->a_p);
if (ready)
return (ready);
}
if (ap->a_which & FWRITE) {
filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock;
ready = soo_select(&filetmp, ap->a_which, ap->a_wql, ap->a_p);
if (ready)
return (ready);
}
return (0);
}
int
fifo_inactive(ap)
struct vop_inactive_args *ap;
{
VOP_UNLOCK(ap->a_vp, 0, ap->a_p);
return (0);
}
fifo_bmap(ap)
struct vop_bmap_args *ap;
{
if (ap->a_vpp != NULL)
*ap->a_vpp = ap->a_vp;
if (ap->a_bnp != NULL)
*ap->a_bnp = ap->a_bn;
if (ap->a_runp != NULL)
*ap->a_runp = 0;
return (0);
}
fifo_close(ap)
struct vop_close_args *ap;
{
register struct vnode *vp = ap->a_vp;
register struct fifoinfo *fip = vp->v_fifoinfo;
int error1, error2;
if (ap->a_fflag & FREAD) {
fip->fi_readers--;
if (fip->fi_readers == 0){
thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
socantsendmore(fip->fi_writesock);
thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
}
}
if (ap->a_fflag & FWRITE) {
fip->fi_writers--;
if (fip->fi_writers == 0) {
thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
socantrcvmore(fip->fi_readsock);
thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
}
}
if (vp->v_usecount > 1)
return (0);
thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
error1 = soclose(fip->fi_readsock);
error2 = soclose(fip->fi_writesock);
thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
FREE_ZONE(fip, sizeof *fip, M_VNODE);
vp->v_fifoinfo = NULL;
if (error1)
return (error1);
return (error2);
}
fifo_print(ap)
struct vop_print_args *ap;
{
printf("tag VT_NON");
fifo_printinfo(ap->a_vp);
printf("\n");
}
fifo_printinfo(vp)
struct vnode *vp;
{
register struct fifoinfo *fip = vp->v_fifoinfo;
printf(", fifo with %d readers and %d writers",
fip->fi_readers, fip->fi_writers);
}
fifo_pathconf(ap)
struct vop_pathconf_args *ap;
{
switch (ap->a_name) {
case _PC_LINK_MAX:
*ap->a_retval = LINK_MAX;
return (0);
case _PC_PIPE_BUF:
*ap->a_retval = PIPE_BUF;
return (0);
case _PC_CHOWN_RESTRICTED:
*ap->a_retval = 1;
return (0);
default:
return (EINVAL);
}
}
fifo_ebadf()
{
return (EBADF);
}
fifo_advlock(ap)
struct vop_advlock_args *ap;
{
return (EOPNOTSUPP);
}