#include <sys/param.h>
#include <sys/proc.h>
#include <sys/time.h>
#include <sys/namei.h>
#include <sys/vnode_internal.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/stat.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/file_internal.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <miscfs/fifofs/fifo.h>
#include <vfs/vfs_support.h>
#define VOPFUNC int (*)(void *)
extern int soo_ioctl(struct fileproc *fp, u_long cmd, caddr_t data, vfs_context_t ctx);
extern int soo_select(struct fileproc *fp, int which, void * wql, vfs_context_t ctx);
int (**fifo_vnodeop_p)(void *);
struct vnodeopv_entry_desc fifo_vnodeop_entries[] = {
{ &vnop_default_desc, (VOPFUNC)vn_default_error },
{ &vnop_lookup_desc, (VOPFUNC)fifo_lookup },
{ &vnop_create_desc, (VOPFUNC)err_create },
{ &vnop_mknod_desc, (VOPFUNC)err_mknod },
{ &vnop_open_desc, (VOPFUNC)fifo_open },
{ &vnop_close_desc, (VOPFUNC)fifo_close },
{ &vnop_access_desc, (VOPFUNC)fifo_access },
{ &vnop_getattr_desc, (VOPFUNC)fifo_getattr },
{ &vnop_setattr_desc, (VOPFUNC)fifo_setattr },
{ &vnop_read_desc, (VOPFUNC)fifo_read },
{ &vnop_write_desc, (VOPFUNC)fifo_write },
{ &vnop_ioctl_desc, (VOPFUNC)fifo_ioctl },
{ &vnop_select_desc, (VOPFUNC)fifo_select },
{ &vnop_revoke_desc, (VOPFUNC)fifo_revoke },
{ &vnop_mmap_desc, (VOPFUNC)err_mmap },
{ &vnop_fsync_desc, (VOPFUNC)fifo_fsync },
{ &vnop_remove_desc, (VOPFUNC)err_remove },
{ &vnop_link_desc, (VOPFUNC)err_link },
{ &vnop_rename_desc, (VOPFUNC)err_rename },
{ &vnop_mkdir_desc, (VOPFUNC)err_mkdir },
{ &vnop_rmdir_desc, (VOPFUNC)err_rmdir },
{ &vnop_symlink_desc, (VOPFUNC)err_symlink },
{ &vnop_readdir_desc, (VOPFUNC)err_readdir },
{ &vnop_readlink_desc, (VOPFUNC)err_readlink },
{ &vnop_inactive_desc, (VOPFUNC)fifo_inactive },
{ &vnop_reclaim_desc, (VOPFUNC)fifo_reclaim },
{ &vnop_strategy_desc, (VOPFUNC)err_strategy },
{ &vnop_pathconf_desc, (VOPFUNC)fifo_pathconf },
{ &vnop_advlock_desc, (VOPFUNC)fifo_advlock },
{ &vnop_bwrite_desc, (VOPFUNC)fifo_bwrite },
{ &vnop_pagein_desc, (VOPFUNC)err_pagein },
{ &vnop_pageout_desc, (VOPFUNC)err_pageout },
{ &vnop_copyfile_desc, (VOPFUNC)err_copyfile },
{ &vnop_blktooff_desc, (VOPFUNC)err_blktooff },
{ &vnop_offtoblk_desc, (VOPFUNC)err_offtoblk },
{ &vnop_blockmap_desc, (VOPFUNC)err_blockmap },
{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
};
struct vnodeopv_desc fifo_vnodeop_opv_desc =
{ &fifo_vnodeop_p, fifo_vnodeop_entries };
int
fifo_lookup(struct vnop_lookup_args *ap)
{
*ap->a_vpp = NULL;
return (ENOTDIR);
}
int
fifo_open(struct vnop_open_args *ap)
{
struct vnode *vp = ap->a_vp;
struct fifoinfo *fip;
struct socket *rso, *wso;
int error;
vnode_lock(vp);
retry:
fip = vp->v_fifoinfo;
if (fip == (struct fifoinfo *)0)
panic("fifo_open with no fifoinfo");
if ((fip->fi_flags & FIFO_CREATED) == 0) {
if (fip->fi_flags & FIFO_INCREATE) {
fip->fi_flags |= FIFO_CREATEWAIT;
error = msleep(&fip->fi_flags, &vp->v_lock, PRIBIO | PCATCH, "fifocreatewait", NULL);
if (error) {
vnode_unlock(vp);
return(error);
}
goto retry;
} else {
fip->fi_flags |= FIFO_INCREATE;
vnode_unlock(vp);
if ( (error = socreate(AF_LOCAL, &rso, SOCK_STREAM, 0)) ) {
goto bad1;
}
if ( (error = socreate(AF_LOCAL, &wso, SOCK_STREAM, 0)) ) {
(void)soclose(rso);
goto bad1;
}
if ( (error = soconnect2(wso, rso)) ) {
(void)soclose(wso);
(void)soclose(rso);
goto bad1;
}
fip->fi_readers = fip->fi_writers = 0;
socket_lock(wso, 1);
wso->so_state |= SS_CANTRCVMORE;
wso->so_snd.sb_lowat = PIPE_BUF;
socket_unlock(wso, 1);
socket_lock(rso, 1);
rso->so_state |= SS_CANTSENDMORE;
socket_unlock(rso, 1);
vnode_lock(vp);
fip->fi_readsock = rso;
fip->fi_writesock = wso;
fip->fi_flags |= FIFO_CREATED;
fip->fi_flags &= ~FIFO_INCREATE;
if ((fip->fi_flags & FIFO_CREATEWAIT)) {
fip->fi_flags &= ~FIFO_CREATEWAIT;
wakeup(&fip->fi_flags);
}
}
}
if (ap->a_mode & FREAD) {
fip->fi_readers++;
if (fip->fi_readers == 1) {
socket_lock(fip->fi_writesock, 1);
fip->fi_writesock->so_state &= ~SS_CANTSENDMORE;
socket_unlock(fip->fi_writesock, 1);
if (fip->fi_writers > 0)
wakeup((caddr_t)&fip->fi_writers);
}
}
if (ap->a_mode & FWRITE) {
fip->fi_writers++;
if (fip->fi_writers == 1) {
socket_lock(fip->fi_readsock, 1);
fip->fi_readsock->so_state &= ~SS_CANTRCVMORE;
socket_unlock(fip->fi_readsock, 1);
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) {
error = msleep((caddr_t)&fip->fi_readers, &vp->v_lock,
PCATCH | PSOCK, "fifoor", NULL);
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) {
error = msleep((caddr_t)&fip->fi_writers,&vp->v_lock,
PCATCH | PSOCK, "fifoow", NULL);
if (error)
goto bad;
if (fip->fi_writers == 1) {
if (fip->fi_readers > 0)
wakeup((caddr_t)&fip->fi_readers);
}
}
}
}
vnode_unlock(vp);
return (0);
bad:
fifo_close_internal(vp, ap->a_mode, ap->a_context, 1);
vnode_unlock(vp);
return (error);
bad1:
vnode_lock(vp);
fip->fi_flags &= ~FIFO_INCREATE;
if ((fip->fi_flags & FIFO_CREATEWAIT)) {
fip->fi_flags &= ~FIFO_CREATEWAIT;
wakeup(&fip->fi_flags);
}
vnode_unlock(vp);
return (error);
}
int
fifo_read(struct vnop_read_args *ap)
{
struct uio *uio = ap->a_uio;
struct socket *rso = ap->a_vp->v_fifoinfo->fi_readsock;
user_ssize_t startresid;
int error;
int rflags;
#if DIAGNOSTIC
if (uio->uio_rw != UIO_READ)
panic("fifo_read mode");
#endif
if (uio_resid(uio) == 0)
return (0);
rflags = (ap->a_ioflag & IO_NDELAY) ? MSG_NBIO : 0;
startresid = uio_resid(uio);
error = 0;
if (ap->a_vp->v_fifoinfo->fi_writers < 1) {
socket_lock(rso, 1);
error = (rso->so_rcv.sb_cc == 0) ? EWOULDBLOCK : 0;
socket_unlock(rso, 1);
}
if (error != EWOULDBLOCK) {
error = soreceive(rso, (struct sockaddr **)0, uio, (struct mbuf **)0,
(struct mbuf **)0, &rflags);
}
else {
error = 0;
}
if (uio_resid(uio) == startresid) {
socket_lock(rso, 1);
rso->so_state &= ~SS_CANTRCVMORE;
socket_unlock(rso, 1);
}
return (error);
}
int
fifo_write(struct vnop_write_args *ap)
{
struct socket *wso = ap->a_vp->v_fifoinfo->fi_writesock;
int error;
#if DIAGNOSTIC
if (ap->a_uio->uio_rw != UIO_WRITE)
panic("fifo_write mode");
#endif
error = sosend(wso, (struct sockaddr *)0, ap->a_uio, NULL,
(struct mbuf *)0, (ap->a_ioflag & IO_NDELAY) ? MSG_NBIO : 0);
return (error);
}
int
fifo_ioctl(struct vnop_ioctl_args *ap)
{
struct fileproc filetmp;
struct fileglob filefg;
int error;
if (ap->a_command == FIONBIO)
return (0);
bzero(&filetmp, sizeof(struct fileproc));
filetmp.f_fglob = &filefg;
if (ap->a_fflag & FREAD) {
filetmp.f_fglob->fg_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock;
error = soo_ioctl(&filetmp, ap->a_command, ap->a_data, ap->a_context);
if (error)
return (error);
}
if (ap->a_fflag & FWRITE) {
filetmp.f_fglob->fg_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock;
error = soo_ioctl(&filetmp, ap->a_command, ap->a_data, ap->a_context);
if (error)
return (error);
}
return (0);
}
int
fifo_select(struct vnop_select_args *ap)
{
struct fileproc filetmp;
struct fileglob filefg;
int ready;
bzero(&filetmp, sizeof(struct fileproc));
filetmp.f_fglob = &filefg;
if (ap->a_which & FREAD) {
filetmp.f_fglob->fg_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock;
ready = soo_select(&filetmp, ap->a_which, ap->a_wql, ap->a_context);
if (ready)
return (ready);
}
if (ap->a_which & FWRITE) {
filetmp.f_fglob->fg_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock;
ready = soo_select(&filetmp, ap->a_which, ap->a_wql, ap->a_context);
if (ready)
return (ready);
}
return (0);
}
int
fifo_inactive(__unused struct vnop_inactive_args *ap)
{
return (0);
}
int
fifo_close(struct vnop_close_args *ap)
{
return fifo_close_internal(ap->a_vp, ap->a_fflag, ap->a_context, 0);
}
int
fifo_close_internal(vnode_t vp, int fflag, __unused vfs_context_t context, int locked)
{
struct fifoinfo *fip = vp->v_fifoinfo;
int error1, error2;
struct socket *rso;
struct socket *wso;
if (!locked)
vnode_lock(vp);
if ((fip->fi_flags & FIFO_CREATED) == 0) {
if (!locked)
vnode_unlock(vp);
return(0);
}
if (fflag & FREAD) {
fip->fi_readers--;
if (fip->fi_readers == 0){
socket_lock(fip->fi_writesock, 1);
socantsendmore(fip->fi_writesock);
socket_unlock(fip->fi_writesock, 1);
}
}
if (fflag & FWRITE) {
fip->fi_writers--;
if (fip->fi_writers == 0) {
socket_lock(fip->fi_readsock, 1);
socantrcvmore(fip->fi_readsock);
socket_unlock(fip->fi_readsock, 1);
}
}
#if 0
if (vnode_isinuse_locked(vp, 0, 1)) {
if (!locked)
vnode_unlock(vp);
return (0);
}
#endif
if (fip->fi_writers || fip->fi_readers) {
if (!locked)
vnode_unlock(vp);
return (0);
}
wso = fip->fi_writesock;
rso = fip->fi_readsock;
fip->fi_readsock = NULL;
fip->fi_writesock = NULL;
fip->fi_flags &= ~FIFO_CREATED;
if (!locked)
vnode_unlock(vp);
error1 = soclose(rso);
error2 = soclose(wso);
if (error1)
return (error1);
return (error2);
}
void
fifo_printinfo(struct vnode *vp)
{
struct fifoinfo *fip = vp->v_fifoinfo;
printf(", fifo with %ld readers and %ld writers",
fip->fi_readers, fip->fi_writers);
}
int
fifo_pathconf(struct vnop_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 = 200112;
return (0);
default:
return (EINVAL);
}
}
int
fifo_ebadf(__unused void *dummy)
{
return (EBADF);
}
int
fifo_advlock(__unused struct vnop_advlock_args *ap)
{
return (ENOTSUP);
}
int
fifo_freespace(struct vnode *vp, long *count)
{
struct socket *rsock;
rsock = vp->v_fifoinfo->fi_readsock;
socket_lock(rsock, 1);
*count = sbspace(&rsock->so_rcv);
socket_unlock(rsock, 1);
return 0;
}
int
fifo_charcount(struct vnode *vp, int *count)
{
int mcount;
int err = sock_ioctl(vp->v_fifoinfo->fi_readsock, FIONREAD, (void*)&mcount);
if (err == 0) {
*count = mcount;
}
return err;
}