#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysmacros.h>
#include <sys/cred.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/fcntl.h>
#include <sys/kmem.h>
#include <sys/uio.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/vfs_opreg.h>
#include <sys/pathname.h>
#include <sys/signal.h>
#include <sys/user.h>
#include <sys/strsubr.h>
#include <sys/stream.h>
#include <sys/strsun.h>
#include <sys/strredir.h>
#include <sys/fs/fifonode.h>
#include <sys/fs/namenode.h>
#include <sys/stropts.h>
#include <sys/proc.h>
#include <sys/unistd.h>
#include <sys/debug.h>
#include <fs/fs_subr.h>
#include <sys/filio.h>
#include <sys/termio.h>
#include <sys/ddi.h>
#include <sys/vtrace.h>
#include <sys/policy.h>
#include <sys/tsol/label.h>
static int fifo_read(vnode_t *, uio_t *, int, cred_t *, caller_context_t *);
static int fifo_write(vnode_t *, uio_t *, int, cred_t *, caller_context_t *);
static int fifo_getattr(vnode_t *, vattr_t *, int, cred_t *,
caller_context_t *);
static int fifo_setattr(vnode_t *, vattr_t *, int, cred_t *,
caller_context_t *);
static int fifo_realvp(vnode_t *, vnode_t **, caller_context_t *);
static int fifo_access(vnode_t *, int, int, cred_t *, caller_context_t *);
static int fifo_create(struct vnode *, char *, vattr_t *, enum vcexcl,
int, struct vnode **, struct cred *, int, caller_context_t *,
vsecattr_t *);
static int fifo_fid(vnode_t *, fid_t *, caller_context_t *);
static int fifo_fsync(vnode_t *, int, cred_t *, caller_context_t *);
static int fifo_seek(vnode_t *, offset_t, offset_t *, caller_context_t *);
static int fifo_ioctl(vnode_t *, int, intptr_t, int, cred_t *, int *,
caller_context_t *);
static int fifo_fastioctl(vnode_t *, int, intptr_t, int, cred_t *, int *);
static int fifo_strioctl(vnode_t *, int, intptr_t, int, cred_t *, int *);
static int fifo_poll(vnode_t *, short, int, short *, pollhead_t **,
caller_context_t *);
static int fifo_pathconf(vnode_t *, int, ulong_t *, cred_t *,
caller_context_t *);
static void fifo_inactive(vnode_t *, cred_t *, caller_context_t *);
static int fifo_rwlock(vnode_t *, int, caller_context_t *);
static void fifo_rwunlock(vnode_t *, int, caller_context_t *);
static int fifo_setsecattr(struct vnode *, vsecattr_t *, int, struct cred *,
caller_context_t *);
static int fifo_getsecattr(struct vnode *, vsecattr_t *, int, struct cred *,
caller_context_t *);
static boolean_t fifo_stayfast_enter(fifonode_t *);
static void fifo_stayfast_exit(fifonode_t *);
extern dev_t fifodev;
extern struct qinit fifo_stwdata;
extern struct qinit fifo_strdata;
extern kmutex_t ftable_lock;
struct streamtab fifoinfo = { &fifo_strdata, &fifo_stwdata, NULL, NULL };
struct vnodeops *fifo_vnodeops;
const fs_operation_def_t fifo_vnodeops_template[] = {
VOPNAME_OPEN, { .vop_open = fifo_open },
VOPNAME_CLOSE, { .vop_close = fifo_close },
VOPNAME_READ, { .vop_read = fifo_read },
VOPNAME_WRITE, { .vop_write = fifo_write },
VOPNAME_IOCTL, { .vop_ioctl = fifo_ioctl },
VOPNAME_GETATTR, { .vop_getattr = fifo_getattr },
VOPNAME_SETATTR, { .vop_setattr = fifo_setattr },
VOPNAME_ACCESS, { .vop_access = fifo_access },
VOPNAME_CREATE, { .vop_create = fifo_create },
VOPNAME_FSYNC, { .vop_fsync = fifo_fsync },
VOPNAME_INACTIVE, { .vop_inactive = fifo_inactive },
VOPNAME_FID, { .vop_fid = fifo_fid },
VOPNAME_RWLOCK, { .vop_rwlock = fifo_rwlock },
VOPNAME_RWUNLOCK, { .vop_rwunlock = fifo_rwunlock },
VOPNAME_SEEK, { .vop_seek = fifo_seek },
VOPNAME_REALVP, { .vop_realvp = fifo_realvp },
VOPNAME_POLL, { .vop_poll = fifo_poll },
VOPNAME_PATHCONF, { .vop_pathconf = fifo_pathconf },
VOPNAME_DISPOSE, { .error = fs_error },
VOPNAME_SETSECATTR, { .vop_setsecattr = fifo_setsecattr },
VOPNAME_GETSECATTR, { .vop_getsecattr = fifo_getsecattr },
NULL, NULL
};
struct streamtab *
fifo_getinfo()
{
return (&fifoinfo);
}
static boolean_t
tsol_fifo_access(vnode_t *vp, int flag, cred_t *crp)
{
fifonode_t *fnp = VTOF(vp);
if (is_system_labeled() &&
(flag & FWRITE) &&
(!(fnp->fn_flag & ISPIPE))) {
zone_t *proc_zone;
proc_zone = crgetzone(crp);
if (proc_zone != global_zone) {
char vpath[MAXPATHLEN];
zone_t *fifo_zone;
if (vnodetopath(rootdir, vp, vpath, sizeof (vpath),
kcred) == 0) {
fifo_zone = zone_find_by_path(vpath);
zone_rele(fifo_zone);
if (fifo_zone != global_zone &&
fifo_zone != proc_zone) {
return (B_FALSE);
}
} else {
return (B_FALSE);
}
}
}
return (B_TRUE);
}
int
fifo_open(vnode_t **vpp, int flag, cred_t *crp, caller_context_t *ct)
{
vnode_t *vp = *vpp;
fifonode_t *fnp = VTOF(vp);
fifolock_t *fn_lock = fnp->fn_lock;
int error;
ASSERT(vp->v_type == VFIFO);
ASSERT(vn_matchops(vp, fifo_vnodeops));
if (!tsol_fifo_access(vp, flag, crp))
return (EACCES);
mutex_enter(&fn_lock->flk_lock);
if (flag & FREAD) {
fnp->fn_rcnt++;
if (! (fnp->fn_flag & ISPIPE))
fnp->fn_rsynccnt++;
}
if (flag & FWRITE) {
fnp->fn_wcnt++;
if (! (fnp->fn_flag & ISPIPE))
fnp->fn_wsynccnt++;
}
error = fifo_stropen(vpp, flag, crp, 1, 1);
ASSERT(MUTEX_HELD(&VTOF(*vpp)->fn_lock->flk_lock));
if (fnp->fn_flag & ISPIPE) {
ASSERT(VTOF(*vpp)->fn_flag & ISPIPE);
ASSERT(VTOF(*vpp)->fn_rsynccnt == 0);
ASSERT(VTOF(*vpp)->fn_rsynccnt == 0);
ASSERT(fnp->fn_rsynccnt == 0);
ASSERT(fnp->fn_wsynccnt == 0);
mutex_exit(&VTOF(*vpp)->fn_lock->flk_lock);
return (error);
}
ASSERT(vp == *vpp);
if (flag & FREAD) {
fnp->fn_rsynccnt--;
if (fnp->fn_flag & FIFOSYNC) {
fnp->fn_flag |= FIFOROCR;
cv_broadcast(&fnp->fn_wait_cv);
}
}
if (flag & FWRITE) {
fnp->fn_wsynccnt--;
if (fnp->fn_flag & FIFOSYNC) {
fnp->fn_flag |= FIFOWOCR;
cv_broadcast(&fnp->fn_wait_cv);
}
}
fnp->fn_flag &= ~FIFOSYNC;
if (error != 0) {
mutex_exit(&fnp->fn_lock->flk_lock);
goto done;
}
ASSERT(fnp->fn_rsynccnt <= fnp->fn_rcnt);
ASSERT(fnp->fn_wsynccnt <= fnp->fn_wcnt);
if (flag & FREAD) {
while ((fnp->fn_flag & FIFOWOCR) == 0 &&
fnp->fn_wcnt == fnp->fn_wsynccnt) {
if (flag & (FNDELAY|FNONBLOCK)) {
mutex_exit(&fnp->fn_lock->flk_lock);
goto done;
}
fnp->fn_insync++;
fnp->fn_flag |= FIFOSYNC;
if (!cv_wait_sig_swap(&fnp->fn_wait_cv,
&fnp->fn_lock->flk_lock)) {
if (--fnp->fn_insync == 0 &&
fnp->fn_flag & FIFOWOCR) {
fnp->fn_flag &= ~(FIFOWOCR|FIFOROCR);
}
mutex_exit(&fnp->fn_lock->flk_lock);
(void) fifo_close(*vpp, flag, 1, 0, crp, ct);
error = EINTR;
goto done;
}
if (--fnp->fn_insync == 0 &&
fnp->fn_flag & FIFOWOCR) {
fnp->fn_flag &= ~(FIFOWOCR|FIFOROCR);
break;
}
}
} else if (flag & FWRITE) {
while ((fnp->fn_flag & FIFOROCR) == 0 &&
fnp->fn_rcnt == fnp->fn_rsynccnt) {
if ((flag & (FNDELAY|FNONBLOCK)) && fnp->fn_rcnt == 0) {
mutex_exit(&fnp->fn_lock->flk_lock);
(void) fifo_close(*vpp, flag, 1, 0, crp, ct);
error = ENXIO;
goto done;
}
fnp->fn_flag |= FIFOSYNC;
fnp->fn_insync++;
if (!cv_wait_sig_swap(&fnp->fn_wait_cv,
&fnp->fn_lock->flk_lock)) {
if (--fnp->fn_insync == 0 &&
(fnp->fn_flag & FIFOROCR) != 0) {
fnp->fn_flag &= ~(FIFOWOCR|FIFOROCR);
}
mutex_exit(&fnp->fn_lock->flk_lock);
(void) fifo_close(*vpp, flag, 1, 0, crp, ct);
error = EINTR;
goto done;
}
if (--fnp->fn_insync == 0 &&
(fnp->fn_flag & FIFOROCR) != 0) {
fnp->fn_flag &= ~(FIFOWOCR|FIFOROCR);
break;
}
}
}
mutex_exit(&fn_lock->flk_lock);
done:
return (error);
}
int
fifo_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *crp,
caller_context_t *ct)
{
fifonode_t *fnp = VTOF(vp);
fifonode_t *fn_dest = fnp->fn_dest;
int error = 0;
fifolock_t *fn_lock = fnp->fn_lock;
queue_t *sd_wrq;
vnode_t *fn_dest_vp;
int senthang = 0;
ASSERT(vp->v_stream != NULL);
(void) cleanlocks(vp, ttoproc(curthread)->p_pid, 0);
cleanshares(vp, ttoproc(curthread)->p_pid);
strclean(vp);
if (count > 1)
return (0);
sd_wrq = strvp2wq(vp);
mutex_enter(&fn_lock->flk_lock);
while (fn_lock->flk_ocsync)
cv_wait(&fn_lock->flk_wait_cv, &fn_lock->flk_lock);
fn_lock->flk_ocsync = 1;
if (flag & FREAD) {
fnp->fn_rcnt--;
}
if (flag & FWRITE) {
if (--fnp->fn_wcnt == 0 && fn_dest->fn_rcnt > 0) {
if ((fn_dest->fn_flag & (FIFOFAST | FIFOWANTR)) ==
(FIFOFAST | FIFOWANTR)) {
fn_dest->fn_flag &= ~(FIFOWANTR | FIFOWANTW);
cv_broadcast(&fn_dest->fn_wait_cv);
}
if (!(fnp->fn_flag & ISPIPE))
fnp->fn_flag |= FIFOCLOSE;
(void) putnextctl_wait(sd_wrq, M_HANGUP);
senthang = 1;
}
}
if (fnp->fn_rcnt == 0 && fn_dest->fn_wcnt > 0) {
if ((fn_dest->fn_flag & (FIFOFAST | FIFOWANTW)) ==
(FIFOFAST | FIFOWANTW)) {
fn_dest->fn_flag &= ~FIFOWANTW;
cv_broadcast(&fn_dest->fn_wait_cv);
}
}
if (--fnp->fn_open > 0) {
ASSERT((fnp->fn_rcnt + fnp->fn_wcnt) != 0);
fn_lock->flk_ocsync = 0;
cv_broadcast(&fn_lock->flk_wait_cv);
mutex_exit(&fn_lock->flk_lock);
return (0);
}
if (fn_dest->fn_open && senthang == 0)
(void) putnextctl_wait(sd_wrq, M_HANGUP);
fnp->fn_flag &= ~FIFOISOPEN;
if ((fnp->fn_flag & ISPIPE) && !(fnp->fn_flag & FIFOCLOSE)) {
fnp->fn_flag |= FIFOCLOSE;
fn_dest->fn_flag |= FIFOCLOSE;
if (fnp->fn_flag & FIFOFAST)
fifo_fastflush(fnp);
if (vp->v_stream != NULL) {
mutex_exit(&fn_lock->flk_lock);
(void) strclose(vp, flag, crp);
mutex_enter(&fn_lock->flk_lock);
}
cv_broadcast(&fn_dest->fn_wait_cv);
fn_lock->flk_ocsync = 0;
cv_broadcast(&fn_lock->flk_wait_cv);
fn_dest_vp = FTOV(fn_dest);
if (fn_dest_vp->v_stream &&
(fn_dest_vp->v_stream->sd_flag & STRMOUNT)) {
VN_HOLD(fn_dest_vp);
mutex_exit(&fn_lock->flk_lock);
error = nm_unmountall(fn_dest_vp, crp);
ASSERT(error == 0);
VN_RELE(fn_dest_vp);
} else {
ASSERT(vp->v_count >= 1);
mutex_exit(&fn_lock->flk_lock);
}
} else {
if (fnp->fn_flag & FIFOFAST)
fifo_fastflush(fnp);
#if DEBUG
fn_dest_vp = FTOV(fn_dest);
if (fn_dest_vp->v_stream)
ASSERT((fn_dest_vp->v_stream->sd_flag & STRMOUNT) == 0);
#endif
if (vp->v_stream != NULL) {
mutex_exit(&fn_lock->flk_lock);
(void) strclose(vp, flag, crp);
mutex_enter(&fn_lock->flk_lock);
}
fn_lock->flk_ocsync = 0;
cv_broadcast(&fn_lock->flk_wait_cv);
cv_broadcast(&fn_dest->fn_wait_cv);
mutex_exit(&fn_lock->flk_lock);
}
return (error);
}
static int
fifo_read(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *crp,
caller_context_t *ct)
{
fifonode_t *fnp = VTOF(vp);
fifonode_t *fn_dest;
fifolock_t *fn_lock = fnp->fn_lock;
int error = 0;
mblk_t *bp;
ASSERT(vp->v_stream != NULL);
if (uiop->uio_resid == 0)
return (0);
mutex_enter(&fn_lock->flk_lock);
TRACE_2(TR_FAC_FIFO, TR_FIFOREAD_IN, "fifo_read in:%p fnp %p", vp, fnp);
if (! (fnp->fn_flag & FIFOFAST))
goto stream_mode;
fn_dest = fnp->fn_dest;
while (fnp->fn_count == 0) {
if (fn_dest->fn_wcnt == 0 || fn_dest->fn_rcnt == 0) {
mutex_exit(&fn_lock->flk_lock);
return (0);
}
if (uiop->uio_fmode & (FNDELAY|FNONBLOCK)) {
mutex_exit(&fn_lock->flk_lock);
if (uiop->uio_fmode & FNONBLOCK)
return (EAGAIN);
return (0);
}
ASSERT((fnp->fn_flag & (ISPIPE|FIFOCLOSE)) !=
(ISPIPE|FIFOCLOSE));
fnp->fn_flag |= FIFOWANTR;
TRACE_1(TR_FAC_FIFO, TR_FIFOREAD_WAIT, "fiforead wait: %p", vp);
if (!cv_wait_sig_swap(&fnp->fn_wait_cv,
&fn_lock->flk_lock)) {
error = EINTR;
goto done;
}
TRACE_1(TR_FAC_FIFO, TR_FIFOREAD_WAKE,
"fiforead awake: %p", vp);
if (!(fnp->fn_flag & FIFOFAST))
goto stream_mode;
}
ASSERT(fnp->fn_mp != NULL);
uiop->uio_extflg |= UIO_COPY_CACHED;
do {
int bpsize = MBLKL(fnp->fn_mp);
int uiosize = MIN(bpsize, uiop->uio_resid);
error = uiomove(fnp->fn_mp->b_rptr, uiosize, UIO_READ, uiop);
if (error != 0)
break;
fnp->fn_count -= uiosize;
if (bpsize <= uiosize) {
bp = fnp->fn_mp;
fnp->fn_mp = fnp->fn_mp->b_cont;
freeb(bp);
if (uiop->uio_resid == 0)
break;
while (fnp->fn_mp == NULL && fn_dest->fn_wwaitcnt > 0) {
ASSERT(fnp->fn_count == 0);
if (uiop->uio_fmode & (FNDELAY|FNONBLOCK))
goto trywake;
fnp->fn_flag |= FIFOWANTR;
fifo_wakewriter(fn_dest, fn_lock);
if (!cv_wait_sig(&fnp->fn_wait_cv,
&fn_lock->flk_lock))
goto trywake;
if (!(fnp->fn_flag & FIFOFAST))
goto stream_mode;
}
} else {
fnp->fn_mp->b_rptr += uiosize;
ASSERT(uiop->uio_resid == 0);
}
} while (uiop->uio_resid != 0 && fnp->fn_mp != NULL);
trywake:
ASSERT(msgdsize(fnp->fn_mp) == fnp->fn_count);
if (fn_dest->fn_flag & (FIFOWANTW | FIFOHIWATW) &&
fnp->fn_count < Fifohiwat) {
fifo_wakewriter(fn_dest, fn_lock);
}
goto done;
stream_mode:
mutex_exit(&fn_lock->flk_lock);
TRACE_1(TR_FAC_FIFO,
TR_FIFOREAD_STREAM, "fifo_read stream_mode:%p", vp);
error = strread(vp, uiop, crp);
mutex_enter(&fn_lock->flk_lock);
done:
if (error == 0) {
gethrestime(&fnp->fn_atime);
if (fnp->fn_flag & ISPIPE)
fnp->fn_dest->fn_atime = fnp->fn_atime;
}
TRACE_2(TR_FAC_FIFO, TR_FIFOREAD_OUT,
"fifo_read out:%p error %d", vp, error);
mutex_exit(&fn_lock->flk_lock);
return (error);
}
static int
fifo_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *crp,
caller_context_t *ct)
{
struct fifonode *fnp, *fn_dest;
fifolock_t *fn_lock;
struct stdata *stp;
int error = 0;
int write_size;
int size;
int fmode;
mblk_t *bp;
boolean_t hotread;
ASSERT(vp->v_stream);
uiop->uio_loffset = 0;
stp = vp->v_stream;
write_size = uiop->uio_resid;
if ((write_size == 0) && !(stp->sd_wput_opt & SW_SNDZERO))
return (0);
fnp = VTOF(vp);
fn_lock = fnp->fn_lock;
fn_dest = fnp->fn_dest;
mutex_enter(&fn_lock->flk_lock);
TRACE_3(TR_FAC_FIFO, TR_FIFOWRITE_IN,
"fifo_write in:%p fnp %p size %d", vp, fnp, write_size);
if (fn_dest->fn_rcnt == 0 || fn_dest->fn_wcnt == 0) {
goto epipe;
}
if (!(fnp->fn_flag & FIFOFAST))
goto stream_mode;
fmode = uiop->uio_fmode & (FNDELAY|FNONBLOCK);
uiop->uio_extflg |= UIO_COPY_CACHED;
do {
while (fn_dest->fn_count >= Fifohiwat) {
if (fmode) {
fnp->fn_flag |= FIFOHIWATW;
if (uiop->uio_resid == write_size) {
mutex_exit(&fn_lock->flk_lock);
if (fmode & FNDELAY)
return (0);
else
return (EAGAIN);
}
goto done;
}
fnp->fn_flag |= FIFOWANTW;
fnp->fn_wwaitcnt++;
TRACE_1(TR_FAC_FIFO, TR_FIFOWRITE_WAIT,
"fifo_write wait: %p", vp);
if (!cv_wait_sig_swap(&fnp->fn_wait_cv,
&fn_lock->flk_lock)) {
error = EINTR;
fnp->fn_wwaitcnt--;
fifo_wakereader(fn_dest, fn_lock);
goto done;
}
fnp->fn_wwaitcnt--;
TRACE_1(TR_FAC_FIFO, TR_FIFOWRITE_WAKE,
"fifo_write wake: %p", vp);
if (!(fnp->fn_flag & FIFOFAST))
goto stream_mode;
if (fn_dest->fn_rcnt == 0 || fn_dest->fn_wcnt == 0) {
goto epipe;
}
}
if (uiop->uio_resid + fn_dest->fn_count > Fifohiwat)
size = MIN(uiop->uio_resid, PIPE_BUF);
else
size = uiop->uio_resid;
hotread = fn_dest->fn_count > 0;
if (hotread) {
if (!fifo_stayfast_enter(fnp))
goto stream_mode;
mutex_exit(&fn_lock->flk_lock);
}
ASSERT(size != 0);
if ((bp = allocb(size + 8, BPRI_MED)) == NULL) {
if (!hotread)
mutex_exit(&fn_lock->flk_lock);
error = strwaitbuf(size, BPRI_MED);
mutex_enter(&fn_lock->flk_lock);
if (hotread) {
fifo_stayfast_exit(fnp);
}
if (error != 0) {
goto done;
}
if (!(fnp->fn_flag & FIFOFAST))
goto stream_mode;
if (fn_dest->fn_rcnt == 0 || fn_dest->fn_wcnt == 0) {
goto epipe;
}
continue;
}
bp->b_rptr += ((uintptr_t)uiop->uio_iov->iov_base & 0x7);
bp->b_wptr = bp->b_rptr + size;
error = uiomove((caddr_t)bp->b_rptr, size, UIO_WRITE, uiop);
if (hotread) {
mutex_enter(&fn_lock->flk_lock);
fifo_stayfast_exit(fnp);
if (fn_dest->fn_rcnt == 0 || fn_dest->fn_wcnt == 0) {
freeb(bp);
goto epipe;
}
}
if (error != 0) {
freeb(bp);
goto done;
}
fn_dest->fn_count += size;
if (fn_dest->fn_mp != NULL) {
fn_dest->fn_tail->b_cont = bp;
fn_dest->fn_tail = bp;
} else {
fn_dest->fn_mp = fn_dest->fn_tail = bp;
fifo_wakereader(fn_dest, fn_lock);
}
} while (uiop->uio_resid != 0);
goto done;
stream_mode:
ASSERT(MUTEX_HELD(&fn_lock->flk_lock));
mutex_exit(&fn_lock->flk_lock);
TRACE_1(TR_FAC_FIFO,
TR_FIFOWRITE_STREAM, "fifo_write stream_mode:%p", vp);
error = strwrite(vp, uiop, crp);
mutex_enter(&fn_lock->flk_lock);
done:
if (error == 0 && write_size != uiop->uio_resid) {
timestruc_t now;
gethrestime(&now);
if (fnp->fn_flag & ISPIPE) {
fn_dest->fn_mtime = fn_dest->fn_ctime = now;
}
fnp->fn_mtime = fnp->fn_ctime = now;
} else if (fn_dest->fn_rcnt == 0 || fn_dest->fn_wcnt == 0) {
goto epipe;
}
TRACE_3(TR_FAC_FIFO, TR_FIFOWRITE_OUT,
"fifo_write out: vp %p error %d fnp %p", vp, error, fnp);
mutex_exit(&fn_lock->flk_lock);
return (error);
epipe:
error = EPIPE;
TRACE_3(TR_FAC_FIFO, TR_FIFOWRITE_OUT,
"fifo_write out: vp %p error %d fnp %p", vp, error, fnp);
mutex_exit(&fn_lock->flk_lock);
tsignal(curthread, SIGPIPE);
return (error);
}
static int
fifo_ioctl(vnode_t *vp, int cmd, intptr_t arg, int mode, cred_t *cr,
int *rvalp, caller_context_t *ct)
{
return ((VTOF(vp)->fn_flag & FIFOFAST) ?
fifo_fastioctl(vp, cmd, arg, mode, cr, rvalp) :
fifo_strioctl(vp, cmd, arg, mode, cr, rvalp));
}
static inline int
fifo_ioctl_getpeercred(fifonode_t *fnp, intptr_t arg, int mode)
{
k_peercred_t *kp = (k_peercred_t *)arg;
if (mode == FKIOCTL && fnp->fn_pcredp != NULL) {
crhold(fnp->fn_pcredp);
kp->pc_cr = fnp->fn_pcredp;
kp->pc_cpid = fnp->fn_cpid;
return (0);
} else {
return (ENOTSUP);
}
}
static int
fifo_fastioctl(vnode_t *vp, int cmd, intptr_t arg, int mode, cred_t *cr,
int *rvalp)
{
fifonode_t *fnp = VTOF(vp);
fifonode_t *fn_dest;
int error = 0;
fifolock_t *fn_lock = fnp->fn_lock;
int cnt;
if (((cmd & IOCTYPE) == LDIOC) ||
((cmd & IOCTYPE) == tIOC) ||
((cmd & IOCTYPE) == TIOC)) {
return (EINVAL);
}
mutex_enter(&fn_lock->flk_lock);
if (!(fnp->fn_flag & FIFOFAST)) {
goto stream_mode;
}
switch (cmd) {
default:
case I_STR:
case I_SRDOPT:
case I_PUSH:
case I_FDINSERT:
case I_SENDFD:
case I_RECVFD:
case I_E_RECVFD:
case I_ATMARK:
case I_CKBAND:
case I_GETBAND:
case I_SWROPT:
goto turn_fastoff;
case I_FIND:
case I_GETSIG:
case FIONBIO:
case FIOASYNC:
case I_GRDOPT:
case I_GWROPT:
case I_LIST:
case I_SETCLTIME:
case I_GETCLTIME:
mutex_exit(&fn_lock->flk_lock);
return (strioctl(vp, cmd, arg, mode, U_TO_K, cr, rvalp));
case I_CANPUT:
if (arg != 0) {
goto turn_fastoff;
}
*rvalp = (fnp->fn_dest->fn_count < Fifohiwat) ? 1 : 0;
mutex_exit(&fn_lock->flk_lock);
return (0);
case I_NREAD:
error = copyout((caddr_t)&fnp->fn_count, (caddr_t)arg,
sizeof (cnt));
if (error == 0) {
*rvalp = (fnp->fn_count == 0) ? 0 : 1;
}
break;
case FIORDCHK:
*rvalp = fnp->fn_count;
break;
case I_PEEK:
{
STRUCT_DECL(strpeek, strpeek);
struct uio uio;
struct iovec iov;
int count;
mblk_t *bp;
int len;
STRUCT_INIT(strpeek, mode);
if (fnp->fn_count == 0) {
*rvalp = 0;
break;
}
error = copyin((caddr_t)arg, STRUCT_BUF(strpeek),
STRUCT_SIZE(strpeek));
if (error)
break;
if (STRUCT_FGET(strpeek, flags) & RS_HIPRI) {
*rvalp = 0;
break;
}
len = STRUCT_FGET(strpeek, databuf.maxlen);
if (len <= 0) {
STRUCT_FSET(strpeek, databuf.len, len);
} else {
iov.iov_base = STRUCT_FGETP(strpeek, databuf.buf);
iov.iov_len = len;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_loffset = 0;
uio.uio_segflg = UIO_USERSPACE;
uio.uio_fmode = 0;
uio.uio_extflg = UIO_COPY_CACHED;
uio.uio_resid = iov.iov_len;
count = fnp->fn_count;
bp = fnp->fn_mp;
while (count > 0 && uio.uio_resid) {
cnt = MIN(uio.uio_resid, MBLKL(bp));
if ((error = uiomove((char *)bp->b_rptr, cnt,
UIO_READ, &uio)) != 0) {
break;
}
count -= cnt;
bp = bp->b_cont;
}
STRUCT_FSET(strpeek, databuf.len, len - uio.uio_resid);
}
STRUCT_FSET(strpeek, flags, 0);
STRUCT_FSET(strpeek, ctlbuf.len, -1);
error = copyout(STRUCT_BUF(strpeek), (caddr_t)arg,
STRUCT_SIZE(strpeek));
if (error == 0 && len >= 0)
*rvalp = 1;
break;
}
case FIONREAD:
error = copyout((caddr_t)&fnp->fn_count, (caddr_t)arg,
sizeof (fnp->fn_count));
if (error == 0)
*rvalp = 0;
break;
case I_SETSIG:
error = strioctl(vp, cmd, arg, mode, U_TO_K, cr, rvalp);
if (vp->v_stream->sd_sigflags & (S_INPUT|S_RDNORM|S_WRNORM))
fnp->fn_flag |= FIFOSETSIG;
else
fnp->fn_flag &= ~FIFOSETSIG;
break;
case I_FLUSH:
if (arg & ~FLUSHRW) {
error = EINVAL;
break;
}
if (arg & FLUSHR) {
fifo_fastflush(fnp);
}
fn_dest = fnp->fn_dest;
if ((arg & FLUSHW)) {
fifo_fastflush(fn_dest);
}
if (fn_dest->fn_flag & (FIFOWANTW | FIFOWANTR)) {
fn_dest->fn_flag &= ~(FIFOWANTW | FIFOWANTR);
cv_broadcast(&fn_dest->fn_wait_cv);
}
*rvalp = 0;
break;
case I_FLUSHBAND:
error = 0;
*rvalp = 0;
break;
case _I_GETPEERCRED:
error = fifo_ioctl_getpeercred(fnp, arg, mode);
break;
case I_POP:
case I_LOOK:
case I_LINK:
case I_PLINK:
case I_UNLINK:
case I_PUNLINK:
case SRIOCSREDIR:
case SRIOCISREDIR:
error = EINVAL;
break;
}
mutex_exit(&fn_lock->flk_lock);
return (error);
turn_fastoff:
fifo_fastoff(fnp);
stream_mode:
mutex_exit(&fn_lock->flk_lock);
return (fifo_strioctl(vp, cmd, arg, mode, cr, rvalp));
}
static int
fifo_strioctl(vnode_t *vp, int cmd, intptr_t arg, int mode, cred_t *cr,
int *rvalp)
{
fifonode_t *fnp = VTOF(vp);
int error;
fifolock_t *fn_lock;
if (cmd == _I_GETPEERCRED)
return (fifo_ioctl_getpeercred(fnp, arg, mode));
error = strioctl(vp, cmd, arg, mode, U_TO_K, cr, rvalp);
switch (cmd) {
case I_RECVFD:
case I_E_RECVFD:
if (error == 0) {
fn_lock = fnp->fn_lock;
mutex_enter(&fn_lock->flk_lock);
if (fnp->fn_flag & FIFOSEND) {
fnp->fn_flag &= ~FIFOSEND;
cv_broadcast(&fnp->fn_dest->fn_wait_cv);
}
mutex_exit(&fn_lock->flk_lock);
}
break;
default:
break;
}
return (error);
}
int
fifo_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *crp,
caller_context_t *ct)
{
int error = 0;
fifonode_t *fnp = VTOF(vp);
queue_t *qp;
qband_t *bandp;
fifolock_t *fn_lock = fnp->fn_lock;
if (fnp->fn_realvp) {
if (error = VOP_GETATTR(fnp->fn_realvp, vap, flags, crp, ct))
return (error);
mutex_enter(&fn_lock->flk_lock);
vap->va_atime = fnp->fn_atime;
vap->va_mtime = fnp->fn_mtime;
vap->va_ctime = fnp->fn_ctime;
} else {
vap->va_mode = 0;
mutex_enter(&fn_lock->flk_lock);
vap->va_atime = fnp->fn_atime;
vap->va_mtime = fnp->fn_mtime;
vap->va_ctime = fnp->fn_ctime;
vap->va_uid = crgetuid(crp);
vap->va_gid = crgetgid(crp);
vap->va_nlink = 0;
vap->va_fsid = fifodev;
vap->va_nodeid = (ino64_t)fnp->fn_ino;
vap->va_rdev = 0;
}
vap->va_type = VFIFO;
vap->va_blksize = PIPE_BUF;
if (vp->v_stream && (fnp->fn_flag & FIFOISOPEN)) {
if ((fnp->fn_flag & FIFOFAST)) {
vap->va_size = (u_offset_t)fnp->fn_count;
} else {
qp = RD((strvp2wq(vp)));
vap->va_size = (u_offset_t)qp->q_count;
if (qp->q_nband != 0) {
mutex_enter(QLOCK(qp));
for (bandp = qp->q_bandp; bandp;
bandp = bandp->qb_next)
vap->va_size += bandp->qb_count;
mutex_exit(QLOCK(qp));
}
}
vap->va_nblocks = (fsblkcnt64_t)btod(vap->va_size);
} else {
vap->va_size = (u_offset_t)0;
vap->va_nblocks = (fsblkcnt64_t)0;
}
mutex_exit(&fn_lock->flk_lock);
vap->va_seq = 0;
return (0);
}
int
fifo_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *crp,
caller_context_t *ctp)
{
fifonode_t *fnp = VTOF(vp);
int error = 0;
fifolock_t *fn_lock;
if (fnp->fn_realvp)
error = VOP_SETATTR(fnp->fn_realvp, vap, flags, crp, ctp);
if (error == 0) {
fn_lock = fnp->fn_lock;
mutex_enter(&fn_lock->flk_lock);
if (vap->va_mask & AT_ATIME)
fnp->fn_atime = vap->va_atime;
if (vap->va_mask & AT_MTIME)
fnp->fn_mtime = vap->va_mtime;
gethrestime(&fnp->fn_ctime);
mutex_exit(&fn_lock->flk_lock);
}
return (error);
}
int
fifo_access(vnode_t *vp, int mode, int flags, cred_t *crp, caller_context_t *ct)
{
if (VTOF(vp)->fn_realvp)
return (VOP_ACCESS(VTOF(vp)->fn_realvp, mode, flags, crp, ct));
else
return (0);
}
static int
fifo_create(struct vnode *dvp, char *name, vattr_t *vap, enum vcexcl excl,
int mode, struct vnode **vpp, struct cred *cr, int flag,
caller_context_t *ct, vsecattr_t *vsecp)
{
int error;
ASSERT(dvp && (dvp->v_flag & VROOT) && *name == '\0');
if (excl == NONEXCL) {
if (mode && (error = fifo_access(dvp, mode, 0, cr, ct)))
return (error);
VN_HOLD(dvp);
return (0);
}
return (EEXIST);
}
static boolean_t
fifo_hres_gt(const timestruc_t *targ, const timestruc_t *base)
{
if (targ->tv_sec > base->tv_sec) {
return (B_TRUE);
}
return (targ->tv_sec == base->tv_sec &&
targ->tv_nsec > base->tv_nsec);
}
int
fifo_fsync(vnode_t *vp, int syncflag, cred_t *crp, caller_context_t *ct)
{
fifonode_t *fnp = VTOF(vp);
vattr_t va;
if (fnp->fn_realvp == NULL)
return (0);
bzero((caddr_t)&va, sizeof (va));
va.va_mask = AT_MTIME | AT_ATIME;
if (VOP_GETATTR(fnp->fn_realvp, &va, 0, crp, ct) == 0) {
va.va_mask = 0;
if (fifo_hres_gt(&fnp->fn_mtime, &va.va_mtime)) {
va.va_mtime = fnp->fn_mtime;
va.va_mask = AT_MTIME;
}
if (fifo_hres_gt(&fnp->fn_atime, &va.va_atime)) {
va.va_atime = fnp->fn_atime;
va.va_mask |= AT_ATIME;
}
if (va.va_mask != 0)
(void) VOP_SETATTR(fnp->fn_realvp, &va, 0, crp, ct);
}
return (VOP_FSYNC(fnp->fn_realvp, syncflag, crp, ct));
}
void
fifo_inactive(vnode_t *vp, cred_t *crp, caller_context_t *ct)
{
fifonode_t *fnp;
fifolock_t *fn_lock;
mutex_enter(&ftable_lock);
mutex_enter(&vp->v_lock);
ASSERT(vp->v_count >= 1);
VN_RELE_LOCKED(vp);
if (vp->v_count != 0) {
mutex_exit(&vp->v_lock);
mutex_exit(&ftable_lock);
return;
}
mutex_exit(&vp->v_lock);
fnp = VTOF(vp);
if (fnp->fn_realvp) {
(void) fiforemove(fnp);
mutex_exit(&ftable_lock);
(void) fifo_fsync(vp, FSYNC, crp, ct);
VN_RELE(fnp->fn_realvp);
VFS_RELE(vp->v_vfsp);
vp->v_vfsp = NULL;
} else
mutex_exit(&ftable_lock);
fn_lock = fnp->fn_lock;
mutex_enter(&fn_lock->flk_lock);
ASSERT(vp->v_stream == NULL);
ASSERT(vp->v_count == 0);
if (--fn_lock->flk_ref == 0) {
mutex_exit(&fn_lock->flk_lock);
ASSERT(fnp->fn_open == 0);
ASSERT(fnp->fn_dest->fn_open == 0);
if (fnp->fn_mp) {
freemsg(fnp->fn_mp);
fnp->fn_mp = NULL;
fnp->fn_count = 0;
}
if (fnp->fn_pcredp != NULL) {
crfree(fnp->fn_pcredp);
fnp->fn_pcredp = NULL;
}
if (fnp->fn_flag & ISPIPE) {
fifonode_t *fn_dest = fnp->fn_dest;
vp = FTOV(fn_dest);
if (fn_dest->fn_mp) {
freemsg(fn_dest->fn_mp);
fn_dest->fn_mp = NULL;
fn_dest->fn_count = 0;
}
if (fn_dest->fn_pcredp != NULL) {
crfree(fn_dest->fn_pcredp);
fn_dest->fn_pcredp = NULL;
}
kmem_cache_free(pipe_cache, (fifodata_t *)fn_lock);
} else
kmem_cache_free(fnode_cache, (fifodata_t *)fn_lock);
} else {
mutex_exit(&fn_lock->flk_lock);
}
}
int
fifo_fid(vnode_t *vp, fid_t *fidfnp, caller_context_t *ct)
{
if (VTOF(vp)->fn_realvp)
return (VOP_FID(VTOF(vp)->fn_realvp, fidfnp, ct));
else
return (EINVAL);
}
int
fifo_rwlock(vnode_t *vp, int write_lock, caller_context_t *ctp)
{
return (-1);
}
void
fifo_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ctp)
{
}
int
fifo_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct)
{
return (ESPIPE);
}
int
fifo_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct)
{
vnode_t *rvp;
if ((rvp = VTOF(vp)->fn_realvp) != NULL) {
vp = rvp;
if (VOP_REALVP(vp, &rvp, ct) == 0)
vp = rvp;
}
*vpp = vp;
return (0);
}
int
fifo_poll(vnode_t *vp, short events, int anyyet, short *reventsp,
pollhead_t **phpp, caller_context_t *ct)
{
fifonode_t *fnp, *fn_dest;
fifolock_t *fn_lock;
int retevents;
struct stdata *stp;
ASSERT(vp->v_stream != NULL);
stp = vp->v_stream;
retevents = 0;
fnp = VTOF(vp);
fn_dest = fnp->fn_dest;
fn_lock = fnp->fn_lock;
if (polllock(&stp->sd_pollist, &fn_lock->flk_lock) != 0) {
*reventsp = POLLNVAL;
return (0);
}
if ((fnp->fn_flag & FIFOISOPEN) == 0) {
if (((events & (POLLIN | POLLRDNORM | POLLPRI | POLLRDBAND)) &&
fnp->fn_rcnt == 0) ||
((events & (POLLWRNORM | POLLWRBAND)) &&
fnp->fn_wcnt == 0)) {
mutex_exit(&fnp->fn_lock->flk_lock);
*reventsp = POLLERR;
return (0);
}
}
if (!(fnp->fn_flag & FIFOFAST)) {
mutex_exit(&fnp->fn_lock->flk_lock);
goto stream_mode;
}
if ((fnp->fn_flag & ISPIPE) && (fn_dest->fn_open == 0)) {
retevents = POLLHUP;
} else if ((fnp->fn_flag & (FIFOCLOSE | ISPIPE)) == FIFOCLOSE &&
(fn_dest->fn_wcnt == 0)) {
retevents = POLLHUP;
} else if (events & (POLLWRNORM | POLLWRBAND)) {
if (events & POLLWRNORM) {
if (fn_dest->fn_count < Fifohiwat)
retevents = POLLWRNORM;
else
fnp->fn_flag |= FIFOHIWATW;
}
if (events & POLLWRBAND)
retevents |= POLLWRBAND;
}
if (events & (POLLIN | POLLRDNORM)) {
if (fnp->fn_count)
retevents |= (events & (POLLIN | POLLRDNORM));
}
if ((*reventsp = (short)retevents) != 0 && !(events & POLLET)) {
mutex_exit(&fnp->fn_lock->flk_lock);
return (0);
}
if (!anyyet) {
if (events & POLLWRNORM)
fnp->fn_flag |= FIFOPOLLW;
if (events & (POLLIN | POLLRDNORM))
fnp->fn_flag |= FIFOPOLLR;
if (events & POLLRDBAND)
fnp->fn_flag |= FIFOPOLLRBAND;
*phpp = &stp->sd_pollist;
}
mutex_exit(&fnp->fn_lock->flk_lock);
return (0);
stream_mode:
return (strpoll(stp, events, anyyet, reventsp, phpp));
}
int
fifo_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
caller_context_t *ct)
{
ulong_t val;
int error = 0;
switch (cmd) {
case _PC_LINK_MAX:
val = MAXLINK;
break;
case _PC_MAX_CANON:
val = MAX_CANON;
break;
case _PC_MAX_INPUT:
val = MAX_INPUT;
break;
case _PC_NAME_MAX:
error = EINVAL;
break;
case _PC_PATH_MAX:
case _PC_SYMLINK_MAX:
val = MAXPATHLEN;
break;
case _PC_PIPE_BUF:
val = PIPE_BUF;
break;
case _PC_NO_TRUNC:
if (vp->v_vfsp->vfs_flag & VFS_NOTRUNC)
val = 1;
else
val = (ulong_t)-1;
break;
case _PC_VDISABLE:
val = _POSIX_VDISABLE;
break;
case _PC_CHOWN_RESTRICTED:
if (rstchown)
val = rstchown;
else
val = (ulong_t)-1;
break;
case _PC_FILESIZEBITS:
val = (ulong_t)-1;
break;
default:
if (VTOF(vp)->fn_realvp)
error = VOP_PATHCONF(VTOF(vp)->fn_realvp, cmd,
&val, cr, ct);
else
error = EINVAL;
break;
}
if (error == 0)
*valp = val;
return (error);
}
int
fifo_setsecattr(struct vnode *vp, vsecattr_t *vsap, int flag, struct cred *crp,
caller_context_t *ct)
{
int error;
if (VTOF(vp)->fn_realvp) {
(void) VOP_RWLOCK(VTOF(vp)->fn_realvp, V_WRITELOCK_TRUE, ct);
error = VOP_SETSECATTR(VTOF(vp)->fn_realvp, vsap, flag,
crp, ct);
VOP_RWUNLOCK(VTOF(vp)->fn_realvp, V_WRITELOCK_TRUE, ct);
return (error);
} else
return (fs_nosys());
}
int
fifo_getsecattr(struct vnode *vp, vsecattr_t *vsap, int flag, struct cred *crp,
caller_context_t *ct)
{
if (VTOF(vp)->fn_realvp)
return (VOP_GETSECATTR(VTOF(vp)->fn_realvp, vsap, flag,
crp, ct));
else
return (fs_fab_acl(vp, vsap, flag, crp, ct));
}
static boolean_t
fifo_stayfast_enter(fifonode_t *fnp)
{
ASSERT(MUTEX_HELD(&fnp->fn_lock->flk_lock));
while (fnp->fn_flag & FIFOSTAYFAST) {
fnp->fn_flag |= FIFOWAITMODE;
cv_wait(&fnp->fn_wait_cv, &fnp->fn_lock->flk_lock);
fnp->fn_flag &= ~FIFOWAITMODE;
}
if (!(fnp->fn_flag & FIFOFAST))
return (B_FALSE);
fnp->fn_flag |= FIFOSTAYFAST;
return (B_TRUE);
}
static void
fifo_stayfast_exit(fifonode_t *fnp)
{
fifonode_t *fn_dest = fnp->fn_dest;
ASSERT(MUTEX_HELD(&fnp->fn_lock->flk_lock));
fnp->fn_flag &= ~FIFOSTAYFAST;
if (fnp->fn_flag & FIFOWAITMODE)
cv_broadcast(&fnp->fn_wait_cv);
if ((fnp->fn_flag & ISPIPE) && (fn_dest->fn_flag & FIFOWAITMODE))
cv_broadcast(&fn_dest->fn_wait_cv);
}