123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855 |
- /*++
- Copyright (c) 2013 Minoca Corp. All Rights Reserved
- Module Name:
- pipe.c
- Abstract:
- This module implements support for pipes.
- Author:
- Evan Green 25-Apr-2013
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/kernel/kernel.h>
- #include "iop.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // Define pipe flags.
- //
- //
- // This flag is set if the pipe has a name in the object manager directory.
- // Note that normal named pipes coming from the file system do not have this
- // flag set.
- //
- #define PIPE_FLAG_OBJECT_NAMED 0x00000001
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- /*++
- Structure Description:
- This structure defines a data pipe.
- Members:
- Header - Stores the standard object header.
- Flags - Stores the flags used when the pipe was created.
- StreamBuffer - Stores a pointer to the stream buffer backing the pipe.
- ReaderCount - Stores the number of readers that have the pipe open.
- WriterCount - Stores the number of writers that have the pipe open.
- --*/
- typedef struct _PIPE {
- OBJECT_HEADER Header;
- ULONG Flags;
- PSTREAM_BUFFER StreamBuffer;
- ULONG ReaderCount;
- ULONG WriterCount;
- } PIPE, *PPIPE;
- /*++
- Structure Description:
- This structure defines the parameters needed to create a pipe.
- Members:
- BufferSize - Stores the suggested size for the internal stream buffer.
- Supply 0 to use the system default size.
- --*/
- typedef struct _PIPE_CREATION_PARAMETERS {
- ULONG BufferSize;
- } PIPE_CREATION_PARAMETERS, *PPIPE_CREATION_PARAMETERS;
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- VOID
- IopDestroyPipe (
- PVOID PipeObject
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // Store a pointer to the pipes directory.
- //
- POBJECT_HEADER IoPipeDirectory;
- //
- // ------------------------------------------------------------------ Functions
- //
- KERNEL_API
- KSTATUS
- IoCreatePipe (
- BOOL FromKernelMode,
- PIO_HANDLE Directory,
- PSTR Path,
- ULONG PathLength,
- ULONG OpenFlags,
- FILE_PERMISSIONS CreatePermissions,
- PIO_HANDLE *ReadHandle,
- PIO_HANDLE *WriteHandle
- )
- /*++
- Routine Description:
- This routine creates and opens a new pipe.
- Arguments:
- FromKernelMode - Supplies a boolean indicating whether this request is
- originating from kernel mode (and should use the root path as a base)
- or user mode.
- Directory - Supplies an optional pointer to an open handle to a directory
- for relative paths. Supply NULL to use the current working directory.
- Path - Supplies an optional pointer to the path to open.
- PathLength - Supplies the length of the path buffer in bytes, including the
- null terminator.
- OpenFlags - Supplies the open flags for the pipe. See OPEN_FLAG_*
- definitions. OPEN_FLAG_CREATE and OPEN_FLAG_FAIL_IF_EXISTS are
- automatically applied.
- CreatePermissions - Supplies the permissions to apply to the created pipe.
- ReadHandle - Supplies a pointer where a handle to the read side of the pipe
- will be returned.
- WriteHandle - Supplies a pointer where a handle to the write side of the
- pipe will be returned.
- Return Value:
- Status code.
- --*/
- {
- KSTATUS Status;
- *ReadHandle = NULL;
- *WriteHandle = NULL;
- //
- // Create and open the read side.
- //
- Status = IopOpen(FromKernelMode,
- Directory,
- Path,
- PathLength,
- IO_ACCESS_READ,
- OpenFlags | OPEN_FLAG_CREATE | OPEN_FLAG_FAIL_IF_EXISTS,
- IoObjectPipe,
- NULL,
- CreatePermissions,
- ReadHandle);
- if (!KSUCCESS(Status)) {
- goto CreatePipeEnd;
- }
- //
- // Also open the write side.
- //
- Status = IopOpenPathPoint(&((*ReadHandle)->PathPoint),
- IO_ACCESS_WRITE,
- OpenFlags,
- WriteHandle);
- if (!KSUCCESS(Status)) {
- goto CreatePipeEnd;
- }
- CreatePipeEnd:
- if (!KSUCCESS(Status)) {
- if (*ReadHandle != NULL) {
- IoClose(*ReadHandle);
- *ReadHandle = NULL;
- }
- if (*WriteHandle != NULL) {
- IoClose(*WriteHandle);
- *WriteHandle = NULL;
- }
- }
- return Status;
- }
- POBJECT_HEADER
- IopGetPipeDirectory (
- VOID
- )
- /*++
- Routine Description:
- This routine returns the pipe root directory in the object system. This is
- the only place in the object system pipe creation is allowed.
- Arguments:
- None.
- Return Value:
- Returns a pointer to the pipe directory.
- --*/
- {
- return IoPipeDirectory;
- }
- KSTATUS
- IopCreatePipe (
- PSTR Name,
- ULONG NameSize,
- FILE_PERMISSIONS Permissions,
- PFILE_OBJECT *FileObject
- )
- /*++
- Routine Description:
- This routine actually creates a new pipe.
- Arguments:
- Name - Supplies an optional pointer to the pipe name. This is only used for
- named pipes created in the pipe directory.
- NameSize - Supplies the size of the name in bytes including the null
- terminator.
- Permissions - Supplies the permissions to give to the file object.
- FileObject - Supplies a pointer where a pointer to a newly created pipe
- file object will be returned on success.
- Return Value:
- Status code.
- --*/
- {
- BOOL Created;
- PPIPE ExistingPipe;
- FILE_PROPERTIES FileProperties;
- PFILE_OBJECT NewFileObject;
- PPIPE NewPipe;
- KSTATUS Status;
- PKTHREAD Thread;
- NewFileObject = NULL;
- NewPipe = NULL;
- //
- // Make sure there is not already an existing pipe by the same name. The
- // caller should have the appropriate locks to make the check and create
- // synchronous.
- //
- if (Name != NULL) {
- ExistingPipe = ObFindObject(Name, NameSize, IoPipeDirectory);
- if (ExistingPipe != NULL) {
- ObReleaseReference(ExistingPipe);
- Status = STATUS_FILE_EXISTS;
- goto CreatePipeEnd;
- }
- }
- //
- // Create the actual object. This reference is transferred to the file
- // object's special I/O member on success.
- //
- NewPipe = ObCreateObject(ObjectPipe,
- IoPipeDirectory,
- Name,
- NameSize,
- sizeof(PIPE),
- IopDestroyPipe,
- 0,
- IO_ALLOCATION_TAG);
- if (NewPipe == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreatePipeEnd;
- }
- //
- // Record if the pipe got a name in the pipe directory.
- //
- if (Name != NULL) {
- NewPipe->Flags |= PIPE_FLAG_OBJECT_NAMED;
- }
- //
- // Create a file object if needed.
- //
- if (*FileObject == NULL) {
- Thread = KeGetCurrentThread();
- IopFillOutFilePropertiesForObject(&FileProperties, &(NewPipe->Header));
- FileProperties.Permissions = Permissions;
- FileProperties.Type = IoObjectPipe;
- FileProperties.UserId = Thread->Identity.EffectiveUserId;
- FileProperties.GroupId = Thread->Identity.EffectiveGroupId;
- Status = IopCreateOrLookupFileObject(&FileProperties,
- ObGetRootObject(),
- 0,
- &NewFileObject,
- &Created);
- if (!KSUCCESS(Status)) {
- //
- // Release the references added by filling out the file properties.
- //
- ObReleaseReference(NewPipe);
- goto CreatePipeEnd;
- }
- ASSERT(Created != FALSE);
- *FileObject = NewFileObject;
- }
- ASSERT((*FileObject)->Properties.Type == IoObjectPipe);
- //
- // Now fill in the pipe with the I/O object state.
- //
- ASSERT((*FileObject)->IoState != NULL);
- NewPipe->StreamBuffer = IoCreateStreamBuffer((*FileObject)->IoState,
- 0,
- 0,
- PIPE_ATOMIC_WRITE_SIZE);
- if (NewPipe->StreamBuffer == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreatePipeEnd;
- }
- //
- // Now that the pipe's ready, release anyone else who happened to find this
- // file object in the mean time.
- //
- ASSERT(((*FileObject)->SpecialIo == NULL) &&
- ((KeGetEventState((*FileObject)->ReadyEvent) == NotSignaled) ||
- (KeGetEventState((*FileObject)->ReadyEvent) ==
- NotSignaledWithWaiters)));
- (*FileObject)->SpecialIo = NewPipe;
- NewPipe = NULL;
- Status = STATUS_SUCCESS;
- CreatePipeEnd:
- //
- // On both success and failure, the file object's ready event needs to be
- // signaled. Other threads may be waiting on the event.
- //
- if (*FileObject != NULL) {
- KeSignalEvent((*FileObject)->ReadyEvent, SignalOptionSignalAll);
- }
- if (!KSUCCESS(Status)) {
- if (NewFileObject != NULL) {
- *FileObject = NULL;
- IopFileObjectReleaseReference(NewFileObject);
- }
- if (NewPipe != NULL) {
- ObReleaseReference(NewPipe);
- NewPipe = NULL;
- }
- }
- return Status;
- }
- KSTATUS
- IopUnlinkPipe (
- PFILE_OBJECT FileObject,
- PBOOL Unlinked
- )
- /*++
- Routine Description:
- This routine unlinks a pipe from the accessible namespace.
- Arguments:
- FileObject - Supplies a pointer to the pipe's file object.
- Unlinked - Supplies a pointer to a boolean that receives whether or not the
- terminal was successfully unlinked.
- Return Value:
- Status code.
- --*/
- {
- PPIPE Pipe;
- KSTATUS Status;
- ASSERT(FileObject->Properties.Type == IoObjectPipe);
- ASSERT(KeIsSharedExclusiveLockHeldExclusive(FileObject->Lock) != FALSE);
- Pipe = FileObject->SpecialIo;
- ASSERT(Pipe != NULL);
- ASSERT((Pipe->Flags & PIPE_FLAG_OBJECT_NAMED) != 0);
- *Unlinked = FALSE;
- Status = ObUnlinkObject(Pipe);
- if (KSUCCESS(Status)) {
- *Unlinked = TRUE;
- }
- return Status;
- }
- KSTATUS
- IopOpenPipe (
- PIO_HANDLE IoHandle
- )
- /*++
- Routine Description:
- This routine is called when a pipe is opened.
- Arguments:
- IoHandle - Supplies a pointer to the new I/O handle.
- Return Value:
- Status code.
- --*/
- {
- PFILE_OBJECT FileObject;
- PIO_OBJECT_STATE IoState;
- BOOL NonBlocking;
- PPIPE Pipe;
- BOOL PipeOpened;
- ULONG ReturnedEvents;
- KSTATUS Status;
- PipeOpened = FALSE;
- FileObject = IoHandle->FileObject;
- ASSERT(FileObject->Properties.Type == IoObjectPipe);
- KeAcquireSharedExclusiveLockExclusive(FileObject->Lock);
- Pipe = FileObject->SpecialIo;
- if (Pipe == NULL) {
- ASSERT(FALSE);
- Status = STATUS_TOO_LATE;
- goto OpenPipeEnd;
- }
- if ((IoHandle->Access & IO_ACCESS_EXECUTE) != 0) {
- Status = STATUS_INVALID_PARAMETER;
- goto OpenPipeEnd;
- }
- IoState = IoStreamBufferGetIoObjectState(Pipe->StreamBuffer);
- if ((IoHandle->Access & IO_ACCESS_READ) != 0) {
- Pipe->ReaderCount += 1;
- //
- // Clear the error event.
- //
- IoSetIoObjectState(IoState, POLL_EVENT_ERROR, FALSE);
- }
- if ((IoHandle->Access & IO_ACCESS_WRITE) != 0) {
- Pipe->WriterCount += 1;
- //
- // Clear the disconnect event.
- //
- IoSetIoObjectState(IoState, POLL_EVENT_DISCONNECTED, FALSE);
- }
- PipeOpened = TRUE;
- //
- // Determine whether this is a blocking or non-blocking open. The initial
- // create/open call is also set non-blocking, and this relies a bit on the
- // fact that the read end is opened first.
- //
- NonBlocking = FALSE;
- if (((IoHandle->OpenFlags & OPEN_FLAG_NON_BLOCKING) != 0) ||
- ((IoHandle->OpenFlags &
- (OPEN_FLAG_CREATE | OPEN_FLAG_FAIL_IF_EXISTS)) ==
- (OPEN_FLAG_CREATE | OPEN_FLAG_FAIL_IF_EXISTS))) {
- NonBlocking = TRUE;
- }
- //
- // In non-blocking mode, open access for write only returns an error if no
- // process currently has the pipe open for reading.
- //
- if (NonBlocking != FALSE) {
- if (((IoHandle->Access & IO_ACCESS_WRITE) != 0) &&
- (Pipe->ReaderCount == 0)) {
- Status = STATUS_NO_SUCH_DEVICE_OR_ADDRESS;
- goto OpenPipeEnd;
- }
- //
- // Handle a blocking open on a pipe, which blocks until the other end
- // connects.
- //
- } else {
- //
- // If there's no jelly for your peanut buffer, wait for some to arrive.
- // Borrow the write event to block on.
- //
- if ((((IoHandle->Access & IO_ACCESS_WRITE) != 0) &&
- (Pipe->ReaderCount == 0)) ||
- (((IoHandle->Access & IO_ACCESS_READ) != 0) &&
- (Pipe->WriterCount == 0))) {
- IoSetIoObjectState(IoState, POLL_EVENT_OUT, FALSE);
- KeReleaseSharedExclusiveLockExclusive(FileObject->Lock);
- Status = IoWaitForIoObjectState(IoState,
- POLL_EVENT_OUT | POLL_EVENT_ERROR,
- TRUE,
- WAIT_TIME_INDEFINITE,
- &ReturnedEvents);
- KeAcquireSharedExclusiveLockExclusive(FileObject->Lock);
- if (!KSUCCESS(Status)) {
- goto OpenPipeEnd;
- }
- if ((ReturnedEvents & POLL_EVENT_OUT) == 0) {
- Status = STATUS_NOT_READY;
- goto OpenPipeEnd;
- }
- }
- }
- //
- // Reset the I/O object state, which sets the in and out poll events
- // properly.
- //
- Status = IoStreamBufferConnect(Pipe->StreamBuffer);
- if (!KSUCCESS(Status)) {
- goto OpenPipeEnd;
- }
- Status = STATUS_SUCCESS;
- OpenPipeEnd:
- KeReleaseSharedExclusiveLockExclusive(FileObject->Lock);
- if (!KSUCCESS(Status)) {
- if (PipeOpened != FALSE) {
- IopClosePipe(IoHandle);
- }
- }
- return Status;
- }
- KSTATUS
- IopClosePipe (
- PIO_HANDLE IoHandle
- )
- /*++
- Routine Description:
- This routine is called when a pipe is closed.
- Arguments:
- IoHandle - Supplies a pointer to the I/O handle being closed.
- Return Value:
- Status code.
- --*/
- {
- PFILE_OBJECT FileObject;
- PFILE_PROPERTIES FileProperties;
- PIO_OBJECT_STATE IoState;
- BOOL LockHeld;
- PPIPE Pipe;
- FileObject = IoHandle->FileObject;
- FileProperties = &(FileObject->Properties);
- ASSERT(FileProperties->Type == IoObjectPipe);
- KeAcquireSharedExclusiveLockExclusive(FileObject->Lock);
- LockHeld = TRUE;
- Pipe = FileObject->SpecialIo;
- IoState = IoStreamBufferGetIoObjectState(Pipe->StreamBuffer);
- if ((IoHandle->Access & IO_ACCESS_READ) != 0) {
- Pipe->ReaderCount -= 1;
- if (Pipe->ReaderCount == 0) {
- //
- // The last reader just closed, so clear the hangup event and the
- // out event. Set the error event.
- //
- IoSetIoObjectState(IoState,
- POLL_EVENT_OUT | POLL_EVENT_DISCONNECTED,
- FALSE);
- IoSetIoObjectState(IoState, POLL_EVENT_ERROR, TRUE);
- }
- }
- if ((IoHandle->Access & IO_ACCESS_WRITE) != 0) {
- Pipe->WriterCount -= 1;
- if (Pipe->WriterCount == 0) {
- //
- // Clear the out event, set the hangup event, and set the read
- // event.
- //
- IoSetIoObjectState(IoState, POLL_EVENT_OUT, FALSE);
- IoSetIoObjectState(IoState,
- POLL_EVENT_DISCONNECTED | POLL_EVENT_IN,
- TRUE);
- }
- }
- //
- // Pipes that are named in the object directory need to be unlinked on
- // the last close. Check to see if the reader and writer counts are both
- // zero. If so, unlink the object. It may be that another thread is about
- // to open the pipe for read and/or write. This is OK, it's got a reference
- // on the file object and can proceed without concern. When it closes the
- // pipe it will attempt the unlink again, but that's fine. No new lookups
- // can occur after the first unlink attempt.
- //
- if ((Pipe->Flags & PIPE_FLAG_OBJECT_NAMED) != 0) {
- if ((Pipe->WriterCount == 0) && (Pipe->ReaderCount == 0)) {
- KeReleaseSharedExclusiveLockExclusive(FileObject->Lock);
- LockHeld = FALSE;
- IopDeleteByHandle(TRUE, IoHandle, 0);
- }
- }
- if (LockHeld != FALSE) {
- KeReleaseSharedExclusiveLockExclusive(FileObject->Lock);
- }
- return STATUS_SUCCESS;
- }
- KSTATUS
- IopPerformPipeIoOperation (
- PIO_HANDLE Handle,
- PIO_CONTEXT IoContext
- )
- /*++
- Routine Description:
- This routine reads from or writes to a pipe.
- Arguments:
- Handle - Supplies a pointer to the pipe I/O handle.
- IoContext - Supplies a pointer to the I/O context.
- Return Value:
- Status code. A failing status code does not necessarily mean no I/O made it
- in or out. Check the bytes completed value in the I/O context to find out
- how much occurred.
- --*/
- {
- PFILE_OBJECT FileObject;
- BOOL NonBlocking;
- PPIPE Pipe;
- UINTN PipeBytesCompleted;
- KSTATUS Status;
- FileObject = Handle->FileObject;
- ASSERT(IoContext->IoBuffer != NULL);
- ASSERT(FileObject->Properties.Type == IoObjectPipe);
- Pipe = FileObject->SpecialIo;
- PipeBytesCompleted = 0;
- NonBlocking = FALSE;
- if (IoContext->Write != FALSE) {
- //
- // If there are no readers, send a pipe signal to the calling
- // application.
- //
- if (Pipe->ReaderCount == 0) {
- Status = STATUS_BROKEN_PIPE;
- } else {
- Status = IoWriteStreamBuffer(Pipe->StreamBuffer,
- IoContext->IoBuffer,
- IoContext->SizeInBytes,
- IoContext->TimeoutInMilliseconds,
- NonBlocking,
- &PipeBytesCompleted);
- }
- } else {
- if (Pipe->WriterCount == 0) {
- NonBlocking = TRUE;
- }
- Status = IoReadStreamBuffer(Pipe->StreamBuffer,
- IoContext->IoBuffer,
- IoContext->SizeInBytes,
- IoContext->TimeoutInMilliseconds,
- NonBlocking,
- &PipeBytesCompleted);
- if ((Status == STATUS_NO_DATA_AVAILABLE) && (Pipe->WriterCount == 0)) {
- ASSERT(PipeBytesCompleted == 0);
- Status = STATUS_END_OF_FILE;
- }
- }
- IoContext->BytesCompleted = PipeBytesCompleted;
- return Status;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- VOID
- IopDestroyPipe (
- PVOID PipeObject
- )
- /*++
- Routine Description:
- This routine destroys all resources associated with a pipe.
- Arguments:
- PipeObject - Supplies a pointer to the pipe object being destroyed.
- Return Value:
- None.
- --*/
- {
- PPIPE Pipe;
- Pipe = (PPIPE)PipeObject;
- if (Pipe->StreamBuffer != NULL) {
- IoDestroyStreamBuffer(Pipe->StreamBuffer);
- }
- return;
- }
|