/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    StrucSup.c

Abstract:

    This module implements the Named Pipe in-memory data structure manipulation
    routines

Author:

    Gary Kimura     [GaryKi]    22-Jan-1990

Revision History:

--*/

#include "NpProcs.h"

//
//  The Bug check file id for this module
//

#define BugCheckFileId                   (NPFS_BUG_CHECK_STRUCSUP)

//
//  The debug trace level
//

#define Dbg                              (DEBUG_TRACE_STRUCSUP)


WCHAR NpRootDCBName[] = L"\\";

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, NpInitializeVcb)
#pragma alloc_text(INIT, NpCreateRootDcb)
#pragma alloc_text(PAGE, NpCreateCcb)
#pragma alloc_text(PAGE, NpCreateFcb)
#pragma alloc_text(PAGE, NpCreateRootDcbCcb)
#pragma alloc_text(PAGE, NpDeleteCcb)
#pragma alloc_text(PAGE, NpDeleteFcb)
#pragma alloc_text(PAGE, NpDeleteRootDcb)
#pragma alloc_text(PAGE, NpDeleteVcb)
#endif


VOID
NpInitializeVcb (
    VOID
    )

/*++

Routine Description:

    This routine initializes new Vcb record. The Vcb record "hangs" off the
    end of the Npfs device object and must be allocated by our caller.

Arguments:

    None.

Return Value:

    None.

--*/

{

    PAGED_CODE();

    DebugTrace(+1, Dbg, "NpInitializeVcb, Vcb = %08lx\n", NpVcb);

    //
    //  We start by first zeroing out all of the VCB, this will guarantee
    //  that any stale data is wiped clean
    //

    RtlZeroMemory( NpVcb, sizeof(VCB) );

    //
    //  Set the proper node type code and node byte size
    //

    NpVcb->NodeTypeCode = NPFS_NTC_VCB;

    //
    //  Initialize the Prefix table
    //

    RtlInitializeUnicodePrefix( &NpVcb->PrefixTable );

    //
    //  Initialize the resource variable for the Vcb
    //

    ExInitializeResourceLite( &NpVcb->Resource );

    //
    //  Initialize the event table
    //

    NpInitializeEventTable( &NpVcb->EventTable );

    //
    //  Initialize the wait queue
    //

    NpInitializeWaitQueue( &NpVcb->WaitQueue );


    //
    //  return and tell the caller
    //

    return;
}


VOID
NpDeleteVcb (
    IN PLIST_ENTRY DeferredList
    )

/*++

Routine Description:

    This routine removes the Vcb record from our in-memory data
    structures.  It also will remove all associated underlings
    (i.e., FCB records).

Arguments:

    DeferredList - List of deferred IRPs to complete once we release locks

Return Value:

    None

--*/

{
    PAGED_CODE();

    DebugTrace(+1, Dbg, "NpDeleteVcb, Vcb = %08lx\n", NpVcb);

    //
    //  Make sure the open count is zero, and the open underling count
    //  is also zero.
    //

    if (NpVcb->OpenCount != 0) {

        DebugDump("Error deleting Vcb\n", 0, NpVcb);
        NpBugCheck( 0, 0, 0 );
    }

    //
    //  Remove the Root Dcb
    //

    if (NpVcb->RootDcb != NULL) {

        NpDeleteRootDcb( NpVcb->RootDcb, DeferredList );
    }

    //
    //  Uninitialize the resource variable for the Vcb
    //

    ExDeleteResourceLite( &NpVcb->Resource );

    //
    //  Uninitialize the event table
    //

    NpUninitializeEventTable( &NpVcb->EventTable );

    //
    //  Uninitialize the wait queue
    //

    NpUninitializeWaitQueue( &NpVcb->WaitQueue );

    //
    //  And zero out the Vcb, this will help ensure that any stale data is
    //  wiped clean
    //

    RtlZeroMemory( NpVcb, sizeof(VCB) );

    //
    //  return and tell the caller
    //

    DebugTrace(-1, Dbg, "NpDeleteVcb -> VOID\n", 0);

    return;
}


NTSTATUS
NpCreateRootDcb (
    VOID
    )

/*++

Routine Description:

    This routine allocates, initializes, and inserts a new root DCB record
    into the in memory data structure.

Arguments:

    None.

Return Value:

    None.

--*/

{
    PAGED_CODE();

    DebugTrace(+1, Dbg, "NpCreateRootDcb, Vcb = %08lx\n", NpVcb);

    //
    //  Make sure we don't already have a root dcb for this vcb
    //

    if (NpVcb->RootDcb != NULL) {

        DebugDump("Error trying to create multiple root dcbs\n", 0, NpVcb);
        NpBugCheck( 0, 0, 0 );
    }

    //
    //  Allocate a new DCB and zero it out
    //

    NpVcb->RootDcb = NpAllocatePagedPool ( sizeof(DCB), 'DFpN' );

    if (NpVcb->RootDcb == NULL) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory( NpVcb->RootDcb, sizeof(DCB));

    //
    //  Set the proper node type code and node byte size
    //

    NpVcb->RootDcb->NodeTypeCode = NPFS_NTC_ROOT_DCB;

    //
    //  The root Dcb has an empty parent dcb links field
    //

    InitializeListHead( &NpVcb->RootDcb->ParentDcbLinks );

    //
    //  initialize the notify queues, and the parent dcb queue.
    //

    InitializeListHead( &NpVcb->RootDcb->Specific.Dcb.NotifyFullQueue );
    InitializeListHead( &NpVcb->RootDcb->Specific.Dcb.NotifyPartialQueue );
    InitializeListHead( &NpVcb->RootDcb->Specific.Dcb.ParentDcbQueue );

    NpVcb->RootDcb->FullFileName.Buffer = NpRootDCBName;
    NpVcb->RootDcb->FullFileName.Length = sizeof (NpRootDCBName) - sizeof (UNICODE_NULL);
    NpVcb->RootDcb->FullFileName.MaximumLength = sizeof (NpRootDCBName);

    //
    // Last file name is the same as file name.
    //
    NpVcb->RootDcb->LastFileName = NpVcb->RootDcb->FullFileName;

    //
    //  Insert this dcb into the prefix table
    //

    if (!RtlInsertUnicodePrefix( &NpVcb->PrefixTable,
                                 &NpVcb->RootDcb->FullFileName,
                                 &NpVcb->RootDcb->PrefixTableEntry )) {

        DebugDump("Error trying to insert root dcb into prefix table\n", 0, NpVcb);
        NpBugCheck( 0, 0, 0 );
    }

    DebugTrace(-1, Dbg, "NpCreateRootDcb -> %8lx\n", NpVcb->RootDcb);

    return STATUS_SUCCESS;
}


VOID
NpDeleteRootDcb (
    IN PROOT_DCB RootDcb,
    IN PLIST_ENTRY DeferredList
    )

/*++

Routine Description:

    This routine deallocates and removes the ROOT DCB record
    from our in-memory data structures.  It also will remove all
    associated underlings (i.e., Notify queues and child FCB records).

Arguments:

    RootDcb - Supplies the ROOT DCB to be removed

    DeferredList - List of IRPs to complete after we release locks

Return Value:

    None

--*/

{
    PLIST_ENTRY Links;
    PIRP Irp;

    PAGED_CODE();

    DebugTrace(+1, Dbg, "NpDeleteRootDcb, RootDcb = %08lx\n", RootDcb);

    //
    //  We can only delete this record if the open count is zero.
    //

    if (RootDcb->OpenCount != 0) {

        DebugDump("Error deleting RootDcb, Still Open\n", 0, RootDcb);
        NpBugCheck( 0, 0, 0 );
    }

    //
    //  Remove every Notify Irp from the two notify queues
    //

    while (!IsListEmpty(&RootDcb->Specific.Dcb.NotifyFullQueue)) {

        Links = RemoveHeadList( &RootDcb->Specific.Dcb.NotifyFullQueue );

        Irp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry );

        NpDeferredCompleteRequest( Irp, STATUS_FILE_FORCED_CLOSED, DeferredList );
    }

    while (!IsListEmpty(&RootDcb->Specific.Dcb.NotifyPartialQueue)) {

        Links = RemoveHeadList( &RootDcb->Specific.Dcb.NotifyPartialQueue );

        Irp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry );

        NpDeferredCompleteRequest( Irp, STATUS_FILE_FORCED_CLOSED, DeferredList );
    }

    //
    //  We can only be removed if the no other FCB have us referenced
    //  as a their parent DCB.
    //

    if (!IsListEmpty(&RootDcb->Specific.Dcb.ParentDcbQueue)) {

        DebugDump("Error deleting RootDcb\n", 0, RootDcb);
        NpBugCheck( 0, 0, 0 );
    }

    //
    //  Remove the entry from the prefix table, and then remove the full
    //  file name
    //

    RtlRemoveUnicodePrefix( &NpVcb->PrefixTable, &RootDcb->PrefixTableEntry );

    //
    //  Finally deallocate the Dcb record
    //

    NpFreePool( RootDcb );

    //
    //  and return to our caller
    //

    DebugTrace(-1, Dbg, "NpDeleteRootDcb -> VOID\n", 0);

    return;
}


NTSTATUS
NpCreateFcb (
    IN PDCB ParentDcb,
    IN PUNICODE_STRING FileName,
    IN ULONG MaximumInstances,
    IN LARGE_INTEGER DefaultTimeOut,
    IN NAMED_PIPE_CONFIGURATION NamedPipeConfiguration,
    IN NAMED_PIPE_TYPE NamedPipeType,
    OUT PFCB *ppFcb
    )

/*++

Routine Description:

    This routine allocates, initializes, and inserts a new Fcb record into
    the in memory data structures.

Arguments:

    ParentDcb - Supplies the parent dcb that the new FCB is under.

    FileName - Supplies the file name of the file relative to the directory
        it's in (e.g., the file \config.sys is called "CONFIG.SYS" without
        the preceding backslash).

    MaximumInstances - Supplies the maximum number of pipe instances

    DefaultTimeOut - Supplies the default wait time out value

    NamedPipeConfiguration - Supplies our initial pipe configuration

    NamedPipeType - Supplies our initial pipe type

Return Value:

    PFCB - Returns a pointer to the newly allocated FCB

--*/

{
    PFCB Fcb;
    PWCH Name;
    USHORT Length;
    USHORT MaximumLength;
    BOOLEAN AddBackSlash = FALSE;
    ULONG i;

    PAGED_CODE();

    DebugTrace(+1, Dbg, "NpCreateFcb\n", 0);

    Length = FileName->Length;
    MaximumLength = Length + sizeof (WCHAR);
    if (Length < sizeof (WCHAR) || MaximumLength < Length) {
        return STATUS_INVALID_PARAMETER;
    }
    if (FileName->Buffer[0] != '\\') {
        AddBackSlash = TRUE;
        MaximumLength += sizeof (WCHAR);
        if (MaximumLength < sizeof (WCHAR)) {
            return STATUS_INVALID_PARAMETER;
        }
    }

    //
    //  Allocate a new FCB record and zero it out
    //

    Fcb = NpAllocatePagedPoolWithQuota( sizeof(FCB), 'FfpN' );
    if (Fcb == NULL) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory( Fcb, sizeof(FCB) );

    //
    //  Set the proper node type code and node byte size
    //

    Fcb->NodeTypeCode = NPFS_NTC_FCB;

    //
    //  Point back to our parent dcb
    //

    Fcb->ParentDcb = ParentDcb;

    //
    //  Set our maximum instances, default timeout, and initialize our
    //  ccb queue
    //

    Fcb->Specific.Fcb.MaximumInstances = MaximumInstances;
    Fcb->Specific.Fcb.DefaultTimeOut = DefaultTimeOut;
    InitializeListHead( &Fcb->Specific.Fcb.CcbQueue );

    //
    //  set the file name.  We need to do this from nonpaged pool because
    //  cancel waiters works while holding a spinlock and uses the fcb name
    //


    Name = NpAllocateNonPagedPoolWithQuota( MaximumLength, 'nFpN' );
    if (Name == NULL) {
        NpFreePool (Fcb);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    //  Insert this fcb into our parent dcb's queue
    //

    InsertTailList( &ParentDcb->Specific.Dcb.ParentDcbQueue,
                    &Fcb->ParentDcbLinks );

    i = 0;
    if (AddBackSlash == TRUE) {
        i++;
        Name[0] = '\\';
    }
    RtlCopyMemory( &Name[i], FileName->Buffer, Length );
    Name[ i + Length / sizeof(WCHAR) ] = L'\0';

    Fcb->FullFileName.Length = MaximumLength - sizeof (WCHAR);
    Fcb->FullFileName.MaximumLength = MaximumLength ;
    Fcb->FullFileName.Buffer = Name;

    Fcb->LastFileName.Length = MaximumLength - 2*sizeof (WCHAR);
    Fcb->LastFileName.MaximumLength = MaximumLength - sizeof (WCHAR);
    Fcb->LastFileName.Buffer = &Name[1];

    //
    //  Insert this Fcb into the prefix table
    //

    if (!RtlInsertUnicodePrefix( &NpVcb->PrefixTable,
                                 &Fcb->FullFileName,
                                 &Fcb->PrefixTableEntry )) {

        DebugDump("Error trying to name into prefix table\n", 0, Fcb);
        NpBugCheck( 0, 0, 0 );
    }
    //
    //  Set the configuration and pipe type
    //

    Fcb->Specific.Fcb.NamedPipeConfiguration = NamedPipeConfiguration;
    Fcb->Specific.Fcb.NamedPipeType = NamedPipeType;

    DebugTrace(-1, Dbg, "NpCreateFcb -> %08lx\n", Fcb);

    //
    //  return and tell the caller
    //
    *ppFcb = Fcb;
    return STATUS_SUCCESS;
}


VOID
NpDeleteFcb (
    IN PFCB Fcb,
    IN PLIST_ENTRY DeferredList
    )

/*++

Routine Description:

    This routine deallocates and removes an FCB
    from our in-memory data structures.  It also will remove all
    associated underlings.

Arguments:

    Fcb - Supplies the FCB to be removed

    DeferredList - List of IRPs to complete later

Return Value:

    None

--*/

{
    PDCB ParentDcb;

    PAGED_CODE();

    DebugTrace(+1, Dbg, "NpDeleteFcb, Fcb = %08lx\n", Fcb);

    ParentDcb = Fcb->ParentDcb;

    //
    //  We can only delete this record if the open count is zero.
    //

    if (Fcb->OpenCount != 0) {

        DebugDump("Error deleting Fcb, Still Open\n", 0, Fcb);
        NpBugCheck( 0, 0, 0 );
    }

    //
    // Complete any waiters waiting on this server. All instances have now gone.
    //
    NpCancelWaiter (&NpVcb->WaitQueue,
                    &Fcb->FullFileName,
                    STATUS_OBJECT_NAME_NOT_FOUND,
                    DeferredList);

    //
    //  Remove ourselves from our parents Dcb queue
    //

    RemoveEntryList( &(Fcb->ParentDcbLinks) );

    //
    //  If there is a security descriptor on the named pipe then deassign it
    //

    if (Fcb->SecurityDescriptor != NULL) {

        ObDereferenceSecurityDescriptor( Fcb->SecurityDescriptor, 1 );
    }

    //
    //  Remove the entry from the prefix table, and then remove the full
    //  file name
    //

    RtlRemoveUnicodePrefix( &NpVcb->PrefixTable, &Fcb->PrefixTableEntry );
    NpFreePool( Fcb->FullFileName.Buffer );

    //
    //  Finally deallocate the Fcb record
    //

    NpFreePool( Fcb );

    //
    //  Check for any outstanding notify irps
    //

    NpCheckForNotify( ParentDcb, TRUE, DeferredList );

    //
    //  and return to our caller
    //

    DebugTrace(-1, Dbg, "NpDeleteFcb -> VOID\n", 0);

    return;
}


NTSTATUS
NpCreateCcb (
    IN  PFCB Fcb,
    IN  PFILE_OBJECT ServerFileObject,
    IN  NAMED_PIPE_STATE NamedPipeState,
    IN  READ_MODE ServerReadMode,
    IN  COMPLETION_MODE ServerCompletionMode,
    IN  ULONG InBoundQuota,
    IN  ULONG OutBoundQuota,
    OUT PCCB *ppCcb
    )

/*++

Routine Description:

    This routine creates a new CCB record

Arguments:

    Fcb - Supplies a pointer to the fcb we are attached to

    ServerFileObject - Supplies a pointer to the file object for the server
        end

    NamedPipeState - Supplies the initial pipe state

    ServerReadMode - Supplies our initial read mode

    ServerCompletionMode - Supplies our initial completion mode

    CreatorProcess - Supplies a pointer to our creator process

    InBoundQuota - Supplies the initial inbound quota

    OutBoundQuota - Supplies the initial outbound quota

Return Value:

    PCCB - returns a pointer to the newly allocate CCB

--*/

{
    PCCB Ccb;
    NTSTATUS status;

    PAGED_CODE();

    DebugTrace(+1, Dbg, "NpCreateCcb\n", 0);

    //
    //  Allocate a new CCB record (paged and nonpaged), and zero them out
    //

    Ccb = NpAllocatePagedPoolWithQuotaCold( sizeof(CCB), 'cFpN' );
    if (Ccb == NULL) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory( Ccb, sizeof(CCB) );

    Ccb->NonpagedCcb = NpAllocateNonPagedPoolWithQuota( sizeof(NONPAGED_CCB), 'cFpN');
    if (Ccb->NonpagedCcb == NULL) {
        NpFreePool (Ccb);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory( Ccb->NonpagedCcb, sizeof(NONPAGED_CCB) );

    //
    //  Set the proper node type code and node byte size
    //

    Ccb->NodeTypeCode = NPFS_NTC_CCB;

    Ccb->Fcb = Fcb;

    //
    //  Set the server file object
    //

    Ccb->FileObject[ FILE_PIPE_SERVER_END ] = ServerFileObject;

    //
    //  Initialize the nonpaged ccb
    //
    //  Set the proper node type code and node byte size
    //

    Ccb->NonpagedCcb->NodeTypeCode = NPFS_NTC_NONPAGED_CCB;

    //
    //  Set the pipe state, read mode, completion mode, and creator process
    //

    Ccb->NamedPipeState = (UCHAR) NamedPipeState;
    Ccb->ReadCompletionMode[ FILE_PIPE_SERVER_END ].ReadMode       = (UCHAR) ServerReadMode;
    Ccb->ReadCompletionMode[ FILE_PIPE_SERVER_END ].CompletionMode = (UCHAR) ServerCompletionMode;

    //
    //  Initialize the data queues
    //

    status = NpInitializeDataQueue( &Ccb->DataQueue[ FILE_PIPE_INBOUND ],
                                    InBoundQuota );
    if (!NT_SUCCESS (status)) {
        NpFreePool (Ccb->NonpagedCcb);
        NpFreePool (Ccb);
        return status;
    }

    status = NpInitializeDataQueue( &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ],
                                    OutBoundQuota );
    if (!NT_SUCCESS (status)) {
        NpUninitializeDataQueue (&Ccb->DataQueue[ FILE_PIPE_INBOUND ]);
        NpFreePool (Ccb->NonpagedCcb);
        NpFreePool (Ccb);
    }

    //
    //  Insert ourselves in the list of ccb for the fcb, and increment
    //  the reference count in the fcb.
    //
    InsertTailList( &Fcb->Specific.Fcb.CcbQueue, &Ccb->CcbLinks );
    Fcb->OpenCount += 1;
    Fcb->ServerOpenCount += 1;

    //
    //  Initialize the listening queue
    //

    InitializeListHead( &Ccb->ListeningQueue );

    ExInitializeResourceLite(&Ccb->NonpagedCcb->Resource);

    //
    //  return and tell the caller
    //

    *ppCcb = Ccb;
    return STATUS_SUCCESS;
}


NTSTATUS
NpCreateRootDcbCcb (
    OUT PROOT_DCB_CCB *ppCcb
    )

/*++

Routine Description:

    This routine creates a new ROOT DCB CCB record

Arguments:

Return Value:

    PROOT_DCB_CCB - returns a pointer to the newly allocate ROOT_DCB_CCB

--*/

{
    PROOT_DCB_CCB Ccb;

    PAGED_CODE();

    DebugTrace(+1, Dbg, "NpCreateRootDcbCcb\n", 0);

    //
    //  Allocate a new ROOT DCB CCB record, and zero it out
    //

    Ccb = NpAllocatePagedPoolWithQuotaCold ( sizeof(ROOT_DCB_CCB), 'CFpN' );

    if (Ccb == NULL) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory( Ccb, sizeof(ROOT_DCB_CCB) );

    //
    //  Set the proper node type code and node byte size
    //

    Ccb->NodeTypeCode = NPFS_NTC_ROOT_DCB_CCB;

    //
    //  return and tell the caller
    //

    *ppCcb = Ccb;

    DebugTrace(-1, Dbg, "NpCreateRootDcbCcb -> %08lx\n", Ccb);

    return STATUS_SUCCESS;
}


VOID
NpDeleteCcb (
    IN PCCB Ccb,
    IN PLIST_ENTRY DeferredList
    )

/*++

Routine Description:

    This routine deallocates and removes the specified CCB record
    from the our in memory data structures

Arguments:

    Ccb - Supplies the CCB to remove

    DeferredList - List of IRPs to complete once we drop locks

Return Value:

    None

--*/

{
    PAGED_CODE();

    DebugTrace(+1, Dbg, "NpDeleteCcb, Ccb = %08lx\n", Ccb);

    //
    //  Case on the type of ccb we are deleting
    //

    switch (Ccb->NodeTypeCode) {

    case NPFS_NTC_CCB:

        RemoveEntryList (&Ccb->CcbLinks);
        Ccb->Fcb->OpenCount -= 1;

        NpDeleteEventTableEntry (&NpVcb->EventTable,
                                 Ccb->NonpagedCcb->EventTableEntry[FILE_PIPE_CLIENT_END]);

        NpDeleteEventTableEntry (&NpVcb->EventTable,
                                 Ccb->NonpagedCcb->EventTableEntry[FILE_PIPE_SERVER_END]);

        NpUninitializeDataQueue (&Ccb->DataQueue[FILE_PIPE_INBOUND]);

        NpUninitializeDataQueue (&Ccb->DataQueue[FILE_PIPE_OUTBOUND]);

        //
        //  Check for any outstanding notify irps
        //

        NpCheckForNotify (Ccb->Fcb->ParentDcb, FALSE, DeferredList);

        //
        // Delete the resource
        //
        ExDeleteResourceLite (&Ccb->NonpagedCcb->Resource);

        //
        //  Free up the security fields in the ccb and then free the nonpaged
        //  ccb
        //

        NpUninitializeSecurity (Ccb);

        //
        // Free up client info if it was allocated.
        //
        if (Ccb->ClientInfo != NULL) {
            NpFreePool (Ccb->ClientInfo);
            Ccb->ClientInfo = NULL;
        }

        NpFreePool (Ccb->NonpagedCcb);

        break;

    case NPFS_NTC_ROOT_DCB_CCB:

        if (((PROOT_DCB_CCB)Ccb)->QueryTemplate != NULL) {

            NpFreePool (((PROOT_DCB_CCB)Ccb)->QueryTemplate);
        }
        break;
    }

    //  Deallocate the Ccb record
    //

    NpFreePool (Ccb);

    //
    //  return and tell the caller
    //

    DebugTrace(-1, Dbg, "NpDeleteCcb -> VOID\n", 0);

    return;
}

