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

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

#include "npfs.h"

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

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

WCHAR NpRootDCBName[] = L"\\";
PNP_VCB NpVcb;

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

RTL_GENERIC_COMPARE_RESULTS
NTAPI
NpEventTableCompareRoutine(IN PRTL_GENERIC_TABLE Table,
                           IN PVOID FirstStruct,
                           IN PVOID SecondStruct)
{
    UNIMPLEMENTED;
    return GenericEqual;
}

PVOID
NTAPI
NpEventTableAllocate(IN PRTL_GENERIC_TABLE Table,
                     IN CLONG ByteSize)
{
    UNIMPLEMENTED;
    return NULL;
}

VOID
NTAPI
NpEventTableDeallocate(IN PRTL_GENERIC_TABLE Table,
                       IN PVOID Buffer)
{
    UNIMPLEMENTED;
}

BOOLEAN
NTAPI
NpDeleteEventTableEntry(IN PRTL_GENERIC_TABLE Table,
                        IN PVOID Buffer)
{
    if (!Buffer) return FALSE;

    ObDereferenceObject(((PNP_EVENT_BUFFER)Buffer)->Event);
    return RtlDeleteElementGenericTable(Table, Buffer);
}

VOID
NTAPI
NpDeleteFcb(IN PNP_FCB Fcb,
            IN PLIST_ENTRY ListEntry)
{
    PNP_DCB Dcb;
    PAGED_CODE();

    Dcb = Fcb->ParentDcb;
    if (Fcb->CurrentInstances) NpBugCheck(0, 0, 0);

    NpCancelWaiter(&NpVcb->WaitQueue,
                   &Fcb->FullName,
                   STATUS_OBJECT_NAME_NOT_FOUND,
                   ListEntry);

    RemoveEntryList(&Fcb->DcbEntry);

    if (Fcb->SecurityDescriptor)
    {
        ObDereferenceSecurityDescriptor(Fcb->SecurityDescriptor, 1);
    }

    RtlRemoveUnicodePrefix(&NpVcb->PrefixTable, &Fcb->PrefixTableEntry);
    ExFreePool(Fcb->FullName.Buffer);
    ExFreePool(Fcb);
    NpCheckForNotify(Dcb, TRUE, ListEntry);
}

VOID
NTAPI
NpDeleteCcb(IN PNP_CCB Ccb,
            IN PLIST_ENTRY ListEntry)
{
    PNP_ROOT_DCB_FCB RootDcbCcb;
    PAGED_CODE();

    RootDcbCcb = (PNP_ROOT_DCB_FCB)Ccb;
    if (Ccb->NodeType == NPFS_NTC_CCB)
    {
        RemoveEntryList(&Ccb->CcbEntry);
        --Ccb->Fcb->CurrentInstances;

        NpDeleteEventTableEntry(&NpVcb->EventTable,
                                Ccb->NonPagedCcb->EventBuffer[FILE_PIPE_CLIENT_END]);
        NpDeleteEventTableEntry(&NpVcb->EventTable,
                                Ccb->NonPagedCcb->EventBuffer[FILE_PIPE_SERVER_END]);
        NpUninitializeDataQueue(&Ccb->DataQueue[FILE_PIPE_INBOUND]);
        NpUninitializeDataQueue(&Ccb->DataQueue[FILE_PIPE_OUTBOUND]);
        NpCheckForNotify(Ccb->Fcb->ParentDcb, FALSE, ListEntry);
        ExDeleteResourceLite(&Ccb->NonPagedCcb->Lock);
        NpUninitializeSecurity(Ccb);
        if (Ccb->ClientSession)
        {
            ExFreePool(Ccb->ClientSession);
            Ccb->ClientSession = NULL;
        }
        ExFreePool(Ccb->NonPagedCcb);
    }
    else if (RootDcbCcb->NodeType == NPFS_NTC_ROOT_DCB_CCB && RootDcbCcb->Unknown)
    {
        ExFreePool(RootDcbCcb->Unknown);
    }

    ExFreePool(Ccb);
}

VOID
NTAPI
NpInitializeVcb(VOID)
{
    PAGED_CODE();

    RtlZeroMemory(NpVcb, sizeof(*NpVcb));

    NpVcb->NodeType = NPFS_NTC_VCB;
    RtlInitializeUnicodePrefix(&NpVcb->PrefixTable);
    ExInitializeResourceLite(&NpVcb->Lock);
    RtlInitializeGenericTable(&NpVcb->EventTable,
                              NpEventTableCompareRoutine,
                              NpEventTableAllocate,
                              NpEventTableDeallocate,
                              0);
    NpInitializeWaitQueue(&NpVcb->WaitQueue);
}

NTSTATUS
NTAPI
NpCreateRootDcbCcb(IN PNP_ROOT_DCB_FCB *NewRootCcb)
{
    PNP_ROOT_DCB_FCB RootCcb;
    PAGED_CODE();

    RootCcb = ExAllocatePoolWithTag(PagedPool, sizeof(*RootCcb), NPFS_ROOT_DCB_CCB_TAG);
    if (!RootCcb) return STATUS_INSUFFICIENT_RESOURCES;

    RtlZeroMemory(RootCcb, sizeof(*RootCcb));
    RootCcb->NodeType = NPFS_NTC_ROOT_DCB_CCB;
    *NewRootCcb = RootCcb;
    return STATUS_SUCCESS;
}

NTSTATUS
NTAPI
NpCreateRootDcb(VOID)
{
    PNP_DCB Dcb;
    PAGED_CODE();

    if (NpVcb->RootDcb)
    {
        NpBugCheck(0, 0, 0);
    }

    NpVcb->RootDcb = ExAllocatePoolWithTag(PagedPool, sizeof(*Dcb), NPFS_DCB_TAG);
    if (!NpVcb->RootDcb)
    {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    Dcb = NpVcb->RootDcb;
    RtlZeroMemory(Dcb, sizeof(*Dcb));
    Dcb->NodeType = NPFS_NTC_ROOT_DCB;

    InitializeListHead(&Dcb->DcbEntry);
    InitializeListHead(&Dcb->NotifyList);
    InitializeListHead(&Dcb->NotifyList2);
    InitializeListHead(&Dcb->FcbList);

    Dcb->FullName.Buffer = NpRootDCBName;
    Dcb->FullName.Length = sizeof(NpRootDCBName) - sizeof(UNICODE_NULL);
    Dcb->FullName.MaximumLength = sizeof(NpRootDCBName);

    Dcb->ShortName.Length = Dcb->FullName.Length;
    Dcb->ShortName.MaximumLength = Dcb->FullName.MaximumLength;
    Dcb->ShortName.Buffer = Dcb->FullName.Buffer;

    if (!RtlInsertUnicodePrefix(&NpVcb->PrefixTable,
                                &Dcb->FullName,
                                &Dcb->PrefixTableEntry))
    {
        NpBugCheck(0, 0, 0);
    }

    return STATUS_SUCCESS;
}

NTSTATUS
NTAPI
NpCreateFcb(IN PNP_DCB Dcb,
            IN PUNICODE_STRING PipeName,
            IN ULONG MaximumInstances,
            IN LARGE_INTEGER Timeout,
            IN USHORT NamedPipeConfiguration,
            IN USHORT NamedPipeType,
            OUT PNP_FCB *NewFcb)
{
    PNP_FCB Fcb;
    BOOLEAN RootPipe;
    PWCHAR NameBuffer;
    USHORT Length, MaximumLength;
    PAGED_CODE();

    Length = PipeName->Length;
    MaximumLength = Length + sizeof(UNICODE_NULL);

    if ((Length < sizeof(WCHAR)) || (MaximumLength < Length))
    {
        return STATUS_INVALID_PARAMETER;
    }

    RootPipe = FALSE;
    if (PipeName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR)
    {
        Length += sizeof(OBJ_NAME_PATH_SEPARATOR);
        MaximumLength += sizeof(OBJ_NAME_PATH_SEPARATOR);
        RootPipe = TRUE;
        if (MaximumLength < sizeof(WCHAR))
        {
            return STATUS_INVALID_PARAMETER;
        }
    }

    Fcb = ExAllocatePoolWithTag(PagedPool, sizeof(*Fcb), NPFS_FCB_TAG);
    if (!Fcb) return STATUS_INSUFFICIENT_RESOURCES;

    RtlZeroMemory(Fcb, sizeof(*Fcb));
    Fcb->MaximumInstances = MaximumInstances;
    Fcb->Timeout = Timeout;
    Fcb->NodeType = NPFS_NTC_FCB;
    Fcb->ParentDcb = Dcb;
    InitializeListHead(&Fcb->CcbList);

    NameBuffer = ExAllocatePoolWithTag(PagedPool,
                                       MaximumLength,
                                       NPFS_NAME_BLOCK_TAG);
    if (!NameBuffer)
    {
        ExFreePool(Fcb);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    InsertTailList(&Dcb->FcbList, &Fcb->DcbEntry);

    if (RootPipe)
    {
        NameBuffer[0] = OBJ_NAME_PATH_SEPARATOR;
        RtlCopyMemory(NameBuffer + 1,
                      PipeName->Buffer,
                      PipeName->Length);
    }
    else
    {
        RtlCopyMemory(NameBuffer,
                      PipeName->Buffer,
                      PipeName->Length);
    }

    NameBuffer[Length / sizeof(WCHAR)] = UNICODE_NULL;

    Fcb->FullName.Length = Length;
    Fcb->FullName.MaximumLength = MaximumLength;
    Fcb->FullName.Buffer = NameBuffer;

    Fcb->ShortName.MaximumLength = Length;
    Fcb->ShortName.Length = Length - sizeof(OBJ_NAME_PATH_SEPARATOR);
    Fcb->ShortName.Buffer = NameBuffer + 1;

    if (!RtlInsertUnicodePrefix(&NpVcb->PrefixTable,
                                &Fcb->FullName,
                                &Fcb->PrefixTableEntry))
    {
        NpBugCheck(0, 0, 0);
    }

    Fcb->NamedPipeConfiguration = NamedPipeConfiguration;
    Fcb->NamedPipeType = NamedPipeType;
    *NewFcb = Fcb;
    return STATUS_SUCCESS;
}

NTSTATUS
NTAPI
NpCreateCcb(IN PNP_FCB Fcb,
            IN PFILE_OBJECT FileObject,
            IN UCHAR State,
            IN UCHAR ReadMode,
            IN UCHAR CompletionMode,
            IN ULONG InQuota,
            IN ULONG OutQuota,
            OUT PNP_CCB *NewCcb)
{
    PNP_CCB Ccb;
    PNP_NONPAGED_CCB CcbNonPaged;
    NTSTATUS Status;
    PAGED_CODE();

    Ccb = ExAllocatePoolWithTag(PagedPool, sizeof(*Ccb), NPFS_CCB_TAG);
    if (!Ccb) return STATUS_INSUFFICIENT_RESOURCES;

    CcbNonPaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(*CcbNonPaged), NPFS_CCB_TAG);
    if (!CcbNonPaged)
    {
        ExFreePool(Ccb);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(CcbNonPaged, sizeof(*CcbNonPaged));
    CcbNonPaged->NodeType = NPFS_NTC_NONPAGED_CCB;

    RtlZeroMemory(Ccb, sizeof(*Ccb));
    Ccb->NodeType = NPFS_NTC_CCB;
    Ccb->NonPagedCcb = CcbNonPaged;
    Ccb->FileObject[FILE_PIPE_SERVER_END] = FileObject;
    Ccb->Fcb = Fcb;
    Ccb->NamedPipeState = State;
    Ccb->ReadMode[FILE_PIPE_SERVER_END] = ReadMode;
    Ccb->CompletionMode[FILE_PIPE_SERVER_END] = CompletionMode;

    Status = NpInitializeDataQueue(&Ccb->DataQueue[FILE_PIPE_INBOUND], InQuota);
    if (!NT_SUCCESS(Status))
    {
        ExFreePool(CcbNonPaged);
        ExFreePool(Ccb);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    Status = NpInitializeDataQueue(&Ccb->DataQueue[FILE_PIPE_OUTBOUND], OutQuota);
    if (!NT_SUCCESS(Status))
    {
        NpUninitializeDataQueue(&Ccb->DataQueue[FILE_PIPE_INBOUND]);
        ExFreePool(CcbNonPaged);
        ExFreePool(Ccb);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    InsertTailList(&Fcb->CcbList, &Ccb->CcbEntry);

    Fcb->CurrentInstances++;
    Fcb->ServerOpenCount++;
    InitializeListHead(&Ccb->IrpList);
    ExInitializeResourceLite(&Ccb->NonPagedCcb->Lock);
    *NewCcb = Ccb;
    return STATUS_SUCCESS;
}

/* EOF */