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

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

#include "npfs.h"

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

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

NTSTATUS
NTAPI
NpWriteDataQueue(IN PNP_DATA_QUEUE WriteQueue,
                 IN ULONG Mode,
                 IN PVOID OutBuffer,
                 IN ULONG OutBufferSize,
                 IN ULONG PipeType,
                 OUT PULONG BytesNotWritten,
                 IN PNP_CCB Ccb,
                 IN ULONG NamedPipeEnd,
                 IN PETHREAD Thread,
                 IN PLIST_ENTRY List)
{
    BOOLEAN HaveContext = FALSE, MoreProcessing, AllocatedBuffer;
    PNP_DATA_QUEUE_ENTRY DataEntry;
    ULONG DataSize, BufferSize;
    PIRP WriteIrp;
    PIO_STACK_LOCATION IoStack;
    PVOID Buffer;
    NTSTATUS Status;
    PSECURITY_CLIENT_CONTEXT ClientContext;
    PAGED_CODE();

    *BytesNotWritten = OutBufferSize;

    MoreProcessing = TRUE;
    if ((PipeType != FILE_PIPE_MESSAGE_MODE) || (OutBufferSize))
    {
        MoreProcessing = FALSE;
    }

    for (DataEntry = CONTAINING_RECORD(NpGetNextRealDataQueueEntry(WriteQueue, List),
                                       NP_DATA_QUEUE_ENTRY,
                                       QueueEntry);
         ((WriteQueue->QueueState == ReadEntries) &&
         ((*BytesNotWritten > 0) || (MoreProcessing)));
         DataEntry = CONTAINING_RECORD(NpGetNextRealDataQueueEntry(WriteQueue, List),
                                       NP_DATA_QUEUE_ENTRY,
                                       QueueEntry))
    {
        DataSize = DataEntry->DataSize;

        IoStack = IoGetCurrentIrpStackLocation(DataEntry->Irp);

        if (IoStack->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL &&
            IoStack->Parameters.FileSystemControl.FsControlCode == FSCTL_PIPE_INTERNAL_READ_OVFLOW &&
            (DataSize < OutBufferSize || MoreProcessing))
        {
            WriteIrp = NpRemoveDataQueueEntry(WriteQueue, TRUE, List);
            if (WriteIrp)
            {
                WriteIrp->IoStatus.Status = STATUS_BUFFER_OVERFLOW;
                InsertTailList(List, &WriteIrp->Tail.Overlay.ListEntry);
            }
            continue;
        }

        if (DataEntry->DataEntryType == Unbuffered)
        {
            DataEntry->Irp->Overlay.AllocationSize.QuadPart = 0;
        }

        BufferSize = *BytesNotWritten;
        if (BufferSize >= DataSize) BufferSize = DataSize;

        if (DataEntry->DataEntryType != Unbuffered && BufferSize)
        {
            Buffer = ExAllocatePoolWithTag(NonPagedPool, BufferSize, NPFS_DATA_ENTRY_TAG);
            if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
            AllocatedBuffer = TRUE;
        }
        else
        {
            Buffer = DataEntry->Irp->AssociatedIrp.SystemBuffer;
            AllocatedBuffer = FALSE;
        }

        _SEH2_TRY
        {
            RtlCopyMemory(Buffer,
                          (PVOID)((ULONG_PTR)OutBuffer + OutBufferSize - *BytesNotWritten),
                          BufferSize);
        }
        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
        {
            if (AllocatedBuffer) ExFreePool(Buffer);
            _SEH2_YIELD(return _SEH2_GetExceptionCode());
        }
        _SEH2_END;

        if (!HaveContext)
        {
            HaveContext = TRUE;
            Status = NpGetClientSecurityContext(NamedPipeEnd, Ccb, Thread, &ClientContext);
            if (!NT_SUCCESS(Status))
            {
                if (AllocatedBuffer) ExFreePool(Buffer);
                return Status;
            }

            if (ClientContext)
            {
                NpFreeClientSecurityContext(Ccb->ClientContext);
                Ccb->ClientContext = ClientContext;
            }
        }

        WriteIrp = NpRemoveDataQueueEntry(WriteQueue, TRUE, List);
        if (WriteIrp)
        {
            *BytesNotWritten -= BufferSize;
            WriteIrp->IoStatus.Information = BufferSize;

            if (AllocatedBuffer)
            {
                WriteIrp->AssociatedIrp.SystemBuffer = Buffer;
                WriteIrp->Flags |= IRP_DEALLOCATE_BUFFER  | IRP_BUFFERED_IO | IRP_INPUT_OPERATION;
            }

            if (!*BytesNotWritten)
            {
                MoreProcessing = FALSE;
                WriteIrp->IoStatus.Status = STATUS_SUCCESS;
                InsertTailList(List, &WriteIrp->Tail.Overlay.ListEntry);
                continue;
            }

            if (Mode == FILE_PIPE_MESSAGE_MODE)
            {
                WriteIrp->IoStatus.Status = STATUS_BUFFER_OVERFLOW;
            }
            else
            {
                WriteIrp->IoStatus.Status = STATUS_SUCCESS;
            }

            InsertTailList(List, &WriteIrp->Tail.Overlay.ListEntry);
        }
        else if (AllocatedBuffer)
        {
            ExFreePool(Buffer);
        }
    }

    if (*BytesNotWritten > 0 || MoreProcessing)
    {
        ASSERT(WriteQueue->QueueState != ReadEntries);
        Status = STATUS_MORE_PROCESSING_REQUIRED;
    }
    else
    {
        Status = STATUS_SUCCESS;
    }

    return Status;
}

/* EOF */