/*
 * PROJECT:     ReactOS Named Pipe FileSystem
 * LICENSE:     BSD - See COPYING.ARM in the top level directory
 * FILE:        drivers/filesystems/npfs/write.c
 * PURPOSE:     Pipes Writing
 * PROGRAMMERS: ReactOS Portable Systems Group
 */

/* INCLUDES *******************************************************************/

#include "npfs.h"

// File ID number for NPFS bugchecking support
#define NPFS_BUGCHECK_FILE_ID   (NPFS_BUGCHECK_WRITE)

/* GLOBALS ********************************************************************/

LONG NpSlowWriteCalls;
ULONG NpFastWriteTrue;
ULONG NpFastWriteFalse;

/* FUNCTIONS ******************************************************************/

BOOLEAN
NTAPI
NpCommonWrite(IN PFILE_OBJECT FileObject,
              IN PVOID Buffer,
              IN ULONG DataSize,
              IN PETHREAD Thread,
              IN PIO_STATUS_BLOCK IoStatus,
              IN PIRP Irp,
              IN PLIST_ENTRY List)
{
    NODE_TYPE_CODE NodeType;
    BOOLEAN WriteOk;
    PNP_CCB Ccb;
    PNP_NONPAGED_CCB NonPagedCcb;
    PNP_DATA_QUEUE WriteQueue;
    NTSTATUS Status;
    PNP_EVENT_BUFFER EventBuffer;
    ULONG BytesWritten, NamedPipeEnd, ReadMode;
    PAGED_CODE();

    IoStatus->Information = 0;
    NodeType = NpDecodeFileObject(FileObject, NULL, &Ccb, &NamedPipeEnd);

    if (!NodeType)
    {
        IoStatus->Status = STATUS_PIPE_DISCONNECTED;
        return TRUE;
    }

    if (NodeType != NPFS_NTC_CCB)
    {
        IoStatus->Status = STATUS_INVALID_PARAMETER;
        return TRUE;
    }

    NonPagedCcb = Ccb->NonPagedCcb;
    ExAcquireResourceExclusiveLite(&NonPagedCcb->Lock, TRUE);

    if (Ccb->NamedPipeState != FILE_PIPE_CONNECTED_STATE)
    {
        if (Ccb->NamedPipeState == FILE_PIPE_DISCONNECTED_STATE)
        {
            IoStatus->Status = STATUS_PIPE_DISCONNECTED;
        }
        else if (Ccb->NamedPipeState == FILE_PIPE_LISTENING_STATE)
        {
            IoStatus->Status = STATUS_PIPE_LISTENING;
        }
        else
        {
            ASSERT(Ccb->NamedPipeState == FILE_PIPE_CLOSING_STATE);
            IoStatus->Status = STATUS_PIPE_CLOSING;
        }

        WriteOk = TRUE;
        goto Quickie;
    }

    if ((NamedPipeEnd == FILE_PIPE_SERVER_END && Ccb->Fcb->NamedPipeConfiguration == FILE_PIPE_INBOUND) ||
        (NamedPipeEnd == FILE_PIPE_CLIENT_END && Ccb->Fcb->NamedPipeConfiguration == FILE_PIPE_OUTBOUND))
    {
        IoStatus->Status = STATUS_INVALID_PARAMETER;
        WriteOk = TRUE;
        goto Quickie;
    }

    IoStatus->Status = STATUS_SUCCESS;
    IoStatus->Information = DataSize;

    if (NamedPipeEnd == FILE_PIPE_SERVER_END)
    {
        WriteQueue = &Ccb->DataQueue[FILE_PIPE_OUTBOUND];
        ReadMode = Ccb->ReadMode[FILE_PIPE_CLIENT_END];
    }
    else
    {
        WriteQueue = &Ccb->DataQueue[FILE_PIPE_INBOUND];
        ReadMode = Ccb->ReadMode[FILE_PIPE_SERVER_END];
    }

    EventBuffer = NonPagedCcb->EventBuffer[NamedPipeEnd];

    if ((WriteQueue->QueueState == ReadEntries &&
         WriteQueue->BytesInQueue < DataSize &&
         WriteQueue->Quota < DataSize - WriteQueue->BytesInQueue) ||
        (WriteQueue->QueueState != ReadEntries &&
         WriteQueue->Quota - WriteQueue->QuotaUsed < DataSize))
    {
        if (Ccb->Fcb->NamedPipeType == FILE_PIPE_MESSAGE_TYPE &&
            Ccb->CompletionMode[NamedPipeEnd] == FILE_PIPE_COMPLETE_OPERATION)
        {
            IoStatus->Information = 0;
            IoStatus->Status = STATUS_SUCCESS;
            WriteOk = TRUE;
            goto Quickie;
        }

        if (!Irp)
        {
            WriteOk = FALSE;
            goto Quickie;
        }
    }

    Status = NpWriteDataQueue(WriteQueue,
                              ReadMode,
                              Buffer,
                              DataSize,
                              Ccb->Fcb->NamedPipeType,
                              &BytesWritten,
                              Ccb,
                              NamedPipeEnd,
                              Thread,
                              List);
    IoStatus->Status = Status;

    if (Status == STATUS_MORE_PROCESSING_REQUIRED)
    {
        ASSERT(WriteQueue->QueueState != ReadEntries);
        if ((Ccb->CompletionMode[NamedPipeEnd] == FILE_PIPE_COMPLETE_OPERATION || !Irp) &&
            ((WriteQueue->Quota - WriteQueue->QuotaUsed) < BytesWritten))
        {
            IoStatus->Information = DataSize - BytesWritten;
            IoStatus->Status = STATUS_SUCCESS;
        }
        else
        {
            ASSERT(WriteQueue->QueueState != ReadEntries);

            IoStatus->Status = NpAddDataQueueEntry(NamedPipeEnd,
                                                   Ccb,
                                                   WriteQueue,
                                                   WriteEntries,
                                                   Buffered,
                                                   DataSize,
                                                   Irp,
                                                   Buffer,
                                                   DataSize - BytesWritten);
        }
    }

    if (EventBuffer) KeSetEvent(EventBuffer->Event, IO_NO_INCREMENT, FALSE);
    WriteOk = TRUE;

Quickie:
    ExReleaseResourceLite(&Ccb->NonPagedCcb->Lock);
    return WriteOk;
}

NTSTATUS
NTAPI
NpFsdWrite(IN PDEVICE_OBJECT DeviceObject,
           IN PIRP Irp)
{
    PIO_STACK_LOCATION IoStack;
    IO_STATUS_BLOCK IoStatus;
    LIST_ENTRY DeferredList;
    PAGED_CODE();
    NpSlowWriteCalls++;

    InitializeListHead(&DeferredList);
    IoStack = IoGetCurrentIrpStackLocation(Irp);

    FsRtlEnterFileSystem();
    NpAcquireSharedVcb();

    NpCommonWrite(IoStack->FileObject,
                  Irp->UserBuffer,
                  IoStack->Parameters.Write.Length,
                  Irp->Tail.Overlay.Thread,
                  &IoStatus,
                  Irp,
                  &DeferredList);

    NpReleaseVcb();
    NpCompleteDeferredIrps(&DeferredList);
    FsRtlExitFileSystem();

    if (IoStatus.Status != STATUS_PENDING)
    {
        Irp->IoStatus.Information = IoStatus.Information;
        Irp->IoStatus.Status = IoStatus.Status;
        IoCompleteRequest(Irp, IO_NAMED_PIPE_INCREMENT);
    }

    return IoStatus.Status;
}


_Function_class_(FAST_IO_WRITE)
_IRQL_requires_same_
BOOLEAN
NTAPI
NpFastWrite(
    _In_ PFILE_OBJECT FileObject,
    _In_ PLARGE_INTEGER FileOffset,
    _In_ ULONG Length,
    _In_ BOOLEAN Wait,
    _In_ ULONG LockKey,
    _In_ PVOID Buffer,
    _Out_ PIO_STATUS_BLOCK IoStatus,
    _In_ PDEVICE_OBJECT DeviceObject)
{
    LIST_ENTRY DeferredList;
    BOOLEAN Result;
    PAGED_CODE();

    InitializeListHead(&DeferredList);

    FsRtlEnterFileSystem();
    NpAcquireSharedVcb();

    Result = NpCommonWrite(FileObject,
                           Buffer,
                           Length,
                           PsGetCurrentThread(),
                           IoStatus,
                           NULL,
                           &DeferredList);
    if (Result)
        ++NpFastWriteTrue;
    else
        ++NpFastWriteFalse;

    NpReleaseVcb();
    NpCompleteDeferredIrps(&DeferredList);
    FsRtlExitFileSystem();

    return Result;
}

/* EOF */