/*++ 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; }