/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All rights reserved. */
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright 2015, Joyent, Inc.
*/
/*
* FIFOFS file system vnode operations. This file system
* type supports STREAMS-based pipes and FIFOs.
*/
#include <sys/sysmacros.h>
#include <sys/vfs_opreg.h>
#include <sys/pathname.h>
#include <sys/strredir.h>
/*
*/
caller_context_t *);
caller_context_t *);
vsecattr_t *);
caller_context_t *);
caller_context_t *);
caller_context_t *);
caller_context_t *);
caller_context_t *);
/* functions local to this file */
static void fifo_stayfast_exit(fifonode_t *);
/*
* Define the data structures external to this file.
*/
extern struct qinit fifo_stwdata;
extern struct qinit fifo_strdata;
extern kmutex_t ftable_lock;
};
/*
* Return the fifoinfo structure.
*/
struct streamtab *
{
return (&fifoinfo);
}
/*
* Trusted Extensions enforces a restrictive policy for
* writing via cross-zone named pipes. A privileged global
* zone process may expose a named pipe by loopback mounting
* it from a lower-level zone to a higher-level zone. The
* kernel-enforced mount policy for lofs mounts ensures
* that such mounts are read-only in the higher-level
* zone. But this is not sufficient to prevent writing
* down via fifos. This function prevents writing down
* by comparing the zone of the process which is requesting
* write access with the zone owning the named pipe rendezvous.
* For write access the zone of the named pipe must equal the
* zone of the writing process. Writing up is possible since
* the named pipe can be opened for read by a process in a
* higher level zone.
*
* An exception is made for the global zone to support trusted
* processes which enforce their own data flow policies.
*/
static boolean_t
{
if (is_system_labeled() &&
if (proc_zone != global_zone) {
/*
* Get the pathname and use it to find
* the zone of the fifo.
*/
kcred) == 0) {
if (fifo_zone != global_zone &&
return (B_FALSE);
}
} else {
return (B_FALSE);
}
}
}
return (B_TRUE);
}
/*
* Open and stream a FIFO.
* If this is the first open of the file (FIFO is not streaming),
* initialize the fifonode and attach a stream to the vnode.
*
* Each end of a fifo must be synchronized with the other end.
* If not, the mated end may complete an open, I/O, close sequence
* before the end waiting in open ever wakes up.
* Note: namefs pipes come through this routine too.
*/
int
{
int error;
return (EACCES);
/*
* If we are the first reader, wake up any writers that
* may be waiting around. wait for all of them to
* wake up before proceeding (i.e. fn_wsynccnt == 0)
*/
}
/*
* If we are the first writer, wake up any readers that
* may be waiting around. wait for all of them to
* wake up before proceeding (i.e. fn_rsynccnt == 0)
*/
}
/*
* fifo_stropen will take care of twisting the queues on the first
* open. The 1 being passed in means twist the queues on the first
* open.
*/
/*
* fifo_stropen() could have replaced vpp
* since fifo's are the only thing we need to sync up,
* everything else just returns;
* Note: don't need to hold lock since ISPIPE can't change
* and both old and new vp need to be pipes
*/
/*
* XXX note: should probably hold locks, but
* These values should not be changing
*/
return (error);
}
/*
* vp can't change for FIFOS
*/
/*
* If we are opening for read (or writer)
* indicate that the reader (or writer) is done with open
* if there is a writer (or reader) waiting for us, wake them up
* and indicate that at least 1 read (or write) open has occurred
* this is need in the event the read (or write) side closes
* before the writer (or reader) has a chance to wake up
* i.e. it sees that a reader (or writer) was once there
*/
/*
* This indicates that a read open has occurred
* Only need to set if writer is actually asleep
* Flag will be consumed by writer.
*/
}
}
/*
* This indicates that a write open has occurred
* Only need to set if reader is actually asleep
* Flag will be consumed by reader.
*/
}
}
/*
* errors don't wait around.. just return
* Note: XXX other end will wake up and continue despite error.
* There is no defined semantic on the correct course of option
* so we do what we've done in the past
*/
if (error != 0) {
goto done;
}
/*
* FIFOWOCR (or FIFOROCR) indicates that the writer (or reader)
* has woken us up and is done with open (this way, if the other
* end has made it to close, we don't block forever in open)
* fn_wnct == fn_wsynccnt (or fn_rcnt == fn_rsynccnt) indicates
* that no writer (or reader) has yet made it through open
* This has the side benefit of that the first
* reader (or writer) will wait until the other end finishes open
*/
goto done;
}
/*
* Last reader to wakeup clear writer
* Clear both writer and reader open
* occurred flag incase other end is O_RDWR
*/
}
goto done;
}
/*
* Last reader to wakeup clear writer open occurred flag
* Clear both writer and reader open occurred flag
* incase other end is O_RDWR
*/
break;
}
}
goto done;
}
/*
* Last writer to wakeup clear
* Clear both writer and reader open
* occurred flag in case other end is O_RDWR
*/
}
goto done;
}
/*
* Last writer to wakeup clear reader open occurred flag
* Clear both writer and reader open
* occurred flag in case other end is O_RDWR
*/
break;
}
}
}
done:
return (error);
}
/*
* Close down a stream.
* Call cleanlocks() and strclean() on every close.
* For last close send hangup message and force
* the other end of a named pipe to be unmounted.
* Mount guarantees that the mounted end will only call fifo_close()
* with a count of 1 when the unmount occurs.
* This routine will close down one end of a pipe or FIFO
* and free the stream head via strclose()
*/
/*ARGSUSED*/
int
{
int error = 0;
int senthang = 0;
/*
* clean locks and clear events.
*/
/*
*/
if (count > 1)
return (0);
/*
* wait for pending opens to finish up
* note: this also has the side effect of single threading closes
*/
while (fn_lock->flk_ocsync)
}
/*
* If we are last writer wake up sleeping readers
* (They'll figure out that there are no more writers
* and do the right thing)
* send hangup down stream so that stream head will do the
* right thing.
*/
/*
* While we're at it, clear FIFOWANTW too
* Wake up any sleeping readers or
* writers.
*/
}
/*
* This is needed incase the other side
* was opened non-blocking. It is the
* only way we can tell that wcnt is 0 because
* of close instead of never having a writer
*/
/*
* Note: sending hangup effectively shuts down
* both reader and writer at other end.
*/
senthang = 1;
}
}
/*
* For FIFOs we need to indicate to stream head that last reader
* has gone away so that an error is generated
* Pipes just need to wake up the other end so that it can
* notice this end has gone away.
*/
/*
* wake up any sleeping writers
*/
}
}
/*
* if there are still processes with this FIFO open
* and just return;
*/
fn_lock->flk_ocsync = 0;
return (0);
}
/*
* Need to send HANGUP if other side is still open
* (fnp->fn_rcnt or fnp->fn_wcnt may not be zero (some thread
* on this end of the pipe may still be in fifo_open())
*
* Note: we can get here with fn_rcnt and fn_wcnt != 0 if some
* thread is blocked somewhere in the fifo_open() path prior to
* fifo_stropen() incrementing fn_open. This can occur for
* normal FIFOs as well as named pipes. fn_rcnt and
* fn_wcnt only indicate attempts to open. fn_open indicates
* successful opens. Partially opened FIFOs should proceed
* normally; i.e. they will appear to be new opens. Partially
* opened pipes will probably fail.
*/
/*
* If this a pipe and this is the first end to close,
* then we have a bit of cleanup work to do.
* Mark both ends of pipe as closed.
* Wake up anybody blocked at the other end and for named pipes,
* Close down this end of the stream
* force an unmount of other end.
* Otherwise if this is last close,
* flush messages,
* close down the stream
*/
}
/*
* allow opens and closes to proceed
* Since this end is now closed down, any attempt
* to do anything with this end will fail
*/
fn_lock->flk_ocsync = 0;
/*
* if other end of pipe has been opened and it's
* a named pipe, unmount it
*/
if (fn_dest_vp->v_stream &&
/*
* We must hold the destination vnode because
* nm_unmountall() causes close to be called
* for the other end of named pipe. This
* could free the vnode before we are ready.
*/
} else {
}
} else {
#if DEBUG
if (fn_dest_vp->v_stream)
#endif
}
fn_lock->flk_ocsync = 0;
}
return (error);
}
/*
* Read from a pipe or FIFO.
* return 0 if....
* (1) user read request is 0 or no stream
* (2) broken pipe with no data
* (3) write-only FIFO with no data
* (4) no data and FNDELAY flag is set.
* Otherwise return
* EAGAIN if FNONBLOCK is set and no data to read
* EINTR if signal received while waiting for data
*
* While there is no data to read....
* - wait for a write.
*
*/
/*ARGSUSED*/
static int
{
int error = 0;
return (0);
goto stream_mode;
/*
* Check for data on our input queue
*/
/*
* No data on first attempt and no writer, then EOF
*/
return (0);
}
/*
* no data found.. if non-blocking, return EAGAIN
* otherwise 0.
*/
return (EAGAIN);
return (0);
}
/*
* Note: FIFOs can get here with FIFOCLOSE set if
* write side is in the middle of opeining after
* it once closed. Pipes better not have FIFOCLOSE set
*/
/*
* wait for data
*/
goto done;
}
"fiforead awake: %p", vp);
/*
* check to make sure we are still in fast mode
*/
goto stream_mode;
}
/* For pipes copy should not bypass cache */
do {
if (error != 0)
break;
break;
goto trywake;
/*
* We've consumed all available data but there
* are threads waiting to write more, let them
* proceed before bailing.
*/
goto trywake;
goto stream_mode;
}
} else {
}
/*
* wake up any blocked writers, processes
* sleeping on POLLWRNORM, or processes waiting for SIGPOLL
* Note: checking for fn_count < Fifohiwat emulates
* STREAMS functionality when low water mark is 0
*/
}
goto done;
/*
* FIFO is in streams mode.. let the stream head handle it
*/
done:
/*
* vnode update access time
*/
if (error == 0) {
}
return (error);
}
/*
* send SIGPIPE and return EPIPE if ...
* (1) broken pipe (essentially, reader is gone)
* (2) FIFO is not open for reading
* return 0 if...
* (1) no stream
* (2) user write request is for 0 bytes and SW_SNDZERO is not set
* Note: SW_SNDZERO can't be set in fast mode
* While the stream is flow controlled....
* - unlock the fifonode and sleep waiting for a reader.
* - if a pipe and it has a mate, sleep waiting for its mate
* to read.
*/
/*ARGSUSED*/
static int
{
int error = 0;
int write_size;
int size;
int fmode;
uiop->uio_loffset = 0;
/*
* remember original number of bytes requested. Used to determine if
* we actually have written anything at all
*/
/*
* only send zero-length messages if SW_SNDZERO is set
* Note: we will be in streams mode if SW_SNDZERO is set
* XXX this streams interface should not be exposed
*/
return (0);
/*
* oops, no readers, error
*/
goto epipe;
}
/*
* if we are not in fast mode, let streams handle it
*/
goto stream_mode;
/* For pipes copy should not bypass cache */
do {
/*
* check to make sure we are not over high water mark
*/
/*
* Indicate that we have gone over high
* water mark
*/
/*
* if non-blocking, return
* only happens first time through loop
*/
if (fmode) {
return (0);
else
return (EAGAIN);
}
goto done;
}
/*
* wait for things to drain
*/
fnp->fn_wwaitcnt++;
"fifo_write wait: %p", vp);
fnp->fn_wwaitcnt--;
goto done;
}
fnp->fn_wwaitcnt--;
"fifo_write wake: %p", vp);
/*
* check to make sure we're still in fast mode
*/
goto stream_mode;
/*
* make sure readers didn't go away
*/
goto epipe;
}
}
/*
* If the write will put us over the high water mark,
* then we must break the message up into PIPE_BUF
* chunks to stay compliant with STREAMS
*/
else
/*
* We don't need to hold flk_lock across the allocb() and
* uiomove(). However, on a multiprocessor machine where both
* the reader and writer thread are on cpu's, we must be
* careful to only drop the lock if there's data to be read.
* This forces threads entering fifo_read() to spin or block
* on flk_lock, rather than acquiring flk_lock only to
* discover there's no data to read and being forced to go
* back to sleep, only to be woken up microseconds later by
* this writer thread.
*/
if (hotread) {
if (!fifo_stayfast_enter(fnp))
goto stream_mode;
}
/*
* Align the mblk with the user data so that
* copying in the data can take advantage of
* the double word alignment
*/
if (!hotread)
if (hotread) {
/*
* As we dropped the mutex for a moment, we
* need to wake up any thread waiting to be
* allowed to go from fast mode to stream mode.
*/
}
if (error != 0) {
goto done;
}
/*
* check to make sure we're still in fast mode
*/
goto stream_mode;
/*
* make sure readers didn't go away
*/
goto epipe;
}
/*
* some other thread could have gotten in
* need to go back and check hi water mark
*/
continue;
}
if (hotread) {
/*
* As we dropped the mutex for a moment, we need to:
* - wake up any thread waiting to be allowed to go
* from fast mode to stream mode,
* - make sure readers didn't go away.
*/
goto epipe;
}
}
if (error != 0) {
goto done;
}
} else {
/*
* This is the first bit of data; wake up any sleeping
* readers, processes blocked in poll, and those
* expecting a SIGPOLL.
*/
}
goto done;
/*
* streams mode
* let the stream head handle the write
*/
done:
/*
* update vnode modification and change times
* make sure there were no errors and some data was transferred
*/
}
goto epipe;
}
return (error);
return (error);
}
/*ARGSUSED6*/
static int
{
/*
* Just a quick check
* Once we go to streams mode we don't ever revert back
* So we do this quick check so as not to incur the overhead
* associated with acquiring the lock
*/
}
static int
{
int error = 0;
int cnt;
/*
* tty operations not allowed
*/
return (EINVAL);
}
goto stream_mode;
}
switch (cmd) {
/*
* Things we can't handle
* These will switch us to streams mode.
*/
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;
/*
* Things that don't do damage
* These things don't adjust the state of the
* stream head (i_setcltime does, but we don't care)
*/
case I_FIND:
case I_GETSIG:
case FIONBIO:
case FIOASYNC:
case I_GRDOPT: /* probably should not get this, but no harm */
case I_GWROPT:
case I_LIST:
case I_SETCLTIME:
case I_GETCLTIME:
case I_CANPUT:
/*
* We can only handle normal band canputs.
* XXX : We could just always go to stream mode; after all
* canput is a streams semantics type thing
*/
if (arg != 0) {
goto turn_fastoff;
}
return (0);
case I_NREAD:
/*
* This may seem a bit silly for non-streams semantics,
* (After all, if they really want a message, they'll
* probably use getmsg() anyway). but it doesn't hurt
*/
sizeof (cnt));
if (error == 0) {
}
break;
case FIORDCHK:
break;
case I_PEEK:
{
int count;
int len;
*rvalp = 0;
break;
}
if (error)
break;
/*
* can't have any high priority message when in fast mode
*/
*rvalp = 0;
break;
}
if (len <= 0) {
} else {
uio.uio_loffset = 0;
/* For pipes copy should not bypass cache */
break;
}
}
}
*rvalp = 1;
break;
}
case FIONREAD:
/*
* let user know total number of bytes in message queue
*/
if (error == 0)
*rvalp = 0;
break;
case I_SETSIG:
/*
* let streams set up the signal masking for us
* we just check to see if it's set
* XXX : this interface should not be visible
* i.e. STREAM's framework is exposed.
*/
else
break;
case I_FLUSH:
/*
* flush them message queues
*/
break;
}
}
}
/*
* wake up any sleeping readers or writers
* (waking readers probably doesn't make sense, but it
* doesn't hurt; i.e. we just got rid of all the data
* what's to read ?)
*/
}
*rvalp = 0;
break;
/*
* Since no band data can ever get on a fifo in fast mode
* just return 0.
*/
case I_FLUSHBAND:
error = 0;
*rvalp = 0;
break;
/*
* invalid calls for stream head or fifos
*/
case I_POP: /* shouldn't happen */
case I_LOOK:
case I_LINK:
case I_PLINK:
case I_UNLINK:
case I_PUNLINK:
/*
* more invalid tty type of ioctls
*/
case SRIOCSREDIR:
case SRIOCISREDIR:
break;
}
return (error);
/*
* streams mode
*/
}
/*
* FIFO is in STREAMS mode; STREAMS framework does most of the work.
*/
static int
{
int error;
if (cmd == _I_GETPEERCRED) {
return (0);
} else {
return (ENOTSUP);
}
}
switch (cmd) {
/*
* The FIFOSEND flag is set to inform other processes that a file
* descriptor is pending at the stream head of this pipe.
* The flag is cleared and the sending process is awoken when
* this process has completed receiving the file descriptor.
* XXX This could become out of sync if the process does I_SENDFDs
* and opens on connld attached to the same pipe.
*/
case I_RECVFD:
case I_E_RECVFD:
if (error == 0) {
}
}
break;
default:
break;
}
return (error);
}
/*
* If shadowing a vnode (FIFOs), apply the VOP_GETATTR to the shadowed
* vnode to Obtain the node information. If not shadowing (pipes), obtain
* the node information from the credentials structure.
*/
int
{
int error = 0;
/*
* for FIFOs or mounted pipes
*/
return (error);
/* set current times from fnode, even if older than vnode */
} else {
/*
* for non-attached/ordinary pipes
*/
}
/*
* Size is number of un-read bytes at the stream head and
* nblocks is the unread bytes expressed in blocks.
*/
} else {
}
}
} else {
}
return (0);
}
/*
* If shadowing a vnode, apply the VOP_SETATTR to it, and to the fnode.
* Otherwise, set the time and return 0.
*/
int
int flags,
{
int error = 0;
if (error == 0) {
}
return (error);
}
/*
* If shadowing a vnode, apply VOP_ACCESS to it.
* Otherwise, return 0 (allow all access).
*/
int
{
else
return (0);
}
/*
* This can be called if creat or an open with O_CREAT is done on the root
* of a lofs mount where the mounted entity is a fifo.
*/
/*ARGSUSED*/
static int
{
int error;
return (error);
return (0);
}
return (EEXIST);
}
/*
* If shadowing a vnode, apply the VOP_FSYNC to it.
* Otherwise, return 0.
*/
int
{
return (0);
}
}
}
}
/*
* Called when the upper level no longer holds references to the
* vnode. Sync the file system and free the fifonode.
*/
void
{
/*
* Somebody accessed the fifo before we got a chance to
* remove it. They will remove it when they do a vn_rele.
*/
return;
}
/*
* remove fifo from fifo list so that no other process
* can grab it.
* Drop the reference count on the fifo node's
* underlying vfs.
*/
(void) fiforemove(fnp);
} else
/*
* if this is last reference to the lock, then we can
* free everything up.
*/
}
}
}
}
} else
} else {
}
}
/*
* If shadowing a vnode, apply the VOP_FID to it.
* Otherwise, return EINVAL.
*/
int
{
else
return (EINVAL);
}
/*
* Lock a fifonode.
*/
/* ARGSUSED */
int
{
return (-1);
}
/*
* Unlock a fifonode.
*/
/* ARGSUSED */
void
{
}
/*
* Return error since seeks are not allowed on pipes.
*/
/*ARGSUSED*/
int
{
return (ESPIPE);
}
/*
* If there is a realvp associated with vp, return it.
*/
int
{
}
return (0);
}
/*
* Poll for interesting events on a stream pipe
*/
/* ARGSUSED */
int
{
int retevents;
retevents = 0;
return (0);
}
/*
*/
return (0);
}
}
/*
* if not in fast mode, let the stream head take care of it
*/
goto stream_mode;
}
/*
* If this is a pipe.. check to see if the other
* end is gone. If we are a fifo, check to see
* if write end is gone.
*/
/*
* no writer at other end.
* it was closed (versus yet to be opened)
*/
if (events & POLLWRNORM) {
else
}
/*
* This is always true for fast pipes
* (Note: will go to STREAMS mode if band data is written)
*/
if (events & POLLWRBAND)
retevents |= POLLWRBAND;
}
}
/*
* if we happened to get something and we're not edge-triggered, return
*/
return (0);
}
/*
* If poll() has not found any events yet or we're edge-triggered, set
* up event cell to wake up the poll if a requested event occurs on this
*/
if (!anyyet) {
if (events & POLLWRNORM)
if (events & POLLRDBAND)
/*
* XXX Don't like exposing this from streams
*/
}
return (0);
}
/*
* POSIX pathconf() support.
*/
/* ARGSUSED */
int
{
int error = 0;
switch (cmd) {
case _PC_LINK_MAX:
break;
case _PC_MAX_CANON:
break;
case _PC_MAX_INPUT:
break;
case _PC_NAME_MAX:
break;
case _PC_PATH_MAX:
case _PC_SYMLINK_MAX:
val = MAXPATHLEN;
break;
case _PC_PIPE_BUF:
break;
case _PC_NO_TRUNC:
else
break;
case _PC_VDISABLE:
break;
case _PC_CHOWN_RESTRICTED:
if (rstchown)
else
break;
case _PC_FILESIZEBITS:
break;
default:
else
break;
}
if (error == 0)
return (error);
}
/*
* If shadowing a vnode, apply VOP_SETSECATTR to it.
* Otherwise, return NOSYS.
*/
int
{
int error;
/*
* The acl(2) system call tries to grab the write lock on the
* file when setting an ACL, but fifofs does not implement
* VOP_RWLOCK or VOP_RWUNLOCK, so we do it here instead.
*/
return (error);
} else
return (fs_nosys());
}
/*
* If shadowing a vnode, apply VOP_GETSECATTR to it. Otherwise, fabricate
* an ACL from the permission bits that fifo_getattr() makes up.
*/
int
{
else
}
/*
* Set the FIFOSTAYFAST flag so nobody can turn the fifo into stream mode.
* If the flag is already set then wait until it is removed - releasing
* the lock.
* If the fifo switches into stream mode while we are waiting, return failure.
*/
static boolean_t
{
}
return (B_FALSE);
return (B_TRUE);
}
/*
* Unset the FIFOSTAYFAST flag and notify anybody waiting for this flag
* to be removed:
* - threads wanting to turn into stream mode waiting in fifo_fastoff(),
* - other writers threads waiting in fifo_stayfast_enter().
*/
static void
{
}