diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/npfs | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/npfs')
36 files changed, 19474 insertions, 0 deletions
diff --git a/private/ntos/npfs/aliassup.c b/private/ntos/npfs/aliassup.c new file mode 100644 index 000000000..ca8b20d63 --- /dev/null +++ b/private/ntos/npfs/aliassup.c @@ -0,0 +1,576 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + AliasSup.c + +Abstract: + + This module implements alias support for the Named Pipe file system. + +Author: + + Chuck Lenzmeier [chuckl] 16-Nov-1993 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// Registry path (relative to Services key) to alias list +// + +#define ALIAS_PATH L"Npfs\\Aliases" + +// +// The Alias record defines an aliased pipe name -- what the original +// name is, and what it should be translated to. Alias records are +// linked together in singly linked lists. +// + +typedef struct _ALIAS { + SINGLE_LIST_ENTRY ListEntry; + PUNICODE_STRING TranslationString; + UNICODE_STRING AliasString; +} ALIAS, *PALIAS; + +// +// ALIAS_CONTEXT is used during initialization to pass context to the +// ReadAlias routine, which is called by RtlQueryRegistryValues. +// + +typedef struct _ALIAS_CONTEXT { + BOOLEAN Phase1; + ULONG RequiredSize; + ULONG AliasCount; + ULONG TranslationCount; + PALIAS NextAlias; + PUNICODE_STRING NextTranslation; + PWCH NextStringData; +} ALIAS_CONTEXT, *PALIAS_CONTEXT; + +// +// Forward declarations. +// + +NTSTATUS +ReadAlias ( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,NpInitializeAliases) +#pragma alloc_text(INIT,ReadAlias) +#pragma alloc_text(PAGE, NpTranslateAlias) +#endif + + +NTSTATUS +NpInitializeAliases ( + VOID + ) + +/*++ + +Routine Description: + + This routine initializes the alias package. It reads the registry, + builds the alias list, and sorts it. + +Arguments: + + None. + +Return Value: + + NTSTATUS - Returns an error if the contents of the registry contents + are invalid or if an allocation fails. + +--*/ + +{ + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + ALIAS_CONTEXT Context; + NTSTATUS Status; + PALIAS Alias; + ULONG i; + ULONG Length; + PSINGLE_LIST_ENTRY PreviousEntry; + PSINGLE_LIST_ENTRY Entry; + PALIAS TestAlias; + + // + // Phase 1: Calculate number of aliases and size of alias buffer. + // + + QueryTable[0].QueryRoutine = ReadAlias; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND; + QueryTable[0].Name = NULL; + QueryTable[0].EntryContext = NULL; + QueryTable[0].DefaultType = REG_NONE; + QueryTable[0].DefaultData = NULL; + QueryTable[0].DefaultLength = 0; + + QueryTable[1].QueryRoutine = NULL; + QueryTable[1].Flags = 0; + QueryTable[1].Name = NULL; + + Context.Phase1 = TRUE; + Context.RequiredSize = 0; + Context.AliasCount = 0; + Context.TranslationCount = 0; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL, + ALIAS_PATH, + QueryTable, + &Context, + NULL + ); + + // + // If an error occurred, return that error, unless the alias + // key wasn't present, which is not an error. Also, if the key + // was there, but was empty, this is not an error. + // + + if (!NT_SUCCESS(Status)) { + if ( Status == STATUS_OBJECT_NAME_NOT_FOUND ) { + Status = STATUS_SUCCESS; + } + return Status; + } + + if (Context.RequiredSize == 0) { + return STATUS_SUCCESS; + } + + // + // Allocate a buffer to hold the alias information. + // + + NpAliases = ExAllocatePoolWithTag( NonPagedPool, + Context.RequiredSize, + 'sfpN' + ); + if (NpAliases == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Phase 2: Read alias information into the alias buffer. + // + + Context.Phase1 = FALSE; + Context.NextTranslation = (PUNICODE_STRING)NpAliases; + Alias = Context.NextAlias = + (PALIAS)(Context.NextTranslation + Context.TranslationCount); + Context.NextStringData = (PWCH)(Context.NextAlias + Context.AliasCount); + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL, + ALIAS_PATH, + QueryTable, + &Context, + NULL + ); + if (!NT_SUCCESS(Status)) { + ExFreePool( NpAliases ); + NpAliases = NULL; + return Status; + } + + // + // Phase 3: Link aliases into alias lists. + // + + for ( i = 0; + i < Context.AliasCount; + i++, Alias++ ) { + + // + // Point to the appropriate list head. + // + + Length = Alias->AliasString.Length; + if ( (Length >= MIN_LENGTH_ALIAS_ARRAY) && + (Length <= MAX_LENGTH_ALIAS_ARRAY) ) { + PreviousEntry = &NpAliasListByLength[(Length - MIN_LENGTH_ALIAS_ARRAY) / sizeof(WCHAR)]; + } else { + PreviousEntry = &NpAliasList; + } + + // + // Walk the list to determine the proper place for this alias. + // + + for ( Entry = PreviousEntry->Next; + Entry != NULL; + PreviousEntry = Entry, Entry = Entry->Next ) { + + TestAlias = CONTAINING_RECORD( Entry, ALIAS, ListEntry ); + + // + // If the test alias is longer than the new alias, we want to + // insert the new alias in front of the test alias. If the + // test alias is shorter, we need to continue walking the list. + // + + if ( TestAlias->AliasString.Length > Length ) break; + if ( TestAlias->AliasString.Length < Length ) continue; + + // + // The aliases are the same length. Compare them. If the new + // alias is lexically before the test alias, we want to insert + // it in front of the test alias. If it's after, we need to + // keep walking. + // + // Alias and TestAlias should never have the same string, but + // if they do, we'll insert the second occurrence of the string + // immediately after the first one, and all will be well. + // + + if ( _wcsicmp( Alias->AliasString.Buffer, + TestAlias->AliasString.Buffer ) < 0 ) { + break; + } + + } + + // + // We have found the place where this alias belongs. PreviousEntry + // points to the alias that the new alias should follow. + // (PreviousEntry may point to the list head.) + // + + Alias->ListEntry.Next = PreviousEntry->Next; + PreviousEntry->Next = &Alias->ListEntry; + + } + +#if 0 + for ( Length = MIN_LENGTH_ALIAS_ARRAY; + Length <= MAX_LENGTH_ALIAS_ARRAY + 2; + Length += 2 ) { + if ( (Length >= MIN_LENGTH_ALIAS_ARRAY) && + (Length <= MAX_LENGTH_ALIAS_ARRAY) ) { + PreviousEntry = &NpAliasListByLength[(Length - MIN_LENGTH_ALIAS_ARRAY) / sizeof(WCHAR)]; + DbgPrint( "Length %d list:\n", Length ); + } else { + PreviousEntry = &NpAliasList; + DbgPrint( "Odd length list:\n" ); + } + for ( Entry = PreviousEntry->Next; + Entry != NULL; + Entry = Entry->Next ) { + Alias = CONTAINING_RECORD( Entry, ALIAS, ListEntry ); + DbgPrint( " %wZ -> %wZ\n", &Alias->AliasString, Alias->TranslationString ); + } + } +#endif + + return STATUS_SUCCESS; + +} + + +NTSTATUS +ReadAlias ( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) +{ + PALIAS_CONTEXT Ctx = Context; + USHORT Length; + PWCH p; + PUNICODE_STRING TranslationString; + PALIAS Alias; + + // + // The value must be a REG_MULTI_SZ value. + // + + if (ValueType != REG_MULTI_SZ) { + return STATUS_INVALID_PARAMETER; + } + + // + // In phase 1, we calculate the required size of the alias buffer. + // In phase 2, we build the alias descriptors. + // + + if ( Ctx->Phase1 ) { + + // + // The value name is the translation. The value data is one or + // more strings that are aliases for the translation. + // + // The "1+" and "sizeof(WCHAR)+" are for the '\' that will be + // placed in front of the translation string and the alias string. + // + + Ctx->TranslationCount++; + Length = (USHORT)((1 + wcslen(ValueName) + 1) * sizeof(WCHAR)); + Ctx->RequiredSize += Length + sizeof(UNICODE_STRING); + + p = ValueData; + while ( *p != 0 ) { + Ctx->AliasCount++; + Length = (USHORT)((wcslen(p) + 1) * sizeof(WCHAR)); + Ctx->RequiredSize += sizeof(WCHAR) + Length + sizeof(ALIAS); + p = (PWCH)((PCHAR)p + Length); + } + + } else { + + // + // Build a string descriptor for the translation string. + // + + TranslationString = Ctx->NextTranslation++; + Length = (USHORT)((1 + wcslen(ValueName) + 1) * sizeof(WCHAR)); + TranslationString->Length = Length - sizeof(WCHAR); + TranslationString->MaximumLength = Length; + TranslationString->Buffer = Ctx->NextStringData; + Ctx->NextStringData = (PWCH)((PCHAR)Ctx->NextStringData + Length); + + // + // Copy the string data. Place a '\' at the beginning. + // + + TranslationString->Buffer[0] = L'\\'; + RtlCopyMemory( &TranslationString->Buffer[1], + ValueName, + Length - sizeof(WCHAR) ); + + // + // Build aliases descriptors. + // + + p = ValueData; + + while ( *p != 0 ) { + + Alias = Ctx->NextAlias++; + + // + // Point the alias descriptor to the translation string. + // + + Alias->TranslationString = TranslationString; + + // + // Build the alias string descriptor. + // + + Length = (USHORT)((1 + wcslen(p) + 1) * sizeof(WCHAR)); + Alias->AliasString.Length = Length - sizeof(WCHAR); + Alias->AliasString.MaximumLength = Length; + Alias->AliasString.Buffer = Ctx->NextStringData; + Ctx->NextStringData = (PWCH)((PCHAR)Ctx->NextStringData + Length); + + // + // Copy the string data. Place a '\' at the beginning. + // + + Alias->AliasString.Buffer[0] = L'\\'; + RtlCopyMemory( &Alias->AliasString.Buffer[1], + p, + Length - sizeof(WCHAR) ); + + // + // Upcase the string. + // + + RtlUpcaseUnicodeString( &Alias->AliasString, + &Alias->AliasString, + FALSE ); + + p = (PWCH)((PCHAR)p + Length - sizeof(WCHAR)); + + } + + } + + return STATUS_SUCCESS; + +} + + +NTSTATUS +NpTranslateAlias ( + IN OUT PUNICODE_STRING String + ) + +/*++ + +Routine Description: + + This routine translates a pipe name string based on information + obtained from the registry at boot time. This translation is used + to allow RPC services that had different names in NT 1.0 to have + common names in 1.0a and beyond. + +Arguments: + + String - Supplies the input string to search for; returns the output + string, if the name was translated. If so, the string points to + a buffer allocated from paged pool. The caller should NOT free + this buffer. + +Return Value: + + NTSTATUS - Returns STATUS_SUCCESS unless an allocation failure occurs. + The status does NOT indicate whether the name was translated. + +--*/ + +{ + NTSTATUS Status; + UNICODE_STRING UpcaseString; + ULONG Length; + PSINGLE_LIST_ENTRY Entry; + PALIAS Alias; + PWCH sp, ap; + WCHAR a, s; + BOOLEAN NoSlash; + + WCHAR UpcaseBuffer[MAX_LENGTH_ALIAS_ARRAY]; + BOOLEAN FreeUpcaseBuffer; + + PAGED_CODE(); + + // + // Before upcasing the string (a relatively expensive operation), + // make sure that the string length matches at least one alias. + // + + Length = String->Length; + if ( Length == 0 ) { + return STATUS_SUCCESS; + } + + if ( *String->Buffer != L'\\' ) { + Length += sizeof(WCHAR); + NoSlash = TRUE; + } else { + NoSlash = FALSE; + } + + if ( (Length >= MIN_LENGTH_ALIAS_ARRAY) && + (Length <= MAX_LENGTH_ALIAS_ARRAY) ) { + Entry = NpAliasListByLength[(Length - MIN_LENGTH_ALIAS_ARRAY) / sizeof(WCHAR)].Next; + Alias = CONTAINING_RECORD( Entry, ALIAS, ListEntry ); + } else { + Entry = NpAliasList.Next; + while ( Entry != NULL ) { + Alias = CONTAINING_RECORD( Entry, ALIAS, ListEntry ); + if ( Alias->AliasString.Length == Length ) { + break; + } + if ( Alias->AliasString.Length > Length ) { + return STATUS_SUCCESS; + } + Entry = Entry->Next; + } + } + + if ( Entry == NULL ) { + return STATUS_SUCCESS; + } + + // + // The string's length matches at least one alias. Upcase the string. + // + + if ( Length <= MAX_LENGTH_ALIAS_ARRAY ) { + UpcaseString.MaximumLength = MAX_LENGTH_ALIAS_ARRAY; + UpcaseString.Buffer = UpcaseBuffer; + Status = RtlUpcaseUnicodeString( &UpcaseString, String, FALSE ); + ASSERT( NT_SUCCESS(Status) ); + FreeUpcaseBuffer = FALSE; + } else { + Status = RtlUpcaseUnicodeString( &UpcaseString, String, TRUE ); + if ( !NT_SUCCESS(Status) ) { + return Status; + } + FreeUpcaseBuffer = TRUE; + } + + ASSERT( UpcaseString.Length == (Length - (NoSlash ? sizeof(WCHAR) : 0)) ); + + // + // At this point, Entry points to an alias list entry whose length + // matches that of the input string. This list entry may be the + // first element of a length-specific list (in which all entries + // have the same length), or it may be an element of a length-ordered + // list (in which case we'll need to check each next entry to see if + // it's the same length. In both cases, strings of the same length + // are in lexical order. + // + // Try to match the upcased string up to an alias. + // + + do { + + sp = UpcaseString.Buffer; + ap = Alias->AliasString.Buffer; + if ( NoSlash ) { + ap++; + } + + while ( TRUE ) { + a = *ap; + if ( a == 0 ) { + *String = *Alias->TranslationString; + if ( NoSlash ) { + String->Length -= sizeof(WCHAR); + String->Buffer++; + } + goto exit; + } + s = *sp; + if ( s < a ) goto exit; + if ( s > a ) break; + sp++; + ap++; + } + + // + // The input string doesn't match the current alias. Move to + // the next one. + // + + Entry = Entry->Next; + if ( Entry == NULL ) { + goto exit; + } + + Alias = CONTAINING_RECORD( Entry, ALIAS, ListEntry ); + + } while ( Alias->AliasString.Length == Length ); + +exit: + + if (FreeUpcaseBuffer) { + ASSERT( UpcaseString.Buffer != UpcaseBuffer ); + ExFreePool( UpcaseString.Buffer ); + } + + return STATUS_SUCCESS; + +} + diff --git a/private/ntos/npfs/cleanup.c b/private/ntos/npfs/cleanup.c new file mode 100644 index 000000000..f666b335a --- /dev/null +++ b/private/ntos/npfs/cleanup.c @@ -0,0 +1,229 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + Cleanup.c + +Abstract: + + This module implements the File Cleanup routine for NPFS called by the + dispatch driver. + +Author: + + Gary Kimura [GaryKi] 21-Aug-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_CLEANUP) + +// +// local procedure prototypes +// + +NTSTATUS +NpCommonCleanup ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpCommonCleanup) +#pragma alloc_text(PAGE, NpFsdCleanup) +#endif + + +NTSTATUS +NpFsdCleanup ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtCleanupFile API calls. + +Arguments: + + NpfsDeviceObject - Supplies the device object to use. + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The Fsd status for the Irp + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpFsdCleanup\n", 0); + + // + // Call the common Cleanup routine. + // + + FsRtlEnterFileSystem(); + + try { + + Status = NpCommonCleanup( NpfsDeviceObject, Irp ); + + } except(NpExceptionFilter( GetExceptionCode() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = NpProcessException( NpfsDeviceObject, Irp, GetExceptionCode() ); + } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpFsdCleanup -> %08lx\n", Status ); + + return Status; +} + + +// +// Internal support routine +// + +NTSTATUS +NpCommonCleanup ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for cleanup + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - the return status for the operation + +--*/ + +{ + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp; + + NODE_TYPE_CODE NodeTypeCode; + PCCB Ccb; + NAMED_PIPE_END NamedPipeEnd; + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpCommonCleanup...\n", 0); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); + + // + // Now acquire exclusive access to the Vcb + // + + NpAcquireExclusiveVcb(); + + try { + + // + // Decode the file object to figure out who we are. If the result + // is null then the pipe has been disconnected. + // + + if ((NodeTypeCode = NpDecodeFileObject( IrpSp->FileObject, + NULL, + &Ccb, + &NamedPipeEnd )) == NTC_UNDEFINED) { + + DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + try_return( Status = STATUS_PIPE_DISCONNECTED ); + } + + // + // Now case on the type of file object we're closing + // + + switch (NodeTypeCode) { + + case NPFS_NTC_VCB: + case NPFS_NTC_ROOT_DCB: + + NpCompleteRequest( Irp, STATUS_SUCCESS ); + Status = STATUS_SUCCESS; + + break; + + case NPFS_NTC_CCB: + + // + // If this is the server end of a pipe, decrement the count + // of the number of instances the server end has open. + // When this count is 0, attempts to connect to the pipe + // return OBJECT_NAME_NOT_FOUND instead of + // PIPE_NOT_AVAILABLE. + // + + if ( NamedPipeEnd == FILE_PIPE_SERVER_END ) { + ASSERT( Ccb->Fcb->ServerOpenCount != 0 ); + Ccb->Fcb->ServerOpenCount -= 1; + } + + // + // The set closing state routines does everything to transition + // the named pipe to a closing state. + // + + Status = NpSetClosingPipeState( Ccb, Irp, NamedPipeEnd ); + + break; + } + + try_exit: NOTHING; + } finally { + + NpReleaseVcb( ); + } + + DebugTrace(-1, Dbg, "NpCommonCleanup -> %08lx\n", Status); + return Status; +} + diff --git a/private/ntos/npfs/close.c b/private/ntos/npfs/close.c new file mode 100644 index 000000000..8b169d3b5 --- /dev/null +++ b/private/ntos/npfs/close.c @@ -0,0 +1,236 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + Close.c + +Abstract: + + This module implements the File Close routine for NPFS called by the + dispatch driver. + +Author: + + Gary Kimura [GaryKi] 21-Aug-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_CLOSE) + +// +// local procedure prototypes +// + +NTSTATUS +NpCommonClose ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpCommonClose) +#pragma alloc_text(PAGE, NpFsdClose) +#endif + + +NTSTATUS +NpFsdClose ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtCloseFile API calls. + +Arguments: + + NpfsDeviceObject - Supplies the device object to use. + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The Fsd status for the Irp + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpFsdClose\n", 0); + + // + // Call the common Close routine. + // + + FsRtlEnterFileSystem(); + + try { + + Status = NpCommonClose( NpfsDeviceObject, Irp ); + + } except(NpExceptionFilter( GetExceptionCode() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = NpProcessException( NpfsDeviceObject, Irp, GetExceptionCode() ); + } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpFsdClose -> %08lx\n", Status ); + + return Status; +} + +// +// Internal support routine +// + +NTSTATUS +NpCommonClose ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for creating/opening a file. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - the return status for the operation + +--*/ + +{ + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp; + + NODE_TYPE_CODE NodeTypeCode; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpCommonClose...\n", 0); + DebugTrace( 0, Dbg, " Irp = %08lx\n", Irp); + + // + // Now acquire exclusive access to the vcb + // + + NpAcquireExclusiveVcb(); + + try { + + // + // Decode the file object to figure out who we are. If the result + // is null then the pipe has been disconnected. + // + + if ((NodeTypeCode = NpDecodeFileObject( IrpSp->FileObject, + &Fcb, + &Ccb, + NULL )) == NTC_UNDEFINED) { + + DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + try_return( Status = STATUS_PIPE_DISCONNECTED ); + } + + // + // Now case on the type of file object we're closing + // + + switch (NodeTypeCode) { + + case NPFS_NTC_VCB: + + // + // Decrement the Open count and clear our fields in the file object + // + + NpVcb->OpenCount -= 1; + NpSetFileObject( IrpSp->FileObject, NULL, NULL, FILE_PIPE_SERVER_END ); + + break; + + case NPFS_NTC_ROOT_DCB: + + // + // Decrement the Open count and clear our fields in the file object + // + + Fcb->OpenCount -= 1; + NpSetFileObject( IrpSp->FileObject, NULL, NULL, FILE_PIPE_SERVER_END ); + + // + // Remove the root dcb ccb. + // + + NpDeleteCcb( Ccb ); + + break; + + case NPFS_NTC_CCB: + + break; + } + + // + // Complete the close irp + // + + NpCompleteRequest( Irp, STATUS_SUCCESS ); + + Status = STATUS_SUCCESS; + + try_exit: NOTHING; + } finally { + + NpReleaseVcb( ); + } + + DebugTrace(-1, Dbg, "NpCommonClose -> %08lx\n", Status); + return Status; +} + diff --git a/private/ntos/npfs/create.c b/private/ntos/npfs/create.c new file mode 100644 index 000000000..ea1146051 --- /dev/null +++ b/private/ntos/npfs/create.c @@ -0,0 +1,794 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + Create.c + +Abstract: + + This module implements the File Create routine for NPFS called by the + dispatch driver. + +Author: + + Gary Kimura [GaryKi] 21-Aug-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_CREATE) + +// +// local procedure prototypes +// + +NTSTATUS +NpCommonCreate ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +IO_STATUS_BLOCK +NpCreateClientEnd( + IN PFCB Fcb, + IN PFILE_OBJECT FileObject, + IN ACCESS_MASK DesiredAccess, + IN USHORT ShareAccess, + IN PSECURITY_QUALITY_OF_SERVICE SecurityQos, + IN PACCESS_STATE AccessState, + IN KPROCESSOR_MODE RequestorMode, + IN PETHREAD UserThread + ); + +IO_STATUS_BLOCK +NpOpenNamedPipeFileSystem ( + IN PFILE_OBJECT FileObject, + IN ACCESS_MASK DesiredAccess, + IN USHORT ShareAccess + ); + +IO_STATUS_BLOCK +NpOpenNamedPipeRootDirectory ( + IN PROOT_DCB RootDcb, + IN PFILE_OBJECT FileObject, + IN ACCESS_MASK DesiredAccess, + IN USHORT ShareAccess + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpCommonCreate) +#pragma alloc_text(PAGE, NpFsdCreate) +#pragma alloc_text(PAGE, NpOpenNamedPipeFileSystem) +#pragma alloc_text(PAGE, NpOpenNamedPipeRootDirectory) +#endif + + +NTSTATUS +NpFsdCreate ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtCreateFile and NtOpenFile + API calls. + +Arguments: + + NpfsDeviceObject - Supplies the device object to use. + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The Fsd status for the Irp + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpFsdCreate\n", 0); + + // + // Call the common create routine. + // + + FsRtlEnterFileSystem(); + + try { + + Status = NpCommonCreate( NpfsDeviceObject, Irp ); + + } except(NpExceptionFilter( GetExceptionCode() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = NpProcessException( NpfsDeviceObject, Irp, GetExceptionCode() ); + } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpFsdCreate -> %08lx\n", Status ); + + return Status; +} + +// +// Internal support routine +// + +NTSTATUS +NpCommonCreate ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for creating/opening a file. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - the return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + PFILE_OBJECT FileObject; + PFILE_OBJECT RelatedFileObject; + UNICODE_STRING FileName; + ACCESS_MASK DesiredAccess; + USHORT ShareAccess; + + BOOLEAN CaseInsensitive = TRUE; //**** Make all searches case insensitive + + PFCB Fcb; + + UNICODE_STRING RemainingPart; + + PAGED_CODE(); + + // + // Reference our input parameters to make things easier + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + FileObject = IrpSp->FileObject; + RelatedFileObject = IrpSp->FileObject->RelatedFileObject; + FileName = *(PUNICODE_STRING)&IrpSp->FileObject->FileName; + DesiredAccess = IrpSp->Parameters.Create.SecurityContext->DesiredAccess; + ShareAccess = IrpSp->Parameters.Create.ShareAccess; + + DebugTrace(+1, Dbg, "NpCommonCreate\n", 0 ); + DebugTrace( 0, Dbg, "NpfsDeviceObject = %08xl\n", NpfsDeviceObject ); + DebugTrace( 0, Dbg, "Irp = %08xl\n", Irp ); + DebugTrace( 0, Dbg, "FileObject = %08xl\n", FileObject ); + DebugTrace( 0, Dbg, "RelatedFileObject = %08xl\n", RelatedFileObject ); + DebugTrace( 0, Dbg, "FileName = %Z\n", &FileName ); + DebugTrace( 0, Dbg, "DesiredAccess = %08xl\n", DesiredAccess ); + DebugTrace( 0, Dbg, "ShareAccess = %08xl\n", ShareAccess ); + + // + // Acquire exclusive access to the Vcb + // + + NpAcquireExclusiveVcb(); + + try { + + // + // Check if we are trying to open the named pipe file system + // (i.e., the Vcb). + // + + if ((FileName.Length == 0) && + ((RelatedFileObject == NULL) || (NodeType(RelatedFileObject->FsContext) == NPFS_NTC_VCB))) { + + DebugTrace(0, Dbg, "Open name pipe file system\n", 0); + + Irp->IoStatus = NpOpenNamedPipeFileSystem( FileObject, + DesiredAccess, + ShareAccess ); + + Status = Irp->IoStatus.Status; + NpCompleteRequest( Irp, Status ); + try_return( NOTHING ); + } + + // + // Check if we are trying to open the root directory + // + + if (((FileName.Length == 2) && (FileName.Buffer[0] == L'\\') && (RelatedFileObject == NULL)) + + || + + ((FileName.Length == 0) && (NodeType(RelatedFileObject->FsContext) == NPFS_NTC_ROOT_DCB))) { + + DebugTrace(0, Dbg, "Open root directory system\n", 0); + + Irp->IoStatus = NpOpenNamedPipeRootDirectory( NpVcb->RootDcb, + FileObject, + DesiredAccess, + ShareAccess ); + + Status = Irp->IoStatus.Status; + NpCompleteRequest( Irp, Status ); + try_return( NOTHING ); + } + + // + // If the name is an alias, translate it. + // + + Status = NpTranslateAlias( &FileName ); + if ( !NT_SUCCESS(Status) ) { + NpCompleteRequest( Irp, Status ); + try_return( NOTHING ); + } + + // + // If there is a related file object then this is a relative open + // and it better be the root dcb. Both the then and the else clause + // return an Fcb. + // + + if (RelatedFileObject != NULL) { + + PDCB Dcb; + + Dcb = RelatedFileObject->FsContext; + + if (NodeType(Dcb) != NPFS_NTC_ROOT_DCB) { + + DebugTrace(0, Dbg, "Bad file name\n", 0); + + NpCompleteRequest( Irp, STATUS_OBJECT_NAME_INVALID ); + try_return( Status = STATUS_OBJECT_NAME_INVALID ); + } + + Fcb = NpFindRelativePrefix( Dcb, &FileName, CaseInsensitive, &RemainingPart ); + + } else { + + // + // The only nonrelative name we allow are of the form "\pipe-name" + // + + if ((FileName.Length <= 2) || (FileName.Buffer[0] != L'\\')) { + + DebugTrace(0, Dbg, "Bad file name\n", 0); + + NpCompleteRequest( Irp, STATUS_OBJECT_NAME_INVALID ); + try_return( Status = STATUS_OBJECT_NAME_INVALID ); + } + + Fcb = NpFindPrefix( &FileName, CaseInsensitive, &RemainingPart ); + } + + // + // If the remaining name is not empty then we have an error, either + // we have an illegal name or a non-existent name. + // + + if (RemainingPart.Length != 0) { + + if (Fcb->NodeTypeCode == NPFS_NTC_FCB) { + + // + // We were given a name such as "\pipe-name\another-name" + // + + DebugTrace(0, Dbg, "Illegal object name\n", 0); + + Status = STATUS_OBJECT_NAME_INVALID; + + } else { + + // + // We were given a non-existent name + // + + DebugTrace(0, Dbg, "non-existent name\n", 0); + + Status = STATUS_OBJECT_NAME_NOT_FOUND; + } + + } else { + + // + // The remaining name is empty so we better have an Fcb otherwise + // we have an invalid object name. + // + + if (Fcb->NodeTypeCode == NPFS_NTC_FCB) { + + DebugTrace(0, Dbg, "Create client end named pipe, Fcb = %08lx\n", Fcb ); + + // + // If the server has no handles open, then pretend that + // the pipe name doesn't exist. + // + + if ( Fcb->ServerOpenCount == 0 ) { + + Status = STATUS_OBJECT_NAME_NOT_FOUND; + + } else { + + Irp->IoStatus = NpCreateClientEnd( Fcb, + FileObject, + DesiredAccess, + ShareAccess, + IrpSp->Parameters.Create.SecurityContext->SecurityQos, + IrpSp->Parameters.Create.SecurityContext->AccessState, + (KPROCESSOR_MODE)(FlagOn(IrpSp->Flags, SL_FORCE_ACCESS_CHECK) ? + UserMode : Irp->RequestorMode), + Irp->Tail.Overlay.Thread ); + Status = Irp->IoStatus.Status; + } + + } else { + + DebugTrace(0, Dbg, "Illegal object name\n", 0); + + Status = STATUS_OBJECT_NAME_INVALID; + } + } + + // + // Complete the IRP and return to our caller + // + + NpCompleteRequest( Irp, Status ); + + try_exit: NOTHING; + } finally { + + NpReleaseVcb( ); + + DebugTrace(-1, Dbg, "NpCommonCreate -> %08lx\n", Status); + } + + return Status; +} + + +// +// Internal support routine +// + +IO_STATUS_BLOCK +NpCreateClientEnd ( + IN PFCB Fcb, + IN PFILE_OBJECT FileObject, + IN ACCESS_MASK DesiredAccess, + IN USHORT ShareAccess, + IN PSECURITY_QUALITY_OF_SERVICE SecurityQos, + IN PACCESS_STATE AccessState, + IN KPROCESSOR_MODE RequestorMode, + IN PETHREAD UserThread + ) + +/*++ + +Routine Description: + + This routine performs the operation for opening the client end of a named + pipe. This routine does not complete the IRP, it performs the function + and then returns a status + +Arguments: + + Fcb - Supplies the Fcb for the named pipe being accessed + + FileObject - Supplies the file object associated with the client end + + DesiredAccess - Supplies the callers desired access + + ShareAccess - Supplies the callers share access + + SecurityQos - Supplies the security qos parameter from the create irp + + AccessState - Supplies the access state parameter from the create irp + + RequestorMode - Supplies the mode of the originating irp + + UserTherad - Supplies the client end user thread + +Return Value: + + IO_STATUS_BLOCK - Returns the appropriate status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + + NAMED_PIPE_CONFIGURATION NamedPipeConfiguration; + + BOOLEAN AccessGranted; + ACCESS_MASK GrantedAccess; + UNICODE_STRING Name; + + PCCB Ccb; + PNONPAGED_CCB NonpagedCcb; + PLIST_ENTRY Links; + PPRIVILEGE_SET Privileges = NULL; + + DebugTrace(+1, Dbg, "NpCreateClientEnd\n", 0 ); + + NamedPipeConfiguration = Fcb->Specific.Fcb.NamedPipeConfiguration; + + try { + + // + // First do an access check for the user against the Fcb + // + + SeLockSubjectContext( &AccessState->SubjectSecurityContext ); + + AccessGranted = SeAccessCheck( Fcb->SecurityDescriptor, + &AccessState->SubjectSecurityContext, + TRUE, // Tokens are locked + DesiredAccess, + 0, + &Privileges, + IoGetFileObjectGenericMapping(), + RequestorMode, + &GrantedAccess, + &Iosb.Status + ); + + if (Privileges != NULL) { + + (VOID) SeAppendPrivileges( + AccessState, + Privileges + ); + + SeFreePrivileges( Privileges ); + } + + if (AccessGranted) { + AccessState->PreviouslyGrantedAccess |= GrantedAccess; + AccessState->RemainingDesiredAccess &= ~GrantedAccess; + } + + RtlInitUnicodeString( &Name, L"NamedPipe" ); + + SeOpenObjectAuditAlarm( &Name, + NULL, + &FileObject->FileName, + Fcb->SecurityDescriptor, + AccessState, + FALSE, + AccessGranted, + RequestorMode, + &AccessState->GenerateOnClose ); + + SeUnlockSubjectContext( &AccessState->SubjectSecurityContext ); + + if (!AccessGranted) { + + DebugTrace(0, Dbg, "Access Denied\n", 0 ); + + try_return( Iosb.Status ); + } + + // + // Check if the user wants to write to an outbound pipe or read from + // and inbound pipe. And if so then tell the user the error + // + + if ((FlagOn(DesiredAccess, FILE_READ_DATA) && (NamedPipeConfiguration == FILE_PIPE_INBOUND)) || + (FlagOn(DesiredAccess, FILE_WRITE_DATA) && (NamedPipeConfiguration == FILE_PIPE_OUTBOUND))) { + + Iosb.Status = STATUS_ACCESS_DENIED; + + try_return( Iosb.Status ); + } + + // + // First try and find a ccb that is in the listening state. If we + // exit the loop with Ccb not equal to null then we've found one. + // + + Ccb = NULL; + for (Links = Fcb->Specific.Fcb.CcbQueue.Flink; + Links != &Fcb->Specific.Fcb.CcbQueue; + Links = Links->Flink) { + + Ccb = CONTAINING_RECORD( Links, CCB, CcbLinks ); + NonpagedCcb = Ccb->NonpagedCcb; + + if (Ccb->NamedPipeState == FILE_PIPE_LISTENING_STATE) { + + DebugTrace(0, Dbg, "Located listening ccb = %08lx\n", Ccb); + + break; + } + + Ccb = NULL; + } + + // + // Check that we found one + // + + if (Ccb == NULL) { + + try_return( Iosb.Status = STATUS_PIPE_NOT_AVAILABLE ); + } + + // + // Now make sure our share access is okay. If the user is asking + // for read data then given him shared read, if he's asking for + // write data then given him shared write. + // + + if (NamedPipeConfiguration == FILE_PIPE_OUTBOUND) { + + ShareAccess = FILE_SHARE_READ; + + } else if (NamedPipeConfiguration == FILE_PIPE_INBOUND) { + + ShareAccess = FILE_SHARE_WRITE; + + } else { + + ShareAccess = (FILE_SHARE_READ | FILE_SHARE_WRITE); + } + + if (FlagOn(DesiredAccess, FILE_READ_DATA )) { ShareAccess |= FILE_SHARE_READ; } + if (FlagOn(DesiredAccess, FILE_WRITE_DATA)) { ShareAccess |= FILE_SHARE_WRITE; } + + //if (!NT_SUCCESS(Iosb.Status = IoCheckShareAccess( DesiredAccess, + // ShareAccess, + // FileObject, + // &Ccb->ShareAccess, + // TRUE ))) { + // + // DebugTrace(0, Dbg, "Sharing violation\n", 0); + // + // try_return( NOTHING ); + //} + + // + // Set up the security part of the ccb + // + + if (!NT_SUCCESS(Iosb.Status = NpInitializeSecurity( Ccb, + SecurityQos, + UserThread ))) { + + DebugTrace(0, Dbg, "Security QOS error\n", 0); + + try_return( NOTHING ); + } + + // + // Set the pipe into the connect state, the read mode to byte stream, + // and the completion mode to queued operation. This also + // sets the client file object's back pointer to the ccb + // + + if (!NT_SUCCESS(Iosb.Status = NpSetConnectedPipeState( Ccb, + FileObject ))) { + + try_return( NOTHING ); + } + + // + // Now check to see if there are any waiting listening IRPs + // that need to be completed now + // + + while (!IsListEmpty( &NonpagedCcb->ListeningQueue )) { + PIRP Irp; + + Links = RemoveHeadList( &NonpagedCcb->ListeningQueue ); + + Irp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry ); + + IoAcquireCancelSpinLock( &Irp->CancelIrql ); + IoSetCancelRoutine( Irp, NULL ); + Irp->IoStatus.Information = 0; + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + NpCompleteRequest( Irp, STATUS_PIPE_CONNECTED ); + } + + // + // Set up the client session and process IDs. NULL for the + // client session indicates a local session. + // + + Ccb->ClientSession = NULL; +// Ccb->ClientProcess = THREAD_TO_PROCESS( UserThread ); + Ccb->ClientProcess = IoThreadToProcess( UserThread ); + + // + // And set our return status + // + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = FILE_OPENED; + + try_exit: NOTHING; + } finally { + + DebugTrace(-1, Dbg, "NpCreateClientEnd -> %08lx\n", Iosb.Status); + } + + return Iosb; +} + + +// +// Internal support routine +// + +IO_STATUS_BLOCK +NpOpenNamedPipeFileSystem ( + IN PFILE_OBJECT FileObject, + IN ACCESS_MASK DesiredAccess, + IN USHORT ShareAccess + ) + +{ + IO_STATUS_BLOCK Iosb; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpOpenNamedPipeFileSystem, Vcb = %08lx\n", NpVcb); + + try { + + // + // Set the new share access + // + + if (!NT_SUCCESS(Iosb.Status = IoCheckShareAccess( DesiredAccess, + ShareAccess, + FileObject, + &NpVcb->ShareAccess, + TRUE ))) { + + DebugTrace(0, Dbg, "bad share access\n", 0); + + try_return( NOTHING ); + } + + // + // Have the file object point back to the Vcb, and increment the + // open count. The pipe end on the call to set file object really + // doesn't matter. + // + + NpSetFileObject( FileObject, NpVcb, NULL, FILE_PIPE_CLIENT_END ); + + NpVcb->OpenCount += 1; + + // + // Set our return status + // + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = FILE_OPENED; + + try_exit: NOTHING; + } finally { + + DebugTrace(-1, Dbg, "NpOpenNamedPipeFileSystem -> Iosb.Status = %08lx\n", Iosb.Status); + } + + // + // And return to our caller + // + + return Iosb; +} + + +// +// Internal support routine +// + +IO_STATUS_BLOCK +NpOpenNamedPipeRootDirectory( + IN PROOT_DCB RootDcb, + IN PFILE_OBJECT FileObject, + IN ACCESS_MASK DesiredAccess, + IN USHORT ShareAccess + ) + +{ + IO_STATUS_BLOCK Iosb; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpOpenNamedPipeRootDirectory, RootDcb = %08lx\n", RootDcb); + + try { + + // + // Set the new share access + // + + if (!NT_SUCCESS(Iosb.Status = IoCheckShareAccess( DesiredAccess, + ShareAccess, + FileObject, + &RootDcb->Specific.Dcb.ShareAccess, + TRUE ))) { + + DebugTrace(0, Dbg, "bad share access\n", 0); + + try_return( NOTHING ); + } + + // + // Have the file object point back to the Dcb, and reference the root + // dcb, ccb, and increment our open count. The pipe end on the + // call to set file object really doesn't matter. + // + + NpSetFileObject( FileObject, + RootDcb, + NpCreateRootDcbCcb(), + FILE_PIPE_CLIENT_END ); + + RootDcb->OpenCount += 1; + + // + // Set our return status + // + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = FILE_OPENED; + + try_exit: NOTHING; + } finally { + + DebugTrace(-1, Dbg, "NpOpenNamedPipeRootDirectory -> Iosb.Status = %08lx\n", Iosb.Status); + } + + // + // And return to our caller + // + + return Iosb; +} diff --git a/private/ntos/npfs/createnp.c b/private/ntos/npfs/createnp.c new file mode 100644 index 000000000..20332a1b7 --- /dev/null +++ b/private/ntos/npfs/createnp.c @@ -0,0 +1,895 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + CreateNp.c + +Abstract: + + This module implements the File Create Named Pipe routine for NPFS called + by the dispatch driver. + +Author: + + Gary Kimura [GaryKi] 04-Sep-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_CREATE_NAMED_PIPE) + +// +// local procedure prototypes +// + +NTSTATUS +NpCommonCreateNamedPipe ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +IO_STATUS_BLOCK +NpCreateNewNamedPipe ( + IN PROOT_DCB RootDcb, + IN PFILE_OBJECT FileObject, + IN UNICODE_STRING FileName, + IN ACCESS_MASK DesiredAccess, + IN PACCESS_STATE AccessState, + IN ULONG CreateDisposition, + IN USHORT ShareAccess, + IN NAMED_PIPE_TYPE NamedPipeType, + IN READ_MODE ServerReadMode, + IN COMPLETION_MODE ServerCompletionMode, + IN ULONG MaximumInstances, + IN ULONG InboundQuota, + IN ULONG OutboundQuota, + IN LARGE_INTEGER DefaultTimeout, + IN BOOLEAN TimeoutSpecified, + IN PEPROCESS CreatorProcess + ); + +IO_STATUS_BLOCK +NpCreateExistingNamedPipe ( + IN PFCB Fcb, + IN PFILE_OBJECT FileObject, + IN ACCESS_MASK DesiredAccess, + IN PACCESS_STATE AccessState, + IN KPROCESSOR_MODE RequestorMode, + IN ULONG CreateDisposition, + IN USHORT ShareAccess, + IN READ_MODE ServerReadMode, + IN COMPLETION_MODE ServerCompletionMode, + IN ULONG InboundQuota, + IN ULONG OutboundQuota, + IN PEPROCESS CreatorProcess + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpCommonCreateNamedPipe) +#pragma alloc_text(PAGE, NpCreateExistingNamedPipe) +#pragma alloc_text(PAGE, NpCreateNewNamedPipe) +#pragma alloc_text(PAGE, NpFsdCreateNamedPipe) +#endif + + +NTSTATUS +NpFsdCreateNamedPipe ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtCreateNamedPipeFile + API call. + +Arguments: + + NpfsDeviceObject - Supplies the device object to use. + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The Fsd status for the Irp + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpFsdCreateNamedPipe\n", 0); + + // + // Call the common create routine. + // + + FsRtlEnterFileSystem(); + + try { + + Status = NpCommonCreateNamedPipe( NpfsDeviceObject, Irp ); + + } except(NpExceptionFilter( GetExceptionCode() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = NpProcessException( NpfsDeviceObject, Irp, GetExceptionCode() ); + } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpFsdCreateNamedPipe -> %08lx\n", Status ); + + return Status; +} + +// +// Internal support routine +// + +NTSTATUS +NpCommonCreateNamedPipe ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for creating/opening a file. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - the return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + PFILE_OBJECT FileObject; + PFILE_OBJECT RelatedFileObject; + UNICODE_STRING FileName; + ACCESS_MASK DesiredAccess; + ULONG Options; + USHORT ShareAccess; + PNAMED_PIPE_CREATE_PARAMETERS Parameters; + NAMED_PIPE_TYPE NamedPipeType; + READ_MODE ServerReadMode; + COMPLETION_MODE ServerCompletionMode; + ULONG MaximumInstances; + ULONG InboundQuota; + ULONG OutboundQuota; + LARGE_INTEGER DefaultTimeout; + BOOLEAN TimeoutSpecified; + PEPROCESS CreatorProcess; + + BOOLEAN CaseInsensitive = TRUE; //**** Make all searches case insensitive + + PFCB Fcb; + + ULONG CreateDisposition; + + UNICODE_STRING RemainingPart; + + PAGED_CODE(); + + // + // Reference our input parameters to make things easier + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + FileObject = IrpSp->FileObject; + RelatedFileObject = IrpSp->FileObject->RelatedFileObject; + FileName = *(PUNICODE_STRING)&IrpSp->FileObject->FileName; + DesiredAccess = IrpSp->Parameters.CreatePipe.SecurityContext->DesiredAccess; + Options = IrpSp->Parameters.CreatePipe.Options; + ShareAccess = IrpSp->Parameters.CreatePipe.ShareAccess; + Parameters = IrpSp->Parameters.CreatePipe.Parameters; + NamedPipeType = Parameters->NamedPipeType; + ServerReadMode = Parameters->ReadMode; + ServerCompletionMode = Parameters->CompletionMode; + MaximumInstances = Parameters->MaximumInstances; + InboundQuota = Parameters->InboundQuota; + OutboundQuota = Parameters->OutboundQuota; + DefaultTimeout = Parameters->DefaultTimeout; + TimeoutSpecified = Parameters->TimeoutSpecified; + CreatorProcess = IoGetRequestorProcess( Irp ); + + DebugTrace(+1, Dbg, "NpCommonCreateNamedPipe\n", 0 ); + DebugTrace( 0, Dbg, "NpfsDeviceObject = %08lx\n", NpfsDeviceObject ); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp ); + DebugTrace( 0, Dbg, "FileObject = %08lx\n", FileObject ); + DebugTrace( 0, Dbg, "RelatedFileObject = %08lx\n", RelatedFileObject ); + DebugTrace( 0, Dbg, "FileName = %Z\n", &FileName ); + DebugTrace( 0, Dbg, "DesiredAccess = %08lx\n", DesiredAccess ); + DebugTrace( 0, Dbg, "Options = %08lx\n", Options ); + DebugTrace( 0, Dbg, "ShareAccess = %08lx\n", ShareAccess ); + DebugTrace( 0, Dbg, "Parameters = %08lx\n", Parameters ); + DebugTrace( 0, Dbg, "NamedPipeType = %08lx\n", NamedPipeType ); + DebugTrace( 0, Dbg, "ServerReadMode = %08lx\n", ServerReadMode ); + DebugTrace( 0, Dbg, "ServerCompletionMode = %08lx\n", ServerCompletionMode ); + DebugTrace( 0, Dbg, "MaximumInstances = %08lx\n", MaximumInstances ); + DebugTrace( 0, Dbg, "InboundQuota = %08lx\n", InboundQuota ); + DebugTrace( 0, Dbg, "OutboundQuota = %08lx\n", OutboundQuota ); + DebugTrace( 0, Dbg, "DefaultTimeout = %08lx\n", DefaultTimeout ); + DebugTrace( 0, Dbg, "TimeoutSpecified = %08lx\n", TimeoutSpecified ); + DebugTrace( 0, Dbg, "CreatorProcess = %08lx\n", CreatorProcess ); + + // + // Extract the create disposition + // + + CreateDisposition = (Options >> 24) & 0x000000ff; + + // + // Acquire exclusive access to the Vcb. + // + + NpAcquireExclusiveVcb(); + + try { + + // + // If there is a related file object then this is a relative open + // and it better be the root dcb. Both the then and the else clause + // return an Fcb. + // + + if (RelatedFileObject != NULL) { + + PDCB Dcb; + + Dcb = RelatedFileObject->FsContext; + + if (NodeType(Dcb) != NPFS_NTC_ROOT_DCB) { + + DebugTrace(0, Dbg, "Bad file name\n", 0); + + NpCompleteRequest( Irp, STATUS_OBJECT_NAME_INVALID ); + try_return( Status = STATUS_OBJECT_NAME_INVALID ); + } + + Fcb = NpFindRelativePrefix( Dcb, &FileName, CaseInsensitive, &RemainingPart ); + + } else { + + // + // The only nonrelative name we allow are of the form "\pipe-name" + // + + if ((FileName.Length <= 2) || (FileName.Buffer[0] != L'\\')) { + + DebugTrace(0, Dbg, "Bad file name\n", 0); + + NpCompleteRequest( Irp, STATUS_OBJECT_NAME_INVALID ); + try_return( Status = STATUS_OBJECT_NAME_INVALID ); + } + + Fcb = NpFindPrefix( &FileName, CaseInsensitive, &RemainingPart ); + } + + // + // If the remaining name is empty then we better have an fcb + // otherwise we were given a illegal object name. + // + + if (RemainingPart.Length == 0) { + + if (Fcb->NodeTypeCode == NPFS_NTC_FCB) { + + DebugTrace(0, Dbg, "Create existing named pipe, Fcb = %08lx\n", Fcb ); + + Irp->IoStatus = NpCreateExistingNamedPipe( Fcb, + FileObject, + DesiredAccess, + IrpSp->Parameters.CreatePipe.SecurityContext->AccessState, + (KPROCESSOR_MODE)(FlagOn(IrpSp->Flags, SL_FORCE_ACCESS_CHECK) ? + UserMode : Irp->RequestorMode), + CreateDisposition, + ShareAccess, + ServerReadMode, + ServerCompletionMode, + InboundQuota, + OutboundQuota, + CreatorProcess ); + Status = Irp->IoStatus.Status; + + } else { + + DebugTrace(0, Dbg, "Illegal object name\n", 0); + + Status = STATUS_OBJECT_NAME_INVALID; + } + + } else { + + // + // The remaining name is not empty so we better have the root Dcb + // + + if (Fcb->NodeTypeCode == NPFS_NTC_ROOT_DCB) { + + DebugTrace(0, Dbg, "Create new named pipe, Fcb = %08lx\n", Fcb ); + + Irp->IoStatus = NpCreateNewNamedPipe( Fcb, + FileObject, + FileName, + DesiredAccess, + IrpSp->Parameters.CreatePipe.SecurityContext->AccessState, + CreateDisposition, + ShareAccess, + NamedPipeType, + ServerReadMode, + ServerCompletionMode, + MaximumInstances, + InboundQuota, + OutboundQuota, + DefaultTimeout, + TimeoutSpecified, + CreatorProcess ); + Status = Irp->IoStatus.Status; + + } else { + + DebugTrace(0, Dbg, "Illegal object name\n", 0); + + Status = STATUS_OBJECT_NAME_INVALID; + } + } + + // + // Complete the IRP and return to our caller + // + + NpCompleteRequest( Irp, Status ); + + try_exit: NOTHING; + } finally { + + NpReleaseVcb( ); + + DebugTrace(-1, Dbg, "NpCommonCreateNamedPipe -> %08lx\n", Status); + } + + return Status; +} + + +// +// Internal support routine +// + +IO_STATUS_BLOCK +NpCreateNewNamedPipe ( + IN PROOT_DCB RootDcb, + IN PFILE_OBJECT FileObject, + IN UNICODE_STRING FileName, + IN ACCESS_MASK DesiredAccess, + IN PACCESS_STATE AccessState, + IN ULONG CreateDisposition, + IN USHORT ShareAccess, + IN ULONG NamedPipeType, + IN ULONG ServerReadMode, + IN ULONG ServerCompletionMode, + IN ULONG MaximumInstances, + IN ULONG InboundQuota, + IN ULONG OutboundQuota, + IN LARGE_INTEGER DefaultTimeout, + IN BOOLEAN TimeoutSpecified, + IN PEPROCESS CreatorProcess + ) + +/*++ + +Routine Description: + + This routine performs the operation for creating a new named pipe + Fcb and its first instance. This routine does not complete any + IRP, it preforms its function and then returns an iosb. + +Arguments: + + RootDcb - Supplies the root dcb where this is going to be added + + FileObject - Supplies the file object associated with the first + instance of the named pipe + + FileName - Supplies the name of the named pipe (not qualified i.e., + simply "pipe-name" and not "\pipe-name" + + DesiredAccess - Supplies the callers desired access + + AccessState - Supplies the access state from the irp + + CreateDisposition - Supplies the callers create disposition flags + + ShareAccess - Supplies the caller specified share access + + NamedPipeType - Supplies the named type type + + ServerReadMode - Supplies the named pipe read mode + + ServerCompletionMode - Supplies the named pipe completion mode + + MaximumInstances - Supplies the maximum instances for the named pipe + + InboundQuota - Supplies the inbound quota amount + + OutboundQuota - Supplies the outbound quota amount + + DefaultTimeout - Supplies the default time out value + + TimeoutSpecified - Indicates if the time out value was supplied by the + caller. + + CreatorProcess - Supplies the process creating the named pipe + +Return Value: + + IO_STATUS_BLOCK - Returns the appropriate status for the operation + +--*/ + +{ + // FIX, FIX - TEMPORARY ONLY. + UCHAR SDBody[SECURITY_DESCRIPTOR_MIN_LENGTH]; + PSECURITY_DESCRIPTOR TmpSecurityDescriptor = (PSECURITY_DESCRIPTOR)(&SDBody[0]); + // FIX, FIX - TEMPORARY ONLY. + + IO_STATUS_BLOCK Iosb; + + NAMED_PIPE_CONFIGURATION NamedPipeConfiguration; + + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpCreateNewNamedPipe\n", 0 ); + + Fcb = NULL; + Ccb = NULL; + + try { + + // + // Check the parameters that must be supplied for a new named pipe + // (i.e., the create disposition, timeout, and max instances better + // be greater than zero) + // + + if (!TimeoutSpecified || MaximumInstances <= 0) { + + try_return( Iosb.Status = STATUS_INVALID_PARAMETER ); + } + + // + // The default timeout needs to be less than zero otherwise it + // is an absolute time out which doesn't make sense. + // + + { + PLARGE_INTEGER Int = (PLARGE_INTEGER)&DefaultTimeout; + + if (Int->QuadPart >= 0) { + + try_return( Iosb.Status = STATUS_INVALID_PARAMETER ); + } + } + + if (CreateDisposition == FILE_OPEN) { + + try_return( Iosb.Status = STATUS_OBJECT_NAME_NOT_FOUND ); + } + + // + // Determine the pipe configuration + // + + if (ShareAccess == (FILE_SHARE_READ | FILE_SHARE_WRITE)) { + + NamedPipeConfiguration = FILE_PIPE_FULL_DUPLEX; + + } else if (ShareAccess == FILE_SHARE_READ) { + + NamedPipeConfiguration = FILE_PIPE_OUTBOUND; + + } else if (ShareAccess == FILE_SHARE_WRITE) { + + NamedPipeConfiguration = FILE_PIPE_INBOUND; + + } else { + + try_return( Iosb.Status = STATUS_INVALID_PARAMETER ); + } + + // + // Check that if named pipe type is byte stream then the read mode is + // not message mode + // + + if ((NamedPipeType == FILE_PIPE_BYTE_STREAM_TYPE) && + (ServerReadMode == FILE_PIPE_MESSAGE_MODE)) { + + try_return( Iosb.Status = STATUS_INVALID_PARAMETER ); + } + + // + // Create a new fcb and ccb for the named pipe + // + + Fcb = NpCreateFcb( RootDcb, + &FileName, + MaximumInstances, + DefaultTimeout, + NamedPipeConfiguration, + NamedPipeType ); + + Ccb = NpCreateCcb( Fcb, + FileObject, + FILE_PIPE_LISTENING_STATE, + ServerReadMode, + ServerCompletionMode, + CreatorProcess, + InboundQuota, + OutboundQuota ); + + // + // Set the security descriptor in the Fcb + // + + SeLockSubjectContext( &AccessState->SubjectSecurityContext ); + + Iosb.Status = SeAssignSecurity( NULL, + AccessState->SecurityDescriptor, + &Fcb->SecurityDescriptor, + FALSE, + &AccessState->SubjectSecurityContext, + IoGetFileObjectGenericMapping(), + PagedPool ); + + SeUnlockSubjectContext( &AccessState->SubjectSecurityContext ); + + if (!NT_SUCCESS(Iosb.Status)) { + + DebugTrace(0, Dbg, "Error calling SeAssignSecurity\n", 0 ); + + try_return( Iosb.Status ); + } + + // + // Set the new share access + // + + //IoSetShareAccess( DesiredAccess, + // ShareAccess, + // FileObject, + // &Ccb->ShareAccess ); + + // + // Set the file object back pointers and our pointer to the + // server file object. + // + + NpSetFileObject( FileObject, Ccb, Ccb->NonpagedCcb, FILE_PIPE_SERVER_END ); + Ccb->FileObject[ FILE_PIPE_SERVER_END ] = FileObject; + + // + // Check to see if we need to notify outstanding Irps for any + // changes (i.e., we just added a named pipe). + // + + NpCheckForNotify( RootDcb, TRUE ); + + // + // Set our return status + // + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = FILE_CREATED; + + try_exit: NOTHING; + } finally { + + if (AbnormalTermination()) { + + if (Ccb != NULL) { NpDeleteCcb( Ccb ); } + if (Fcb != NULL) { Fcb->OpenCount = 0; NpDeleteFcb( Fcb ); } + + } else { + + // + // Now if we ever terminate the preceding try-statement with + // a status that is not successful and the ccb or fcb pointer + // is non-null then we need to deallocate the structures + // + + if (!NT_SUCCESS(Iosb.Status) && Ccb != NULL) { NpDeleteCcb( Ccb ); } + if (!NT_SUCCESS(Iosb.Status) && Fcb != NULL) { Fcb->OpenCount = 0; NpDeleteFcb( Fcb ); } + } + + DebugTrace(-1, Dbg, "NpCreateNewNamedPipe -> %08lx\n", Iosb.Status); + } + + return Iosb; +} + + +// +// Internal support routine +// + +IO_STATUS_BLOCK +NpCreateExistingNamedPipe ( + IN PFCB Fcb, + IN PFILE_OBJECT FileObject, + IN ACCESS_MASK DesiredAccess, + IN PACCESS_STATE AccessState, + IN KPROCESSOR_MODE RequestorMode, + IN ULONG CreateDisposition, + IN USHORT ShareAccess, + IN ULONG ServerReadMode, + IN ULONG ServerCompletionMode, + IN ULONG InboundQuota, + IN ULONG OutboundQuota, + IN PEPROCESS CreatorProcess + ) + +/*++ + +Routine Description: + + This routine performs the operation for creating a new instance of + an existing named pipe. This routine does not complete any + IRP, it preforms its function and then returns an iosb. + +Arguments: + + Fcb - Supplies the Fcb for the named pipe being created + + FileObject - Supplies the file object associated with this + instance of the named pipe + + DesiredAccess - Supplies the callers desired access + + CreateDisposition - Supplies the callers create disposition flags + + ShareAccess - Supplies the caller specified share access + + ServerReadMode - Supplies the named pipe read mode + + ServerCompletionMode - Supplies the named pipe completion mode + + InboundQuota - Supplies the inbound quota amount + + OutboundQuota - Supplies the outbound quota amount + + CreatorProcess - Supplies the process creating the named pipe + +Return Value: + + IO_STATUS_BLOCK - Returns the appropriate status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + + BOOLEAN AccessGranted; + ACCESS_MASK GrantedAccess; + UNICODE_STRING Name; + + PCCB Ccb; + + NAMED_PIPE_CONFIGURATION NamedPipeConfiguration; + + USHORT OriginalShareAccess; + + PPRIVILEGE_SET Privileges = NULL; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpCreateExistingNamedPipe\n", 0 ); + + Ccb = NULL; + + try { + + // + // First do an access check for the user against the Fcb + // + + SeLockSubjectContext( &AccessState->SubjectSecurityContext ); + + AccessGranted = SeAccessCheck( Fcb->SecurityDescriptor, + &AccessState->SubjectSecurityContext, + TRUE, // Tokens are locked + DesiredAccess, + 0, + &Privileges, + IoGetFileObjectGenericMapping(), + RequestorMode, + &GrantedAccess, + &Iosb.Status ); + + if (Privileges != NULL) { + + (VOID) SeAppendPrivileges( + AccessState, + Privileges + ); + + SeFreePrivileges( Privileges ); + } + + if (AccessGranted) { + AccessState->PreviouslyGrantedAccess |= GrantedAccess; + AccessState->RemainingDesiredAccess &= ~GrantedAccess; + } + + + RtlInitUnicodeString( &Name, L"NamedPipe" ); + + SeOpenObjectAuditAlarm( &Name, + NULL, + &FileObject->FileName, + Fcb->SecurityDescriptor, + AccessState, + FALSE, + AccessGranted, + RequestorMode, + &AccessState->GenerateOnClose ); + + SeUnlockSubjectContext( &AccessState->SubjectSecurityContext ); + + if (!AccessGranted) { + + DebugTrace(0, Dbg, "Access Denied\n", 0 ); + + try_return( Iosb.Status ); + } + + // + // Check that we're still under the maximum instances count + // + + if (Fcb->OpenCount >= Fcb->Specific.Fcb.MaximumInstances) { + + try_return( Iosb.Status = STATUS_INSTANCE_NOT_AVAILABLE ); + } + + if (CreateDisposition == FILE_CREATE) { + + try_return( Iosb.Status = STATUS_ACCESS_DENIED ); + } + + // + // From the pipe configuration determine the share access specified + // on the first instance of this pipe. All subsequent instances must + // specify the same share access. + // + + NamedPipeConfiguration = Fcb->Specific.Fcb.NamedPipeConfiguration; + + if (NamedPipeConfiguration == FILE_PIPE_OUTBOUND) { + + OriginalShareAccess = FILE_SHARE_READ; + + } else if (NamedPipeConfiguration == FILE_PIPE_INBOUND) { + + OriginalShareAccess = FILE_SHARE_WRITE; + + } else { + + OriginalShareAccess = (FILE_SHARE_READ | FILE_SHARE_WRITE); + } + + if (OriginalShareAccess != ShareAccess) { + + try_return( Iosb.Status = STATUS_ACCESS_DENIED ); + } + + // + // Create a new ccb for the named pipe + // + + Ccb = NpCreateCcb( Fcb, + FileObject, + FILE_PIPE_LISTENING_STATE, + ServerReadMode, + ServerCompletionMode, + CreatorProcess, + InboundQuota, + OutboundQuota ); + + // + // Wake up anyone waiting for an instance to go into the listening state + // + + NpCancelWaiter( &NpVcb->WaitQueue, &Fcb->FullFileName ); + + // + // Set the new share access + // + + //IoSetShareAccess( DesiredAccess, + // ShareAccess, + // FileObject, + // &Ccb->ShareAccess ); + + // + // Set the file object back pointers and our pointer to the + // server file object. + // + + NpSetFileObject( FileObject, Ccb, Ccb->NonpagedCcb, FILE_PIPE_SERVER_END ); + Ccb->FileObject[ FILE_PIPE_SERVER_END ] = FileObject; + + // + // Check to see if we need to notify outstanding Irps for + // changes (i.e., we just added a new instance of a named pipe). + // + + NpCheckForNotify( Fcb->ParentDcb, FALSE ); + + // + // Set our return status + // + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = FILE_CREATED; + + try_exit: NOTHING; + } finally { + + if (AbnormalTermination()) { + + if (Ccb != NULL) { NpDeleteCcb( Ccb ); } + + } else { + + // + // Now if we ever terminate the preceding try-statement with + // a status that is not successful and the ccb pointer + // is non-null then we need to deallocate the structure + // + + if (!NT_SUCCESS(Iosb.Status) && Ccb != NULL) { NpDeleteCcb( Ccb ); } + } + + DebugTrace(-1, Dbg, "NpCreateExistingNamedPipe -> %08lx\n", Iosb.Status); + } + + return Iosb; +} + diff --git a/private/ntos/npfs/datasup.c b/private/ntos/npfs/datasup.c new file mode 100644 index 000000000..1adbace32 --- /dev/null +++ b/private/ntos/npfs/datasup.c @@ -0,0 +1,1077 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + DataSup.c + +Abstract: + + This module implements the Named Pipe data queue support routines. + +Author: + + Gary Kimura [GaryKi] 30-Aug-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_DATASUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpGetNextRealDataQueueEntry) +#pragma alloc_text(PAGE, NpInitializeDataQueue) +#pragma alloc_text(PAGE, NpUninitializeDataQueue) +#endif + +// +// The following macro is used to dump a data queue +// + +#define DumpDataQueue(S,P) { \ + ULONG NpDumpDataQueue(IN PDATA_QUEUE Ptr); \ + DebugTrace(0,Dbg,S,0); \ + DebugTrace(0,Dbg,"", NpDumpDataQueue(P)); \ +} + +// +// This is a debugging aid +// + +_inline BOOLEAN +NpfsVerifyDataQueue( IN ULONG Line, IN PDATA_QUEUE DataQueue ) { + PDATA_ENTRY Entry; + ULONG BytesInQueue = 0; + ULONG EntriesInQueue = 0; + for (Entry = DataQueue->FrontOfQueue; Entry != NULL; Entry = Entry->Next) { + BytesInQueue += Entry->DataSize; + EntriesInQueue += 1; + if (Entry->Next == NULL) { + if (Entry != DataQueue->EndOfQueue) { + DbgPrint("%d DataQueue does not end corretly %08lx\n", Line, DataQueue ); + DbgBreakPoint(); + } + } + } + if ((DataQueue->EntriesInQueue != EntriesInQueue) || + (DataQueue->BytesInQueue != BytesInQueue)) { + DbgPrint("%d DataQueue is illformed %08lx %x %x\n", Line, DataQueue, BytesInQueue, EntriesInQueue); + DbgBreakPoint(); + return FALSE; + } + return TRUE; +} + + +VOID +NpCancelDataQueueIrp ( + IN PDEVICE_OBJECT DevictObject, + IN PIRP Irp + ); + + +VOID +NpInitializeDataQueue ( + IN PDATA_QUEUE DataQueue, + IN PEPROCESS Process, + IN ULONG Quota + ) + +/*++ + +Routine Description: + + This routine initializes a new data queue. The indicated quota is taken + from the process and not returned until the data queue is uninitialized. + +Arguments: + + DataQueue - Supplies the data queue being initialized + + Process - Supplies a pointer to the process creating the named pipe + + Quota - Supplies the quota to assign to the data queue + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpInitializeDataQueue, DataQueue = %08lx\n", DataQueue); + + // + // First thing we do is get the process's quota, if we can't get it + // then this call will raise status + // + + ObReferenceObject( Process ); + PsChargePoolQuota( Process, NonPagedPool, Quota ); + + // + // Now we can initialize the data queue structure + // + + DataQueue->QueueState = Empty; + DataQueue->BytesInQueue = 0; + DataQueue->EntriesInQueue = 0; + DataQueue->Quota = Quota; + DataQueue->QuotaUsed = 0; + DataQueue->FrontOfQueue = NULL; + DataQueue->EndOfQueue = NULL; + DataQueue->NextByteOffset = 0; + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpInitializeDataQueue -> VOID\n", 0); + + return; +} + + +VOID +NpUninitializeDataQueue ( + IN PDATA_QUEUE DataQueue, + IN PEPROCESS Process + ) + +/*++ + +Routine Description: + + This routine uninitializes a data queue. The previously debited quota + is returned to the process. + +Arguments: + + DataQueue - Supplies the data queue being uninitialized + + Process - Supplies a pointer to the process who created the named pipe + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpUninitializeDataQueue, DataQueue = %08lx\n", DataQueue); + + // + // Assert that the queue is empty + // + + ASSERT( DataQueue->QueueState == Empty ); + + // + // Return all of our quota back to the process + // + + PsReturnPoolQuota( Process, NonPagedPool, DataQueue->Quota ); + ObDereferenceObject( Process ); + + // + // Then for safety sake we'll zero out the data queue structure + // + + RtlZeroMemory( DataQueue, sizeof(DATA_QUEUE ) ); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpUnininializeDataQueue -> VOID\n", 0); + + return; +} + + +PDATA_ENTRY +NpAddDataQueueEntry ( + IN PDATA_QUEUE DataQueue, + IN QUEUE_STATE Who, + IN DATA_ENTRY_TYPE Type, + IN ULONG DataSize, + IN PIRP Irp OPTIONAL, + IN PVOID DataPointer OPTIONAL + ) + +/*++ + +Routine Description: + + This routine adds a new data entry to the end of the data queue. + If necessary it will allocate a data entry buffer, or use space in + the IRP, and possibly complete the indicated IRP. + + The different actions we are perform are based on the type and who + parameters and quota requirements. + + Type == Internal (i.e, Unbuffered) + + +------+ - Allocate Data Entry from Irp + |Irp | +----------+ + | |<---|Unbuffered| - Reference Irp + +------+ |InIrp | + | +----------+ - Use system buffer from Irp + v | + +------+ | + |System|<-----+ + |Buffer| + +------+ + + Type == Buffered && Who == ReadEntries + + +----------+ - Allocate Data Entry from Irp + |Irp | +-----------+ + |BufferedIo|<----|Buffered | - Allocate New System buffer + |DeallBu...| |EitherQuota| + +----------+ +-----------+ - Reference and modify Irp to + | | | do Buffered I/O, Deallocate + v | v buffer, and have io completion + +------+ +------>+------+ copy the buffer (Input operation) + |User | |System| + |Buffer| |Buffer| + +------+ +------+ + + Type == Buffered && Who == WriteEntries && PipeQuota Available + + +----------+ - Allocate Data Entry from Quota + |Irp | +-----------+ + | | |Buffered | - Allocate New System buffer + | | |PipeQuota | + +----------+ +-----------+ - Copy data from User buffer to + | | system buffer + v v + +------+ +------+ - Complete Irp + |User |..copy..>|System| + |Buffer| |Buffer| + +------+ +------+ + + Type == Buffered && Who == WriteEntries && PipeQuota Not Available + + +----------+ - Allocate Data Entry from Irp + |Irp | +-----------+ + |BufferedIo|<----|Buffered | - Allocate New System buffer + |DeallBuff | |UserQuota | + +----------+ +-----------+ - Reference and modify Irp to use + | | | the new system buffer, do Buffered + v | v I/O, and Deallocate buffer + +------+ +------>+------+ + |User | |System| - Copy data from User buffer to + |Buffer|..copy..>|Buffer| system buffer + +------+ +------+ + + Type == Flush or Close + + +----------+ - Allocate Data Entry from Irp + |Irp | +-----------+ + | |<----|Buffered | - Reference the Irp + | | |UserQuota | + +----------+ +-----------+ + +Arguments: + + DataQueue - Supplies the Data queue being modified + + Who - Indicates if this is the reader or writer that is adding to the pipe + + Type - Indicates the type of entry to add to the data queue + + DataSize - Indicates the size of the data buffer needed to represent + this entry + + Irp - Supplies a pointer to the Irp responsible for this entry + The irp is only optional for buffered write with available pipe quota + + DataPointer - If the Irp is not supplied then this field points to the + user's write buffer. + +Return Value: + + PDATA_ENTRY - Returns a pointer to the newly added data entry + +--*/ + +{ + PDATA_ENTRY DataEntry; + PIRP IrpToComplete; + + // + // The following array indicates storage that needs to be deallocated + // on an abnormal unwind. + // + + PVOID Unwind[2] = { NULL, NULL }; + + ASSERT((DataQueue->QueueState == Empty) || (DataQueue->QueueState == Who)); + + DebugTrace(+1, Dbg, "NpAddDataQueueEntry, DataQueue = %08lx\n", DataQueue); + + IrpToComplete = NULL; + + try { + + // + // Case on the type of operation we are doing + // + + switch (Type) { + + case Unbuffered: + + ASSERT(ARGUMENT_PRESENT(Irp)); + + // + // Allocate a data entry from the Irp + // + + DataEntry = (PDATA_ENTRY)IoGetNextIrpStackLocation( Irp ); + + DataEntry->DataEntryType = Unbuffered; + DataEntry->From = InIrp; + DataEntry->Irp = Irp; + DataEntry->DataSize = DataSize; + DataEntry->DataPointer = Irp->AssociatedIrp.SystemBuffer; + + DataEntry->SecurityClientContext = NULL; + + ASSERT((DataQueue->QueueState == Empty) || (DataQueue->QueueState == Who)); + + break; + + case Buffered: + + // + // Check if this is the reader or writer + // + + if (Who == ReadEntries) { + + ASSERT(ARGUMENT_PRESENT(Irp)); + + // + // Allocate a data entry from the Irp, and allocate a new + // system buffer + // + + DataEntry = (PDATA_ENTRY)IoGetNextIrpStackLocation( Irp ); + + DataEntry->DataEntryType = Buffered; + DataEntry->Irp = Irp; + DataEntry->DataSize = DataSize; + + if ((DataQueue->Quota - DataQueue->QuotaUsed) >= DataSize) { + + DataEntry->DataPointer = Unwind[0] = (DataSize != 0 ? FsRtlAllocatePool( NonPagedPool, DataSize ) : NULL); + + DataQueue->QuotaUsed += DataSize; + + DataEntry->From = PipeQuota; + + } else { + + DataEntry->DataPointer = Unwind[1] = (DataSize != 0 ? FsRtlAllocatePoolWithQuota( NonPagedPool, DataSize ) : NULL); + + DataEntry->From = UserQuota; + } + + DataEntry->SecurityClientContext = NULL; + + // + // Modify the Irp to be buffered I/O, deallocate the buffer, copy + // the buffer on completion, and to reference the new system + // buffer + // + + if (DataSize != 0) { + + Irp->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION; + Irp->AssociatedIrp.SystemBuffer = DataEntry->DataPointer; + } + + ASSERT((DataQueue->QueueState == Empty) || (DataQueue->QueueState == Who)); + + } else { + + // + // This is a writer entry + // + + // + // If there is enough quota left in the pipe then we will + // allocate the data entry and data buffer from the pipe quota + // + + if ((DataQueue->Quota - DataQueue->QuotaUsed) >= sizeof(DATA_ENTRY) + DataSize) { + + DataEntry = Unwind[0] = FsRtlAllocatePool( NonPagedPool, sizeof(DATA_ENTRY) ); + + DataEntry->DataPointer = Unwind[1] = (DataSize != 0 ? FsRtlAllocatePool( NonPagedPool, DataSize ) : NULL); + + DataQueue->QuotaUsed += sizeof(DATA_ENTRY) + DataSize; + + DataEntry->DataEntryType = Buffered; + DataEntry->From = PipeQuota; + DataEntry->Irp = NULL; + DataEntry->DataSize = DataSize; + + DataEntry->SecurityClientContext = NULL; + + // + // Safely copy the user buffer to the new system buffer using either + // the irp user buffer is supplied of the data pointer we were given + // + + if (ARGUMENT_PRESENT(Irp)) { DataPointer = Irp->UserBuffer; } + + try { + + RtlCopyMemory( DataEntry->DataPointer, DataPointer, DataSize ); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + + IrpToComplete = Irp; + + ASSERT((DataQueue->QueueState == Empty) || (DataQueue->QueueState == Who)); + + } else { + + ASSERT(ARGUMENT_PRESENT(Irp)); + + // + // There isn't enough pipe quota to do this so we will + // use the user quota + // + // Allocate a data entry from the Irp, and allocate a new + // system buffer + // + + DataEntry = (PDATA_ENTRY)IoGetNextIrpStackLocation( Irp ); + + DataEntry->DataEntryType = Buffered; + DataEntry->From = UserQuota; + DataEntry->Irp = Irp; + DataEntry->DataSize = DataSize; + + DataEntry->SecurityClientContext = NULL; + + DataEntry->DataPointer = Unwind[0] = (DataSize != 0 ? FsRtlAllocatePoolWithQuota( NonPagedPool, DataSize ) : NULL); + + // + // Safely copy the user buffer to the new system buffer + // + + try { + + RtlCopyMemory( DataEntry->DataPointer, Irp->UserBuffer, DataSize ); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + + // + // Modify the Irp to be buffered I/O, deallocate the buffer + // and to reference the new system buffer + // + + if (DataSize != 0) { + + Irp->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER; + Irp->AssociatedIrp.SystemBuffer = DataEntry->DataPointer; + } + + ASSERT((DataQueue->QueueState == Empty) || (DataQueue->QueueState == Who)); + } + } + + break; + + case Flush: + case Close: + + ASSERT(ARGUMENT_PRESENT(Irp)); + + // + // Allocate a data entry from the Irp + // + + DataEntry = (PDATA_ENTRY)IoGetNextIrpStackLocation( Irp ); + + DataEntry->DataEntryType = Type; + DataEntry->From = InIrp; + DataEntry->Irp = Irp; + DataEntry->DataSize = 0; + DataEntry->DataPointer = NULL; + + DataEntry->SecurityClientContext = NULL; + + ASSERT((DataQueue->QueueState == Empty) || (DataQueue->QueueState == Who)); + + break; + } + + ASSERT((DataQueue->QueueState == Empty) || (DataQueue->QueueState == Who)); + + // + // Now data entry points to a new data entry to add to the data queue + // Check if the queue is empty otherwise we will add this entry to + // the end of the queue + // + + DataEntry->Next = NULL; + + if (DataQueue->QueueState == Empty) { + + DataQueue->QueueState = Who; + DataQueue->BytesInQueue = DataEntry->DataSize; + DataQueue->EntriesInQueue = 1; + DataQueue->FrontOfQueue = DataEntry; + DataQueue->EndOfQueue = DataEntry; + + } else { + + ASSERT( DataQueue->QueueState == Who ); + + DataQueue->BytesInQueue += DataEntry->DataSize; + DataQueue->EntriesInQueue += 1; + DataQueue->EndOfQueue->Next = DataEntry; + + DataQueue->EndOfQueue = DataEntry; + } + + } finally { + + // + // If this is an abnormal termination then deallocate any storage + // that we may have allocated + // + + if (AbnormalTermination()) { + + if (Unwind[0] != NULL) { ExFreePool( Unwind[0] ); } + if (Unwind[1] != NULL) { ExFreePool( Unwind[1] ); } + + } else { + + if (IrpToComplete != NULL) { + + NpCompleteRequest( IrpToComplete, STATUS_SUCCESS ); + + } else if (ARGUMENT_PRESENT(Irp)) { + + IoAcquireCancelSpinLock( &Irp->CancelIrql ); + Irp->IoStatus.Status = (ULONG)DataQueue; + + if (Irp->Cancel) { + + // + // Indicate in the first parameter that we're calling the + // cancel routine and not the I/O system. Therefore + // the routine won't take out the VCB exclusive. + // + + NpCancelDataQueueIrp( ((PVOID)0x1), Irp ); + + } else { + + IoSetCancelRoutine( Irp, NpCancelDataQueueIrp ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + } + } + } + + DumpDataQueue( "After AddDataQueueEntry\n", DataQueue ); + DebugTrace(-1, Dbg, "NpAddDataQueueEntry -> %08lx\n", DataEntry); + } + + // + // And return to our caller + // + + return DataEntry; +} + + +PIRP +NpRemoveDataQueueEntry ( + IN PDATA_QUEUE DataQueue + ) + +/*++ + +Routine Description: + + This routines remove the first entry from the front of the indicated + data queue, and possibly returns the Irp associated with the entry if + it wasn't already completed when we did the insert. + + If the data entry we are removing indicates buffered I/O then we also + need to deallocate the data buffer besides the data entry but only + if the Irp is null. Note that the data entry might be stored in an IRP. + If it is then we are going to return the IRP it is stored in. + +Arguments: + + DataQueue - Supplies a pointer to the data queue being modifed + +Return Value: + + PIRP - Possibly returns a pointer to an IRP. + +--*/ + +{ + PDATA_ENTRY DataEntry; + + DATA_ENTRY_TYPE DataEntryType; + FROM From; + PIRP Irp; + ULONG DataSize; + PVOID DataPointer; + PSECURITY_CLIENT_CONTEXT ClientContext; + + DebugTrace(+1, Dbg, "NpRemoveDataQueueEntry, DataQueue = %08lx\n", DataQueue); + + // + // Check if the queue is empty, and if so then we simply return null + // + + if (DataQueue->QueueState == Empty) { + + Irp = NULL; + + } else { + + // + // Reference the front of the data queue, and remove the entry + // from the queue itself. + // + + DataEntry = DataQueue->FrontOfQueue; + DataQueue->FrontOfQueue = DataEntry->Next; + DataQueue->BytesInQueue -= DataEntry->DataSize; + DataQueue->EntriesInQueue -= 1; + + // + // Now if the queue is empty we need to reset the end of queue and + // queue state + // + + if (DataQueue->FrontOfQueue == NULL) { + + DataQueue->EndOfQueue = NULL; + DataQueue->QueueState = Empty; + } + + // + // Capture some of the fields from the data entry to make our + // other references a little easier + // + + DataEntryType = DataEntry->DataEntryType; + From = DataEntry->From; + Irp = DataEntry->Irp; + DataSize = DataEntry->DataSize; + DataPointer = DataEntry->DataPointer; + ClientContext = DataEntry->SecurityClientContext; + + // + // Check if we should delete the client context + // + + if (ClientContext != NULL) { + + SeDeleteClientSecurity( ClientContext ); + ExFreePool( ClientContext ); + } + + // + // Check if we need to deallocate the data buffer, we only need + // to deallocate it if it is buffered and the Irp is null + // + + if (DataEntryType == Buffered) { + + if ((Irp == NULL) && (DataPointer != NULL)) { + + ExFreePool( DataPointer ); + } + + // + // Now the preceding call returned the user's quota or it + // simply deallocated the buffer. If it only deallocated + // the buffer then we need to credit our quota + // + + if (From == PipeQuota) { + + DataQueue->QuotaUsed -= DataSize; + } + } + + // + // Now check if we still have an IRP to return. If we do then + // we know that this data entry is located in the current IRP + // stack location and we need to zero out the data entry (skipping + // over the spare field), otherwise we got allocated from either + // the pipe or user quota, and we need to deallocate it ourselves. + // + // Note that we'll keep the data entry type field intact so that + // out caller will know if this is an internal read operation. + // + + if (Irp != NULL) { + + DataEntry->From = 0; + DataEntry->Next = 0; + DataEntry->Irp = 0; + DataEntry->DataSize = 0; + DataEntry->DataPointer = 0; + + DataEntry->SecurityClientContext = NULL; + + IoAcquireCancelSpinLock( &Irp->CancelIrql ); + IoSetCancelRoutine( Irp, NULL ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + } else { + + if (DataPointer != NULL) { + + ExFreePool( DataEntry ); + } + + // + // Now the preceding call returned the user's quota or it + // simply deallocated the buffer. If it only deallocated + // the buffer then we need to credit our quota + // + + if (From == PipeQuota) { + + DataQueue->QuotaUsed -= sizeof(DATA_ENTRY); + } + } + } + + // + // In all cases we'll also zero out the next byte offset. + // + + DataQueue->NextByteOffset = 0; + + // + // And return to our caller + // + + DumpDataQueue( "After RemoveDataQueueEntry\n", DataQueue ); + DebugTrace(-1, Dbg, "NpRemoveDataQueueEntry -> %08lx\n", Irp); + + return Irp; +} + + +PDATA_ENTRY +NpGetNextRealDataQueueEntry ( + IN PDATA_QUEUE DataQueue + ) + +/*++ + +Routine Description: + + This routine will returns a pointer to the next real data queue entry + in the indicated data queue. A real entry is either a read or write + entry (i.e., buffered or unbuffered). It will complete (as necessary) + any flush and close Irps that are in the queue until either the queue + is empty or a real data queue entry is at the front of the queue. + +Arguments: + + DataQueue - Supplies a pointer to the data queue being modified + +Return Value: + + PDATA_ENTRY - Returns a pointer to the next data queue entry or NULL + if there isn't any. + +--*/ + +{ + PDATA_ENTRY DataEntry; + PIRP Irp; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpGetNextRealDataQueueEntry, DataQueue = %08lx\n", DataQueue); + + // + // While the next data queue entry at the head of the data queue is not + // a real data queue entry we'll dequeue that entry and complete + // its corresponding IRP. + // + + for (DataEntry = NpGetNextDataQueueEntry( DataQueue, NULL); + + (DataEntry != NULL) && + ((DataEntry->DataEntryType != Buffered) && + (DataEntry->DataEntryType != Unbuffered)); + + DataEntry = NpGetNextDataQueueEntry( DataQueue, NULL)) { + + // + // We have a non real data queue entry that needs to be removed + // and completed. + // + + Irp = NpRemoveDataQueueEntry( DataQueue ); + + if (Irp != NULL) { + + NpCompleteRequest( Irp, STATUS_SUCCESS ); + } + } + + // + // At this point we either have an empty data queue and data entry is + // null, or we have a real data queue entry. In either case it + // is time to return to our caller + // + + DebugTrace(-1, Dbg, "NpGetNextRealDataQueueEntry -> %08lx\n", DataEntry); + + return DataEntry; +} + + +// +// Local support routine +// + +VOID +NpCancelDataQueueIrp ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the cancel function for an IRP saved in a + data queue + +Arguments: + + DeviceObject - Generally ignored but the low order bit is a flag indicating + if we are being called locally (i.e., not from the I/O system) and + therefore don't need to take out the VCB. + + Irp - Supplies the Irp being cancelled. A pointer to the data queue + structure is stored in the information field of the Irp Iosb + field. + +Return Value: + + None. + +--*/ + +{ + PDATA_QUEUE DataQueue; + + PDATA_ENTRY DataEntry; + PDATA_ENTRY *Previous; + + // + // The status field is used to store a pointer to the data queue + // containing this irp + // + + DataQueue = (PDATA_QUEUE)Irp->IoStatus.Status; + + // + // We now need to void the cancel routine and release the io cancel spinlock + // + + IoSetCancelRoutine( Irp, NULL ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + // + // Get exclusive access to the named pipe vcb so we can now do our work + // but only if we need to + // + + if (DeviceObject != (PVOID)0x1) { NpAcquireExclusiveVcb(); } + + try { + + // + // Scan through the data queue looking for entries that have Irps + // that have been cancelled. We use previous to point to the pointer to + // the data entry we're examining. We cannot do this in a for loop + // because we'll have a tough time setting up ourselves for another iteration + // when we remove an entry + // + + Previous = &DataQueue->FrontOfQueue; + DataEntry = *Previous; + + while (DataEntry != NULL) { + + // + // If the data entry contains an Irp and the irp is cancelled then + // we have some work do to + // + + if ((DataEntry->Irp != NULL) && (DataEntry->Irp->Cancel)) { + + DATA_ENTRY_TYPE DataEntryType; + FROM From; + PIRP Irp; + ULONG DataSize; + PVOID DataPointer; + PSECURITY_CLIENT_CONTEXT ClientContext; + + // + // First remove this data entry from the queue + // Later we will fixup Data entry to the next entry + // + + *Previous = DataEntry->Next; + + // + // If the queue is now empty then we need to fix the queue + // state and end of queue pointer + // + + if (DataQueue->FrontOfQueue == NULL) { + + DataQueue->EndOfQueue = NULL; + DataQueue->QueueState = Empty; + + // + // If we removed the last entry in the list then we need to update + // the end of the queue + // + + } else if (DataEntry == DataQueue->EndOfQueue) { + + DataQueue->EndOfQueue = (PDATA_ENTRY)Previous; + } + + // + // Capture some of the fields from the data entry to make our + // other references a little easier + // + + DataEntryType = DataEntry->DataEntryType; + From = DataEntry->From; + Irp = DataEntry->Irp; + DataSize = DataEntry->DataSize; + DataPointer = DataEntry->DataPointer; + ClientContext = DataEntry->SecurityClientContext; + + // + // Check if we should delete the client context + // + + if (ClientContext != NULL) { + + SeDeleteClientSecurity( ClientContext ); + ExFreePool( ClientContext ); + } + + // + // Check if we need to return pipe quota for a buffered entry. + // + + if ((DataEntryType == Buffered) && (From == PipeQuota)) { + + DataQueue->QuotaUsed -= DataSize; + } + + // + // Update the data queue header information + // + + DataQueue->BytesInQueue -= DataSize; + DataQueue->EntriesInQueue -= 1; + + // + // Zero our the data entry + // + + DataEntry->From = 0; + DataEntry->Next = 0; + DataEntry->Irp = 0; + DataEntry->DataSize = 0; + DataEntry->DataPointer = 0; + + DataEntry->SecurityClientContext = NULL; + + // + // If what we removed is in the front of the queue then we must + // reset the next byte offset + // + + if (Previous == &DataQueue->FrontOfQueue) { + + DataQueue->NextByteOffset = 0; + } + + // + // Finally complete the request saying that it has been cancelled. + // + + NpCompleteRequest( Irp, STATUS_CANCELLED ); + + // + // And because the data entry is gone we now need to reset + // it so we can continue our while loop + // + + DataEntry = *Previous; + + } else { + + // + // Skip over to the next data entry + // + + Previous = &DataEntry->Next; + DataEntry = DataEntry->Next; + } + } + + } finally { + + if (DeviceObject != (PVOID)0x1) { NpReleaseVcb(); } + } + + // + // And return to our caller + // + + return; +} diff --git a/private/ntos/npfs/deviosup.c b/private/ntos/npfs/deviosup.c new file mode 100644 index 000000000..e476f860b --- /dev/null +++ b/private/ntos/npfs/deviosup.c @@ -0,0 +1,141 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + DevIoSup.c + +Abstract: + + This module implements the memory locking routines for Npfs. + +Author: + + Brian Andrew [BrianAn] 03-Apr-1991 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// Local debug trace level +// + +#define Dbg (DEBUG_TRACE_DEVIOSUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpLockUserBuffer) +#pragma alloc_text(PAGE, NpMapUserBuffer) +#endif + + +PVOID +NpMapUserBuffer ( + IN OUT PIRP Irp + ) + +/*++ + +Routine Description: + + This routine conditionally maps the user buffer for the current I/O + request in the specified mode. If the buffer is already mapped, it + just returns its address. + +Arguments: + + Irp - Pointer to the Irp for the request. + +Return Value: + + Mapped address + +--*/ + +{ + PAGED_CODE(); + + // + // If there is no Mdl, then we must be in the Fsd, and we can simply + // return the UserBuffer field from the Irp. + // + + if (Irp->MdlAddress == NULL) { + + return Irp->UserBuffer; + + } else { + + return MmGetSystemAddressForMdl( Irp->MdlAddress ); + } +} + + +VOID +NpLockUserBuffer ( + IN OUT PIRP Irp, + IN LOCK_OPERATION Operation, + IN ULONG BufferLength + ) + +/*++ + +Routine Description: + + This routine locks the specified buffer for the specified type of + access. The file system requires this routine since it does not + ask the I/O system to lock its buffers for direct I/O. This routine + may only be called from the Fsd while still in the user context. + +Arguments: + + Irp - Pointer to the Irp for which the buffer is to be locked. + + Operation - IoWriteAccess for read operations, or IoReadAccess for + write operations. + + BufferLength - Length of user buffer. + +Return Value: + + None + +--*/ + +{ + PMDL Mdl; + + PAGED_CODE(); + + if (Irp->MdlAddress == NULL) { + + // + // This read is bound for the current process. Perform the + // same functions as above, only do not switch processes. + // + + Mdl = IoAllocateMdl( Irp->UserBuffer, BufferLength, FALSE, TRUE, Irp ); + + if (Mdl == NULL) { + + ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); + } + + try { + + MmProbeAndLockPages( Mdl, + Irp->RequestorMode, + Operation ); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + IoFreeMdl( Mdl ); + Irp->MdlAddress = NULL; + ExRaiseStatus( FsRtlNormalizeNtstatus( GetExceptionCode(), + STATUS_INVALID_USER_BUFFER )); + } + } +} diff --git a/private/ntos/npfs/dir.c b/private/ntos/npfs/dir.c new file mode 100644 index 000000000..ec660c487 --- /dev/null +++ b/private/ntos/npfs/dir.c @@ -0,0 +1,882 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + Dir.c + +Abstract: + + This module implements the File Directory routines for the Named Pipe + file system by the dispatch driver. + +Author: + + Gary Kimura [GaryKi] 28-Dec-1989 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (NPFS_BUG_CHECK_DIR) + +// +// Local debug trace level +// + +#define Dbg (DEBUG_TRACE_DIR) + +NTSTATUS +NpCommonDirectoryControl ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpQueryDirectory ( + IN PROOT_DCB RootDcb, + IN PROOT_DCB_CCB Ccb, + IN PIRP Irp + ); + +NTSTATUS +NpNotifyChangeDirectory ( + IN PROOT_DCB RootDcb, + IN PROOT_DCB_CCB Ccb, + IN PIRP Irp + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpCheckForNotify) +#pragma alloc_text(PAGE, NpCommonDirectoryControl) +#pragma alloc_text(PAGE, NpFsdDirectoryControl) +#pragma alloc_text(PAGE, NpNotifyChangeDirectory) +#pragma alloc_text(PAGE, NpQueryDirectory) +#endif + + +NTSTATUS +NpFsdDirectoryControl ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the FSD routine that handles directory control + functions (i.e., query and notify). + +Arguments: + + NpfsDeviceObject - Supplies the device object for the directory function. + + Irp - Supplies the IRP to process + +Return Value: + + NTSTATUS - The appropriate result status + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpFsdDirectoryControl\n", 0); + + // + // Call the common Direcotry Control routine. + // + + FsRtlEnterFileSystem(); + NpAcquireExclusiveVcb( ); + + try { + + Status = NpCommonDirectoryControl( NpfsDeviceObject, Irp ); + + } except(NpExceptionFilter( GetExceptionCode() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = NpProcessException( NpfsDeviceObject, Irp, GetExceptionCode() ); + } + + NpReleaseVcb(); + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpFsdDirectoryControl -> %08lx\n", Status ); + + return Status; +} + +VOID +NpCheckForNotify ( + IN PDCB Dcb, + IN BOOLEAN CheckAllOutstandingIrps + ) + +/*++ + +Routine Description: + + This routine checks the notify queues of a dcb and completes any + outstanding IRPS. + + Note that the caller of this procedure must guarantee that the DCB + is acquired for exclusive access. + +Arguments: + + Dcb - Supplies the Dcb to check if is has any notify Irps outstanding + + CheckAllOutstandingIrps - Indicates if only the NotifyFullQueue should be + checked. If TRUE then all notify queues are checked, and if FALSE + then only the NotifyFullQueue is checked. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY Links; + PIRP Irp; + + PAGED_CODE(); + + // + // We'll always signal the notify full queue entries. They want + // to be notified if every any change is made to a directory + // + + while (!IsListEmpty( &Dcb->Specific.Dcb.NotifyFullQueue )) { + + // + // Remove the Irp from the head of the queue, and complete it + // with success. + // + + Links = RemoveHeadList( &Dcb->Specific.Dcb.NotifyFullQueue ); + + Irp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry ); + + NpCompleteRequest( Irp, STATUS_SUCCESS ); + } + + // + // Now check if we should also do the partial notify queue. + // + + if (CheckAllOutstandingIrps) { + + while (!IsListEmpty( &Dcb->Specific.Dcb.NotifyPartialQueue )) { + + // + // Remove the Irp from the head of the queue, and complete it + // with success. + // + + Links = RemoveHeadList( &Dcb->Specific.Dcb.NotifyPartialQueue ); + + Irp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry ); + + NpCompleteRequest( Irp, STATUS_SUCCESS ); + } + } + + return; +} + + +// +// Local Support Routine +// + +NTSTATUS +NpCommonDirectoryControl ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine does the common code for directory control functions. + +Arguments: + + NpfsDeviceObject - Supplies the named pipe device object + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp; + + PFCB Fcb; + PROOT_DCB_CCB Ccb; + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpCommonDirectoryControl...\n", 0); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); + + // + // Decode the file object to figure out who we are. If the result + // is not the root dcb then its an illegal parameter. + // + + if (NpDecodeFileObject( IrpSp->FileObject, + &Fcb, + (PCCB *)&Ccb, + NULL ) != NPFS_NTC_ROOT_DCB) { + + DebugTrace(0, Dbg, "Not a directory\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); + Status = STATUS_INVALID_PARAMETER; + + DebugTrace(-1, Dbg, "NpCommonDirectoryControl -> %08lx\n", Status ); + return Status; + } + + // + // We know this is a directory control so we'll case on the + // minor function, and call the appropriate work routines. + // + + switch (IrpSp->MinorFunction) { + + case IRP_MN_QUERY_DIRECTORY: + + Status = NpQueryDirectory( Fcb, Ccb, Irp ); + break; + + case IRP_MN_NOTIFY_CHANGE_DIRECTORY: + + Status = NpNotifyChangeDirectory( Fcb, Ccb, Irp ); + break; + + default: + + // + // For all other minor function codes we say they're invalid + // and complete the request. + // + + DebugTrace(0, DEBUG_TRACE_ERROR, "Invalid FS Control Minor Function Code %08lx\n", IrpSp->MinorFunction); + + NpCompleteRequest( Irp, STATUS_INVALID_DEVICE_REQUEST ); + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + DebugTrace(-1, Dbg, "NpCommonDirectoryControl -> %08lx\n", Status); + return Status; +} + + +// +// Internal support routine +// + +NTSTATUS +NpQueryDirectory ( + IN PROOT_DCB RootDcb, + IN PROOT_DCB_CCB Ccb, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the work routine for querying a directory. + +Arugments: + + RootDcb - Supplies the dcb being queried + + Ccb - Supplies the context of the caller + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The return status for the operation. + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + PUCHAR Buffer; + CLONG SystemBufferLength; + + UNICODE_STRING FileName; + ULONG FileIndex; + FILE_INFORMATION_CLASS FileInformationClass; + BOOLEAN RestartScan; + BOOLEAN ReturnSingleEntry; + BOOLEAN IndexSpecified; + + static WCHAR Star = L'*'; + + BOOLEAN CaseInsensitive = TRUE; //*** Make searches case insensitive + + ULONG CurrentIndex; + + ULONG LastEntry; + ULONG NextEntry; + + PLIST_ENTRY Links; + PFCB Fcb; + + PFILE_DIRECTORY_INFORMATION DirInfo; + PFILE_NAMES_INFORMATION NamesInfo; + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpQueryDirectory\n", 0 ); + DebugTrace( 0, Dbg, "RootDcb = %08lx\n", RootDcb); + DebugTrace( 0, Dbg, "Ccb = %08lx\n", Ccb); + DebugTrace( 0, Dbg, "SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer); + DebugTrace( 0, Dbg, "Length = %08lx\n", IrpSp->Parameters.QueryDirectory.Length); + DebugTrace( 0, Dbg, "FileName = %Z\n", IrpSp->Parameters.QueryDirectory.FileName); + DebugTrace( 0, Dbg, "FileIndex = %08lx\n", IrpSp->Parameters.QueryDirectory.FileIndex); + DebugTrace( 0, Dbg, "FileInformationClass = %08lx\n", IrpSp->Parameters.QueryDirectory.FileInformationClass); + DebugTrace( 0, Dbg, "RestartScan = %08lx\n", FlagOn(IrpSp->Flags, SL_RESTART_SCAN)); + DebugTrace( 0, Dbg, "ReturnSingleEntry = %08lx\n", FlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY)); + DebugTrace( 0, Dbg, "IndexSpecified = %08lx\n", FlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED)); + + // + // Save references to the input parameters within the Irp + // + + SystemBufferLength = IrpSp->Parameters.QueryDirectory.Length; + + FileIndex = IrpSp->Parameters.QueryDirectory.FileIndex; + + FileInformationClass = IrpSp->Parameters.QueryDirectory.FileInformationClass; + + RestartScan = BooleanFlagOn(IrpSp->Flags, SL_RESTART_SCAN); + ReturnSingleEntry = BooleanFlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY); + IndexSpecified = BooleanFlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED); + + if (IrpSp->Parameters.QueryDirectory.FileName != NULL) { + + FileName = *(PUNICODE_STRING)IrpSp->Parameters.QueryDirectory.FileName; + + } else { + + FileName.Length = 0; + FileName.Buffer = NULL; + } + + // + // Check if the ccb already has a query template attached. If it + // does not already have one then we either use the string we are + // given or we attach our own containing "*" + // + + if (Ccb->QueryTemplate == NULL) { + + // + // This is our first time calling query directory so we need + // to either set the query template to the user specified string + // or to "*" + // + + if (FileName.Buffer == NULL) { + + DebugTrace(0, Dbg, "Set template to *\n", 0); + + FileName.Length = 2; + FileName.Buffer = ⋆ + } + + DebugTrace(0, Dbg, "Set query template -> %Z\n", &FileName); + + // + // Allocate space for the query template + // + + Ccb->QueryTemplate = FsRtlAllocatePool( PagedPool, + sizeof(UNICODE_STRING) + FileName.Length ); + + // + // Initialize the query template and copy over the string + // + + Ccb->QueryTemplate->Length = FileName.Length; + Ccb->QueryTemplate->Buffer = (PWCH)Ccb->QueryTemplate + + sizeof(UNICODE_STRING) / sizeof(WCHAR); + + RtlCopyMemory( Ccb->QueryTemplate->Buffer, + FileName.Buffer, + FileName.Length ); + + // + // Now zero out the FileName so we won't think we're to use it + // as a subsearch string. + // + + FileName.Length = 0; + FileName.Buffer = NULL; + } + + // + // Check if we were given an index to start with or if we need to + // restart the scan or if we should use the index that was saved in + // the ccb + // + + if (RestartScan) { + + FileIndex = 0; + + } else if (!IndexSpecified) { + + FileIndex = Ccb->IndexOfLastCcbReturned + 1; + } + + // + // Now we are committed to completing the Irp, we do that in + // the finally clause of the following try. + // + + try { + + ULONG BaseLength; + ULONG LengthAdded; + + // + // Map the user buffer. + // + + Buffer = NpMapUserBuffer( Irp ); + + // + // At this point we are about to enter our query loop. We have + // already decided which Fcb index we need to return. The variables + // LastEntry and NextEntry are used to index into the user buffer. + // LastEntry is the last entry we added to the user buffer, and + // NextEntry is the current one we're working on. CurrentIndex + // is the Fcb index that we are looking at next. Logically the + // way the loop works is as follows. + // + // Scan all of the Fcb in the directory + // + // if the Fcb matches the query template then + // + // if the CurrentIndex is >= the FileIndex then + // + // process this fcb, and decide if we should + // continue the main loop + // + // end if + // + // Increment the current index + // + // end if + // + // end scan + // + + CurrentIndex = 0; + + LastEntry = 0; + NextEntry =0; + + switch (FileInformationClass) { + + case FileDirectoryInformation: + + BaseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, + FileName[0] ); + break; + + case FileFullDirectoryInformation: + + BaseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, + FileName[0] ); + break; + + case FileNamesInformation: + + BaseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION, + FileName[0] ); + break; + + case FileBothDirectoryInformation: + + BaseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, + FileName[0] ); + break; + + default: + + try_return( Status = STATUS_INVALID_INFO_CLASS ); + } + + for (Links = RootDcb->Specific.Dcb.ParentDcbQueue.Flink; + Links != &RootDcb->Specific.Dcb.ParentDcbQueue; + Links = Links->Flink) { + + Fcb = CONTAINING_RECORD(Links, FCB, ParentDcbLinks); + + ASSERT(Fcb->NodeTypeCode == NPFS_NTC_FCB); + + DebugTrace(0, Dbg, "Top of Loop\n", 0); + DebugTrace(0, Dbg, "Fcb = %08lx\n", Fcb); + DebugTrace(0, Dbg, "CurrentIndex = %08lx\n", CurrentIndex); + DebugTrace(0, Dbg, "FileIndex = %08lx\n", FileIndex); + DebugTrace(0, Dbg, "LastEntry = %08lx\n", LastEntry); + DebugTrace(0, Dbg, "NextEntry = %08lx\n", NextEntry); + + // + // Check if the Fcb represents a named pipe that is part of + // our query template + // + + if (FsRtlIsNameInExpression( Ccb->QueryTemplate, + &Fcb->LastFileName, + CaseInsensitive, + NULL )) { + + // + // The fcb is in the query template so now check if + // this is the index we should start returning + // + + if (CurrentIndex >= FileIndex) { + + ULONG BytesToCopy; + ULONG BytesRemainingInBuffer; + + // + // Here are the rules concerning filling up the buffer: + // + // 1. The Io system garentees that there will always be + // enough room for at least one base record. + // + // 2. If the full first record (including file name) cannot + // fit, as much of the name as possible is copied and + // STATUS_BUFFER_OVERFLOW is returned. + // + // 3. If a subsequent record cannot completely fit into the + // buffer, none of it (as in 0 bytes) is copied, and + // STATUS_SUCCESS is returned. A subsequent query will + // pick up with this record. + // + + BytesRemainingInBuffer = SystemBufferLength - NextEntry; + + if ( (NextEntry != 0) && + ( (BaseLength + Fcb->LastFileName.Length > BytesRemainingInBuffer) || + (SystemBufferLength < NextEntry) ) ) { + + DebugTrace(0, Dbg, "Next entry won't fit\n", 0); + + try_return( Status = STATUS_SUCCESS ); + } + + ASSERT( BytesRemainingInBuffer >= BaseLength ); + + // + // See how much of the name we will be able to copy into + // the system buffer. This also dictates out return + // value. + // + + if ( BaseLength + Fcb->LastFileName.Length <= + BytesRemainingInBuffer ) { + + BytesToCopy = Fcb->LastFileName.Length; + Status = STATUS_SUCCESS; + + } else { + + BytesToCopy = BytesRemainingInBuffer - BaseLength; + Status = STATUS_BUFFER_OVERFLOW; + } + + // + // Note how much of buffer we are consuming and zero + // the base part of the structure. + // + + LengthAdded = BaseLength + BytesToCopy; + + RtlZeroMemory( &Buffer[NextEntry], BaseLength ); + + // + // Now fill the base parts of the strucure that are + // applicable. + // + + switch (FileInformationClass) { + + case FileBothDirectoryInformation: + + // + // We don't need short name + // + + DebugTrace(0, Dbg, "Getting directory full information\n", 0); + + case FileFullDirectoryInformation: + + // + // We don't use EaLength, so fill in nothing here. + // + + DebugTrace(0, Dbg, "Getting directory full information\n", 0); + + case FileDirectoryInformation: + + DebugTrace(0, Dbg, "Getting directory information\n", 0); + + // + // The eof indicates the number of instances and + // allocation size is the maximum allowed + // + + DirInfo = (PFILE_DIRECTORY_INFORMATION)&Buffer[NextEntry]; + + DirInfo->EndOfFile.QuadPart = Fcb->OpenCount; + DirInfo->AllocationSize.QuadPart = Fcb->Specific.Fcb.MaximumInstances; + + DirInfo->FileAttributes = FILE_ATTRIBUTE_NORMAL; + + DirInfo->FileNameLength = Fcb->LastFileName.Length; + + break; + + case FileNamesInformation: + + DebugTrace(0, Dbg, "Getting names information\n", 0); + + + NamesInfo = (PFILE_NAMES_INFORMATION)&Buffer[NextEntry]; + + NamesInfo->FileNameLength = Fcb->LastFileName.Length; + + break; + + default: + + NpBugCheck( FileInformationClass, 0, 0 ); + } + + RtlCopyMemory( &Buffer[NextEntry + BaseLength], + Fcb->LastFileName.Buffer, + BytesToCopy ); + + // + // Update the ccb to the index we've just used + // + + Ccb->IndexOfLastCcbReturned = CurrentIndex; + + // + // And indicate how much of the system buffer we have + // currently used up. We must compute this value before + // we long align outselves for the next entry + // + + Irp->IoStatus.Information = NextEntry + LengthAdded; + + // + // Setup the previous next entry offset + // + + *((PULONG)(&Buffer[LastEntry])) = NextEntry - LastEntry; + + // + // Check if the last entry didn't completely fit + // + + if ( Status == STATUS_BUFFER_OVERFLOW ) { + + try_return( NOTHING ); + } + + // + // Check if we are only to return a single entry + // + + if (ReturnSingleEntry) { + + try_return( Status = STATUS_SUCCESS ); + } + + // + // Set ourselves up for the next iteration + // + + LastEntry = NextEntry; + NextEntry += (ULONG)QuadAlign( LengthAdded ); + } + + // + // Increment the current index by one + // + + CurrentIndex += 1; + } + } + + // + // At this point we've scanned the entire list of Fcb so if + // the NextEntry is zero then we haven't found anything so we + // will return no more files, otherwise we return success. + // + + if (NextEntry == 0) { + + Status = STATUS_NO_MORE_FILES; + + } else { + + Status = STATUS_SUCCESS; + } + + try_exit: NOTHING; + } finally { + + if (!AbnormalTermination()) { + + NpCompleteRequest( Irp, Status ); + } + + DebugTrace(-1, Dbg, "NpQueryDirectory -> %08lx\n", Status); + } + + return Status; +} + + +// +// Internal support routine +// + +NTSTATUS +NpNotifyChangeDirectory ( + IN PROOT_DCB RootDcb, + IN PROOT_DCB_CCB Ccb, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for doing the notify change directory. + +Arugments: + + RootDcb - Supplies the dcb being queried + + Ccb - Supplies the context of the caller + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - STATUS_PENDING + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + + UNREFERENCED_PARAMETER( Ccb ); + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpNotifyChangeDirectory\n", 0 ); + DebugTrace( 0, Dbg, "RootDcb = %08lx", RootDcb); + DebugTrace( 0, Dbg, "Ccb = %08lx", Ccb); + + // + // Mark the Irp pending. + // + + IoMarkIrpPending( Irp ); + + // + // BUGBUG - For now, simply place the packet on one of the old queues, + // full or partial, based on whether or not changes to anything + // other than names were requested. In the future, the filter + // must actually be implemented. + // + + if (IrpSp->Parameters.NotifyDirectory.CompletionFilter & + ~FILE_NOTIFY_CHANGE_NAME) { + + InsertTailList( &RootDcb->Specific.Dcb.NotifyFullQueue, + &Irp->Tail.Overlay.ListEntry ); + + } else { + + InsertTailList( &RootDcb->Specific.Dcb.NotifyPartialQueue, + &Irp->Tail.Overlay.ListEntry ); + } + + // + // return to our caller a value of status pending + // + + DebugTrace(-1, Dbg, "NpNotifyChangeDirectory -> STATUS_PENDING\n", 0); + + return STATUS_PENDING; +} diff --git a/private/ntos/npfs/dumpsup.c b/private/ntos/npfs/dumpsup.c new file mode 100644 index 000000000..54a7aa35a --- /dev/null +++ b/private/ntos/npfs/dumpsup.c @@ -0,0 +1,491 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + DumpSup.c + +Abstract: + + This module implements a collection of data structure dump routines + for debugging the Named Pipe file system + +Author: + + Gary Kimura [GaryKi] 21-Aug-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +#ifdef NPDBG + +VOID NpDumpEventTableEntry(IN PEVENT_TABLE_ENTRY Ptr); +VOID NpDumpDataQueue(IN PDATA_QUEUE Ptr); +VOID NpDumpDataEntry(IN PDATA_ENTRY Ptr); + +VOID NpDump(IN PVOID Ptr); +VOID NpDumpVcb(IN PVCB Ptr); +VOID NpDumpRootDcb(IN PROOT_DCB Ptr); +VOID NpDumpFcb(IN PFCB Ptr); +VOID NpDumpCcb(IN PCCB Ptr); +VOID NpDumpNonpagedCcb(IN PNONPAGED_CCB Ptr); +VOID NpDumpRootDcbCcb(IN PROOT_DCB_CCB Ptr); + +ULONG NpDumpCurrentColumn; + +#define DumpNewLine() { \ + DbgPrint("\n"); \ + NpDumpCurrentColumn = 1; \ +} + +#define DumpLabel(Label,Width) { \ + ULONG i; \ + CHAR _Str[20]; \ + for(i=0;i<2;i++) { _Str[i] = UCHAR_SP;} \ + strncpy(&_Str[2],#Label,Width); \ + for(i=strlen(_Str);i<Width;i++) {_Str[i] = UCHAR_SP;} \ + _Str[Width] = '\0'; \ + DbgPrint("%s", _Str); \ +} + +#define DumpField(Field) { \ + if ((NpDumpCurrentColumn + 18 + 9 + 9) > 80) {DumpNewLine();} \ + NpDumpCurrentColumn += 18 + 9 + 9; \ + DumpLabel(Field,18); \ + DbgPrint(":%8lx", Ptr->Field); \ + DbgPrint(" "); \ +} + +#define DumpListEntry(Links) { \ + if ((NpDumpCurrentColumn + 18 + 9 + 9) > 80) {DumpNewLine();} \ + NpDumpCurrentColumn += 18 + 9 + 9; \ + DumpLabel(Links,18); \ + DbgPrint(":%8lx", Ptr->Links.Flink); \ + DbgPrint(":%8lx", Ptr->Links.Blink); \ +} + +#define DumpName(Field,Width) { \ + ULONG i; \ + WCHAR _String[64]; \ + if ((NpDumpCurrentColumn + 18 + Width) > 80) {DumpNewLine();} \ + NpDumpCurrentColumn += 18 + Width; \ + DumpLabel(Field,18); \ + for(i=0;i<Width/2;i++) {_String[i] = Ptr->Field[i];} \ + _String[Width] = '\0'; \ + DbgPrint("%s", _String); \ +} + +#define TestForNull(Name) { \ + if (Ptr == NULL) { \ + DbgPrint("%s - Cannot dump a NULL pointer\n", Name); \ + return; \ + } \ +} + + +VOID NpDumpEventTableEntry ( + IN PEVENT_TABLE_ENTRY Ptr + ) + +{ + TestForNull ("NpDumpEventTableEntry"); + + DumpNewLine (); + DbgPrint ("EventTableEntry@ %08lx", (Ptr)); + DumpNewLine (); + + DumpField (Ccb); + DumpField (NamedPipeEnd); + DumpField (EventHandle); + DumpField (Event); + DumpField (KeyValue); + DumpField (Process); + DumpNewLine (); + + return; +} + + +VOID NpDumpDataQueue ( + IN PDATA_QUEUE Ptr + ) + +{ + PDATA_ENTRY Entry; + + TestForNull ("NpDumpDataQueue"); + + DumpNewLine (); + DbgPrint ("DataQueue@ %08lx", (Ptr)); + DumpNewLine (); + + DumpField (QueueState); + DumpField (BytesInQueue); + DumpField (EntriesInQueue); + DumpField (Quota); + DumpField (QuotaUsed); + DumpField (FrontOfQueue); + DumpField (EndOfQueue); + DumpField (NextByteOffset); + DumpNewLine (); + + for (Entry = Ptr->FrontOfQueue; + Entry != NULL; + Entry = Entry->Next) { + + NpDumpDataEntry( Entry ); + } + + return; +} + + +VOID NpDumpDataEntry ( + IN PDATA_ENTRY Ptr + ) + +{ + TestForNull ("NpDumpDataEntry"); + + DumpNewLine (); + DbgPrint ("DataEntry@ %08lx", (Ptr)); + DumpNewLine (); + + DumpField (DataEntryType); + DumpField (From); + DumpField (Next); + DumpField (Irp); + DumpField (DataSize); + DumpField (DataPointer); + DumpField (SecurityClientContext); + DumpNewLine (); + + return; +} + + +VOID NpDump ( + IN PVOID Ptr + ) + +/*++ + +Routine Description: + + This routine determines the type of internal record reference by ptr and + calls the appropriate dump routine. + +Arguments: + + Ptr - Supplies the pointer to the record to be dumped + +Return Value: + + None + +--*/ + +{ + TestForNull("NpDump"); + + // + // We'll switch on the node type code + // + + switch (NodeType(Ptr)) { + + case NPFS_NTC_VCB: NpDumpVcb(Ptr); break; + case NPFS_NTC_ROOT_DCB: NpDumpRootDcb(Ptr); break; + case NPFS_NTC_FCB: NpDumpFcb(Ptr); break; + case NPFS_NTC_CCB: NpDumpCcb(Ptr); break; + case NPFS_NTC_NONPAGED_CCB: NpDumpNonpagedCcb(Ptr); break; + case NPFS_NTC_ROOT_DCB_CCB: NpDumpRootDcbCcb(Ptr); break; + + default : + DbgPrint("NpDump - Unknown Node type code %8lx\n", *((PNODE_TYPE_CODE)(Ptr))); + break; + } + + return; +} + + +VOID NpDumpVcb ( + IN PVCB Ptr + ) + +/*++ + +Routine Description: + + Dump an Vcb structure + +Arguments: + + Ptr - Supplies the Device record to be dumped + +Return Value: + + None + +--*/ + +{ + TestForNull ("NpDumpVcb"); + + DumpNewLine (); + DbgPrint ("Vcb@ %lx", (Ptr)); + DumpNewLine (); + + DumpField (NodeTypeCode); + DumpField (NodeByteSize); + DumpField (RootDcb); + DumpField (OpenCount); + DumpField (OpenUnderlingCount); + DumpNewLine (); + + NpDump (Ptr->RootDcb); + + return; +} + + +VOID NpDumpRootDcb ( + IN PROOT_DCB Ptr + ) + +/*++ + +Routine Description: + + Dump a root dcb structure + +Arguments: + + Ptr - Supplies the Root Dcb record to be dumped + +Return Value: + + None + +--*/ + +{ + PLIST_ENTRY Links; + + TestForNull ("NpDumpRootDcb"); + + DumpNewLine (); + DbgPrint ("RootDcb@ %lx", (Ptr)); + DumpNewLine (); + + DumpField (NodeTypeCode); + DumpField (NodeByteSize); + DumpListEntry (ParentDcbLinks); + DumpField (ParentDcb); + DumpField (OpenCount); + DumpField (FullFileName.Length); + DumpField (FullFileName.Buffer); + DumpName (FullFileName.Buffer, 32); + DumpField (LastFileName.Length); + DumpField (LastFileName.Buffer); + DumpListEntry (Specific.Dcb.NotifyFullQueue); + DumpListEntry (Specific.Dcb.NotifyPartialQueue); + DumpListEntry (Specific.Dcb.ParentDcbQueue); + DumpNewLine (); + + for (Links = Ptr->Specific.Dcb.ParentDcbQueue.Flink; + Links != &Ptr->Specific.Dcb.ParentDcbQueue; + Links = Links->Flink) { + NpDump(CONTAINING_RECORD(Links, FCB, ParentDcbLinks)); + } + + return; +} + + +VOID NpDumpFcb ( + IN PFCB Ptr + ) + +/*++ + +Routine Description: + + Dump an Fcb structure + +Arguments: + + Ptr - Supplies the Fcb record to be dumped + +Return Value: + + None + +--*/ + +{ + PLIST_ENTRY Links; + + TestForNull ("NpDumpFcb"); + + DumpNewLine (); + DbgPrint ("Fcb@ %lx", (Ptr)); + DumpNewLine (); + + DumpField (NodeTypeCode); + DumpField (NodeByteSize); + DumpListEntry (ParentDcbLinks); + DumpField (ParentDcb); + DumpField (OpenCount); + DumpField (FullFileName.Length); + DumpField (FullFileName.Buffer); + DumpName (FullFileName.Buffer, 32); + DumpField (LastFileName.Length); + DumpField (LastFileName.Buffer); + DumpField (Specific.Fcb.NamedPipeConfiguration); + DumpField (Specific.Fcb.NamedPipeType); + DumpField (Specific.Fcb.MaximumInstances); + DumpField (Specific.Fcb.DefaultTimeOut.LowPart); + DumpField (Specific.Fcb.DefaultTimeOut.HighPart); + DumpListEntry (Specific.Fcb.CcbQueue); + DumpNewLine (); + + for (Links = Ptr->Specific.Fcb.CcbQueue.Flink; + Links != &Ptr->Specific.Fcb.CcbQueue; + Links = Links->Flink) { + NpDump(CONTAINING_RECORD(Links, CCB, CcbLinks)); + } + + return; +} + + +VOID NpDumpCcb ( + IN PCCB Ptr + ) + +/*++ + +Routine Description: + + Dump a Ccb structure + +Arguments: + + Ptr - Supplies the Ccb record to be dumped + +Return Value: + + None + +--*/ + +{ + TestForNull ("NpDumpCcb"); + + DumpNewLine (); + DbgPrint ("Ccb@ %lx", (Ptr)); + DumpNewLine (); + + DumpField (NodeTypeCode); + DumpField (NodeByteSize); + DumpField (Fcb); + DumpField (FileObject[0]); + DumpField (FileObject[1]); + DumpField (NamedPipeState); + DumpField (ReadMode[0]); + DumpField (ReadMode[1]); + DumpField (CompletionMode[0]); + DumpField (CompletionMode[1]); + DumpField (CreatorProcess); + DumpField (SecurityClientContext); + DumpNewLine (); + + NpDumpDataQueue(&Ptr->DataQueue[0]); + NpDumpDataQueue(&Ptr->DataQueue[1]); + + NpDump (Ptr->NonpagedCcb); + + return; +} + + +VOID NpDumpNonpagedCcb ( + IN PNONPAGED_CCB Ptr + ) + +/*++ + +Routine Description: + + Dump a Nonpaged Ccb structure + +Arguments: + + Ptr - Supplies the Nonpaged Ccb record to be dumped + +Return Value: + + None + +--*/ + +{ + TestForNull ("NpDumpNonpagedCcb"); + + DumpNewLine (); + DbgPrint ("NonpagedCcb@ %lx", (Ptr)); + DumpNewLine (); + + DumpField (NodeTypeCode); + DumpField (NodeByteSize); + DumpField (EventTableEntry[0]); + DumpField (EventTableEntry[1]); + DumpListEntry (ListeningQueue); + DumpNewLine (); + + return; +} + + +VOID NpDumpRootDcbCcb ( + IN PROOT_DCB_CCB Ptr + ) + +/*++ + +Routine Description: + + Dump a Root Dcb Ccb structure + +Arguments: + + Ptr - Supplies the Root Dcb Ccb record to be dumped + +Return Value: + + None + +--*/ + +{ + TestForNull ("NpDumpRootDcbCcb"); + + DumpNewLine (); + DbgPrint ("RootDcbCcb@ %lx", (Ptr)); + DumpNewLine (); + + DumpField (NodeTypeCode); + DumpField (NodeByteSize); + DumpField (IndexOfLastCcbReturned); + DumpNewLine (); + + return; +} + +#endif // NPDBG diff --git a/private/ntos/npfs/eventsup.c b/private/ntos/npfs/eventsup.c new file mode 100644 index 000000000..438fde7da --- /dev/null +++ b/private/ntos/npfs/eventsup.c @@ -0,0 +1,391 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + EventSup.c + +Abstract: + + This module implements the Named Pipe Event support routines. + +Author: + + Gary Kimura [GaryKi] 30-Aug-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_EVENTSUP) + + +// +// Local procedures +// + + +PEVENT_TABLE_ENTRY +NpAddEventTableEntry ( + IN PEVENT_TABLE EventTable, + IN PCCB Ccb, + IN NAMED_PIPE_END NamedPipeEnd, + IN HANDLE EventHandle, + IN ULONG KeyValue, + IN PEPROCESS Process, + IN KPROCESSOR_MODE PreviousMode + ) + +/*++ + +Routine Description: + + This routine adds a new entry into the event table. If an entry already + exists it overwrites the existing entry. + +Arguments: + + EventTable - Supplies the event table being modified + + Ccb - Supplies a pointer to the ccb to store in event table entry + + NamedPipeEnd - Indicates the server or client end for the event + + EventHandle - Supplies the handle to the event being added. The object + is referenced by this procedure + + KeyValue - Supplies a key value to associate with the event + + Process - Supplies a pointer to the process adding the event + + PreviousMode - Supplies the mode of the user initiating the action + +Return Value: + + PEVENT_TABLE_ENTRY - Returns a pointer to the newly added event. + This is an actual pointer to the table entry. + + This procedure also will raise status if the event handle cannot be + accessed by the caller + +--*/ + +{ + NTSTATUS Status; + KIRQL OldIrql; + + EVENT_TABLE_ENTRY Template; + PEVENT_TABLE_ENTRY EventTableEntry; + PVOID Event; + + DebugTrace(+1, Dbg, "NpAddEventTableEntry, EventTable = %08lx\n", EventTable); + + // + // Reference the event object by handle. + // + + if (!NT_SUCCESS(Status = ObReferenceObjectByHandle( EventHandle, + EVENT_MODIFY_STATE, + NULL, + PreviousMode, + &Event, + NULL ))) { + + ExRaiseStatus( Status ); + } + + // + // Set up the template event entry to lookup + // + + Template.Ccb = Ccb; + Template.NamedPipeEnd = NamedPipeEnd; + Template.EventHandle = EventHandle; + Template.Event = Event; + Template.KeyValue = KeyValue; + Template.Process = Process; + + // + // Now insert this new entry into the event table + // + + EventTableEntry = RtlInsertElementGenericTable( &EventTable->Table, + &Template, + sizeof(EVENT_TABLE_ENTRY), + NULL ); + + // + // Copy over the template again just in case we were given an + // old entry + // + + *EventTableEntry = Template; + + DebugTrace(-1, Dbg, "NpAddEventTableEntry -> %08lx\n", EventTableEntry); + + // + // And now return to our caller + // + + return EventTableEntry; +} + + +VOID +NpDeleteEventTableEntry ( + IN PEVENT_TABLE EventTable, + IN PEVENT_TABLE_ENTRY Template + ) + +/*++ + +Routine Description: + + This routine removes an entry from the event table, it also dereferences + the event object that was referenced when the object was inserted + +Arguments: + + EventTable - Supplies a pointer to the event table being modified + + Template - Supplies a copy of the event table entry we are lookin up. + Note that this can also be a pointer to the actual event table entry. + +Return Value: + + None. + +--*/ + +{ + KIRQL OldIrql; + + DebugTrace(+1, Dbg, "NpDeleteEventTableEntry, EventTable = %08lx\n", EventTable); + + // + // Only do the work if we are given a non null template + // + + if (!ARGUMENT_PRESENT(Template)) { + + DebugTrace(-1, Dbg, "NpDeleteEventTableEntry -> VOID\n", 0); + + return; + } + + // + // Dereference the event object + // + + ObDereferenceObject(Template->Event); + + // + // Now remove this element from the generic table + // + + (VOID)RtlDeleteElementGenericTable( &EventTable->Table, + Template ); + + DebugTrace(-1, Dbg, "NpDeleteEventTableEntry -> VOID\n", 0); + + // + // And now return to our caller + // + + return; +} + + +PEVENT_TABLE_ENTRY +NpGetNextEventTableEntry ( + IN PEVENT_TABLE EventTable, + IN PVOID *RestartKey + ) + +/*++ + +Routine Description: + + This routine enumerates the events stored within an event table. + +Arguments: + + EventTable - Supplies a pointer to the event being enumerated + + Restart - Indicates if the enumeration should restart or continue + +Return Value: + + PEVENT_TABLE_ENTRY - Returns a pointer to the next event table entry + in the table, or NULL if the enumeration is complete. + +--*/ + +{ + KIRQL OldIrql; + PEVENT_TABLE_ENTRY EventTableEntry; + + DebugTrace(+1, Dbg, "NpGetNextEventTableEntry, EventTable = %08lx\n", EventTable); + + // + // Lookup the next element in the table + // + + EventTableEntry = RtlEnumerateGenericTableWithoutSplaying( &EventTable->Table, RestartKey ); + + DebugTrace(-1, Dbg, "NpGetNextEventTableEntry -> %08lx\n", EventTableEntry); + + // + // And now return to our caller + // + + return EventTableEntry; +} + + +// +// Local support routines +// + +RTL_GENERIC_COMPARE_RESULTS +NpEventTableCompareRoutine ( + IN PRTL_GENERIC_TABLE EventTable, + IN PVOID FirstStruct, + IN PVOID SecondStruct + ) + +/*++ + +Routine Description: + + This routine is the comparsion routine for the Event Table which is + implemented as a generic table. + +Arguments: + + EventTable - Supplies a pointer to the event table which is involved + in this action + + FirstStruct - Supplies a pointer to the first event table entry to examine + + SecondStruct - Supplies a pointer to the second event table entry to + examine + +Return Value: + + RTL_GENERIC_COMPARE_RESULTS - GenericLessThan if FirstEntry is less than + SecondEntry, GenericGreaterThan if FirstEntry is greater than + SecondEntry, and GenericEqual otherwise. + +--*/ + +{ + PEVENT_TABLE_ENTRY FirstEntry = FirstStruct; + PEVENT_TABLE_ENTRY SecondEntry = SecondStruct; + + UNREFERENCED_PARAMETER( EventTable ); + + // + // We'll compare first the pointer to the ccb and then compare the + // pipe end types. This will guarantee a unique ordering based on + // the pipe instance and pipe end (i.e., server and client end). + // + + if (FirstEntry->Ccb < SecondEntry->Ccb) { + + return GenericLessThan; + + } else if (FirstEntry->Ccb > SecondEntry->Ccb) { + + return GenericGreaterThan; + + } else if (FirstEntry->NamedPipeEnd < SecondEntry->NamedPipeEnd) { + + return GenericLessThan; + + } else if (FirstEntry->NamedPipeEnd > SecondEntry->NamedPipeEnd) { + + return GenericGreaterThan; + + } else { + + return GenericEqual; + } +} + + +// +// Local support routines +// + +PVOID +NpEventTableAllocate ( + IN PRTL_GENERIC_TABLE EventTable, + IN CLONG ByteSize + ) + +/*++ + +Routine Description: + + This routine is the generic allocation routine for the event table. + +Arguments: + + EventTable - Supplies a pointer to the event table being used + + ByteSize - Supplies the size, in bytes, to allocate. + +Return Value: + + PVOID - Returns a pointer to the newly allocated buffer. + +--*/ + +{ + return FsRtlAllocatePool( (POOL_TYPE)EventTable->TableContext, ByteSize ); +} + + +// +// Local support routines +// + +VOID +NpEventTableDeallocate ( + IN PRTL_GENERIC_TABLE EventTable, + IN PVOID Buffer + ) + +/*++ + +Routine Description: + + This routine is the generic deallocation routine for the event table. + +Arguments: + + EventTable - Supplies a pointer to the event table being used + + Buffer - Supplies the buffer being deallocated + +Return Value: + + None. + +--*/ + +{ + UNREFERENCED_PARAMETER( EventTable ); + + ExFreePool( Buffer ); + + return; +} diff --git a/private/ntos/npfs/fileinfo.c b/private/ntos/npfs/fileinfo.c new file mode 100644 index 000000000..0f2f8d323 --- /dev/null +++ b/private/ntos/npfs/fileinfo.c @@ -0,0 +1,1412 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + FileInfo.c + +Abstract: + + This module implements the File Info routines for NPFS called by the + dispatch driver. There are two entry points NpFsdQueryInformation + and NpFsdSetInformation. + +Author: + + Gary Kimura [GaryKi] 21-Aug-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (NPFS_BUG_CHECK_FILEINFO) + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_FILEINFO) + +// +// local procedure prototypes +// + +NTSTATUS +NpCommonQueryInformation ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + + +NTSTATUS +NpCommonSetInformation ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpQueryBasicInfo ( + IN PCCB Ccb, + IN PFILE_BASIC_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +NpQueryStandardInfo ( + IN PCCB Ccb, + IN PFILE_STANDARD_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +NpQueryInternalInfo ( + IN PCCB Ccb, + IN PFILE_INTERNAL_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +NpQueryEaInfo ( + IN PCCB Ccb, + IN PFILE_EA_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +NpQueryNameInfo ( + IN PCCB Ccb, + IN PFILE_NAME_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +NpQueryPositionInfo ( + IN PCCB Ccb, + IN PFILE_POSITION_INFORMATION Buffer, + IN OUT PULONG Length, + IN NAMED_PIPE_END NamedPipeEnd + ); + +NTSTATUS +NpQueryPipeInfo ( + IN PFCB Fcb, + IN PCCB Ccb, + IN PFILE_PIPE_INFORMATION Buffer, + IN OUT PULONG Length, + IN NAMED_PIPE_END NamedPipeEnd + ); + +NTSTATUS +NpQueryPipeLocalInfo ( + IN PFCB Fcb, + IN PCCB Ccb, + IN PFILE_PIPE_LOCAL_INFORMATION Buffer, + IN OUT PULONG Length, + IN NAMED_PIPE_END NamedPipeEnd + ); + +NTSTATUS +NpSetBasicInfo ( + IN PCCB Ccb, + IN PFILE_BASIC_INFORMATION Buffer + ); + +NTSTATUS +NpSetPipeInfo ( + IN PFCB Fcb, + IN PCCB Ccb, + IN PFILE_PIPE_INFORMATION Buffer, + IN NAMED_PIPE_END NamedPipeEnd + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpCommonQueryInformation) +#pragma alloc_text(PAGE, NpCommonSetInformation) +#pragma alloc_text(PAGE, NpFsdQueryInformation) +#pragma alloc_text(PAGE, NpFsdSetInformation) +#pragma alloc_text(PAGE, NpQueryBasicInfo) +#pragma alloc_text(PAGE, NpQueryEaInfo) +#pragma alloc_text(PAGE, NpQueryInternalInfo) +#pragma alloc_text(PAGE, NpQueryNameInfo) +#pragma alloc_text(PAGE, NpQueryPipeInfo) +#pragma alloc_text(PAGE, NpQueryPipeLocalInfo) +#pragma alloc_text(PAGE, NpQueryPositionInfo) +#pragma alloc_text(PAGE, NpQueryStandardInfo) +#pragma alloc_text(PAGE, NpSetBasicInfo) +#pragma alloc_text(PAGE, NpSetPipeInfo) +#endif + + +NTSTATUS +NpFsdQueryInformation ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtQueryInformationFile API + calls. + +Arguments: + + NpfsDeviceObject - Supplies the device object to use. + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The Fsd status for the Irp + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpFsdQueryInformation\n", 0); + + // + // Call the common Query Information routine. + // + + FsRtlEnterFileSystem(); + + NpAcquireSharedVcb(); + + try { + + Status = NpCommonQueryInformation( NpfsDeviceObject, Irp ); + + } except(NpExceptionFilter( GetExceptionCode() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = NpProcessException( NpfsDeviceObject, Irp, GetExceptionCode() ); + } + + NpReleaseVcb(); + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpFsdQueryInformation -> %08lx\n", Status ); + + return Status; +} + + +NTSTATUS +NpFsdSetInformation ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtSetInformationFile API + calls. + +Arguments: + + NpfsDeviceObject - Supplies the device object to use. + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The Fsd status for the Irp + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpFsdSetInformation\n", 0); + + // + // Call the common Set Information routine. + // + + FsRtlEnterFileSystem(); + + NpAcquireExclusiveVcb(); + + try { + + Status = NpCommonSetInformation( NpfsDeviceObject, Irp ); + + } except(NpExceptionFilter( GetExceptionCode() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = NpProcessException( NpfsDeviceObject, Irp, GetExceptionCode() ); + } + + NpReleaseVcb(); + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpFsdSetInformation -> %08lx\n", Status ); + + return Status; +} + +// +// Internal support routine +// + +NTSTATUS +NpCommonQueryInformation ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for creating/opening a file. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - the return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + NTSTATUS Status; + + ULONG Length; + FILE_INFORMATION_CLASS FileInformationClass; + PVOID Buffer; + + NODE_TYPE_CODE NodeTypeCode; + PFCB Fcb; + PCCB Ccb; + NAMED_PIPE_END NamedPipeEnd; + + PFILE_ALL_INFORMATION AllInfo; + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpCommonQueryInformation...\n", 0); + DebugTrace( 0, Dbg, " Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg, " ->Length = %08lx\n", IrpSp->Parameters.QueryFile.Length); + DebugTrace( 0, Dbg, " ->FileInformationClass = %08lx\n", IrpSp->Parameters.QueryFile.FileInformationClass); + DebugTrace( 0, Dbg, " ->Buffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer); + + // + // Get the ccb and figure out who we are, and make sure we're not + // disconnected. + // + + if ((NodeTypeCode = NpDecodeFileObject( IrpSp->FileObject, + &Fcb, + &Ccb, + &NamedPipeEnd )) == NTC_UNDEFINED) { + + DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + Status = STATUS_PIPE_DISCONNECTED; + + DebugTrace(-1, Dbg, "NpCommonQueryInformation -> %08lx\n", Status ); + return Status; + } + + // + // Case on the type of the context, We can only query information + // on an Fcb, Dcb, or Root Dcb. If we are not passed on of these + // we immediately tell the caller that there is an invalid parameter. + // + + if (NodeTypeCode != NPFS_NTC_CCB) { + + DebugTrace(0, Dbg, "Node type code is not ccb\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "NpCommonQueryInformation -> STATUS_INVALID_PARAMETER\n", 0); + return STATUS_INVALID_PARAMETER; + } + + // + // Reference our input parameter to make things easier + // + + Length = IrpSp->Parameters.QueryFile.Length; + FileInformationClass = IrpSp->Parameters.QueryFile.FileInformationClass; + Buffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Based on the information class we'll do different actions. Each + // of the procedure that we're calling fill up as much of the + // buffer as possible and return the remaining length, and status + // This is done so that we can use them to build up the + // FileAllInformation request. These procedures do not complete the + // Irp, instead this procedure must complete the Irp. + // + + switch (FileInformationClass) { + + case FileAllInformation: + + // + // For the all information class we'll typecast a local + // pointer to the output buffer and then call the + // individual routines to fill in the buffer. + // + + AllInfo = Buffer; + + Length -= (sizeof(FILE_ACCESS_INFORMATION) + + sizeof(FILE_MODE_INFORMATION) + + sizeof(FILE_ALIGNMENT_INFORMATION)); + + // + // Only the QueryName call can return non-success + // + + (VOID)NpQueryBasicInfo( Ccb, &AllInfo->BasicInformation, &Length ); + (VOID)NpQueryStandardInfo( Ccb, &AllInfo->StandardInformation, &Length ); + (VOID)NpQueryInternalInfo( Ccb, &AllInfo->InternalInformation, &Length ); + (VOID)NpQueryEaInfo( Ccb, &AllInfo->EaInformation, &Length ); + (VOID)NpQueryPositionInfo( Ccb, &AllInfo->PositionInformation, &Length, NamedPipeEnd ); + + Status = NpQueryNameInfo( Ccb, &AllInfo->NameInformation, &Length ); + + break; + + case FileBasicInformation: + + Status = NpQueryBasicInfo( Ccb, Buffer, &Length ); + break; + + case FileStandardInformation: + + Status = NpQueryStandardInfo( Ccb, Buffer, &Length ); + break; + + case FileInternalInformation: + + Status = NpQueryInternalInfo( Ccb, Buffer, &Length ); + break; + + case FileEaInformation: + + Status = NpQueryEaInfo( Ccb, Buffer, &Length ); + break; + + case FilePositionInformation: + + Status = NpQueryPositionInfo( Ccb, Buffer, &Length, NamedPipeEnd ); + break; + + case FileNameInformation: + + Status = NpQueryNameInfo( Ccb, Buffer, &Length ); + break; + + case FilePipeInformation: + + Status = NpQueryPipeInfo( Fcb, Ccb, Buffer, &Length, NamedPipeEnd ); + break; + + case FilePipeLocalInformation: + + Status = NpQueryPipeLocalInfo( Fcb, Ccb, Buffer, &Length, NamedPipeEnd ); + break; + + default: + + Status = STATUS_INVALID_PARAMETER; + break; + } + + // + // Set the information field to the number of bytes actually filled in + // and then complete the request + // + + Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - Length; + NpCompleteRequest( Irp, Status ); + + DebugTrace(-1, Dbg, "NpCommonQueryInformation -> %08lx\n", Status ); + return Status; +} + + +// +// Internal support routine +// + +NTSTATUS +NpCommonSetInformation ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for creating/opening a file. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - the return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + NTSTATUS Status; + + ULONG Length; + FILE_INFORMATION_CLASS FileInformationClass; + PVOID Buffer; + + NODE_TYPE_CODE NodeTypeCode; + PFCB Fcb; + PCCB Ccb; + NAMED_PIPE_END NamedPipeEnd; + + PAGED_CODE(); + + // + // Get the current Irp stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpCommonSetInformation...\n", 0); + DebugTrace( 0, Dbg, " Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg, " ->Length = %08lx\n", IrpSp->Parameters.SetFile.Length); + DebugTrace( 0, Dbg, " ->FileInformationClass = %08lx\n", IrpSp->Parameters.SetFile.FileInformationClass); + DebugTrace( 0, Dbg, " ->Buffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer); + + // + // Get the ccb and figure out who we are, and make sure we're not + // disconnected. + // + + if ((NodeTypeCode = NpDecodeFileObject( IrpSp->FileObject, + &Fcb, + &Ccb, + &NamedPipeEnd )) == NTC_UNDEFINED) { + + DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + Status = STATUS_PIPE_DISCONNECTED; + + DebugTrace(-1, Dbg, "NpCommonSetInformation -> %08lx\n", Status ); + return Status; + } + + // + // Case on the type of the context, We can only query information + // on an Fcb, Dcb, or Root Dcb. If we are not passed on of these + // we immediately tell the caller that there is an invalid parameter. + // + + if (NodeTypeCode != NPFS_NTC_CCB) { + + NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "NpCommonQueryInformation -> STATUS_INVALID_PARAMETER\n", 0); + return STATUS_INVALID_PARAMETER; + } + + // + // Reference our input parameter to make things easier + // + + Length = IrpSp->Parameters.SetFile.Length; + FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass; + Buffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Based on the information class we'll do differnt actions. Each + // procedure that we're calling will complete the request. + // + + switch (FileInformationClass) { + + case FileBasicInformation: + + Status = NpSetBasicInfo( Ccb, Buffer ); + break; + + case FilePipeInformation: + + Status = NpSetPipeInfo( Fcb, Ccb, Buffer, NamedPipeEnd ); + break; + + default: + + Status = STATUS_INVALID_PARAMETER; + break; + } + + // + // complete the request + // + + NpCompleteRequest( Irp, Status ); + + DebugTrace(-1, Dbg, "NpCommonSetInformation -> %08lx\n", Status); + return Status; +} + + +// +// Internal support routine +// + +NTSTATUS +NpQueryBasicInfo ( + IN PCCB Ccb, + IN PFILE_BASIC_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query basic information operation. + +Arguments: + + Ccb - Supplies the Ccb of the named pipe being queried + + Buffer - Supplies a pointer to the buffer where the information is + to be returned + + Length - Supplies the length of the buffer in bytes. This variable + upon return will receive the remaining bytes free in the buffer. + +Return Value: + + NTSTATUS - The result of this query + +--*/ + +{ + UNREFERENCED_PARAMETER( Ccb ); + + PAGED_CODE(); + + DebugTrace(0, Dbg, "NpQueryBasicInfo...\n", 0); + + // + // Update the length field, and zero out the buffer + // + + *Length -= sizeof( FILE_BASIC_INFORMATION ); + RtlZeroMemory( Buffer, sizeof(FILE_BASIC_INFORMATION) ); + + // + // Set the various fields in the record + // + //**** need to add the time fields to the fcb/ccb + // + + Buffer->CreationTime.LowPart = 0; Buffer->CreationTime.HighPart = 0; + Buffer->LastAccessTime.LowPart = 0; Buffer->LastAccessTime.HighPart = 0; + Buffer->LastWriteTime.LowPart = 0; Buffer->LastWriteTime.HighPart = 0; + Buffer->ChangeTime.LowPart = 0; Buffer->ChangeTime.HighPart = 0; + + Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL; + + // + // and return to our caller + // + + return STATUS_SUCCESS; +} + + +// +// Internal support routine +// + +NTSTATUS +NpQueryStandardInfo ( + IN PCCB Ccb, + IN PFILE_STANDARD_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query standard information operation. + +Arguments: + + Ccb - Supplies the Ccb of the named pipe being queried + + Buffer - Supplies a pointer to the buffer where the information is + to be returned + + Length - Supplies the length of the buffer in bytes. This variable + upon return will receive the remaining bytes free in the buffer. + +Return Value: + + NTSTATUS - The result of this query + +--*/ + +{ + PDATA_QUEUE Inbound; + PDATA_QUEUE Outbound; + + PAGED_CODE(); + + DebugTrace(0, Dbg, "NpQueryStandardInfo...\n", 0); + + // + // Update the length field, and zero out the buffer + // + + *Length -= sizeof( FILE_STANDARD_INFORMATION ); + RtlZeroMemory( Buffer, sizeof(FILE_STANDARD_INFORMATION) ); + + // + // Set the various fields in the record + // + + Inbound = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + Outbound = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + + // + // The allocation size is the amount of quota we've charged this pipe + // instance + // + + Buffer->AllocationSize.QuadPart = Inbound->Quota + Outbound->Quota; + + // + // The Eof is the number of writen bytes ready to be read from the inbound + // queue + // + + if (NpIsDataQueueWriters( Inbound )) { + + Buffer->EndOfFile.QuadPart = Inbound->BytesInQueue; + } + + Buffer->NumberOfLinks = 1; + Buffer->DeletePending = TRUE; + Buffer->Directory = FALSE; + + // + // And return to our caller + // + + return STATUS_SUCCESS; +} + + +// +// Internal support routine +// + +NTSTATUS +NpQueryInternalInfo ( + IN PCCB Ccb, + IN PFILE_INTERNAL_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query internal information operation. + +Arguments: + + Ccb - Supplies the Ccb of the named pipe being queried + + Buffer - Supplies a pointer to the buffer where the information is + to be returned + + Length - Supplies the length of the buffer in bytes. This variable + upon return will receive the remaining bytes free in the buffer. + +Return Value: + + NTSTATUS - The result of this query + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(0, Dbg, "NpQueryInternalInfo...\n", 0); + + // + // Update the length field, and zero out the buffer + // + + *Length -= sizeof(FILE_INTERNAL_INFORMATION); + RtlZeroMemory(Buffer, sizeof(FILE_INTERNAL_INFORMATION)); + + // + // Set the internal index number to be the fnode lbn; + // + + Buffer->IndexNumber.LowPart = (ULONG)Ccb; + Buffer->IndexNumber.HighPart = 0; + + // + // And return to our caller + // + + return STATUS_SUCCESS; +} + + +// +// Internal support routine +// + +NTSTATUS +NpQueryEaInfo ( + IN PCCB Ccb, + IN PFILE_EA_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query Ea information operation. + +Arguments: + + Ccb - Supplies the Ccb of the named pipe being queried + + Buffer - Supplies a pointer to the buffer where the information is + to be returned + + Length - Supplies the length of the buffer in bytes. This variable + upon return will receive the remaining bytes free in the buffer. + +Return Value: + + NTSTATUS - The result of this query + +--*/ + +{ + UNREFERENCED_PARAMETER( Ccb ); + + PAGED_CODE(); + + DebugTrace(0, Dbg, "NpQueryEaInfo...\n", 0); + + // + // Update the length field, and zero out the buffer + // + + *Length -= sizeof(FILE_EA_INFORMATION); + RtlZeroMemory(Buffer, sizeof(FILE_EA_INFORMATION)); + + // + // And return to our caller + // + + return STATUS_SUCCESS; +} + + +// +// Internal support routine +// + +NTSTATUS +NpQueryNameInfo ( + IN PCCB Ccb, + IN PFILE_NAME_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query name information operation. + +Arguments: + + Ccb - Supplies the Ccb of the named pipe being queried + + Buffer - Supplies a pointer to the buffer where the information is + to be returned + + Length - Supplies the length of the buffer in bytes. This variable + upon return will receive the remaining bytes free in the buffer. + +Return Value: + + NTSTATUS - The result of this query + +--*/ + +{ + ULONG bytesToCopy; + ULONG fileNameSize; + + NTSTATUS status; + + PAGED_CODE(); + + DebugTrace(0, Dbg, "NpQueryNameInfo...\n", 0); + + // + // See if the buffer is large enough, and decide how many bytes to copy. + // + + *Length -= FIELD_OFFSET( FILE_NAME_INFORMATION, FileName[0] ); + + fileNameSize = Ccb->Fcb->FullFileName.Length; + + if ( *Length >= fileNameSize ) { + + status = STATUS_SUCCESS; + + bytesToCopy = fileNameSize; + + } else { + + status = STATUS_BUFFER_OVERFLOW; + + bytesToCopy = *Length; + } + + // + // Copy over the file name and its length. + // + + RtlCopyMemory( + Buffer->FileName, + Ccb->Fcb->FullFileName.Buffer, + bytesToCopy); + + Buffer->FileNameLength = bytesToCopy; + + *Length -= bytesToCopy; + + return status; +} + + +// +// Internal support routine +// + +NTSTATUS +NpQueryPositionInfo ( + IN PCCB Ccb, + IN PFILE_POSITION_INFORMATION Buffer, + IN OUT PULONG Length, + IN NAMED_PIPE_END NamedPipeEnd + ) + +/*++ + +Routine Description: + + This routine performs the query position information operation. + +Arguments: + + Ccb - Supplies the Ccb of the named pipe being queried + + Buffer - Supplies a pointer to the buffer where the information is + to be returned + + Length - Supplies the length of the buffer in bytes. This variable + upon return will receive the remaining bytes free in the buffer. + + NamedPipeEnd - Indicates if the server or client is calling + +Return Value: + + NTSTATUS - The result of this query + +--*/ + +{ + PDATA_QUEUE Inbound; + PDATA_QUEUE Outbound; + + PAGED_CODE(); + + DebugTrace(0, Dbg, "PbQueryPositionInfo...\n", 0); + + // + // Update the length field, and zero out the buffer + // + + *Length -= sizeof(FILE_POSITION_INFORMATION); + RtlZeroMemory(Buffer, sizeof(FILE_POSITION_INFORMATION)); + + Inbound = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + Outbound = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + + // + // The current byte offset is the number of bytes available in the + // read end of the caller's buffer. The client read from the outbound + // end and the server reads from the inbound end. + // + + if (NamedPipeEnd == FILE_PIPE_CLIENT_END) { + + if (NpIsDataQueueWriters( Outbound )) { + + Buffer->CurrentByteOffset.QuadPart = Outbound->BytesInQueue; + } + + } else { + + if (NpIsDataQueueWriters( Inbound )) { + + Buffer->CurrentByteOffset.QuadPart = Inbound->BytesInQueue; + } + } + + // + // And return to our caller + // + + return STATUS_SUCCESS; +} + + +// +// Internal support routine +// + +NTSTATUS +NpQueryPipeInfo ( + IN PFCB Fcb, + IN PCCB Ccb, + IN PFILE_PIPE_INFORMATION Buffer, + IN OUT PULONG Length, + IN NAMED_PIPE_END NamedPipeEnd + ) + +/*++ + +Routine Description: + + This routine performs the query pipe information operation. + +Arguments: + + Fcb - Supplies the Fcb of the named pipe being queried + + Ccb - Supplies the Ccb of the named pipe being queried + + Buffer - Supplies a pointer to the buffer where the information is + to be returned + + Length - Supplies the length of the buffer in bytes. This variable + upon return will receive the remaining bytes free in the buffer. + + NamedPipeEnd - Indicates if the server or client is calling + +Return Value: + + NTSTATUS - The result of this query + +--*/ + +{ + UNREFERENCED_PARAMETER( Fcb ); + UNREFERENCED_PARAMETER( Ccb ); + + PAGED_CODE(); + + DebugTrace(0, Dbg, "PbQueryPipeInfo...\n", 0); + + // + // Update the length field, and zero out the buffer + // + + *Length -= sizeof(FILE_PIPE_INFORMATION); + RtlZeroMemory(Buffer, sizeof(FILE_PIPE_INFORMATION)); + + // + // Set the fields in the record + // + + Buffer->ReadMode = Ccb->ReadMode[ NamedPipeEnd ]; + Buffer->CompletionMode = Ccb->CompletionMode[ NamedPipeEnd ]; + + // + // And return to our caller + // + + return STATUS_SUCCESS; +} + + +// +// Internal support routine +// + +NTSTATUS +NpQueryPipeLocalInfo ( + IN PFCB Fcb, + IN PCCB Ccb, + IN PFILE_PIPE_LOCAL_INFORMATION Buffer, + IN OUT PULONG Length, + IN NAMED_PIPE_END NamedPipeEnd + ) + +/*++ + +Routine Description: + + This routine performs the query pipe information operation. + +Arguments: + + Fcb - Supplies the Fcb of the named pipe being queried + + Ccb - Supplies the Ccb of the named pipe being queried + + Buffer - Supplies a pointer to the buffer where the information is + to be returned + + Length - Supplies the length of the buffer in bytes. This variable + upon return will receive the remaining bytes free in the buffer. + + NamedPipeEnd - Indicates if the server or client is calling + +Return Value: + + NTSTATUS - The result of this query + +--*/ + +{ + PDATA_QUEUE Inbound; + PDATA_QUEUE Outbound; + + UNREFERENCED_PARAMETER( Ccb ); + + PAGED_CODE(); + + DebugTrace(0, Dbg, "PbQueryPipeLocalInfo...\n", 0); + + // + // Update the length field, and zero out the buffer + // + + *Length -= sizeof(FILE_PIPE_LOCAL_INFORMATION); + RtlZeroMemory(Buffer, sizeof(FILE_PIPE_LOCAL_INFORMATION)); + + Inbound = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + Outbound = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + + // + // Set the fields in the record + // + + Buffer->NamedPipeType = Fcb->Specific.Fcb.NamedPipeType; + Buffer->NamedPipeConfiguration = Fcb->Specific.Fcb.NamedPipeConfiguration; + Buffer->MaximumInstances = Fcb->Specific.Fcb.MaximumInstances; + Buffer->CurrentInstances = Fcb->OpenCount; + Buffer->InboundQuota = Inbound->Quota; + Buffer->OutboundQuota = Outbound->Quota; + Buffer->NamedPipeState = Ccb->NamedPipeState; + Buffer->NamedPipeEnd = NamedPipeEnd; + + // + // The read data available and write quota available depend on which + // end of the pipe is doing the query. The client reads from the outbound + // queue, and writes to the inbound queue. + // + + if (NamedPipeEnd == FILE_PIPE_CLIENT_END) { + + if (NpIsDataQueueWriters( Outbound )) { + + Buffer->ReadDataAvailable = Outbound->BytesInQueue; + } + + Buffer->WriteQuotaAvailable = Inbound->Quota - Inbound->QuotaUsed; + + } else { + + if (NpIsDataQueueWriters( Inbound )) { + + Buffer->ReadDataAvailable = Inbound->BytesInQueue; + } + + Buffer->WriteQuotaAvailable = Outbound->Quota - Outbound->QuotaUsed; + } + + // + // And return to our caller + // + + return STATUS_SUCCESS; +} + + +// +// Internal support routine +// + +NTSTATUS +NpSetBasicInfo ( + IN PCCB Ccb, + IN PFILE_BASIC_INFORMATION Buffer + ) + +/*++ + +Routine Description: + + This routine sets the basic information for a named pipe. + +Arguments: + + Ccb - Supplies the ccb for the named pipe being modified + + Buffer - Supplies the buffer containing the data being set + +Return Value: + + NTSTATUS - Returns our completion status + +--*/ + +{ + UNREFERENCED_PARAMETER( Ccb ); + + PAGED_CODE(); + + DebugTrace(0, Dbg, "NpSetBasicInfo...\n", 0); + + if (((PLARGE_INTEGER)&Buffer->CreationTime)->QuadPart != 0) { + + // + // Modify the creation time + // + + //**** need to add time fields + } + + if (((PLARGE_INTEGER)&Buffer->LastAccessTime)->QuadPart != 0) { + + // + // Modify the last access time + // + + //**** need to add time fields + } + + if (((PLARGE_INTEGER)&Buffer->LastWriteTime)->QuadPart != 0) { + + // + // Modify the last write time + // + + //**** need to add time fields + } + + if (((PLARGE_INTEGER)&Buffer->ChangeTime)->QuadPart != 0) { + + // + // Modify the change time + // + + //**** need to add time fields + } + + // + // And return to our caller + // + + return STATUS_SUCCESS; +} + + +// +// Internal support routine +// + +NTSTATUS +NpSetPipeInfo ( + IN PFCB Fcb, + IN PCCB Ccb, + IN PFILE_PIPE_INFORMATION Buffer, + IN NAMED_PIPE_END NamedPipeEnd + ) + +/*++ + +Routine Description: + + This routine sets the pipe information for a named pipe. + +Arguments: + + Fcb - Supplies the Fcb for the named pipe being modified + + Ccb - Supplies the ccb for the named pipe being modified + + Buffer - Supplies the buffer containing the data being set + + NamedPipeEnd - Supplies the server/client end doing the operation + +Return Value: + + NTSTATUS - Returns our completion status + +--*/ + +{ + PDATA_QUEUE ReadQueue; + PDATA_QUEUE WriteQueue; + + UNREFERENCED_PARAMETER( Ccb ); + + PAGED_CODE(); + + DebugTrace(0, Dbg, "NpSetPipeInfo...\n", 0); + + // + // If the caller requests message mode reads but the pipe is + // byte stream then its an invalid parameter + // + + if ((Buffer->ReadMode == FILE_PIPE_MESSAGE_MODE) && + (Fcb->Specific.Fcb.NamedPipeType == FILE_PIPE_BYTE_STREAM_MODE)) { + + return STATUS_INVALID_PARAMETER; + } + + // + // Get a reference to our read queue + // + + switch (NamedPipeEnd) { + + case FILE_PIPE_SERVER_END: + + ReadQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + WriteQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + + break; + + case FILE_PIPE_CLIENT_END: + + ReadQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + WriteQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + + break; + + default: + + NpBugCheck( NamedPipeEnd, 0, 0 ); + } + + // + // If the completion mode is complete operations and the current mode + // is queue operations and there and the data queues are not empty + // then its pipe busy + // + + if ((Buffer->CompletionMode == FILE_PIPE_COMPLETE_OPERATION) + + && + + (Ccb->CompletionMode[ NamedPipeEnd ] == FILE_PIPE_QUEUE_OPERATION) + + && + + ((NpIsDataQueueReaders(ReadQueue)) || + (NpIsDataQueueWriters(WriteQueue)))) { + + return STATUS_PIPE_BUSY; + } + + // + // Everything is fine so update the pipe + // + + Ccb->ReadMode[ NamedPipeEnd ] = Buffer->ReadMode; + Ccb->CompletionMode[ NamedPipeEnd ] = Buffer->CompletionMode; + + // + // Check for notify + // + + NpCheckForNotify( Fcb->ParentDcb, FALSE ); + + // + // And return to our caller + // + + return STATUS_SUCCESS; +} diff --git a/private/ntos/npfs/filobsup.c b/private/ntos/npfs/filobsup.c new file mode 100644 index 000000000..a2c9b1196 --- /dev/null +++ b/private/ntos/npfs/filobsup.c @@ -0,0 +1,272 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + FilObSup.c + +Abstract: + + This module implements the Named Pipe File object support routines. + +Author: + + Gary Kimura [GaryKi] 30-Aug-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (NPFS_BUG_CHECK_FILOBSUP) + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_FILOBSUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpDecodeFileObject) +#pragma alloc_text(PAGE, NpSetFileObject) +#endif + + +VOID +NpSetFileObject ( + IN PFILE_OBJECT FileObject OPTIONAL, + IN PVOID FsContext, + IN PVOID FsContext2, + IN NAMED_PIPE_END NamedPipeEnd + ) + +/*++ + +Routine Description: + + This routine sets the file system pointers within the file object + and handles the indicator for storing the named pipe end. + +Arguments: + + FileObject - Supplies a pointer to the file object being modified, and + can optionally be null. + + FsContext - Supplies a pointer to either a ccb, vcb, or root_dcb + structure. + + FsContext2 - Supplies a pointer to either a nonpaged ccb, root_dcb_ccb, + or is null + + NamedPipeEnd - Supplies the indication if this is either the server end + or client end file object. This is only applicable if the + fscontext points to a ccb. + +Return Value: + + None. + +--*/ + +{ + BOOLEAN GotCcb; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpSetFileObject, FileObject = %08lx\n", FileObject ); + + // + // If no file object was given, do nothing. + // + + if (ARGUMENT_PRESENT( FileObject )) { + + // + // Check if we need to add in the named pipe end to the + // fscontext pointer. We only need to 'OR' in a 1 if this is + // the server end and fscontext points to a ccb. Also remember + // now if this is a pointer is a ccb, so that we can later set + // the fo_named_pipe flag + // + + if ((FsContext != NULL) && + (*(PNODE_TYPE_CODE)FsContext == NPFS_NTC_CCB)) { + + GotCcb = TRUE; + + if (NamedPipeEnd == FILE_PIPE_SERVER_END) { + + FsContext = (PVOID)((ULONG)FsContext | 0x00000001); + } + + } else { + + GotCcb = FALSE; + } + + // + // Now set the fscontext fields of the file object, and conditionally + // set the named pipe flag in the file object if necessary. + // + + FileObject->FsContext = FsContext; + FileObject->FsContext2 = FsContext2; + + // + // Set the private cache map to 1 and that what we will get our + // fast I/O routines called + // + + FileObject->PrivateCacheMap = (PVOID)1; + + if (GotCcb) { + + FileObject->Flags |= FO_NAMED_PIPE; + } + } + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpSetFileObject -> VOID\n", 0); + + return; +} + + +NODE_TYPE_CODE +NpDecodeFileObject ( + IN PFILE_OBJECT FileObject, + OUT PFCB *Fcb OPTIONAL, + OUT PCCB *Ccb, + OUT PNAMED_PIPE_END NamedPipeEnd OPTIONAL + ) + +/*++ + +Routine Description: + + This procedure takes a pointer to a file object, that has already been + opened by the named pipe file system and figures out what it really + is opened. + +Arguments: + + FileObject - Supplies the file object pointer being interrogated + + Fcb - Receives a pointer to the Fcb for the file object, if we can + find it. + + Ccb - Receives a pointer to the Ccb for the file object, if we can + find it + + NamedPipeEnd - Receives a value indicating if this is a server + or client end file object. + +Return Value: + + NODE_TYPE_CODE - Returns the node type code for a Vcb, RootDcb, Ccb, + or zero. + + Vcb - indicates that file object opens the named pipe driver. + Fcb and Ccb are NOT returned. + + RootDcb - indicates that the file object is for the root directory. + Fcb (RootDcb), and Ccb (RootDcbCcb) are set. + + Ccb - indicates that the file object is for a named pipe instance. + Ccb is set, while Fcb is optionally set. + + Zero - indicates that the file object was for a named pipe instance + but became disconnected. Fcb, Ccb, and NamedPipeEnd are NOT + returned. + +--*/ + +{ + NODE_TYPE_CODE NodeTypeCode; + PVOID FsContext; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpDecodeFileObject, FileObject = %08lx\n", FileObject); + + // + // Reference the fs context fields of the file object. + // + + FsContext = FileObject->FsContext; + + // + // If the fscontext field is null then we been disconnected. + // + + if ((FsContext == NULL) || ((ULONG)FsContext == 1)) { + + NodeTypeCode = 0; + + } else { + + // + // We're actually pointing to something so first extract the + // named pipe end information and then we can reference through + // the fscontext pointer after we clean it up. + // + + if (ARGUMENT_PRESENT(NamedPipeEnd)) { + if (FlagOn((ULONG)FsContext, 0x00000001)) { + *NamedPipeEnd = FILE_PIPE_SERVER_END; + } else { + *NamedPipeEnd = FILE_PIPE_CLIENT_END; + } + } + + FsContext = (PVOID)((ULONG)FsContext & 0xfffffffe); + + // + // Now we can case on the node type code of the fscontext pointer + // and set the appropriate out pointers + // + + NodeTypeCode = *(PNODE_TYPE_CODE)FsContext; + + switch (NodeTypeCode) { + + case NPFS_NTC_VCB: + + break; + + case NPFS_NTC_ROOT_DCB: + + *Ccb = FileObject->FsContext2; + if (ARGUMENT_PRESENT(Fcb)) { *Fcb = FsContext; } + break; + + case NPFS_NTC_CCB: + + *Ccb = FsContext; + if (ARGUMENT_PRESENT(Fcb)) { *Fcb = ((PCCB)FsContext)->Fcb; } + break; + + default: + + NpBugCheck( NodeTypeCode, 0, 0 ); + } + } + + // + // and return to our caller + // + + DebugTrace(-1, Dbg, "NpDecodeFileObject -> %08lx\n", NodeTypeCode); + + return NodeTypeCode; +} + diff --git a/private/ntos/npfs/flushbuf.c b/private/ntos/npfs/flushbuf.c new file mode 100644 index 000000000..32bd7d923 --- /dev/null +++ b/private/ntos/npfs/flushbuf.c @@ -0,0 +1,239 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + FlushBuf.c + +Abstract: + + This module implements the File Flush Buffers routine for NPFS called by + the dispatch driver. + +Author: + + Gary Kimura [GaryKi] 21-Aug-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_FLUSH_BUFFERS) + +// +// local procedure prototypes +// + +NTSTATUS +NpCommonFlushBuffers ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpCommonFlushBuffers) +#pragma alloc_text(PAGE, NpFsdFlushBuffers) +#endif + + +NTSTATUS +NpFsdFlushBuffers ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtFlushBuffersFile API calls. + +Arguments: + + NpfsDeviceObject - Supplies the device object to use. + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The Fsd status for the Irp + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpFsdFlushBuffers\n", 0); + + // + // Call the common Flush routine. + // + + FsRtlEnterFileSystem(); + + NpAcquireSharedVcb(); + + try { + + Status = NpCommonFlushBuffers( NpfsDeviceObject, Irp ); + + } except(NpExceptionFilter( GetExceptionCode() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = NpProcessException( NpfsDeviceObject, Irp, GetExceptionCode() ); + } + + NpReleaseVcb(); + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpFsdFlushBuffers -> %08lx\n", Status ); + + return Status; +} + + +// +// Internal support routine +// + +NTSTATUS +NpCommonFlushBuffers ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for Flushing buffers for a file. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - the return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + PCCB Ccb; + NAMED_PIPE_END NamedPipeEnd; + + PDATA_QUEUE WriteQueue; + + // + // Get the current stack location + // + + PAGED_CODE(); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpCommonFlushBuffers\n", 0); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg, "FileObject = %08lx\n", IrpSp->FileObject); + + // + // Decode the file object to figure out who we are. If the result + // is not a ccb then the pipe has been disconnected. We don't need the + // Fcb back from the call + // + + if (NpDecodeFileObject( IrpSp->FileObject, + NULL, + &Ccb, + &NamedPipeEnd ) != NPFS_NTC_CCB) { + + DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + Status = STATUS_PIPE_DISCONNECTED; + + DebugTrace(-1, Dbg, "NpCommonFlushBuffers -> %08lx\n", Status ); + return Status; + } + + NpAcquireExclusiveCcb(Ccb); + + try { + + // + // Figure out the data queue that the flush buffer is + // targetted at. It is the queue that we do writes into + // + + if (NamedPipeEnd == FILE_PIPE_SERVER_END) { + + WriteQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + + } else { + + WriteQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + } + + // + // Now from the write queue check if contains write entries. If + // it does not contain write entries then we immediately complete + // this irp with success because there isn't anything to flush + // + + if (!NpIsDataQueueWriters( WriteQueue )) { + + DebugTrace(0, Dbg, "Pipe does not contain write entries\n", 0); + + NpCompleteRequest( Irp, STATUS_SUCCESS ); + try_return(Status = STATUS_SUCCESS); + } + + // + // Otherwise the queue is full of writes so we simply + // enqueue this irp to the back to the queue and set our + // return status to pending, also mark the irp pending + // + + IoMarkIrpPending( Irp ); + + (VOID)NpAddDataQueueEntry( WriteQueue, + WriteEntries, + Flush, + 0, + Irp, + NULL ); + Status = STATUS_PENDING; + + try_exit: NOTHING; + } finally { + NpReleaseCcb(Ccb); + } + + + DebugTrace(-1, Dbg, "NpCommonFlushBuffers -> %08lx\n", Status); + return Status; +} + diff --git a/private/ntos/npfs/fsctrl.c b/private/ntos/npfs/fsctrl.c new file mode 100644 index 000000000..1d86754a5 --- /dev/null +++ b/private/ntos/npfs/fsctrl.c @@ -0,0 +1,3296 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + FsContrl.c + +Abstract: + + This module implements the File System Control routine for NPFS called by + the dispatch driver. + +Author: + + Gary Kimura [GaryKi] 21-Aug-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (NPFS_BUG_CHECK_FSCTRL) + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_FSCONTRL) + + +// +// Local procedure prototypes +// + +NTSTATUS +NpCommonFileSystemControl ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpAssignEvent ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpDisconnect ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpListen ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpPeek ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpQueryEvent ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpTransceive ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpWaitForNamedPipe ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpImpersonate ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpInternalRead ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp, + IN BOOLEAN ReadOverflowOperation + ); + +NTSTATUS +NpInternalWrite ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpInternalTransceive ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpQueryClientProcess ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpSetClientProcess ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpCompleteTransceiveIrp ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpAssignEvent) +#pragma alloc_text(PAGE, NpCommonFileSystemControl) +#pragma alloc_text(PAGE, NpCompleteTransceiveIrp) +#pragma alloc_text(PAGE, NpDisconnect) +#pragma alloc_text(PAGE, NpFsdFileSystemControl) +#pragma alloc_text(PAGE, NpImpersonate) +#pragma alloc_text(PAGE, NpInternalRead) +#pragma alloc_text(PAGE, NpInternalTransceive) +#pragma alloc_text(PAGE, NpInternalWrite) +#pragma alloc_text(PAGE, NpListen) +#pragma alloc_text(PAGE, NpPeek) +#pragma alloc_text(PAGE, NpQueryClientProcess) +#pragma alloc_text(PAGE, NpQueryEvent) +#pragma alloc_text(PAGE, NpSetClientProcess) +#pragma alloc_text(PAGE, NpTransceive) +#pragma alloc_text(PAGE, NpWaitForNamedPipe) +#endif + +// +// Define a structure used for posting DPC requests to an ExWorkerThread. +// + +typedef struct _FSP_CONTEXT { + + WORK_QUEUE_ITEM Item; + PNPFS_DEVICE_OBJECT NpfsDeviceObject; + PIRP Irp; + +} FSP_CONTEXT; + +typedef FSP_CONTEXT *PFSP_CONTEXT; + + +NTSTATUS +NpFsdFileSystemControl ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtFsControlFile API calls. + +Arguments + + NpfsDeviceObject - Supplies the device object to use. + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The Fsd status for the Irp + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpFsdFileSystemControl\n", 0); + + // + // Call the common FsControl routine. + // + + FsRtlEnterFileSystem(); + + try { + + Status = NpCommonFileSystemControl( NpfsDeviceObject, + Irp ); + + } except(NpExceptionFilter( GetExceptionCode() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = NpProcessException( NpfsDeviceObject, Irp, GetExceptionCode() ); + } + + NpReleaseVcb(); + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpFsdFileSystemControl -> %08lx\n", Status ); + + return Status; +} + + +// +// Internal Support Routine +// + +NTSTATUS +NpCommonFileSystemControl ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine does the common code for handling/dispatching an fsctl + function. + +Arguments: + + NpfsDeviceObject - Supplies the named pipe device object + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + BOOLEAN ReadOverflowOperation; + + PAGED_CODE(); + + // + // Reference our input parameters to make things easier + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpCommonFileSystemControl\n", 0); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg, "OutputBufferLength = %08lx\n", IrpSp->Parameters.FileSystemControl.OutputBufferLength); + DebugTrace( 0, Dbg, "InputBufferLength = %08lx\n", IrpSp->Parameters.FileSystemControl.InputBufferLength); + DebugTrace( 0, Dbg, "FsControlCode = %08lx\n", IrpSp->Parameters.FileSystemControl.FsControlCode); + + // + // Case on the type of function we're trying to do. In each case + // we'll call a local work routine to do the actual work. + // + + ReadOverflowOperation = FALSE; + + switch (IrpSp->Parameters.FileSystemControl.FsControlCode) { + + case FSCTL_PIPE_ASSIGN_EVENT: + + NpAcquireExclusiveVcb(); + Status = NpAssignEvent( NpfsDeviceObject, Irp ); + break; + + case FSCTL_PIPE_DISCONNECT: + + NpAcquireExclusiveVcb(); + Status = NpDisconnect( NpfsDeviceObject, Irp ); + break; + + case FSCTL_PIPE_LISTEN: + + NpAcquireSharedVcb(); + Status = NpListen( NpfsDeviceObject, Irp ); + break; + + case FSCTL_PIPE_PEEK: + + NpAcquireExclusiveVcb(); + Status = NpPeek( NpfsDeviceObject, Irp ); + break; + + case FSCTL_PIPE_QUERY_EVENT: + + NpAcquireExclusiveVcb(); + Status = NpQueryEvent( NpfsDeviceObject, Irp ); + break; + + case FSCTL_PIPE_TRANSCEIVE: + + NpAcquireSharedVcb(); + Status = NpTransceive( NpfsDeviceObject, Irp ); + break; + + case FSCTL_PIPE_WAIT: + + NpAcquireExclusiveVcb(); + Status = NpWaitForNamedPipe( NpfsDeviceObject, Irp ); + break; + + case FSCTL_PIPE_IMPERSONATE: + + NpAcquireExclusiveVcb(); + Status = NpImpersonate( NpfsDeviceObject, Irp ); + break; + + case FSCTL_PIPE_INTERNAL_READ_OVFLOW: + + ReadOverflowOperation = TRUE; + + case FSCTL_PIPE_INTERNAL_READ: + + NpAcquireSharedVcb(); + Status = NpInternalRead( NpfsDeviceObject, Irp, ReadOverflowOperation ); + break; + + case FSCTL_PIPE_INTERNAL_WRITE: + + NpAcquireSharedVcb(); + Status = NpInternalWrite( NpfsDeviceObject, Irp ); + break; + + case FSCTL_PIPE_INTERNAL_TRANSCEIVE: + + NpAcquireSharedVcb(); + Status = NpInternalTransceive( NpfsDeviceObject, Irp ); + break; + + case FSCTL_PIPE_QUERY_CLIENT_PROCESS: + + NpAcquireSharedVcb(); + Status = NpQueryClientProcess( NpfsDeviceObject, Irp ); + break; + + case FSCTL_PIPE_SET_CLIENT_PROCESS: + + NpAcquireExclusiveVcb(); + Status = NpSetClientProcess( NpfsDeviceObject, Irp ); + break; + + default: + + NpAcquireExclusiveVcb(); + NpCompleteRequest( Irp, Status = STATUS_NOT_SUPPORTED ); + break; + } + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpCommonFileSystemControl -> %08lx\n", Status); + + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +NpAssignEvent ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine does the assign event control function + +Arguments: + + NpfsDeviceObject - Supplies our device object + + Irp - Supplies the Irp specifying the function + +Return Value: + + NTSTATUS - An appropriate return status + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + + ULONG InputBufferLength; + ULONG FsControlCode; + + PCCB Ccb; + PNONPAGED_CCB NonpagedCcb; + NAMED_PIPE_END NamedPipeEnd; + + PFILE_PIPE_ASSIGN_EVENT_BUFFER EventBuffer; + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpAssignEvent...\n", 0); + + InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode; + + // + // Decode the file object to figure out who we are. If the result + // is not a ccb then the pipe has been disconnected. + // + + if (NpDecodeFileObject( IrpSp->FileObject, + NULL, + &Ccb, + &NamedPipeEnd ) != NPFS_NTC_CCB) { + + DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + return STATUS_PIPE_DISCONNECTED; + } + + NonpagedCcb = Ccb->NonpagedCcb; + + // + // Reference the system buffer as an assign event buffer and make + // sure it's large enough + // + + if (InputBufferLength < sizeof(FILE_PIPE_ASSIGN_EVENT_BUFFER)) { + + DebugTrace(0, Dbg, "System buffer size is too small\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + EventBuffer = Irp->AssociatedIrp.SystemBuffer; + + // + // First thing we do is delete the old event if there is one + // for this end of the pipe + // + + NpDeleteEventTableEntry( &NpVcb->EventTable, + NonpagedCcb->EventTableEntry[ NamedPipeEnd ] ); + + NonpagedCcb->EventTableEntry[ NamedPipeEnd ] = NULL; + + // + // Now if the new event handle is not null then we'll add the new + // event to the event table + // + + if (EventBuffer->EventHandle != NULL) { + + NonpagedCcb->EventTableEntry[ NamedPipeEnd ] = + NpAddEventTableEntry( &NpVcb->EventTable, + Ccb, + NamedPipeEnd, + EventBuffer->EventHandle, + EventBuffer->KeyValue, + PsGetCurrentProcess(), + Irp->RequestorMode ); + } + + // + // Complete the Irp with success + // + + NpCompleteRequest( Irp, STATUS_SUCCESS ); + + DebugTrace(-1, Dbg, "NpAssignEvent -> STATUS_SUCCESS\n", 0); + return STATUS_SUCCESS; +} + + +// +// Local Support Routine +// + +NTSTATUS +NpDisconnect ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine does the disconnect control function + +Arguments: + + NpfsDeviceObject - Supplies our device object + + Irp - Supplies the being processed + +Return Value: + + NTSTATUS - An apprropriate return status + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + ULONG FsControlCode; + + PCCB Ccb; + NAMED_PIPE_END NamedPipeEnd; + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpDisconnect...\n", 0); + + FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode; + + // + // Decode the file object to figure out who we are. If the result + // is not a ccb then the pipe has been disconnected. + // + + if (NpDecodeFileObject( IrpSp->FileObject, + NULL, + &Ccb, + &NamedPipeEnd ) != NPFS_NTC_CCB) { + + DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + return STATUS_PIPE_DISCONNECTED; + } + + // + // Make sure that this is only the server that is doing this + // action. + // + + if (NamedPipeEnd != FILE_PIPE_SERVER_END) { + + DebugTrace(0, Dbg, "Not the server end\n", 0); + + NpCompleteRequest( Irp, STATUS_ILLEGAL_FUNCTION ); + return STATUS_ILLEGAL_FUNCTION; + } + + NpAcquireExclusiveCcb(Ccb); + + // + // Now call the state support routine to set the ccb to + // a disconnected state and remove the client's cached security + // context. + // + + Status = NpSetDisconnectedPipeState( Ccb ); + + NpUninitializeSecurity( Ccb ); + + NpReleaseCcb(Ccb); + + // + // Complete the Irp with our returned status + // + + NpCompleteRequest( Irp, Status ); + + DebugTrace(-1, Dbg, "NpDisconnect -> %08lx\n", Status); + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +NpListen ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine does the listen control function + +Arguments: + + NpfsDeviceObject - Supplies our device object + + Irp - Supplies the being processed + +Return Value: + + NTSTATUS - An apprropriate return status + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + ULONG FsControlCode; + + PCCB Ccb; + NAMED_PIPE_END NamedPipeEnd; + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpListen...\n", 0); + + FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode; + + // + // Decode the file object to figure out who we are. If the result + // is not a ccb then the pipe has been disconnected. + // + + if (NpDecodeFileObject( IrpSp->FileObject, + NULL, + &Ccb, + &NamedPipeEnd ) != NPFS_NTC_CCB) { + + DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0); + + NpCompleteRequest( Irp, STATUS_ILLEGAL_FUNCTION ); + + DebugTrace(-1, Dbg, "NpListen -> STATUS_ILLEGAL_FUNCTION\n", 0 ); + return STATUS_ILLEGAL_FUNCTION; + } + + // + // Make sure that this is only the server that is doing this + // action. + // + + if (NamedPipeEnd != FILE_PIPE_SERVER_END) { + + DebugTrace(0, Dbg, "Not the server end\n", 0); + + NpCompleteRequest( Irp, STATUS_ILLEGAL_FUNCTION ); + + DebugTrace(-1, Dbg, "NpListen -> STATUS_ILLEGAL_FUNCTION\n", 0 ); + return STATUS_ILLEGAL_FUNCTION; + } + + NpAcquireExclusiveCcb(Ccb); + + // + // Now call the state support routine to set the ccb to + // a listening state. This routine will complete the Irp + // for us. + // + + Status = NpSetListeningPipeState( Ccb, Irp ); + + NpReleaseCcb(Ccb); + + DebugTrace(-1, Dbg, "NpListen -> %08lx\n", Status); + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +NpPeek ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine does the peek control function + +Arguments: + + NpfsDeviceObject - Supplies our device object + + Irp - Supplies the being processed + +Return Value: + + NTSTATUS - An apprropriate return status + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + ULONG OutputBufferLength; + ULONG FsControlCode; + + NODE_TYPE_CODE NodeTypeCode; + PCCB Ccb; + PNONPAGED_CCB NonpagedCcb; + NAMED_PIPE_END NamedPipeEnd; + + PFILE_PIPE_PEEK_BUFFER PeekBuffer; + + PDATA_QUEUE ReadQueue; + READ_MODE ReadMode; + + ULONG LengthWritten; + + PDATA_ENTRY DataEntry; + + PUCHAR ReadBuffer; + ULONG ReadLength; + ULONG ReadRemaining; + + PAGED_CODE(); + + // + // Get the current Irp stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpPeek...\n", 0); + + // + // Extract the important fields from the IrpSp + // + + OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; + FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode; + + DebugTrace( 0, Dbg, "OutputBufferLength = %08lx\n", OutputBufferLength); + DebugTrace( 0, Dbg, "FsControlCode = %08lx\n", FsControlCode); + + // + // Decode the file object to figure out who we are. The results + // have a disconnected pipe if we get back an undefined ntc + // + + if ((NodeTypeCode = NpDecodeFileObject( IrpSp->FileObject, + NULL, + &Ccb, + &NamedPipeEnd )) == NTC_UNDEFINED) { + + DebugTrace(0, Dbg, "FileObject has been disconnected\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + + DebugTrace(-1, Dbg, "NpPeek -> STATUS_PIPE_DISCONNECTED\n", 0 ); + return STATUS_PIPE_DISCONNECTED; + } + + // + // Now make sure the node type code is for a ccb otherwise it is an + // invalid parameter + // + + if (NodeTypeCode != NPFS_NTC_CCB) { + + DebugTrace(0, Dbg, "FileObject is not for a ccb\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "NpPeek -> STATUS_INVALID_PARAMETER\n", 0 ); + return STATUS_INVALID_PARAMETER; + } + + NonpagedCcb = Ccb->NonpagedCcb; + + // + // Reference the system buffer as a peek buffer and make sure it's + // large enough + // + + if (OutputBufferLength < (ULONG)FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0])) { + + DebugTrace(0, Dbg, "Output buffer is too small\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "NpPeek -> STATUS_INVALID_PARAMETER\n", 0 ); + return STATUS_INVALID_PARAMETER; + } + + PeekBuffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Now the data queue that we read from is based on the named pipe + // end. The server reads from the inbound queue and the client reads + // from the outbound queue + // + + switch (NamedPipeEnd) { + + case FILE_PIPE_SERVER_END: + + ReadQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + //ReadMode = Ccb->ReadMode[ FILE_PIPE_SERVER_END ]; + + break; + + case FILE_PIPE_CLIENT_END: + + ReadQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + //ReadMode = Ccb->ReadMode[ FILE_PIPE_CLIENT_END ]; + + break; + + default: + + NpBugCheck( NamedPipeEnd, 0, 0 ); + } + + // + // Our read mode is really based upon the pipe type and not the set + // read mode for the pipe end. + // + + if (Ccb->Fcb->Specific.Fcb.NamedPipeType == FILE_PIPE_MESSAGE_TYPE) { + + ReadMode = FILE_PIPE_MESSAGE_MODE; + + } else { + + ReadMode = FILE_PIPE_BYTE_STREAM_MODE; + } + + DebugTrace(0, Dbg, "ReadQueue = %08lx\n", ReadQueue); + DebugTrace(0, Dbg, "ReadMode = %08lx\n", ReadMode); + + // + // If the state of the pipe is not in the connected or closing + // state then it is an invalid pipe state + // + + if ((Ccb->NamedPipeState != FILE_PIPE_CONNECTED_STATE) && + (Ccb->NamedPipeState != FILE_PIPE_CLOSING_STATE)) { + + DebugTrace(0, Dbg, "pipe not connected or closing\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_PIPE_STATE ); + return STATUS_INVALID_PIPE_STATE; + } + + // + // If the state of the pipe is closing and the queue does + // not contain any writers then we return eof + // + + if ((Ccb->NamedPipeState == FILE_PIPE_CLOSING_STATE) && + (!NpIsDataQueueWriters( ReadQueue ))) { + + DebugTrace(0, Dbg, "pipe closing and is empty\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_BROKEN ); + return STATUS_PIPE_BROKEN; + } + + // + // Zero out the standard header part of the peek buffer and + // set the length written to the amount we've just zeroed out + // + + RtlZeroMemory( PeekBuffer, FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0]) ); + LengthWritten = FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0]); + + // + // Set the named pipe state + // + + PeekBuffer->NamedPipeState = Ccb->NamedPipeState; + + // + // There is only data available if the read queue contains + // write entries otherwise the rest of record is all zero. + // + + if (NpIsDataQueueWriters( ReadQueue )) { + + // + // Now find the first real entry in the read queue. The + // first entry actually better be a real one. + // + + DataEntry = NpGetNextDataQueueEntry( ReadQueue, NULL ); + + ASSERT( (DataEntry->DataEntryType == Buffered) || + (DataEntry->DataEntryType == Unbuffered) ); + + // + // Indicate how many bytes are available to read + // + + PeekBuffer->ReadDataAvailable = ReadQueue->BytesInQueue; + + // + // The number of messages a message length is only filled + // in for a message mode pipe + // + + if (ReadMode == FILE_PIPE_MESSAGE_MODE) { + + PeekBuffer->NumberOfMessages = ReadQueue->EntriesInQueue; + PeekBuffer->MessageLength = DataEntry->DataSize - ReadQueue->NextByteOffset; + } + + // + // Now we are ready to copy over the data from the read queue + // into the peek buffer. First establish how much room we + // have in the peek buffer and who much is remaining. + // + + ReadBuffer = &PeekBuffer->Data[0]; + ReadLength = OutputBufferLength - FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0]); + ReadRemaining = ReadLength; + + DebugTrace(0, Dbg, "ReadBuffer = %08lx\n", ReadBuffer); + DebugTrace(0, Dbg, "ReadLength = %08lx\n", ReadLength); + + // + // Now read the data queue. + // + + if ( ReadLength != 0 ) { + IO_STATUS_BLOCK Iosb; + + Iosb = NpReadDataQueue( ReadQueue, + TRUE, + FALSE, + ReadBuffer, + ReadLength, + ReadMode, + Ccb ); + + Status = Iosb.Status; + LengthWritten += Iosb.Information; + + } else { + + if ( PeekBuffer->ReadDataAvailable == 0) { + + Status = STATUS_SUCCESS; + + } else { + + Status = STATUS_BUFFER_OVERFLOW; + } + } + + } else { + + Status = STATUS_SUCCESS; + } + + // + // Complete the request. The amount of information copied + // is stored in length written + // + + Irp->IoStatus.Information = LengthWritten; + + NpCompleteRequest(Irp, Status); + + DebugTrace(-1, Dbg, "NpPeek -> %08lx\n", Status); + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +NpQueryEvent ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine does the query event control function + +Arguments: + + NpfsDeviceObject - Supplies our device object + + Irp - Supplies the Irp specifying the function + +Return Value: + + NTSTATUS - An appropriate return status + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + + ULONG InputBufferLength; + ULONG OutputBufferLength; + ULONG FsControlCode; + + PCCB Ccb; + NAMED_PIPE_END NamedPipeEnd; + + HANDLE EventHandle; + PFILE_PIPE_EVENT_BUFFER EventArray; + PFILE_PIPE_EVENT_BUFFER EventBuffer; + ULONG EventArrayMaximumCount; + ULONG EventCount; + + PEPROCESS Process; + + PEVENT_TABLE_ENTRY Ete; + PDATA_QUEUE ReadQueue; + PDATA_QUEUE WriteQueue; + + PVOID RestartKey; + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpQueryEvent...\n", 0); + + InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; + FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode; + + // + // Decode the file object to figure out who we are. If the result + // is not a Vcb then its an invalid parameter + // + + if (NpDecodeFileObject( IrpSp->FileObject, + NULL, + &Ccb, + &NamedPipeEnd ) != NPFS_NTC_VCB) { + + DebugTrace(0, Dbg, "FileObject is not the named pipe driver\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Reference the system buffer as a handle and make sure it's large + // enough + // + + if (InputBufferLength < sizeof(HANDLE)) { + + DebugTrace(0, Dbg, "Input System buffer size is too small\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + EventHandle = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer; + + // + // Reference the system buffer as an output event buffer, and compute + // how many event buffer records we can put in the buffer. + // + + EventArray = Irp->AssociatedIrp.SystemBuffer; + EventArrayMaximumCount = OutputBufferLength / sizeof(FILE_PIPE_EVENT_BUFFER); + EventCount = 0; + + // + // Get our current process pointer that we'll need for our search + // + + Process = PsGetCurrentProcess(); + + // + // Now enumerate the event table entries in the event table + // + + RestartKey = NULL; + for (Ete = NpGetNextEventTableEntry( &NpVcb->EventTable, &RestartKey); + Ete != NULL; + Ete = NpGetNextEventTableEntry( &NpVcb->EventTable, &RestartKey)) { + + // + // Check if the event table entry matches the event handle + // and the process + // + + if ((Ete->EventHandle == EventHandle) && + (Ete->Process == Process)) { + + // + // Now based on the named pipe end we treat the inbound/ + // outbound as a read/write queue. + // + + NpAcquireExclusiveCcb(Ete->Ccb); + + switch (Ete->NamedPipeEnd) { + + case FILE_PIPE_CLIENT_END: + + ReadQueue = &Ete->Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + WriteQueue = &Ete->Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + + break; + + case FILE_PIPE_SERVER_END: + + ReadQueue = &Ete->Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + WriteQueue = &Ete->Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + + break; + + default: + + NpBugCheck( Ete->NamedPipeEnd, 0, 0 ); + } + + // + // Now if there is any data in the read queue to be read + // we fill in the buffer + // + + if (NpIsDataQueueWriters(ReadQueue)) { + + // + // First make sure there is enough room in the + // EventBuffer to hold another entry + // + + if (EventCount >= EventArrayMaximumCount) { + + DebugTrace(0, Dbg, "The event buffer is full\n", 0); + + NpReleaseCcb(Ete->Ccb); + break; + } + + // + // Reference the event buffer and increment the + // counter + // + + EventBuffer = &EventArray[EventCount]; + EventCount += 1; + + // + // Fill in the event buffer entry + // + + EventBuffer->NamedPipeState = Ete->Ccb->NamedPipeState; + EventBuffer->EntryType = FILE_PIPE_READ_DATA; + EventBuffer->ByteCount = ReadQueue->BytesInQueue; + EventBuffer->KeyValue = Ete->KeyValue; + EventBuffer->NumberRequests = ReadQueue->EntriesInQueue; + } + + // + // We'll always fill in a write space buffer. The amount + // will either be bytes of write space available or + // the quota of write space that we can use. + // + + // + // First make sure there is enough room in the + // EventBuffer to hold another entry + // + + if (EventCount >= EventArrayMaximumCount) { + + DebugTrace(0, Dbg, "The event buffer is full\n", 0); + + NpReleaseCcb(Ete->Ccb); + break; + } + + // + // Reference the event buffer and increment the + // counter + // + + EventBuffer = &EventArray[EventCount]; + EventCount += 1; + + // + // Fill in the event buffer entry + // + + EventBuffer->NamedPipeState = Ete->Ccb->NamedPipeState; + EventBuffer->EntryType = FILE_PIPE_WRITE_SPACE; + EventBuffer->KeyValue = Ete->KeyValue; + + // + // Now either we put in the write space available or + // we put in the quota available + // + + if (NpIsDataQueueReaders(WriteQueue)) { + + EventBuffer->ByteCount = WriteQueue->BytesInQueue; + EventBuffer->NumberRequests = WriteQueue->EntriesInQueue; + + } else { + + EventBuffer->ByteCount = WriteQueue->Quota - WriteQueue->QuotaUsed; + EventBuffer->NumberRequests = 0; + } + + NpReleaseCcb(Ete->Ccb); + } + } + + // + // Set the information field to be the number of bytes of output + // data we've fill into the system buffer + // + + Irp->IoStatus.Information = EventCount * sizeof(FILE_PIPE_EVENT_BUFFER); + + // + // And complete the Irp with success + // + + NpCompleteRequest( Irp, STATUS_SUCCESS ); + + DebugTrace(-1, Dbg, "NpQueryEvent -> STATUS_SUCCESS\n", 0); + return STATUS_SUCCESS; +} + + +// +// Local Support Routine +// + +NTSTATUS +NpTransceive ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine does the transceive named pipe control function + +Arguments: + + NpfsDeviceObject - Supplies our device object + + Irp - Supplies the being processed + +Return Value: + + NTSTATUS - An apprropriate return status + +--*/ + +{ + static IO_STATUS_BLOCK Iosb; + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp; + PETHREAD UserThread; + + PUCHAR WriteBuffer; + ULONG WriteLength; + + PUCHAR ReadBuffer; + ULONG ReadLength; + + NODE_TYPE_CODE NodeTypeCode; + PCCB Ccb; + PNONPAGED_CCB NonpagedCcb; + NAMED_PIPE_END NamedPipeEnd; + + PDATA_QUEUE ReadQueue; + PDATA_QUEUE WriteQueue; + PEVENT_TABLE_ENTRY Event; + READ_MODE ReadMode; + + NAMED_PIPE_CONFIGURATION NamedPipeConfiguration; + + ULONG WriteRemaining; + + PIRP WriteIrp; + + PDATA_ENTRY DataEntry; + + // + // The following variable is used during abnormal unwind + // + + PVOID UnwindStorage = NULL; + + PAGED_CODE(); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpTransceive\n", 0); + DebugTrace( 0, Dbg, "NpfsDeviceObject = %08lx\n", NpfsDeviceObject); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg, "FileObject = %08lx\n", IrpSp->FileObject); + + WriteLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + WriteBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer; + + ReadLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; + ReadBuffer = Irp->UserBuffer; + + // + // Now if the requestor mode is user mode we need to probe the buffers + // We do now need to have an exception handler here because our top + // level caller already has one that will complete the Irp with + // the appropriate status if we access violate. + // + + if (Irp->RequestorMode != KernelMode) { + + try { + + ProbeForRead( WriteBuffer, WriteLength, sizeof(UCHAR) ); + ProbeForWrite( ReadBuffer, ReadLength, sizeof(UCHAR) ); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + } + + // + // Get the Ccb and figure out who we are, and make sure we're not + // disconnected + // + + if ((NodeTypeCode = NpDecodeFileObject( IrpSp->FileObject, + NULL, + &Ccb, + &NamedPipeEnd )) == NTC_UNDEFINED) { + + DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + + DebugTrace(-1, Dbg, "NpTransceive -> STATUS_PIPE_DISCONNECTED\n", 0 ); + return STATUS_PIPE_DISCONNECTED; + } + + // + // Now we only will allow transceive operations on the pipe and not a + // directory or the device + // + + if (NodeTypeCode != NPFS_NTC_CCB) { + + DebugTrace(0, Dbg, "FileObject is not for a named pipe\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "NpTransceive -> STATUS_PIPE_DISCONNECTED\n", 0 ); + return STATUS_PIPE_DISCONNECTED; + } + + NonpagedCcb = Ccb->NonpagedCcb; + + NpAcquireExclusiveCcb(Ccb); + WriteIrp = NULL; + + try { + + // + // Check that the pipe is in the connected state + // + + if (Ccb->NamedPipeState != FILE_PIPE_CONNECTED_STATE) { + + DebugTrace(0, Dbg, "Pipe not connected\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_PIPE_STATE ); + try_return( Status = STATUS_INVALID_PIPE_STATE ); + } + + // + // Figure out the read/write queue, read mode, and event based + // on the end of the named pipe doing the transceive. + // + + switch (NamedPipeEnd) { + + case FILE_PIPE_SERVER_END: + + ReadQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + WriteQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + + Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ]; + ReadMode = Ccb->ReadMode[ FILE_PIPE_SERVER_END ]; + + break; + + case FILE_PIPE_CLIENT_END: + + ReadQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + WriteQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + + Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_SERVER_END ]; + ReadMode = Ccb->ReadMode[ FILE_PIPE_CLIENT_END ]; + + break; + + default: + + NpBugCheck( NamedPipeEnd, 0, 0 ); + } + + // + // We only allow a transceive on a message mode, full duplex pipe. + // + + NamedPipeConfiguration = Ccb->Fcb->Specific.Fcb.NamedPipeConfiguration; + + if ((NamedPipeConfiguration != FILE_PIPE_FULL_DUPLEX) || + (ReadMode != FILE_PIPE_MESSAGE_MODE)) { + + DebugTrace(0, Dbg, "Bad pipe configuration or read mode\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_READ_MODE ); + try_return( Status = STATUS_INVALID_READ_MODE ); + } + + // + // Check that the read queue is empty. + // + + if (!NpIsDataQueueEmpty( ReadQueue )) { + + DebugTrace(0, Dbg, "Read queue is not empty\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_BUSY ); + try_return( Status = STATUS_PIPE_BUSY ); + } + + // + // Do the transceive write operation. We first try and push the data + // from the write buffer into any waiting readers in the write queue + // and if that succeeds then we can go on and do the read operation + // otherwise we need to make a copy of irp and to enqueue as + // a data entry into the write queue. + // + // Now we'll call our common write data queue routine to + // transfer data out of our write buffer into the data queue. + // If the result of the call is FALSE then we still have some + // write data to put into the write queue. + // + + UserThread = Irp->Tail.Overlay.Thread; + + if (!NpWriteDataQueue( WriteQueue, + ReadMode, + WriteBuffer, + WriteLength, + Ccb->Fcb->Specific.Fcb.NamedPipeType, + &WriteRemaining, + Ccb, + NamedPipeEnd, + UserThread )) { + + PIO_STACK_LOCATION WriteIrpSp; + + ASSERT( !NpIsDataQueueReaders( WriteQueue )); + + DebugTrace(0, Dbg, "Add write to data queue\n", 0); + + // + // We need to do some more write processing. So to handle + // this case we'll allocate a new irp and set its system + // buffer to be the remaining part of the write buffer + // + + if ((WriteIrp = IoAllocateIrp( NpfsDeviceObject->DeviceObject.StackSize, FALSE )) == NULL) { + + ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); + } + + IoSetCompletionRoutine( WriteIrp, NpCompleteTransceiveIrp, NULL, TRUE, TRUE, TRUE ); + + WriteIrpSp = IoGetNextIrpStackLocation( WriteIrp ); + + if (WriteRemaining > 0) { + + WriteIrp->AssociatedIrp.SystemBuffer = UnwindStorage = FsRtlAllocatePoolWithQuota( NonPagedPool, + WriteRemaining ); + + // + // Safely do the copy + // + + try { + + RtlCopyMemory( WriteIrp->AssociatedIrp.SystemBuffer, + &WriteBuffer[ WriteLength - WriteRemaining ], + WriteRemaining ); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + + } else { + + WriteIrp->AssociatedIrp.SystemBuffer = UnwindStorage = NULL; + } + + // + // Set the current stack location, and set in the amount we are + // try to write. + // + + WriteIrp->CurrentLocation -= 1; + WriteIrp->Tail.Overlay.CurrentStackLocation = WriteIrpSp; + WriteIrpSp->Parameters.Write.Length = WriteRemaining; + WriteIrp->IoStatus.Information = WriteRemaining; + + // + // Set it up to do buffered I/O and deallocate the buffer + // on completion. + + if (WriteRemaining > 0) { + + WriteIrp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER; + } + + WriteIrp->UserIosb = &Iosb; + + // + // Add this write request to the write queue + // + + DataEntry = NpAddDataQueueEntry( WriteQueue, + WriteEntries, + Unbuffered, + WriteRemaining, + WriteIrp, + NULL ); + + // + // And set the security part of the data entry + // + + NpSetDataEntryClientContext( NamedPipeEnd, + Ccb, + DataEntry, + UserThread ); + + // + // Now null out the write irp variable so that we know not + // to deallocate it on an error + // + + WriteIrp = NULL; + UnwindStorage = NULL; + } + + // + // Now we need to advance the write queue to the next read irp to + // skip over flushes and closes + // + + //****NpWriteDataQueue does this for us**** (VOID)NpGetNextRealDataQueueEntry( WriteQueue ); + + // + // And because we've done something we need to signal the + // other ends event + // + + NpSignalEventTableEntry( Event ); + + // + // Do the transceive read operation. This is just like a + // buffered read. + // + // Now we know that the read queue is empty so we'll enqueue this + // Irp to the read queue and return status pending, also mark the + // irp pending + // + + ASSERT( NpIsDataQueueEmpty( ReadQueue )); + + (VOID)NpAddDataQueueEntry( ReadQueue, + ReadEntries, + Buffered, + ReadLength, + Irp, + NULL ); + + IoMarkIrpPending( Irp ); + + (VOID)NpGetNextRealDataQueueEntry( ReadQueue ); + + // + // And because we've done something we need to signal the + // other ends event + // + + NpSignalEventTableEntry( Event ); + + Status = STATUS_PENDING; + + try_exit: NOTHING; + } finally { + + NpReleaseCcb(Ccb); + if (WriteIrp != NULL) { IoFreeIrp( WriteIrp ); } + if (UnwindStorage != NULL) { ExFreePool( UnwindStorage ); } + } + + DebugTrace(-1, Dbg, "NpTransceive -> %08lx\n", Status); + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +NpWaitForNamedPipe ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine does the wait for named pipe control function + +Arguments: + + NpfsDeviceObject - Supplies our device object + + Irp - Supplies the being processed + +Return Value: + + NTSTATUS - An apprropriate return status + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + ULONG InputBufferLength; + ULONG FsControlCode; + + PFCB Fcb; + PCCB Ccb; + + PFILE_PIPE_WAIT_FOR_BUFFER WaitBuffer; + UNICODE_STRING Name; + PVOID LocalBuffer; + + PLIST_ENTRY Links; + + BOOLEAN CaseInsensitive = TRUE; //**** Make all searches case insensitive + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpWaitForNamedPipe...\n", 0); + + // + // Extract the important fields from the IrpSp + // + + InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode; + + Name.Buffer = NULL; + LocalBuffer = NULL; + + try { + + // + // Decode the file object to figure out who we are. If the result + // is an error if the we weren't given a Vcb. + // + + { + PCCB Ccb; + NAMED_PIPE_END NamedPipeEnd; + + if (NpDecodeFileObject( IrpSp->FileObject, + NULL, + &Ccb, + &NamedPipeEnd ) != NPFS_NTC_ROOT_DCB) { + + DebugTrace(0, Dbg, "File Object is not for the named pipe root directory\n", 0); + + NpCompleteRequest( Irp, STATUS_ILLEGAL_FUNCTION ); + try_return( Status = STATUS_ILLEGAL_FUNCTION ); + } + } + + // + // Reference the system buffer as a wait for buffer and make + // sure it's large enough + // + + if (InputBufferLength < sizeof(FILE_PIPE_WAIT_FOR_BUFFER)) { + + DebugTrace(0, Dbg, "System buffer size is too small\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + WaitBuffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Check for an invalid buffer + // + + if (FIELD_OFFSET(FILE_PIPE_WAIT_FOR_BUFFER, Name[0]) + WaitBuffer->NameLength > + InputBufferLength) { + + DebugTrace(0, Dbg, "System buffer size is too small\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // Set up the local variable Name to be the name we're looking + // for + // + + Name.Length = (USHORT)(WaitBuffer->NameLength + 2); + Name.Buffer = LocalBuffer = FsRtlAllocatePool( PagedPool, Name.Length ); + + Name.Buffer[0] = L'\\'; + + RtlCopyMemory( &Name.Buffer[1], + &WaitBuffer->Name[0], + WaitBuffer->NameLength ); + + // + // If the name is an alias, translate it. + // + + Status = NpTranslateAlias( &Name ); + if ( !NT_SUCCESS(Status) ) { + NpCompleteRequest( Irp, Status ); + try_return( NOTHING ); + } + + // + // Now check to see if we can find a named pipe with the right + // name + // + + Fcb = NpFindPrefix( &Name, CaseInsensitive, &Name ); + + // + // If the Fcb is null then we can't wait for it, Also if the + // Fcb is not an Fcb then we also have nothing to wait for + // + + if (NodeType(Fcb) != NPFS_NTC_FCB) { + + DebugTrace(0, Dbg, "Bad nonexistent named pipe name", 0); + + NpCompleteRequest( Irp, STATUS_OBJECT_NAME_NOT_FOUND ); + try_return( Status = STATUS_OBJECT_NAME_NOT_FOUND ); + } + + // + // Now we need to search to see if we find a ccb already in the + // listening state + // First try and find a ccb that is in the listening state + // If we exit the loop with ccb null then we haven't found + // one + // + + Ccb = NULL; + for (Links = Fcb->Specific.Fcb.CcbQueue.Flink; + Links != &Fcb->Specific.Fcb.CcbQueue; + Links = Links->Flink) { + + Ccb = CONTAINING_RECORD( Links, CCB, CcbLinks ); + + if (Ccb->NamedPipeState == FILE_PIPE_LISTENING_STATE) { + + break; + } + + Ccb = NULL; + } + + // + // Check if we found one + // + + if (Ccb != NULL) { + + DebugTrace(0, Dbg, "Found a ccb in listening state\n", 0); + + NpCompleteRequest( Irp, STATUS_SUCCESS ); + try_return( Status = STATUS_SUCCESS ); + } + + // + // We weren't able to find one so we need to add a new waiter + // Mark the irp pending and add this to the wait queue + // + + IoMarkIrpPending( Irp ); + + try { + + NpAddWaiter( &NpVcb->WaitQueue, + Fcb->Specific.Fcb.DefaultTimeOut, + Irp ); + } finally { + + // + // If we bomb out trying to add the waiter then we better + // not mark this irp as pending. We have to do it after the + // fact because once the irp is successfully in the wait queue + // it is out of our hands. + // + + if (AbnormalTermination()) { + + IoGetCurrentIrpStackLocation((Irp))->Control &= ~SL_PENDING_RETURNED; + } + } + + Status = STATUS_PENDING; + + try_exit: NOTHING; + } finally { + + if (LocalBuffer != NULL) { ExFreePool( LocalBuffer ); } + } + + DebugTrace(-1, Dbg, "NpWaitForNamedPipe -> %08lx\n", Status); + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +NpImpersonate ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine does the impersonate of the named pipe + +Arguments: + + NpfsDeviceObject - Supplies our device object + + Irp - Supplies the being processed + +Return Value: + + NTSTATUS - An apprropriate return status + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + PCCB Ccb; + NAMED_PIPE_END NamedPipeEnd; + + UNREFERENCED_PARAMETER( NpfsDeviceObject ); + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpImpersonate...\n", 0); + + // + // Decode the file object to figure out who we are. If the result + // is an error if the we weren't given a Vcb. + // + + if (NpDecodeFileObject( IrpSp->FileObject, + NULL, + &Ccb, + &NamedPipeEnd ) != NPFS_NTC_CCB) { + + DebugTrace(0, Dbg, "File Object is not a named pipe\n", 0); + + NpCompleteRequest( Irp, STATUS_ILLEGAL_FUNCTION ); + + DebugTrace(-1, Dbg, "NpImpersonate -> STATUS_ILLEGAL_FUNCTION\n", 0 ); + return STATUS_ILLEGAL_FUNCTION; + } + + // + // Make sure that we are the server end and not the client end + // + + if (NamedPipeEnd != FILE_PIPE_SERVER_END) { + + DebugTrace(0, Dbg, "Not the server end\n", 0); + + NpCompleteRequest( Irp, STATUS_ILLEGAL_FUNCTION ); + + DebugTrace(-1, Dbg, "NpImpersonate -> STATUS_ILLEGAL_FUNCTION\n", 0 ); + return STATUS_ILLEGAL_FUNCTION; + } + + // + // set up the impersonation + // + + Status = NpImpersonateClientContext( Ccb ); + + NpCompleteRequest( Irp, STATUS_SUCCESS ); + + DebugTrace(-1, Dbg, "NpImpersonate -> %08lx\n", Status); + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +NpInternalRead ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp, + IN BOOLEAN ReadOverflowOperation + ) + +/*++ + +Routine Description: + + This routine does the unbuffered read named pipe control function + +Arguments: + + NpfsDeviceObject - Supplies our device object + + Irp - Supplies the being processed + + ReadOverflowOperation - Used to indicate if the read being processed is a read overflow + operation. + +Return Value: + + NTSTATUS - An apprropriate return status + +--*/ + +{ + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp; + + NODE_TYPE_CODE NodeTypeCode; + PCCB Ccb; + PNONPAGED_CCB NonpagedCcb; + NAMED_PIPE_END NamedPipeEnd; + + NAMED_PIPE_CONFIGURATION NamedPipeConfiguration; + + PIRP ReadIrp; + PUCHAR ReadBuffer; + ULONG ReadLength; + ULONG ReadRemaining; + READ_MODE ReadMode; + COMPLETION_MODE CompletionMode; + PDATA_QUEUE ReadQueue; + PEVENT_TABLE_ENTRY Event; + + PAGED_CODE(); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpInternalRead\n", 0); + DebugTrace( 0, Dbg, "NpfsDeviceObject = %08lx\n", NpfsDeviceObject); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg, "FileObject = %08lx\n", IrpSp->FileObject); + + // + // Get the Ccb and figure out who we are, and make sure we're not + // disconnected + // + + if ((NodeTypeCode = NpDecodeFileObject( IrpSp->FileObject, + NULL, + &Ccb, + &NamedPipeEnd )) == NTC_UNDEFINED) { + + DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + + DebugTrace(-1, Dbg, "NpInternalRead -> STATUS_PIPE_DISCONNECTED\n", 0 ); + return STATUS_PIPE_DISCONNECTED; + } + + // + // Now we only will allow Read operations on the pipe and not a directory + // or the device + // + + if (NodeTypeCode != NPFS_NTC_CCB) { + + DebugTrace(0, Dbg, "FileObject is not for a named pipe\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "NpInternalRead -> STATUS_INVALID_PARAMETER\n", 0 ); + return STATUS_INVALID_PARAMETER; + } + + NonpagedCcb = Ccb->NonpagedCcb; + + NpAcquireExclusiveCcb(Ccb); + + // + // Check if the pipe is not in the connected state. + // + + switch (Ccb->NamedPipeState) { + + case FILE_PIPE_DISCONNECTED_STATE: + + DebugTrace(0, Dbg, "Pipe in disconnected state\n", 0); + + NpReleaseCcb(Ccb); + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + + DebugTrace(-1, Dbg, "NpInternalRead -> STATUS_PIPE_DISCONNECTED\n", 0 ); + return STATUS_PIPE_DISCONNECTED; + + case FILE_PIPE_LISTENING_STATE: + + DebugTrace(0, Dbg, "Pipe in listening state\n", 0); + + NpReleaseCcb(Ccb); + NpCompleteRequest( Irp, STATUS_PIPE_LISTENING ); + + DebugTrace(-1, Dbg, "NpInternalRead -> STATUS_PIPE_LISTENING\n", 0 ); + return STATUS_PIPE_LISTENING; + + case FILE_PIPE_CONNECTED_STATE: + case FILE_PIPE_CLOSING_STATE: + + break; + + default: + + DebugTrace(0, Dbg, "Illegal pipe state = %08lx\n", Ccb->NamedPipeState); + NpBugCheck( Ccb->NamedPipeState, 0, 0 ); + } + + // + // We only allow a read by the server on a non outbound only pipe + // and by the client on a non inbound only pipe + // + + NamedPipeConfiguration = Ccb->Fcb->Specific.Fcb.NamedPipeConfiguration; + + if (((NamedPipeEnd == FILE_PIPE_SERVER_END) && + (NamedPipeConfiguration == FILE_PIPE_OUTBOUND)) + + || + + ((NamedPipeEnd == FILE_PIPE_CLIENT_END) && + (NamedPipeConfiguration == FILE_PIPE_INBOUND))) { + + DebugTrace(0, Dbg, "Trying to read to the wrong pipe configuration\n", 0); + + NpReleaseCcb(Ccb); + NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "NpInternalRead -> STATUS_INVALID_PARAMETER\n", 0 ); + return STATUS_INVALID_PARAMETER; + } + + // + // Reference our input parameters to make things easier, and + // initialize our main variables that describe the Read command + // + + ReadIrp = Irp; + ReadBuffer = Irp->AssociatedIrp.SystemBuffer; + ReadLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; + ReadRemaining = ReadLength; + ReadMode = Ccb->ReadMode[ NamedPipeEnd ]; + CompletionMode = Ccb->CompletionMode[ NamedPipeEnd ]; + + // + // Now the data queue that we read from into and the event that we signal + // are based on the named pipe end. The server read from the inbound + // queue and signals the client event. The client does just the + // opposite. + // + + switch (NamedPipeEnd) { + + case FILE_PIPE_SERVER_END: + + ReadQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + + Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ]; + + break; + + case FILE_PIPE_CLIENT_END: + + ReadQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + + Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_SERVER_END ]; + + break; + + default: + + NpBugCheck( NamedPipeEnd, 0, 0 ); + } + + DebugTrace(0, Dbg, "ReadBuffer = %08lx\n", ReadBuffer); + DebugTrace(0, Dbg, "ReadLength = %08lx\n", ReadLength); + DebugTrace(0, Dbg, "ReadMode = %08lx\n", ReadMode); + DebugTrace(0, Dbg, "CompletionMode = %08lx\n", CompletionMode); + DebugTrace(0, Dbg, "ReadQueue = %08lx\n", ReadQueue); + DebugTrace(0, Dbg, "Event = %08lx\n", Event); + + // + // if the read queue does not contain any write entries + // then we either need to enqueue this operation or + // fail immediately + // + + if (!NpIsDataQueueWriters( ReadQueue )) { + + // + // Check if the other end of the pipe is closing, and if + // so then we complete it with end of file. + // Otherwise check to see if we should enqueue the irp + // or complete the operation and tell the user the pipe is empty. + // + + if (Ccb->NamedPipeState == FILE_PIPE_CLOSING_STATE) { + + DebugTrace(0, Dbg, "Complete the irp with eof\n", 0); + + NpCompleteRequest( ReadIrp, STATUS_PIPE_BROKEN ); + + Status = STATUS_PIPE_BROKEN; + + } else if (CompletionMode == FILE_PIPE_QUEUE_OPERATION) { + + DebugTrace(0, Dbg, "Put the irp into the read queue\n", 0); + + (VOID)NpAddDataQueueEntry( ReadQueue, + ReadEntries, + Unbuffered, + ReadLength, + ReadIrp, + NULL ); + + IoMarkIrpPending( Irp ); + + Status = STATUS_PENDING; + + } else { + + DebugTrace(0, Dbg, "Complete the irp with pipe empty\n", 0); + + NpCompleteRequest( ReadIrp, STATUS_PIPE_EMPTY ); + + Status = STATUS_PIPE_EMPTY; + } + + } else { + + // + // otherwise there we have a read irp against a read queue + // that contains one or more write entries. + // + + ReadIrp->IoStatus = NpReadDataQueue( ReadQueue, + FALSE, + ReadOverflowOperation, + ReadBuffer, + ReadLength, + ReadMode, + Ccb ); + + Status = ReadIrp->IoStatus.Status; + + // + // Now set the remaining byte count in the allocation size of + // the Irp. + // + + ReadIrp->Overlay.AllocationSize.QuadPart = ReadQueue->BytesInQueue; + + // + // Finish up the read irp. + // + + NpCompleteRequest( ReadIrp, Status ); + } + + // + // Now we need to advance the read queue to the next write irp to + // skip over flushes and closes + // + + (VOID)NpGetNextRealDataQueueEntry( ReadQueue ); + + // + // And because we've done something we need to signal the + // other ends event + // + + NpSignalEventTableEntry( Event ); + + NpReleaseCcb(Ccb); + + DebugTrace(-1, Dbg, "NpInternalRead -> %08lx\n", Status); + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +NpInternalWrite ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine does the unbuffered write named pipe control function + +Arguments: + + NpfsDeviceObject - Supplies our device object + + Irp - Supplies the being processed + +Return Value: + + NTSTATUS - An apprropriate return status + +--*/ + +{ + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp; + PETHREAD UserThread; + + NODE_TYPE_CODE NodeTypeCode; + PCCB Ccb; + PNONPAGED_CCB NonpagedCcb; + NAMED_PIPE_END NamedPipeEnd; + + NAMED_PIPE_CONFIGURATION NamedPipeConfiguration; + + PIRP WriteIrp; + PUCHAR WriteBuffer; + ULONG WriteLength; + ULONG WriteRemaining; + PDATA_QUEUE WriteQueue; + + PEVENT_TABLE_ENTRY Event; + READ_MODE ReadMode; + + PDATA_ENTRY DataEntry; + + PAGED_CODE(); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpInternalWrite\n", 0); + DebugTrace( 0, Dbg, "NpfsDeviceObject = %08lx\n", NpfsDeviceObject); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg, "FileObject = %08lx\n", IrpSp->FileObject); + + // + // Get the Ccb and figure out who we are, and make sure we're not + // disconnected + // + + if ((NodeTypeCode = NpDecodeFileObject( IrpSp->FileObject, + NULL, + &Ccb, + &NamedPipeEnd )) == NTC_UNDEFINED) { + + DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + + DebugTrace(-1, Dbg, "NpInternalWrite -> STATUS_PIPE_DISCONNECTED\n", 0 ); + return STATUS_PIPE_DISCONNECTED; + } + + // + // Now we only will allow write operations on the pipe and not a directory + // or the device + // + + if (NodeTypeCode != NPFS_NTC_CCB) { + + DebugTrace(0, Dbg, "FileObject is not for a named pipe\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "NpInternalWrite -> STATUS_PIPE_DISCONNECTED\n", 0); + return STATUS_PIPE_DISCONNECTED; + } + + NonpagedCcb = Ccb->NonpagedCcb; + + NpAcquireExclusiveCcb(Ccb); + + // + // We only allow a write by the server on a non inbound only pipe + // and by the client on a non outbound only pipe + // + + NamedPipeConfiguration = Ccb->Fcb->Specific.Fcb.NamedPipeConfiguration; + + if (((NamedPipeEnd == FILE_PIPE_SERVER_END) && + (NamedPipeConfiguration == FILE_PIPE_INBOUND)) + + || + + ((NamedPipeEnd == FILE_PIPE_CLIENT_END) && + (NamedPipeConfiguration == FILE_PIPE_OUTBOUND))) { + + DebugTrace(0, Dbg, "Trying to write to the wrong pipe configuration\n", 0); + + NpReleaseCcb(Ccb); + NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "NpInternalWrite -> STATUS_PIPE_DISCONNECTED\n", 0); + return STATUS_PIPE_DISCONNECTED; + } + + // + // Reference our input parameters to make things easier, and + // initialize our main variables that describe the write command + // + + WriteIrp = Irp; + WriteBuffer = Irp->AssociatedIrp.SystemBuffer; + WriteLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + + // + // Set up the amount of data we will have written by the time this + // irp gets completed + // + + WriteIrp->IoStatus.Information = WriteLength; + + // + // Now the data queue that we write into and the event that we signal + // are based on the named pipe end. The server writes to the outbound + // queue and signals the client event. The client does just the + // opposite. We also need to figure out the read mode for the opposite + // end of the pipe. + // + + switch (NamedPipeEnd) { + + case FILE_PIPE_SERVER_END: + + WriteQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + + Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ]; + ReadMode = Ccb->ReadMode[ FILE_PIPE_CLIENT_END ]; + + break; + + case FILE_PIPE_CLIENT_END: + + WriteQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + + Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_SERVER_END ]; + ReadMode = Ccb->ReadMode[ FILE_PIPE_SERVER_END ]; + + break; + + default: + + NpBugCheck( NamedPipeEnd, 0, 0 ); + } + + // + // Check if the pipe is not in the connected state. + // + + switch (Ccb->NamedPipeState) { + + case FILE_PIPE_DISCONNECTED_STATE: + + DebugTrace(0, Dbg, "Pipe in disconnected state\n", 0); + + NpReleaseCcb(Ccb); + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + return STATUS_PIPE_DISCONNECTED; + + case FILE_PIPE_LISTENING_STATE: + + DebugTrace(0, Dbg, "Pipe in listening state\n", 0); + + NpReleaseCcb(Ccb); + NpCompleteRequest( Irp, STATUS_PIPE_LISTENING ); + return STATUS_PIPE_LISTENING; + + case FILE_PIPE_CONNECTED_STATE: + + break; + + case FILE_PIPE_CLOSING_STATE: + + DebugTrace(0, Dbg, "Pipe in closing state\n", 0); + + NpReleaseCcb(Ccb); + NpCompleteRequest( Irp, STATUS_PIPE_CLOSING ); + return STATUS_PIPE_CLOSING; + + default: + + DebugTrace(0, Dbg, "Illegal pipe state = %08lx\n", Ccb->NamedPipeState); + NpBugCheck( Ccb->NamedPipeState, 0, 0 ); + } + + // + // Check if this is a message type pipe and the operation type is complete + // operation, If so then we also check that the queued reads is enough to + // complete the message otherwise we need to abort the write irp immediately. + // + + if ((Ccb->Fcb->Specific.Fcb.NamedPipeType == FILE_PIPE_MESSAGE_TYPE) && + (Ccb->CompletionMode[NamedPipeEnd] == FILE_PIPE_COMPLETE_OPERATION)) { + + // + // If the pipe contains readers and amount to read is less than the write + // length then we cannot do it the write. + // Or if pipe does not contain reads then we also cannot do the write. + // + + if ((NpIsDataQueueReaders( WriteQueue ) && + (WriteQueue->BytesInQueue < (WriteLength + sizeof(DATA_ENTRY)))) + + || + + (!NpIsDataQueueReaders( WriteQueue ))) { + + DebugTrace(0, Dbg, "Cannot complete the message without blocking\n", 0); + + NpReleaseCcb(Ccb); + Irp->IoStatus.Information = 0; + NpCompleteRequest( Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; + } + } + + // + // Now we'll call our common write data queue routine to + // transfer data out of our write buffer into the data queue. + // If the result of the call is FALSE then we still have some + // write data to put into the write queue. + // + + UserThread = Irp->Tail.Overlay.Thread; + + if (!NpWriteDataQueue( WriteQueue, + ReadMode, + WriteBuffer, + WriteLength, + Ccb->Fcb->Specific.Fcb.NamedPipeType, + &WriteRemaining, + Ccb, + NamedPipeEnd, + UserThread )) { + + ASSERT( !NpIsDataQueueReaders( WriteQueue )); + + ASSERT((Ccb->Fcb->Specific.Fcb.NamedPipeType == FILE_PIPE_BYTE_STREAM_TYPE) || + (Ccb->CompletionMode[NamedPipeEnd] == FILE_PIPE_QUEUE_OPERATION) || + (WriteRemaining <= (WriteQueue->Quota - WriteQueue->QuotaUsed))); + + // + // Check if the operation is not to block and if so then we + // will complete the operation now with what we're written, if what is + // left will not fit in the quota for the file + // + + if (Ccb->CompletionMode[NamedPipeEnd] == FILE_PIPE_COMPLETE_OPERATION) { + + DebugTrace(0, Dbg, "Complete the byte stream write immediately\n", 0); + + Irp->IoStatus.Information = WriteLength - WriteRemaining; + + NpCompleteRequest( Irp, Status = STATUS_SUCCESS ); + + } else { + + DebugTrace(0, Dbg, "Add write to data queue\n", 0); + + IoMarkIrpPending( Irp ); + + // + // Add this write request to the write queue + // + + try { + + DataEntry = NpAddDataQueueEntry( WriteQueue, + WriteEntries, + Unbuffered, + WriteLength, + Irp, + NULL ); + + // + // And set the security part of the data entry + // + + NpSetDataEntryClientContext( NamedPipeEnd, + Ccb, + DataEntry, + UserThread ); + + } finally { + + if (AbnormalTermination()) { + + IoGetCurrentIrpStackLocation((Irp))->Control &= ~SL_PENDING_RETURNED; + } + } + + // + // Now if the remaining length is not equal to the original + // write length then this must have been the first write entry + // into the data queue and we need to set the Next Byte + // field + // + + if (WriteLength > WriteRemaining) { + + WriteQueue->NextByteOffset = WriteLength - WriteRemaining; + } + + // + // Set our status for the write irp to pending + // + + Status = STATUS_PENDING; + } + + } else { + + DebugTrace(0, Dbg, "Complete the Write Irp\n", 0); + + + // + // The write irp is finished so we can complete it now + // + + NpCompleteRequest( WriteIrp, STATUS_SUCCESS ); + + // + // Set our status for the write irp to success + // + + Status = STATUS_SUCCESS; + } + + // + // Now we need to advance the write queue to the next read irp to + // skip over flushes and closes + // + + //****NpWriteDataQueue does this for us**** (VOID)NpGetNextRealDataQueueEntry( WriteQueue ); + + // + // And because we've done something we need to signal the + // other ends event + // + + NpSignalEventTableEntry( Event ); + + NpReleaseCcb(Ccb); + + DebugTrace(-1, Dbg, "NpInternalWrite -> %08lx\n", Status); + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +NpInternalTransceive ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine does the internal (i.e., unbuffered) transceive named pipe + control function + +Arguments: + + NpfsDeviceObject - Supplies our device object + + Irp - Supplies the being processed + +Return Value: + + NTSTATUS - An apprropriate return status + +--*/ + +{ + static IO_STATUS_BLOCK Iosb; + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp; + PETHREAD UserThread; + + PUCHAR WriteBuffer; + ULONG WriteLength; + + PUCHAR ReadBuffer; + ULONG ReadLength; + + NODE_TYPE_CODE NodeTypeCode; + PCCB Ccb; + PNONPAGED_CCB NonpagedCcb; + NAMED_PIPE_END NamedPipeEnd; + + PDATA_QUEUE ReadQueue; + PDATA_QUEUE WriteQueue; + PEVENT_TABLE_ENTRY Event; + READ_MODE ReadMode; + + NAMED_PIPE_CONFIGURATION NamedPipeConfiguration; + + ULONG WriteRemaining; + + PIRP WriteIrp; + PDATA_ENTRY DataEntry; + + // + // The following variable is used for abnormal unwind + // + + PVOID UnwindStorage = NULL; + + PAGED_CODE(); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpInternalTransceive\n", 0); + DebugTrace( 0, Dbg, "NpfsDeviceObject = %08lx\n", NpfsDeviceObject); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg, "FileObject = %08lx\n", IrpSp->FileObject); + + WriteLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + WriteBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer; + + ReadLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; + ReadBuffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Get the Ccb and figure out who we are, and make sure we're not + // disconnected + // + + if ((NodeTypeCode = NpDecodeFileObject( IrpSp->FileObject, + NULL, + &Ccb, + &NamedPipeEnd )) == NTC_UNDEFINED) { + + DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + + DebugTrace(-1, Dbg, "NpInternalTransceive -> STATUS_PIPE_DISCONNECTED\n", 0 ); + return STATUS_PIPE_DISCONNECTED; + } + + // + // Now we only will allow transceive operations on the pipe and not a + // directory or the device + // + + if (NodeTypeCode != NPFS_NTC_CCB) { + + DebugTrace(0, Dbg, "FileObject is not for a named pipe\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "NpInternalTransceive -> STATUS_INVALID_PARAMETER\n", 0 ); + return STATUS_INVALID_PARAMETER; + } + + NonpagedCcb = Ccb->NonpagedCcb; + + WriteIrp = NULL; + NpAcquireExclusiveCcb(Ccb); + + try { + + // + // Check that the pipe is in the connected state + // + + if (Ccb->NamedPipeState != FILE_PIPE_CONNECTED_STATE) { + + DebugTrace(0, Dbg, "Pipe not connected\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_PIPE_STATE ); + try_return( Status = STATUS_INVALID_PIPE_STATE ); + } + + // + // Figure out the read/write queue, read mode, and event based + // on the end of the named pipe doing the transceive. + // + + switch (NamedPipeEnd) { + + case FILE_PIPE_SERVER_END: + + ReadQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + WriteQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + + Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ]; + ReadMode = Ccb->ReadMode[ FILE_PIPE_SERVER_END ]; + + break; + + case FILE_PIPE_CLIENT_END: + + ReadQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + WriteQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + + Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_SERVER_END ]; + ReadMode = Ccb->ReadMode[ FILE_PIPE_CLIENT_END ]; + + break; + + default: + + NpBugCheck( NamedPipeEnd, 0, 0 ); + } + + // + // We only allow a transceive on a message mode, full duplex pipe. + // + + NamedPipeConfiguration = Ccb->Fcb->Specific.Fcb.NamedPipeConfiguration; + + if ((NamedPipeConfiguration != FILE_PIPE_FULL_DUPLEX) || + (ReadMode != FILE_PIPE_MESSAGE_MODE)) { + + DebugTrace(0, Dbg, "Bad pipe configuration or read mode\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_READ_MODE ); + try_return( Status = STATUS_INVALID_READ_MODE ); + } + + // + // Check that the read queue is empty. + // + + if (!NpIsDataQueueEmpty( ReadQueue )) { + + DebugTrace(0, Dbg, "Read queue is not empty\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_BUSY ); + try_return( Status = STATUS_PIPE_BUSY ); + } + + // + // Do the transceive write operation. We first try and push the data + // from the write buffer into any waiting readers in the write queue + // and if that succeeds then we can go on and do the read operation + // otherwise we need to make a copy of irp and to enqueue as + // a data entry into the write queue. + // + // Now we'll call our common write data queue routine to + // transfer data out of our write buffer into the data queue. + // If the result of the call is FALSE then we still have some + // write data to put into the write queue. + // + + UserThread = Irp->Tail.Overlay.Thread; + + if (!NpWriteDataQueue( WriteQueue, + ReadMode, + WriteBuffer, + WriteLength, + Ccb->Fcb->Specific.Fcb.NamedPipeType, + &WriteRemaining, + Ccb, + NamedPipeEnd, + UserThread )) { + + PIO_STACK_LOCATION WriteIrpSp; + + ASSERT( !NpIsDataQueueReaders( WriteQueue )); + + DebugTrace(0, Dbg, "Add write to data queue\n", 0); + + // + // We need to do some more write processing. So to handle + // this case we'll allocate a new irp and set its system + // buffer to be the remaining part of the write buffer + // + + if ((WriteIrp = IoAllocateIrp( NpfsDeviceObject->DeviceObject.StackSize, FALSE )) == NULL) { + + ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); + } + + IoSetCompletionRoutine( WriteIrp, NpCompleteTransceiveIrp, NULL, TRUE, TRUE, TRUE ); + + WriteIrpSp = IoGetNextIrpStackLocation( WriteIrp ); + + if (WriteRemaining > 0) { + + WriteIrp->AssociatedIrp.SystemBuffer = UnwindStorage = FsRtlAllocatePool( NonPagedPool, + WriteRemaining ); + // + // Safely do the copy + // + + try { + + RtlCopyMemory( WriteIrp->AssociatedIrp.SystemBuffer, + &WriteBuffer[ WriteLength - WriteRemaining ], + WriteRemaining ); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + + } else { + + WriteIrp->AssociatedIrp.SystemBuffer = UnwindStorage = NULL; + } + + // + // Set the current stack location + // + + WriteIrp->CurrentLocation -= 1; + WriteIrp->Tail.Overlay.CurrentStackLocation = WriteIrpSp; + + // + // Set it up to do buffered I/O and deallocate the buffer + // on completion. + + if (WriteRemaining > 0) { + + WriteIrp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER; + } + + WriteIrp->UserIosb = &Iosb; + + // + // Add this write request to the write queue + // + + DataEntry = NpAddDataQueueEntry( WriteQueue, + WriteEntries, + Unbuffered, + WriteRemaining, + WriteIrp, + NULL ); + + // + // And set the security part of the data entry + // + + NpSetDataEntryClientContext( NamedPipeEnd, + Ccb, + DataEntry, + UserThread ); + + // + // Now null out the write irp variable so that we know not + // to deallocate it on an error + // + + WriteIrp = NULL; + UnwindStorage = NULL; + } + + // + // Now we need to advance the write queue to the next read irp to + // skip over flushes and closes + // + + //****NpWriteDataQueue does this for us**** (VOID)NpGetNextRealDataQueueEntry( WriteQueue ); + + // + // And because we've done something we need to signal the + // other ends event + // + + NpSignalEventTableEntry( Event ); + + // + // Do the transceive read operation. This is just like an + // unbuffered read. + // + // Now we know that the read queue is empty so we'll enqueue this + // Irp to the read queue and return status pending, also mark the + // irp pending + // + + ASSERT( NpIsDataQueueEmpty( ReadQueue )); + + (VOID)NpAddDataQueueEntry( ReadQueue, + ReadEntries, + Unbuffered, + ReadLength, + Irp, + NULL ); + + IoMarkIrpPending( Irp ); + + (VOID)NpGetNextRealDataQueueEntry( ReadQueue ); + + // + // And because we've done something we need to signal the + // other ends event + // + + NpSignalEventTableEntry( Event ); + + Status = STATUS_PENDING; + + try_exit: NOTHING; + } finally { + + NpReleaseCcb(Ccb); + if (WriteIrp != NULL) { IoFreeIrp( WriteIrp ); } + if (UnwindStorage != NULL) { ExFreePool( UnwindStorage ); } + } + + DebugTrace(-1, Dbg, "NpInternalTransceive -> %08lx\n", Status); + return Status; +} + + +// +// Internal support routine +// + +NTSTATUS +NpQueryClientProcess ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine does the query client process named pipe control function + +Arguments: + + NpfsDeviceObject - Supplies our device object + + Irp - Supplies the being processed + +Return Value: + + NTSTATUS - An apprropriate return status + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + + ULONG OutputBufferLength; + + PCCB Ccb; + + PFILE_PIPE_CLIENT_PROCESS_BUFFER ClientProcessBuffer; + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpQueryClientProcess\n", 0); + + OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; + + // + // Decode the file object to figure out who we are. + // + + if (NpDecodeFileObject( IrpSp->FileObject, NULL, &Ccb, NULL ) != NPFS_NTC_CCB) { + + DebugTrace(0, Dbg, "Pipe is disconnected\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + return STATUS_PIPE_DISCONNECTED; + } + + // + // Make sure the output buffer is large enough + // + + if (OutputBufferLength < sizeof(FILE_PIPE_CLIENT_PROCESS_BUFFER)) { + + DebugTrace(0, Dbg, "Output System buffer size is too small\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + NpAcquireExclusiveCcb(Ccb); + + // + // Copy over the client process ID + // + + ClientProcessBuffer = Irp->AssociatedIrp.SystemBuffer; + + ClientProcessBuffer->ClientSession = Ccb->ClientSession; + ClientProcessBuffer->ClientProcess = Ccb->ClientProcess; + + // + // Set the information field to the size of the client process + // buffer + // + + Irp->IoStatus.Information = sizeof(FILE_PIPE_CLIENT_PROCESS_BUFFER); + + NpReleaseCcb(Ccb); + + NpCompleteRequest( Irp, STATUS_SUCCESS ); + + DebugTrace(-1, Dbg, "NpQueryClientProcess -> STATUS_SUCCESS\n", 0); + return STATUS_SUCCESS; +} + + +// +// Internal support routine +// + +NTSTATUS +NpSetClientProcess ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine does the set client process named pipe control function + +Arguments: + + NpfsDeviceObject - Supplies our device object + + Irp - Supplies the being processed + +Return Value: + + NTSTATUS - An apprropriate return status + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + + ULONG InputBufferLength; + + PCCB Ccb; + + PFILE_PIPE_CLIENT_PROCESS_BUFFER ClientProcessBuffer; + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpSetClientProcess\n", 0); + + InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + + // + // Decode the file object to figure out who we are. + // + + if (NpDecodeFileObject( IrpSp->FileObject, NULL, &Ccb, NULL ) != NPFS_NTC_CCB) { + + DebugTrace(0, Dbg, "Pipe is disconnected\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + return STATUS_PIPE_DISCONNECTED; + } + + // + // Make sure the input buffer is large enough + // + + if (InputBufferLength < sizeof(FILE_PIPE_CLIENT_PROCESS_BUFFER)) { + + DebugTrace(0, Dbg, "Input System buffer size is too small\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Copy over the client process ID + // + + ClientProcessBuffer = Irp->AssociatedIrp.SystemBuffer; + + Ccb->ClientSession = ClientProcessBuffer->ClientSession; + Ccb->ClientProcess = ClientProcessBuffer->ClientProcess; + + NpCompleteRequest( Irp, STATUS_SUCCESS ); + + DebugTrace(-1, Dbg, "NpSetClientProcess -> STATUS_SUCCESS\n", 0); + return STATUS_SUCCESS; +} + + +// +// Internal support routine +// + +NTSTATUS +NpCompleteTransceiveIrp ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This is a local i/o completion routine used to complete the special + Irps allocated for transcieve. This routine simply deallocate the + irp and return status more processing + +Arguments: + + DeviceObject - Supplies the device object + + Irp - Supplies the Irp to complete + + Context - Supplies the context for the Irp + +Return Value: + + NTSTATUS - STATUS_MORE_PROCESSING_REQUIRED + +--*/ + +{ + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Context ); + + PAGED_CODE(); + + if (Irp->AssociatedIrp.SystemBuffer != NULL) { + + ExFreePool( Irp->AssociatedIrp.SystemBuffer ); + } + + IoFreeIrp( Irp ); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + diff --git a/private/ntos/npfs/makefile b/private/ntos/npfs/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/npfs/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/npfs/nodetype.h b/private/ntos/npfs/nodetype.h new file mode 100644 index 000000000..3ace73772 --- /dev/null +++ b/private/ntos/npfs/nodetype.h @@ -0,0 +1,143 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + NodeType.h + +Abstract: + + This module defines all of the node type codes used in this development + shell. Every major data structure in the file system is assigned a node + type code that is. This code is the first CSHORT in the structure and is + followed by a CSHORT containing the size, in bytes, of the structure. + +Author: + + Gary Kimura [GaryKi] 20-Aug-1990 + +Revision History: + +--*/ + +#ifndef _NODETYPE_ +#define _NODETYPE_ + +typedef CSHORT NODE_TYPE_CODE; +typedef NODE_TYPE_CODE *PNODE_TYPE_CODE; + +#define NTC_UNDEFINED ((NODE_TYPE_CODE)0x0000) + +#define NPFS_NTC_VCB ((NODE_TYPE_CODE)0x0401) + +#define NPFS_NTC_ROOT_DCB ((NODE_TYPE_CODE)0x0402) + +#define NPFS_NTC_FCB ((NODE_TYPE_CODE)0x0404) + +#define NPFS_NTC_CCB ((NODE_TYPE_CODE)0x0406) +#define NPFS_NTC_NONPAGED_CCB ((NODE_TYPE_CODE)0x0407) + +#define NPFS_NTC_ROOT_DCB_CCB ((NODE_TYPE_CODE)0x0408) + +typedef CSHORT NODE_BYTE_SIZE; + +// +// So all records start with +// +// typedef struct _RECORD_NAME { +// NODE_TYPE_CODE NodeTypeCode; +// NODE_BYTE_SIZE NodeByteSize; +// : +// } RECORD_NAME; +// typedef RECORD_NAME *PRECORD_NAME; +// + +#define NodeType(Ptr) ((Ptr) == NULL ? NTC_UNDEFINED : *((PNODE_TYPE_CODE)(Ptr))) + + +// +// The following definitions are used to generate meaningful blue bugcheck +// screens. On a bugcheck the file system can output 4 ulongs of useful +// information. The first ulong will have encoded in it a source file id +// (in the high word) and the line number of the bugcheck (in the low word). +// The other values can be whatever the caller of the bugcheck routine deems +// necessary. +// +// Each individual file that calls bugcheck needs to have defined at the +// start of the file a constant called BugCheckFileId with one of the +// NPFS_BUG_CHECK_ values defined below and then use NpBugCheck to bugcheck +// the system. +// + +#define NPFS_BUG_CHECK_CLEANUP (0x00010000) +#define NPFS_BUG_CHECK_CLOSE (0x00020000) +#define NPFS_BUG_CHECK_CREATE (0x00030000) +#define NPFS_BUG_CHECK_CREATENP (0x00040000) +#define NPFS_BUG_CHECK_DIR (0x00050000) +#define NPFS_BUG_CHECK_DATASUP (0x00060000) +#define NPFS_BUG_CHECK_DEVIOSUP (0x00070000) +#define NPFS_BUG_CHECK_DUMPSUP (0x00080000) +#define NPFS_BUG_CHECK_EVENTSUP (0x00090000) +#define NPFS_BUG_CHECK_FILEINFO (0x000a0000) +#define NPFS_BUG_CHECK_FILOBSUP (0x000b0000) +#define NPFS_BUG_CHECK_FLUSHBUF (0x000c0000) +#define NPFS_BUG_CHECK_FSCTRL (0x000d0000) +#define NPFS_BUG_CHECK_NPINIT (0x000e0000) +#define NPFS_BUG_CHECK_NPDATA (0x000f0000) +#define NPFS_BUG_CHECK_PREFXSUP (0x00100000) +#define NPFS_BUG_CHECK_READ (0x00110000) +#define NPFS_BUG_CHECK_READSUP (0x00120000) +#define NPFS_BUG_CHECK_RESRCSUP (0x00130000) +#define NPFS_BUG_CHECK_SEINFO (0x00140000) +#define NPFS_BUG_CHECK_SECURSUP (0x00150000) +#define NPFS_BUG_CHECK_STATESUP (0x00160000) +#define NPFS_BUG_CHECK_STRUCSUP (0x00170000) +#define NPFS_BUG_CHECK_VOLINFO (0x00180000) +#define NPFS_BUG_CHECK_WAITSUP (0x00190000) +#define NPFS_BUG_CHECK_WRITE (0x001a0000) +#define NPFS_BUG_CHECK_WRITESUP (0x001b0000) + +#define NpBugCheck(A,B,C) { KeBugCheckEx(NPFS_FILE_SYSTEM, BugCheckFileId | __LINE__, A, B, C ); } + + +// +// In this module we'll also define some globally known constants +// + +#define UCHAR_NUL 0x00 +#define UCHAR_SOH 0x01 +#define UCHAR_STX 0x02 +#define UCHAR_ETX 0x03 +#define UCHAR_EOT 0x04 +#define UCHAR_ENQ 0x05 +#define UCHAR_ACK 0x06 +#define UCHAR_BEL 0x07 +#define UCHAR_BS 0x08 +#define UCHAR_HT 0x09 +#define UCHAR_LF 0x0a +#define UCHAR_VT 0x0b +#define UCHAR_FF 0x0c +#define UCHAR_CR 0x0d +#define UCHAR_SO 0x0e +#define UCHAR_SI 0x0f +#define UCHAR_DLE 0x10 +#define UCHAR_DC1 0x11 +#define UCHAR_DC2 0x12 +#define UCHAR_DC3 0x13 +#define UCHAR_DC4 0x14 +#define UCHAR_NAK 0x15 +#define UCHAR_SYN 0x16 +#define UCHAR_ETB 0x17 +#define UCHAR_CAN 0x18 +#define UCHAR_EM 0x19 +#define UCHAR_SUB 0x1a +#define UCHAR_ESC 0x1b +#define UCHAR_FS 0x1c +#define UCHAR_GS 0x1d +#define UCHAR_RS 0x1e +#define UCHAR_US 0x1f +#define UCHAR_SP 0x20 + +#endif // _NODETYPE_ + diff --git a/private/ntos/npfs/npdata.c b/private/ntos/npfs/npdata.c new file mode 100644 index 000000000..7836dea37 --- /dev/null +++ b/private/ntos/npfs/npdata.c @@ -0,0 +1,117 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + NpData.c + +Abstract: + + This module declares the global data used by the Named Pipe file system. + +Author: + + Gary Kimura [GaryKi] 28-Dec-1989 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (NPFS_BUG_CHECK_NPDATA) + +// +// Local debug trace level +// + +#define Dbg (DEBUG_TRACE_CATCH_EXCEPTIONS) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpExceptionFilter) +#pragma alloc_text(PAGE, NpProcessException) +#endif + +PVCB NpVcb = NULL; + +FAST_IO_DISPATCH NpFastIoDispatch = { sizeof(FAST_IO_DISPATCH), + NULL, // FastIoCheck + NpFastRead, // Read + NpFastWrite, // Write + NULL, // QueryBasicInfo + NULL, // QueryStandardInfo + NULL, // Lock + NULL, // UnlockSingle + NULL, // UnlockAll + NULL }; // UnlockAllByKey + +// +// Lists of pipe name aliases. +// + +SINGLE_LIST_ENTRY NpAliasListByLength[(MAX_LENGTH_ALIAS_ARRAY-MIN_LENGTH_ALIAS_ARRAY)/sizeof(WCHAR)+1] = {NULL}; +SINGLE_LIST_ENTRY NpAliasList = {NULL}; + +PVOID NpAliases = NULL; // single allocation containing all aliases + + +#ifdef NPDBG +LONG NpDebugTraceLevel = 0x00000000; +LONG NpDebugTraceIndent = 0; +#endif // NPDBG + + +LONG +NpExceptionFilter ( + IN NTSTATUS ExceptionCode + ) +{ + PAGED_CODE(); + + DebugTrace(0, Dbg, "NpExceptionFilter %08lx\n", ExceptionCode); + DebugDump("", Dbg, NULL ); + + if (FsRtlIsNtstatusExpected( ExceptionCode )) { + + return EXCEPTION_EXECUTE_HANDLER; + + } else { + + return EXCEPTION_CONTINUE_SEARCH; + } +} + +NTSTATUS +NpProcessException ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp, + IN NTSTATUS ExceptionCode + ) +{ + NTSTATUS FinalExceptionCode; + + PAGED_CODE(); + + FinalExceptionCode = ExceptionCode; + + if (FsRtlIsNtstatusExpected( ExceptionCode )) { + + if ( FlagOn(Irp->Flags, IRP_INPUT_OPERATION) ) { + + Irp->IoStatus.Information = 0; + } + + NpCompleteRequest( Irp, ExceptionCode ); + + } else { + + NpBugCheck( ExceptionCode, 0, 0 ); + } + + return FinalExceptionCode; +} diff --git a/private/ntos/npfs/npdata.h b/private/ntos/npfs/npdata.h new file mode 100644 index 000000000..08e8dc860 --- /dev/null +++ b/private/ntos/npfs/npdata.h @@ -0,0 +1,182 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + NpData.c + +Abstract: + + This module declares the global data used by the Named Pipe file system. + +Author: + + Gary Kimura [GaryKi] 20-Aug-1990 + +Revision History: + +--*/ + +#ifndef _NPDATA_ +#define _NPDATA_ + +extern PVCB NpVcb; + +// +// The global structure used to contain our fast I/O callbacks +// + +extern FAST_IO_DISPATCH NpFastIoDispatch; + +// +// Lists of pipe name aliases. +// + +#define MIN_LENGTH_ALIAS_ARRAY (5 * sizeof(WCHAR)) // includes '\' +#define MAX_LENGTH_ALIAS_ARRAY (9 * sizeof(WCHAR)) + +extern SINGLE_LIST_ENTRY NpAliasListByLength[(MAX_LENGTH_ALIAS_ARRAY-MIN_LENGTH_ALIAS_ARRAY)/sizeof(WCHAR)+1]; +extern SINGLE_LIST_ENTRY NpAliasList; + +extern PVOID NpAliases; // single allocation containing all aliases + +// +// The global Named Pipe debug level variable, its values are: +// +// 0x00000000 Always gets printed (used when about to bug check) +// +// 0x00000001 +// 0x00000002 +// 0x00000004 +// 0x00000008 +// +// 0x00000010 +// 0x00000020 +// 0x00000040 +// 0x00000080 +// +// 0x00000100 +// 0x00000200 +// 0x00000400 +// 0x00000800 +// +// 0x00001000 +// 0x00002000 +// 0x00004000 +// 0x00008000 +// +// 0x00010000 +// 0x00020000 +// 0x00040000 +// 0x00080000 +// +// 0x00100000 +// 0x00200000 +// 0x00400000 +// 0x00800000 +// +// 0x01000000 +// 0x02000000 +// 0x04000000 +// 0x08000000 +// +// 0x10000000 +// 0x20000000 +// 0x40000000 +// 0x80000000 +// + +#ifdef NPDBG + +#define DEBUG_TRACE_ERROR (0x00000001) +#define DEBUG_TRACE_DEBUG_HOOKS (0x00000002) +#define DEBUG_TRACE_CATCH_EXCEPTIONS (0x00000004) +#define DEBUG_TRACE_CREATE (0x00000008) +#define DEBUG_TRACE_CLOSE (0x00000010) +#define DEBUG_TRACE_READ (0x00000020) +#define DEBUG_TRACE_WRITE (0x00000040) +#define DEBUG_TRACE_FILEINFO (0x00000080) +#define DEBUG_TRACE_CLEANUP (0x00000100) +#define DEBUG_TRACE_DIR (0x00000200) +#define DEBUG_TRACE_FSCONTRL (0x00000400) +#define DEBUG_TRACE_CREATE_NAMED_PIPE (0x00000800) +#define DEBUG_TRACE_FLUSH_BUFFERS (0x00001000) +#define DEBUG_TRACE_VOLINFO (0x00002000) +#define DEBUG_TRACE_SEINFO (0x00004000) +#define DEBUG_TRACE_0x00008000 (0x00008000) +#define DEBUG_TRACE_0x00010000 (0x00010000) +#define DEBUG_TRACE_SECURSUP (0x00020000) +#define DEBUG_TRACE_DEVIOSUP (0x00040000) +#define DEBUG_TRACE_RESRCSUP (0x00080000) +#define DEBUG_TRACE_READSUP (0x00100000) +#define DEBUG_TRACE_WRITESUP (0x00200000) +#define DEBUG_TRACE_STATESUP (0x00400000) +#define DEBUG_TRACE_FILOBSUP (0x00800000) +#define DEBUG_TRACE_PREFXSUP (0x01000000) +#define DEBUG_TRACE_CNTXTSUP (0x02000000) +#define DEBUG_TRACE_DATASUP (0x04000000) +#define DEBUG_TRACE_WAITSUP (0x08000000) +#define DEBUG_TRACE_EVENTSUP (0x10000000) +#define DEBUG_TRACE_STRUCSUP (0x20000000) + +extern LONG NpDebugTraceLevel; +extern LONG NpDebugTraceIndent; + +#define DebugTrace(INDENT,LEVEL,X,Y) { \ + LONG _i; \ + if (((LEVEL) == 0) || (NpDebugTraceLevel & (LEVEL))) { \ + _i = (ULONG)PsGetCurrentThread(); \ + DbgPrint("%08lx:",_i); \ + if ((INDENT) < 0) { \ + NpDebugTraceIndent += (INDENT); \ + } \ + if (NpDebugTraceIndent < 0) { \ + NpDebugTraceIndent = 0; \ + } \ + for (_i=0; _i<NpDebugTraceIndent; _i+=1) { \ + DbgPrint(" "); \ + } \ + DbgPrint(X,Y); \ + if ((INDENT) > 0) { \ + NpDebugTraceIndent += (INDENT); \ + } \ + } \ +} + +#define DebugDump(STR,LEVEL,PTR) { \ + ULONG _i; \ + VOID NpDump(PVOID Ptr); \ + if (((LEVEL) == 0) || (NpDebugTraceLevel & (LEVEL))) { \ + _i = (ULONG)PsGetCurrentThread(); \ + DbgPrint("%08lx:",_i); \ + DbgPrint(STR); \ + if (PTR != NULL) {NpDump(PTR);} \ + DbgBreakPoint(); \ + } \ +} + +#else + +#define DebugTrace(INDENT,LEVEL,X,Y) {NOTHING;} +#define DebugDump(STR,LEVEL,PTR) {NOTHING;} + +#endif // NPDBG + +// +// The following macro is for all people who compile with the DBG switch +// set, not just fastfat dbg users +// + +#if DBG + +#define DbgDoit(X) {X;} + +#else + +#define DbgDoit(X) {NOTHING;} + +#endif // DBG + + +#endif // _NPDATA_ diff --git a/private/ntos/npfs/npfs.prf b/private/ntos/npfs/npfs.prf new file mode 100644 index 000000000..24024ae35 --- /dev/null +++ b/private/ntos/npfs/npfs.prf @@ -0,0 +1 @@ +NpDeleteCcb@4 diff --git a/private/ntos/npfs/npfs.rc b/private/ntos/npfs/npfs.rc new file mode 100644 index 000000000..ff0ac95c3 --- /dev/null +++ b/private/ntos/npfs/npfs.rc @@ -0,0 +1,12 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "NPFS Driver" +#define VER_INTERNALNAME_STR "npfs.sys" +#define VER_ORIGINALFILENAME_STR "npfs.sys" + +#include "common.ver" + diff --git a/private/ntos/npfs/npinit.c b/private/ntos/npfs/npinit.c new file mode 100644 index 000000000..3c68a0508 --- /dev/null +++ b/private/ntos/npfs/npinit.c @@ -0,0 +1,178 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + NpInit.c + +Abstract: + + This module implements the DRIVER_INITIALIZATION routine for the Named + Pipe file system. + +Author: + + Gary Kimura [GaryKi] 21-Aug-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" +//#include <zwapi.h> + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,DriverEntry) +#endif + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This is the initialization routine for the Named Pipe file system + device driver. This routine creates the device object for the named pipe + device and performs all other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + NTSTATUS - The function value is the final status from the initialization + operation. + +--*/ + +{ + NTSTATUS Status; + UNICODE_STRING NameString; + PDEVICE_OBJECT DeviceObject; + PNPFS_DEVICE_OBJECT NpfsDeviceObject; + + BOOLEAN VcbInitialized; + + PAGED_CODE(); + + // + // Create the alias lists. + // + + Status = NpInitializeAliases( ); + if (!NT_SUCCESS( Status )) { + return Status; + } + + // + // Create the device object. + // + + RtlInitUnicodeString( &NameString, L"\\Device\\NamedPipe" ); + + Status = IoCreateDevice( DriverObject, + sizeof(NPFS_DEVICE_OBJECT)-sizeof(DEVICE_OBJECT), + &NameString, + FILE_DEVICE_NAMED_PIPE, + 0, + FALSE, + &DeviceObject ); + + if (!NT_SUCCESS( Status )) { + ExFreePool( NpAliases ); + return Status; + } + + // + // Now because we use the irp stack for storing a data entry we need + // to bump up the stack size in the device object we just created. + // + + DeviceObject->StackSize += 1; + + // + // Note that because of the way data copying is done, we set neither + // the Direct I/O or Buffered I/O bit in DeviceObject->Flags. If + // data is not buffered we may set up for Direct I/O by hand. We do, + // however, set the long term request flag so that IRPs that get + // allocated for functions such as Listen requests come out of non-paged + // pool always. + // + + DeviceObject->Flags |= DO_LONG_TERM_REQUESTS; + + // + // Initialize the driver object with this driver's entry points. + // + + DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)NpFsdCreate; + DriverObject->MajorFunction[IRP_MJ_CREATE_NAMED_PIPE] = (PDRIVER_DISPATCH)NpFsdCreateNamedPipe; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)NpFsdClose; + DriverObject->MajorFunction[IRP_MJ_READ] = (PDRIVER_DISPATCH)NpFsdRead; + DriverObject->MajorFunction[IRP_MJ_WRITE] = (PDRIVER_DISPATCH)NpFsdWrite; + DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = (PDRIVER_DISPATCH)NpFsdQueryInformation; + DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = (PDRIVER_DISPATCH)NpFsdSetInformation; + DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)NpFsdQueryVolumeInformation; + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = (PDRIVER_DISPATCH)NpFsdCleanup; + DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = (PDRIVER_DISPATCH)NpFsdFlushBuffers; + DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] = (PDRIVER_DISPATCH)NpFsdDirectoryControl; + DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = (PDRIVER_DISPATCH)NpFsdFileSystemControl; + DriverObject->MajorFunction[IRP_MJ_QUERY_SECURITY] = (PDRIVER_DISPATCH)NpFsdQuerySecurityInfo; + DriverObject->MajorFunction[IRP_MJ_SET_SECURITY] = (PDRIVER_DISPATCH)NpFsdSetSecurityInfo; + +#ifdef _PNP_POWER_ + // + // Npfs doesn't need to handle SetPower requests. Local named pipes + // won't lose any state. Remote pipes will be lost, by a network driver + // will fail PowerQuery if there are open network connections. + // + + DeviceObject->DeviceObjectExtension->PowerControlNeeded = FALSE; +#endif + + + DriverObject->FastIoDispatch = &NpFastIoDispatch; + + VcbInitialized = FALSE; + + try { + + // + // Now initialize the Vcb, and create the root dcb + // + + NpfsDeviceObject = (PNPFS_DEVICE_OBJECT)DeviceObject; + + NpVcb = &NpfsDeviceObject->Vcb; + NpInitializeVcb( ); + VcbInitialized = TRUE; + + (VOID)NpCreateRootDcb( ); + + } finally { + + if (AbnormalTermination()) { + + if (VcbInitialized) { NpDeleteVcb( ); } + } + } + + // + // And return to our caller + // + + return( STATUS_SUCCESS ); +} diff --git a/private/ntos/npfs/npprocs.h b/private/ntos/npfs/npprocs.h new file mode 100644 index 000000000..ef16e97f0 --- /dev/null +++ b/private/ntos/npfs/npprocs.h @@ -0,0 +1,791 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + NpProcs.h + +Abstract: + + This module defines all of the globally used procedures in the Named + Pipe file system. + +Author: + + Gary Kimura [GaryKi] 20-Aug-1990 + +Revision History: + +--*/ + +#ifndef _NPPROCS_ +#define _NPPROCS_ + +#include <NtIfs.h> + +//#include <Ntos.h> +//#include <FsRtl.h> +//#include <String.h> + +#include "NodeType.h" +#include "NpStruc.h" +#include "NpData.h" + +// +// Tag all of our allocations if tagging is turned on +// + +#undef FsRtlAllocatePool +#undef FsRtlAllocatePoolWithQuota + +#define FsRtlAllocatePool(a,b) FsRtlAllocatePoolWithTag(a,b,'sfpN') +#define FsRtlAllocatePoolWithQuota(a,b) FsRtlAllocatePoolWithQuotaTag(a,b,'sfpN') + + +// +// Data queue support routines, implemented in DataSup.c +// + +VOID +NpInitializeDataQueue ( + IN PDATA_QUEUE DataQueue, + IN PEPROCESS Process, + IN ULONG Quota + ); + +VOID +NpUninitializeDataQueue ( + IN PDATA_QUEUE DataQueue, + IN PEPROCESS Process + ); + +PDATA_ENTRY +NpAddDataQueueEntry ( + IN PDATA_QUEUE DataQueue, + IN QUEUE_STATE Who, + IN DATA_ENTRY_TYPE Type, + IN ULONG DataSize, + IN PIRP Irp OPTIONAL, + IN PVOID DataPointer OPTIONAL + ); + +PIRP +NpRemoveDataQueueEntry ( + IN PDATA_QUEUE DataQueue + ); + +//PDATA_ENTRY +//NpGetNextDataQueueEntry ( +// IN PDATA_QUEUE DataQueue, +// IN PDATA_ENTRY PreviousDataEntry OPTIONAL +// ); +#define NpGetNextDataQueueEntry(_dq,_pde) \ + ((_pde) != NULL ? ((PDATA_ENTRY)(_pde))->Next : (_dq)->FrontOfQueue) + +PDATA_ENTRY +NpGetNextRealDataQueueEntry ( + IN PDATA_QUEUE DataQueue + ); + +//BOOLEAN +//NpIsDataQueueEmpty ( +// IN PDATA_QUEUE DataQueue +// ); +#define NpIsDataQueueEmpty(_dq) ((_dq)->QueueState == Empty) + +//BOOLEAN +//NpIsDataQueueReaders ( +// IN PDATA_QUEUE DataQueue +// ); +#define NpIsDataQueueReaders(_dq) ((_dq)->QueueState == ReadEntries) + +//BOOLEAN +//NpIsDataQueueWriters ( +// IN PDATA_QUEUE DataQueue +// ); +#define NpIsDataQueueWriters(_dq) ((_dq)->QueueState == WriteEntries) + + +// +// The following routines are used to manipulate the input buffers and are +// implemented in DevioSup.c +// + +//PVOID +//NpMapUserBuffer ( +// IN OUT PIRP Irp +// ); +#define NpMapUserBuffer(_irp) \ + (Irp->MdlAddress == NULL ? Irp->UserBuffer : \ + MmGetSystemAddressForMdl( Irp->MdlAddress )) + + +VOID +NpLockUserBuffer ( + IN OUT PIRP Irp, + IN LOCK_OPERATION Operation, + IN ULONG BufferLength + ); + + +// +// The event support routines, implemented in EventSup.c +// + +RTL_GENERIC_COMPARE_RESULTS +NpEventTableCompareRoutine ( + IN PRTL_GENERIC_TABLE EventTable, + IN PVOID FirstStruct, + IN PVOID SecondStruct + ); + +PVOID +NpEventTableAllocate ( + IN PRTL_GENERIC_TABLE EventTable, + IN CLONG ByteSize + ); + +VOID +NpEventTableDeallocate ( + IN PRTL_GENERIC_TABLE EventTable, + IN PVOID Buffer + ); + +// +// VOID +// NpInitializeEventTable ( +// IN PEVENT_TABLE EventTable +// ); +// + +#define NpInitializeEventTable(_et) { \ + RtlInitializeGenericTable( &(_et)->Table, \ + NpEventTableCompareRoutine, \ + NpEventTableAllocate, \ + NpEventTableDeallocate, \ + (PVOID)NonPagedPool ); \ +} + + +//VOID +//NpUninitializeEventTable ( +// IN PEVENT_TABLE EventTable +// ); +#define NpUninitializeEventTable(_et) NOTHING + +PEVENT_TABLE_ENTRY +NpAddEventTableEntry ( + IN PEVENT_TABLE EventTable, + IN PCCB Ccb, + IN NAMED_PIPE_END NamedPipeEnd, + IN HANDLE EventHandle, + IN ULONG KeyValue, + IN PEPROCESS Process, + IN KPROCESSOR_MODE PreviousMode + ); + +VOID +NpDeleteEventTableEntry ( + IN PEVENT_TABLE EventTable, + IN PEVENT_TABLE_ENTRY Template + ); + +// VOID +// NpSignalEventTableEntry ( +// IN PEVENT_TABLE_ENTRY EventTableEntry OPTIONAL +// ); +#define NpSignalEventTableEntry(_ete) \ + if (ARGUMENT_PRESENT(_ete)) { \ + KeSetEvent((PKEVENT)(_ete)->Event, 0, FALSE); \ + } + +PEVENT_TABLE_ENTRY +NpGetNextEventTableEntry ( + IN PEVENT_TABLE EventTable, + IN PVOID *RestartKey + ); + + +// +// The following routines are used to manipulate the fscontext fields of +// a file object, implemented in FilObSup.c +// + +VOID +NpSetFileObject ( + IN PFILE_OBJECT FileObject OPTIONAL, + IN PVOID FsContext, + IN PVOID FsContext2, + IN NAMED_PIPE_END NamedPipeEnd + ); + +NODE_TYPE_CODE +NpDecodeFileObject ( + IN PFILE_OBJECT FileObject, + OUT PFCB *Fcb OPTIONAL, + OUT PCCB *Ccb, + OUT PNAMED_PIPE_END NamedPipeEnd OPTIONAL + ); + + +// +// Largest matching prefix searching routines, implemented in PrefxSup.c +// + +PFCB +NpFindPrefix ( + IN PUNICODE_STRING String, + IN BOOLEAN CaseInsensitive, + OUT PUNICODE_STRING RemainingPart + ); + +PFCB +NpFindRelativePrefix ( + IN PDCB Dcb, + IN PUNICODE_STRING String, + IN BOOLEAN CaseInsensitive, + OUT PUNICODE_STRING RemainingPart + ); + + +// +// Pipe name aliases, implemented in AliasSup.c +// + +NTSTATUS +NpInitializeAliases ( + VOID + ); + +NTSTATUS +NpTranslateAlias ( + IN OUT PUNICODE_STRING String + ); + + +// +// The follow routine provides common read data queue support +// for buffered read, unbuffered read, peek, and transceive +// + +IO_STATUS_BLOCK +NpReadDataQueue ( // implemented in ReadSup.c + IN PDATA_QUEUE ReadQueue, + IN BOOLEAN PeekOperation, + IN BOOLEAN ReadOverflowOperation, + IN PUCHAR ReadBuffer, + IN ULONG ReadLength, + IN READ_MODE ReadMode, + IN PCCB Ccb + ); + + +// +// The following routines are used for setting and manipulating the +// security fields in the data entry, and nonpaged ccb, implemented in +// SecurSup.c +// + +NTSTATUS +NpInitializeSecurity ( + IN PCCB Ccb, + IN PSECURITY_QUALITY_OF_SERVICE SecurityQos, + IN PETHREAD UserThread + ); + +VOID +NpUninitializeSecurity ( + IN PCCB Ccb + ); + +NTSTATUS +NpSetDataEntryClientContext ( + IN NAMED_PIPE_END NamedPipeEnd, + IN PCCB Ccb, + IN PDATA_ENTRY DataEntry, + IN PETHREAD UserThread + ); + +VOID +NpCopyClientContext ( + IN PCCB Ccb, + IN PDATA_ENTRY DataEntry + ); + +NTSTATUS +NpImpersonateClientContext ( + IN PCCB Ccb + ); + + +// +// The following routines are used to manipulate the named pipe state +// implemented in StateSup.c +// + +VOID +NpInitializePipeState ( + IN PCCB Ccb, + IN PFILE_OBJECT ServerFileObject + ); + +VOID +NpUninitializePipeState ( + IN PCCB Ccb + ); + +NTSTATUS +NpSetListeningPipeState ( + IN PCCB Ccb, + IN PIRP Irp + ); + +NTSTATUS +NpSetConnectedPipeState ( + IN PCCB Ccb, + IN PFILE_OBJECT ClientFileObject + ); + +NTSTATUS +NpSetClosingPipeState ( + IN PCCB Ccb, + IN PIRP Irp, + IN NAMED_PIPE_END NamedPipeEnd + ); + +NTSTATUS +NpSetDisconnectedPipeState ( + IN PCCB Ccb + ); + + +// +// Internal Named Pipe data Structure Routines, implemented in StrucSup.c. +// +// These routines maniuplate the in memory data structures. +// + +VOID +NpInitializeVcb ( + VOID + ); + +VOID +NpDeleteVcb ( + VOID + ); + +VOID +NpCreateRootDcb ( + VOID + ); + +VOID +NpDeleteRootDcb ( + IN PROOT_DCB Dcb + ); + +PFCB +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 + ); + +VOID +NpDeleteFcb ( + IN PFCB Fcb + ); + +PCCB +NpCreateCcb ( + IN PFCB Fcb, + IN PFILE_OBJECT ServerFileObject, + IN NAMED_PIPE_STATE NamedPipeState, + IN READ_MODE ServerReadMode, + IN COMPLETION_MODE ServerCompletionMode, + IN PEPROCESS CreatorProcess, + IN ULONG InBoundQuota, + IN ULONG OutBoundQuota + ); + +PROOT_DCB_CCB +NpCreateRootDcbCcb ( + ); + +VOID +NpDeleteCcb ( + IN PCCB Ccb + ); + + +// +// Waiting for a named pipe support routines, implemented in WaitSup.c +// + +VOID +NpInitializeWaitQueue ( + IN PWAIT_QUEUE WaitQueue + ); + +VOID +NpUninitializeWaitQueue ( + IN PWAIT_QUEUE WaitQueue + ); + +VOID +NpAddWaiter ( + IN PWAIT_QUEUE WaitQueue, + IN LARGE_INTEGER DefaultTimeOut, + IN PIRP Irp + ); + +VOID +NpCancelWaiter ( + IN PWAIT_QUEUE WaitQueue, + IN PUNICODE_STRING NameOfPipe + ); + + +// +// The follow routine provides common write data queue support +// for buffered write, unbuffered write, peek, and transceive +// + +BOOLEAN +NpWriteDataQueue ( // implemented in WriteSup.c + IN PDATA_QUEUE WriteQueue, + IN READ_MODE ReadMode, + IN PUCHAR WriteBuffer, + IN ULONG WriteLength, + IN NAMED_PIPE_TYPE PipeType, + OUT PULONG WriteRemaining, + IN PCCB Ccb, + IN NAMED_PIPE_END NamedPipeEnd, + IN PETHREAD UserThread + ); + + +// +// Miscellaneous support routines +// + +#define BooleanFlagOn(F,SF) ( \ + (BOOLEAN)(((F) & (SF)) != 0) \ +) + +// +// This macro takes a pointer (or ulong) and returns its rounded up word +// value +// + +#define WordAlign(Ptr) ( \ + ((((ULONG)(Ptr)) + 1) & 0xfffffffe) \ + ) + +// +// This macro takes a pointer (or ulong) and returns its rounded up longword +// value +// + +#define LongAlign(Ptr) ( \ + ((((ULONG)(Ptr)) + 3) & 0xfffffffc) \ + ) + +// +// This macro takes a pointer (or ulong) and returns its rounded up quadword +// value +// + +#define QuadAlign(Ptr) ( \ + ((((ULONG)(Ptr)) + 7) & 0xfffffff8) \ + ) + +// +// The following types and macros are used to help unpack the packed and +// misaligned fields found in the Bios parameter block +// + +typedef union _UCHAR1 { + UCHAR Uchar[1]; + UCHAR ForceAlignment; +} UCHAR1, *PUCHAR1; + +typedef union _UCHAR2 { + UCHAR Uchar[2]; + USHORT ForceAlignment; +} UCHAR2, *PUCHAR2; + +typedef union _UCHAR4 { + UCHAR Uchar[4]; + ULONG ForceAlignment; +} UCHAR4, *PUCHAR4; + +// +// This macro copies an unaligned src byte to an aligned dst byte +// + +#define CopyUchar1(Dst,Src) { \ + *((UCHAR1 *)(Dst)) = *((UNALIGNED UCHAR1 *)(Src)); \ + } + +// +// This macro copies an unaligned src word to an aligned dst word +// + +#define CopyUchar2(Dst,Src) { \ + *((UCHAR2 *)(Dst)) = *((UNALIGNED UCHAR2 *)(Src)); \ + } + +// +// This macro copies an unaligned src longword to an aligned dsr longword +// + +#define CopyUchar4(Dst,Src) { \ + *((UCHAR4 *)(Dst)) = *((UNALIGNED UCHAR4 *)(Src)); \ + } + + +// +// VOID +// NpAcquireExclusiveVcb ( +// ); +// +// VOID +// NpAcquireSharedVcb ( +// ); +// +// VOID +// NpReleaseVcb ( +// ); +// + +#define NpAcquireExclusiveVcb() (VOID)ExAcquireResourceExclusive( &NpVcb->Resource, TRUE ) + +#define NpAcquireSharedVcb() (VOID)ExAcquireResourceShared( &NpVcb->Resource, TRUE ) + +#define NpReleaseVcb() ExReleaseResource( &NpVcb->Resource ) + +#define NpAcquireExclusiveCcb(Ccb) ExAcquireResourceExclusive(&Ccb->NonpagedCcb->Resource,TRUE); +#define NpReleaseCcb(Ccb) ExReleaseResource(&Ccb->NonpagedCcb->Resource); + + + +// +// The FSD Level dispatch routines. These routines are called by the +// I/O system via the dispatch table in the Driver Object. +// +// They each accept as input a pointer to a device object (actually most +// expect an npfs device object), and a pointer to the IRP. +// + +NTSTATUS +NpFsdCreate ( // implemented in Create.c + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpFsdCreateNamedPipe ( // implemented in CreateNp.c + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpFsdClose ( // implemented in Close.c + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpFsdRead ( // implemented in Read.c + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpFsdWrite ( // implemented in Write.c + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpFsdQueryInformation ( // implemented in FileInfo.c + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpFsdSetInformation ( // implemented in FileInfo.c + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpFsdCleanup ( // implemented in Cleanup.c + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpFsdFlushBuffers ( // implemented in Flush.c + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpFsdDirectoryControl ( // implemented in Dir.c + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpFsdFileSystemControl ( // implemented in FsContrl.c + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpFsdSetSecurityInfo ( // implemented in SeInfo.c + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpFsdQuerySecurityInfo ( // implemented in SeInfo.c + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NpFsdQueryVolumeInformation ( // implemented in VolInfo.c + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + + +// +// The following procedures are callbacks used to do fast I/O +// + +BOOLEAN +NpFastRead ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN ULONG Length, + IN BOOLEAN Wait, + IN ULONG LockKey, + OUT PVOID Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +BOOLEAN +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 + ); + + +// +// Miscellaneous routines. +// + +VOID +NpCheckForNotify ( // implemented in Dir.c + IN PDCB Dcb, + IN BOOLEAN CheckAllOutstandingIrps + ); + +// +// The following macro is used by the FSD routines to complete +// an IRP. +// + +#define NpCompleteRequest(IRP,STATUS) { \ + IoSetCancelRoutine( (IRP), NULL ); \ + FsRtlCompleteRequest( (IRP), (STATUS) ); \ +} + + +// +// The following two macro are used by the Fsd exception handlers to +// process an exception. The first macro is the exception filter used in the +// Fsd to decide if an exception should be handled at this level. +// The second macro decides if the exception is to be finished off by +// completing the IRP, and cleaning up the Irp Context, or if we should +// bugcheck. Exception values such as STATUS_FILE_INVALID (raised by +// VerfySup.c) cause us to complete the Irp and cleanup, while exceptions +// such as accvio cause us to bugcheck. +// +// The basic structure for fsd exception handling is as follows: +// +// NpFsdXxx(...) +// { +// try { +// +// ... +// +// } except(NpExceptionFilter( GetExceptionCode() )) { +// +// Status = NpProcessException( NpfsDeviceObject, Irp, GetExceptionCode() ); +// } +// +// Return Status; +// } +// + +LONG +NpExceptionFilter ( + IN NTSTATUS ExceptionCode + ); + +NTSTATUS +NpProcessException ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp, + IN NTSTATUS ExceptionCode + ); + + +// +// The following macros are used to establish the semantics needed +// to do a return from within a try-finally clause. As a rule every +// try clause must end with a label call try_exit. For example, +// +// try { +// : +// : +// +// try_exit: NOTHING; +// } finally { +// +// : +// : +// } +// +// Every return statement executed inside of a try clause should use the +// try_return macro. If the compiler fully supports the try-finally construct +// then the macro should be +// +// #define try_return(S) { return(S); } +// +// If the compiler does not support the try-finally construct then the macro +// should be +// +// #define try_return(S) { S; goto try_exit; } +// + +#define try_return(S) { S; goto try_exit; } + +#endif // _NPPROCS_ diff --git a/private/ntos/npfs/npstruc.h b/private/ntos/npfs/npstruc.h new file mode 100644 index 000000000..566828798 --- /dev/null +++ b/private/ntos/npfs/npstruc.h @@ -0,0 +1,810 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + NpStruc.h + +Abstract: + + This module defines the data structures that make up the major internal + part of the Named Pipe file system. + +Author: + + Gary Kimura [GaryKi] 20-Aug-1990 + +Revision History: + +--*/ + +#ifndef _NPSTRUC_ +#define _NPSTRUC_ + + +// +// The VCB record is the top record in the Named Pipe file system in-memory +// data structure. This structure must be allocated from non-paged pool +// and immediately follows (in memory) the Device object for the named +// pipe. Structurally the layout of the data structure is as follows +// +// +------------+ +// |NPDO | +// | | +// +------------+ +// |Vcb | +// | | +// | EventTable | +// | WaitQueue | +// | | +// +------------+ +// | ^ +// | | +// | | +// v | +// +-------------+ +// |RootDcb | +// | |<-+ +// +-------------+ | +// | | +// v | +// +-------------+ | +// |NonPaged | | +// | | | +// +-------------+ | +// : | +// : | +// : | +// v | +// +----------------+ +-------------------+ +---------+ +// |Fcb | |Ccb | |ServerFO | +// | |<---| | | | +// | MaxInstances | | ServerFO |<-------|- 1| +// | CurrentInst | | ClientFO | | | +// | DefaultTimeOut |...>| |<-+ +--|- | +// | | | | | | | | +// +----------------+ +-------------------+ | | +---------+ +// | | | | +// v v | | +// +----------------+ +-------------------+ | | +---------+ +// |NonPagedFcb | |NonPagedCcb |<-|--+ |ClientFO | +// | |<---| | | | | +// | PipeConfig | | PipeState | +-----|- 0| +// | PipeType | | ReadMode[2] | | | +// | | | CompletionMode[2] |<-------|- | +// | | | CreatorProcess | | | +// | | | EventTabEnt[2] | +---------+ +// | | | DataQueue[2] | +// | | | | (low bit determines +// +----------------+ +-------------------+ server/client) +// +// +// Where there is only one Vcb for the entire Named Pipe file system, and +// it contains a single pointer to the root dcb for the file system. Off +// of the Dcb is a queue of Fcb's. There is one Fcb for every named pipe. +// There is one Ccb for every instance of a named pipe. There are also +// two additional ccb types for the vcb and the root dcb, and notify records +// for the notify change operations. +// +// A newly initialized named pipe file system only contains the Vcb and +// the root dcb. A new Fcb is created when a new named pipe is created +// and then a ccb must also be created. The file object for the creater +// (i.e., server end) points to the ccb and indicates that it is the server +// end. When a user does an open on the named pipe its file object is +// set to point to the same ccb and is also set to indicate that it is the +// client end. This is denoted by using the last bit of the FsContext pointer +// if the bit is 1 it is a server end file object, if the bit is 0 it is +// the client end. +// +// A file object with a null pointer to the FsContext field is a closed or +// disconnected pipe. +// +// The Ccb also contains back pointer to the file objects that have it opened +// + + +// +// The following types are used to help during development by keeping the +// data types distinct. The manifest contants that go in each is declared +// in the ntioapi.h file +// + +typedef ULONG NAMED_PIPE_TYPE; +typedef NAMED_PIPE_TYPE *PNAMED_PIPE_TYPE; + +typedef ULONG READ_MODE; +typedef READ_MODE *PREAD_MODE; + +typedef ULONG COMPLETION_MODE; +typedef COMPLETION_MODE *PCOMPLETION_MODE; + +typedef ULONG NAMED_PIPE_CONFIGURATION; +typedef NAMED_PIPE_CONFIGURATION *PNAMED_PIPE_CONFIGURATION; + +typedef ULONG NAMED_PIPE_STATE; +typedef NAMED_PIPE_STATE *PNAMED_PIPE_STATE; + +typedef ULONG NAMED_PIPE_END; +typedef NAMED_PIPE_END *PNAMED_PIPE_END; + + +// +// The following two types are used by the event table package. The first +// is the event table itself which is just a generic table. It is protected +// by the vcb resource, and the second structure is an event table entry. +// + +typedef struct _EVENT_TABLE { + + RTL_GENERIC_TABLE Table; + +} EVENT_TABLE; +typedef EVENT_TABLE *PEVENT_TABLE; + +// +// The event table is a generic table of event table entries. Each Ccb +// optionally contains a pointer to an event table entry for each direction. +// The entries are part of the global event table defined off of the Vcb +// + +typedef struct _EVENT_TABLE_ENTRY { + + // + // The first two fields are used as keys in the generic table's + // comparison routines. The pipe end will either be FILE_PIPE_CLIENT_END + // or FILE_PIPE_SERVER_END. + // + + struct _CCB *Ccb; + NAMED_PIPE_END NamedPipeEnd; + + // + // The following three fields are used to identify the event entry + // to the named pipe user + // + + HANDLE EventHandle; + PVOID Event; + ULONG KeyValue; + PEPROCESS Process; + +} EVENT_TABLE_ENTRY; +typedef EVENT_TABLE_ENTRY *PEVENT_TABLE_ENTRY; + + +// +// Each Ccb has two data queues for holding the outstanding in-bound and +// out-bound read/write requests. The following type is used to determine +// if the data queue contains read requests, write requests, or is empty. +// + +typedef enum _QUEUE_STATE { + + ReadEntries, + WriteEntries, + Empty + +} QUEUE_STATE; + +// +// The data queue is a structure that contains the queue state, quota +// information, and the list head. The quota information is used to +// maintain pipe quota. +// + +typedef struct _DATA_QUEUE { + + // + // The current state of what is contained in this data queue, + // how many bytes of read/write data there are, and how many individual + // requests there are in the queue that contain data (includes + // close or flush requests). + // + + QUEUE_STATE QueueState; + ULONG BytesInQueue; + ULONG EntriesInQueue; + + // + // The following two fields denote who much quota was reserved for + // this pipe direction and how much we've used up. This is only + // the creator quota and not the user quota. + // + + ULONG Quota; + ULONG QuotaUsed; + + // + // This is the head of a queue of data entries (singly linked) + // + + struct _DATA_ENTRY *FrontOfQueue; + struct _DATA_ENTRY *EndOfQueue; + + // + // The following field indicates how far we've already processed + // into the first entry in the data queue + // + + ULONG NextByteOffset; + +} DATA_QUEUE; +typedef DATA_QUEUE *PDATA_QUEUE; + +// +// Each data entry has a type field that tells us if the operation +// for the entry is buffered, unbuffered, flush, or a close entry. +// + +typedef enum _DATA_ENTRY_TYPE { + + Buffered, + Unbuffered, + Flush, + Close + +} DATA_ENTRY_TYPE; + +// +// The following type is used to denote where we got the memory for the +// data entry and possibly the data buffer. We either got the memory +// from the pipe quota, the user quota, or it is part of the next IRP stack +// location. +// + +typedef enum _FROM { + + PipeQuota, + UserQuota, + InIrp + +} FROM; + +// +// Each entry in the data queue is a data entry. Processing an IRP +// has the potential of creating and inserting a new data entry. If the +// memory for the entry is taken from the IRP we use the next stack +// location. +// + +typedef struct _DATA_ENTRY { + + // + // The following field is how we connect into the queue of data entries + // + + struct _DATA_ENTRY *Next; + + // + // The following two fields describe the type of data entry and where + // we got its memory from. If the type is buffered then the From field + // also denotes where we got the memory for the data buffer, otherwise + // the data buffer has been supplied by the IRP + // + + DATA_ENTRY_TYPE DataEntryType : 8; + FROM From : 8; + + // + // The following field indicates if we still have an IRP associated + // with this data entry that need to be completed when the remove + // the data entry. Note that if From is InIrp that this IRP field + // must not be null. + // + + PIRP Irp; + + // + // The following two fields describe the size and location of the data + // buffer described by this entry. These fields are only used if the + // type is buffer, or unbuffered, and are ignored otherwise. + // + + ULONG DataSize; + PVOID DataPointer; + + // + // The following field is used to point to the client context if dynamic + // impersonation is being used + // + + PSECURITY_CLIENT_CONTEXT SecurityClientContext; + +} DATA_ENTRY; +typedef DATA_ENTRY *PDATA_ENTRY; + + +// +// The following type is used by the wait queue package +// + +typedef struct _WAIT_QUEUE { + + LIST_ENTRY Queue; + + KSPIN_LOCK SpinLock; + +} WAIT_QUEUE; +typedef WAIT_QUEUE *PWAIT_QUEUE; + + +typedef struct _VCB { + + // + // The type and size of this record (must be NPFS_NTC_VCB) + // + + NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // A pointer to the root DCB for this volume + // + + struct _FCB *RootDcb; + + // + // A count of the number of file objects that have opened the \NamedPipe + // object directly, and also a count of the number of file objects + // that have opened a name pipe or the root directory. + // + + CLONG OpenCount; + CLONG OpenUnderlingCount; + + // + // A prefix table that is used for quick, prefix directed, lookup of + // FCBs/DCBs that are part of this volume + // + + UNICODE_PREFIX_TABLE PrefixTable; + + // + // A resource variable to control access to the volume specific data + // structures + // + + ERESOURCE Resource; + + // + // The following table is used to hold the named pipe events + // + + EVENT_TABLE EventTable; + + // + // The following field is a queue of waiting IRPS of type WaitForNamedPipe + // + + WAIT_QUEUE WaitQueue; + + // + // The following field is used to check share access people who want + // to open the named pipe driver + // + + SHARE_ACCESS ShareAccess; + +} VCB; +typedef VCB *PVCB; + + +// +// The Named Pipe Device Object is an I/O system device object with +// additional workqueue parameters appended to the end. There is only +// one of these records created for the entire system during system +// initialization. +// + +typedef struct _NPFS_DEVICE_OBJECT { + + DEVICE_OBJECT DeviceObject; + + // + // This is the file system specific volume control block. + // + + VCB Vcb; + +} NPFS_DEVICE_OBJECT; +typedef NPFS_DEVICE_OBJECT *PNPFS_DEVICE_OBJECT; + + +// +// The Fcb/Dcb record corresponds to every opened named pipe and directory, +// and to every directory on an opened path. +// +// The structure is really divided into two parts. FCB can be allocated +// from paged pool which the NONPAGED_FCB must be allocated from non-paged +// pool. +// + +typedef struct _FCB { + + // + // Type and size of this record (must be NPFS_NTC_FCB, or + // NPFS_NTC_ROOT_DCB) + // + + NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // The links for the queue of all fcbs for a specific dcb off of + // Dcb.ParentDcbQueue. For the root directory this queue is empty + // + + LIST_ENTRY ParentDcbLinks; + + // + // A pointer to the Dcb that is the parent directory containing + // this fcb. If this record itself is the root dcb then this field + // is null. + // + + struct _FCB *ParentDcb; + + // + // A pointer to the Vcb containing this fcb + // + + PVCB Vcb; + + // + // A count of the number of file objects that have opened + // this file/directory. For a pipe this is also the number of instances + // created for the pipe. + // + + CLONG OpenCount; + + // + // A count of the number of server end file objects that have opened + // this pipe. ServerOpenCount is incremented when OpenCount is + // incremented (when the server end creates an instance), but is + // decremented when the server end handle is closed, where OpenCount + // isn't decremented until both side's handles are closed. When + // ServerOpenCount is 0, a client's attempt to open a named pipe is + // met with STATUS_OBJECT_NAME_NOT_FOUND, not STATUS_PIPE_NOT_AVAILABLE, + // based on an assumption that since the server doesn't think it has + // any instances open, the pipe really doesn't exist anymore. An + // example of when this distinction is useful is when the server + // process exits, but the client processes haven't closed their + // handles yet. + // + + CLONG ServerOpenCount; + + // + // The following field points to the security descriptor for this named pipe + // + + PSECURITY_DESCRIPTOR SecurityDescriptor; + + // + // The following union is cased off of the node type code for the fcb. + // There is a seperate case for the directory versus file fcbs. + // + + union { + + // + // A Directory Control Block (Dcb) + // + + struct { + + // + // A queue of the notify IRPs that will be completed when any + // change is made to a file in the directory. Enqueued using + // the Tail.Overlay.ListEntry of the Irp. + // + + LIST_ENTRY NotifyFullQueue; + + // + // A queue of the notify IRPs that will be completed only if a + // file is added, deleted, or renamed in the directory. Enqueued + // using the Tail.Overlay.ListEntry of the Irp. + // + + LIST_ENTRY NotifyPartialQueue; + + // + // A queue of all the fcbs/dcbs that are opened under this + // Dcb. + // + + LIST_ENTRY ParentDcbQueue; + + // + // The following field is used to check share access people + // who want to open the directory. + // + + SHARE_ACCESS ShareAccess; + + } Dcb; + + // + // An File Control Block (Fcb) + // + + struct { + + // + // This is the maximum number of instances we can have for the + // named pipe and the current number of instances is the open + // count for the fcb (note that the current number also + // correspondsto the number of Ccbs) + // + + ULONG MaximumInstances; + + // + // The assigned pipe configuration (FILE_PIPE_INBOUND, + // FILE_PIPE_OUTBOUND, or FILE_PIPE_FULL_DUPLEX) and pipe + // type (FILE_PIPE_MESSAGE_TYPE or + // FILE_PIPE_BYTE_STREAM_TYPE). + // + + NAMED_PIPE_CONFIGURATION NamedPipeConfiguration : 16; + NAMED_PIPE_TYPE NamedPipeType : 16; + + // + // The following field is the default timeout assigned to the + // named pipe + // + + LARGE_INTEGER DefaultTimeOut; + + // + // The Following field is a queue head for a list of ccbs + // that are opened under us + // + + LIST_ENTRY CcbQueue; + + } Fcb; + + } Specific; + + // + // The following field is the fully qualified file name for this FCB/DCB + // starting from the root of the volume, and last file name in the + // fully qualified name. + // + + UNICODE_STRING FullFileName; + UNICODE_STRING LastFileName; + + // + // The following field contains a prefix table entry that is used when + // searching a volume for a name (or longest matching prefix) + // + + UNICODE_PREFIX_TABLE_ENTRY PrefixTableEntry; + + // + // A pointer to the specific non-paged data for the Fcb. + // + + struct _NONPAGED_FCB *NonpagedFcb; + +} FCB, DCB, ROOT_DCB; + +typedef FCB *PFCB; +typedef DCB *PDCB; +typedef ROOT_DCB *PROOT_DCB; + +typedef struct _NONPAGED_FCB { + + // + // Type and size of this record (must be NPFS_NTC_NONPAGED_FCB, or + // NPFS_NTC_NONPAGED_ROOT_DCB) + // + + NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + +} NONPAGED_FCB, NONPAGED_DCB, NONPAGED_ROOT_DCB; + +typedef NONPAGED_FCB *PNONPAGED_FCB; +typedef NONPAGED_DCB *PNONPAGED_DCB; +typedef NONPAGED_ROOT_DCB *PNONPAGED_ROOT_DCB; + + +// +// The Ccb record is allocated for every opened instance of a named pipe. +// There are two parts to a ccb a paged part and a Nonpaged part. Both +// parts are pointed at by the FsContext and FsContext2 field of a file +// object. +// + +typedef struct _CCB { + + // + // Type and size of this record (must be NPFS_NTC_CCB). + // + + NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // The following field is a list entry for the list of ccb that we + // are a member of + // + + LIST_ENTRY CcbLinks; + + // + // A pointer to the paged Fcb, or Vcb that we are tied to + // + + PFCB Fcb; + + // + // Back pointers to the server and client file objects that have us + // opened. This is indexed by either FILE_PIPE_CLIENT_END or + // FILE_PIPE_SERVER_END. + // + + PFILE_OBJECT FileObject[2]; + + // + // The internal state of the Ccb. This is the shared access for each + // time this pipe/directory is opened. + // + + SHARE_ACCESS ShareAccess; + + // + // The following fields contain the session and process IDs of the + // client side of the named pipe instance. They are originally set + // to NULL (indicating local session) and the real client process + // ID but can be changed via FsCtl calls. + // + + PVOID ClientSession; + PVOID ClientProcess; + + // + // A pointer to the Nonpaged part of the ccb + // + + struct _NONPAGED_CCB *NonpagedCcb; + + // + // A pointer to the paged Fcb, or Vcb that we are tied to + // + + PNONPAGED_FCB NonpagedFcb; + + // + // Pipe state indicates the current state of the pipe + // (FILE_PIPE_DISCONNECTED_STATE, FILE_PIPE_LISTENING_STATE, + // FILE_PIPE_CONNECTED_STATE, or FILE_PIPE_CLOSING_STATE). + // + + NAMED_PIPE_STATE NamedPipeState; + + // + // read mode (FILE_PIPE_MESSAGE_MODE or FILE_PIPE_BYTE_STREAM_MODE), + // and completion mode (FILE_PIPE_QUEUE_OPERATION or + // FILE_PIPE_COMPLETE_OPERATION) describe how to handle requests to the + // pipe. Both of these fields are indexed by either FILE_PIPE_SERVER_END + // or FILE_PIPE_CLIENT_END. + // + + READ_MODE ReadMode[2]; + COMPLETION_MODE CompletionMode[2]; + + // + // The following field is used to remember the process that created this + // instance of the named pipe. It is needed to allocate quota and + // return quota + // + + PEPROCESS CreatorProcess; + + // + // The following data queues are used to contain the buffered information + // for each direction in the pipe. They array is indexed by + // PIPE_DIRECTION. + // + + DATA_QUEUE DataQueue[2]; + + // + // The following fields are used for security impersonation + // Only the server end can impersonate and only for the inbound path + // (i.e., client writes then server reads and impersonates). + // + // If it is static tracking (SecurityQos contains the type of tracking) + // then we set the quality of service and the client context in the + // nonpaged ccb on open and we never change anything. + // + // If it is dynamic tracking then we set the quality of service in the + // nonpaged ccb and on every write by the client we set the client + // context in the data entry and when read by the server we update the + // client context field of the nonpaged ccb to value stored in the data + // entry. + // + // On impersonation we use the client context stored in the nonpaged ccb + // + + SECURITY_QUALITY_OF_SERVICE SecurityQos; + PSECURITY_CLIENT_CONTEXT SecurityClientContext; + +} CCB; +typedef CCB *PCCB; + +typedef struct _NONPAGED_CCB { + + // + // Type and size of this record (must be NPFS_NTC_NONPAGED_CCB) + // + + NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // The following pointers denote the events we are to signal for the + // server and client ends of the named pipe. The actual entry + // is stored in the event table, and referenced here for easy access. + // The client end is signaled if ever a read/write occurs to the client + // of the pipe, and likewise for the server end. The array is + // indexed by either FILE_PIPE_SERVER_END or FILE_PIPE_CLIENT_END. + // + + PEVENT_TABLE_ENTRY EventTableEntry[2]; + + // + // A queue of waiting listening IRPs. They are linked into the + // Tail.Overlay.ListEntry field in the Irp. + // + + LIST_ENTRY ListeningQueue; + + // + // Resource for synchronizing access + // + ERESOURCE Resource; + +} NONPAGED_CCB; +typedef NONPAGED_CCB *PNONPAGED_CCB; + + +// +// The Root Dcb Ccb record is allocated for every opened instance of the +// root dcb. This record is pointed at by FsContext2. +// + +typedef struct _ROOT_DCB_CCB { + + // + // Type and size of this record (must be NPFS_NTC_ROOT_DCB_CCB). + // + + NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // The following field is a count of the last index returned + // by query directory. + // + + ULONG IndexOfLastCcbReturned; + + // + // The following string is used as a query template for directory + // query operations + // + + PUNICODE_STRING QueryTemplate; + +} ROOT_DCB_CCB; +typedef ROOT_DCB_CCB *PROOT_DCB_CCB; + +#endif // _NPSTRUC_ diff --git a/private/ntos/npfs/prefxsup.c b/private/ntos/npfs/prefxsup.c new file mode 100644 index 000000000..4771f305f --- /dev/null +++ b/private/ntos/npfs/prefxsup.c @@ -0,0 +1,255 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + PrefxSup.c + +Abstract: + + This module implements the Named Pipe Prefix support routines + +Author: + + Gary Kimura [GaryKi] 13-Feb-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (NPFS_BUG_CHECK_PREFXSUP) + +// +// The debug trace level for this module +// + +#define Dbg (DEBUG_TRACE_PREFXSUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpFindPrefix) +#pragma alloc_text(PAGE, NpFindRelativePrefix) +#endif + + +PFCB +NpFindPrefix ( + IN PUNICODE_STRING String, + IN BOOLEAN CaseInsensitive, + OUT PUNICODE_STRING RemainingPart + ) + +/*++ + +Routine Description: + + This routine searches the FCBs/DCBs of a volume and locates the + FCB/DCB with longest matching prefix for the given input string. The + search is relative to the root of the volume. So all names must start + with a "\". + +Arguments: + + String - Supplies the input string to search for + + CaseInsensitive - Specifies if the search is to be done case sensitive + (FALSE) or insensitive (TRUE) + + RemainingPart - Returns the string when the prefix no longer matches. + For example, if the input string is "\alpha\beta" only matches the + root directory then the remaining string is "alpha\beta". If the + same string matches a DCB for "\alpha" then the remaining string is + "beta". + +Return Value: + + PFCB - Returns a pointer to either an FCB or a DCB whichever is the + longest matching prefix. + +--*/ + +{ + PUNICODE_PREFIX_TABLE_ENTRY PrefixTableEntry; + PFCB Fcb; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpFindPrefix, NpVcb = %08lx\n", NpVcb); + DebugTrace( 0, Dbg, " String = %Z\n", String); + + // + // Find the longest matching prefix + // + + PrefixTableEntry = RtlFindUnicodePrefix( &NpVcb->PrefixTable, + String, + CaseInsensitive ); + + // + // If we didn't find one then it's an error + // + + if (PrefixTableEntry == NULL) { + + DebugDump("Error looking up a prefix", 0, NpVcb); + NpBugCheck( 0, 0, 0 ); + } + + // + // Get a pointer to the Fcb containing the prefix table entry + // + + Fcb = CONTAINING_RECORD( PrefixTableEntry, FCB, PrefixTableEntry ); + + // + // Tell the caller how many characters we were able to match. We first + // set the remaining part to the original string minus the matched + // prefix, then we check if the remaining part starts with a backslash + // and if it does then we remove the backslash from the remaining string. + // + + RemainingPart->Length = String->Length - Fcb->FullFileName.Length; + RemainingPart->MaximumLength = RemainingPart->Length; + RemainingPart->Buffer = &String->Buffer[ Fcb->FullFileName.Length/sizeof(WCHAR) ]; + + if ((RemainingPart->Length > 0) && + (RemainingPart->Buffer[0] == L'\\')) { + + RemainingPart->Length -= sizeof(WCHAR); + RemainingPart->MaximumLength -= sizeof(WCHAR); + RemainingPart->Buffer += 1; + } + + DebugTrace(0, Dbg, "RemainingPart set to %Z\n", RemainingPart); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpFindPrefix -> %08lx\n", Fcb); + + return Fcb; +} + + +PFCB +NpFindRelativePrefix ( + IN PDCB Dcb, + IN PUNICODE_STRING String, + IN BOOLEAN CaseInsensitive, + OUT PUNICODE_STRING RemainingPart + ) + +/*++ + +Routine Description: + + This routine searches the FCBs/DCBs of a volume and locates the + FCB/DCB with longest matching prefix for the given input string. The + search is relative to a input DCB, and must not start with a leading "\" + All searching is done case insensitive. + +Arguments: + + Dcb - Supplies the Dcb to start searching from + + String - Supplies the input string to search for + + CaseInsensitive - Specifies if the search is to be done case sensitive + (FALSE) or insensitive (TRUE) + + RemainingPart - Returns the index into the string when the prefix no + longer matches. For example, if the input string is "beta\gamma" + and the input Dcb is for "\alpha" and we only match beta then + the remaining string is "gamma". + +Return Value: + + PFCB - Returns a pointer to either an FCB or a DCB whichever is the + longest matching prefix. + +--*/ + +{ + ULONG NameLength; + PWCH Name; + + UNICODE_STRING FullString; + PWCH Temp; + + PFCB Fcb; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpFindRelativePrefix, Dcb = %08lx\n", Dcb); + DebugTrace( 0, Dbg, "String = %08lx\n", String); + + // + // Initialize the Temp buffer to null so in our termination handler + // we'll know to release pool + // + + Temp = NULL; + + try { + + // + // We first need to build the complete name and then do a relative + // search from the root + // + + NameLength = String->Length; + Name = String->Buffer; + + ASSERT(NodeType(Dcb) == NPFS_NTC_ROOT_DCB); + + Temp = FsRtlAllocatePool( PagedPool, NameLength + 2*sizeof(WCHAR) ); + + Temp[0] = L'\\'; + RtlCopyMemory( &Temp[1], Name, NameLength ); + Temp[NameLength/sizeof(WCHAR) + 1] = L'\0'; + + RtlInitUnicodeString( &FullString, Temp ); + + // + // Find the prefix relative to the volume + // + + Fcb = NpFindPrefix( &FullString, + CaseInsensitive, + RemainingPart ); + + // + // Now adjust the remaining part to take care of the relative + // volume prefix. + // + + RemainingPart->Buffer = &String->Buffer[(String->Length - + RemainingPart->Length) / sizeof(WCHAR)]; + + DebugTrace(0, Dbg, "RemainingPart set to %Z\n", RemainingPart); + + } finally { + + // + // Release the pool if we it was allocated + // + + if (Temp != NULL) { ExFreePool( Temp ); } + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpFindRelativePrefix -> %08lx\n", Fcb); + } + + return Fcb; +} + diff --git a/private/ntos/npfs/read.c b/private/ntos/npfs/read.c new file mode 100644 index 000000000..19b930ee0 --- /dev/null +++ b/private/ntos/npfs/read.c @@ -0,0 +1,529 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + Read.c + +Abstract: + + This module implements the File Read routine for NPFS called by the + dispatch driver. + +Author: + + Gary Kimura [GaryKi] 21-Aug-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_READ) + +#if DBG +ULONG NpFastReadTrue = 0; +ULONG NpFastReadFalse = 0; +ULONG NpSlowReadCalls = 0; +#endif + +// +// local procedure prototypes +// + +BOOLEAN +NpCommonRead ( + IN PFILE_OBJECT FileObject, + OUT PVOID ReadBuffer, + IN ULONG ReadLength, + OUT PIO_STATUS_BLOCK Iosb, + IN PIRP Irp OPTIONAL + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpCommonRead) +#pragma alloc_text(PAGE, NpFastRead) +#pragma alloc_text(PAGE, NpFsdRead) +#endif + + +NTSTATUS +NpFsdRead ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtReadFile API calls. + +Arguments: + + NpfsDeviceObject - Supplies the device object to use. + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The Fsd status for the Irp + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + PIO_STACK_LOCATION IrpSp; + + DebugTrace(+1, Dbg, "NpFsdRead\n", 0); + DbgDoit( NpSlowReadCalls += 1 ); + + PAGED_CODE(); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + FsRtlEnterFileSystem(); + + NpAcquireSharedVcb(); + + try { + + (VOID) NpCommonRead( IrpSp->FileObject, + Irp->UserBuffer, + IrpSp->Parameters.Read.Length, + &Iosb, + Irp ); + + } except(NpExceptionFilter( GetExceptionCode() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Iosb.Status = NpProcessException( NpfsDeviceObject, Irp, GetExceptionCode() ); + } + + NpReleaseVcb(); + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpFsdRead -> %08lx\n", Iosb.Status ); + + return Iosb.Status; +} + + +BOOLEAN +NpFastRead ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN ULONG Length, + IN BOOLEAN Wait, + IN ULONG LockKey, + OUT PVOID Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine does a fast read bypassing the usual file system + entry routine (i.e., without the Irp). + +Arguments: + + FileObject - Pointer to the file object being read. + + FileOffset - Byte offset in file for desired data. + + Length - Length of desired data in bytes. + + Wait - FALSE if caller may not block, TRUE otherwise + + LockKey - Supplies the Key used to use if the byte range being read is locked. + + Buffer - Pointer to output buffer to which data should be copied. + + IoStatus - Pointer to standard I/O status block to receive the status + for the transfer. + +Return Value: + + BOOLEAN - TRUE if the operation completed successfully and FALSE if the + caller needs to take the long IRP based route. + +--*/ + +{ + BOOLEAN Results = FALSE; + + UNREFERENCED_PARAMETER( FileOffset ); + UNREFERENCED_PARAMETER( Wait ); + UNREFERENCED_PARAMETER( LockKey ); + UNREFERENCED_PARAMETER( DeviceObject ); + + PAGED_CODE(); + +#if DBG + + FsRtlEnterFileSystem(); + + NpAcquireSharedVcb(); + + try { + + if (NpCommonRead( FileObject, + Buffer, + Length, + IoStatus, + NULL )) { + + NpFastReadTrue += 1; + + Results = TRUE; + + } else { + + NpFastReadFalse += 1; + } + + } except( FsRtlIsNtstatusExpected(GetExceptionCode()) + ? EXCEPTION_EXECUTE_HANDLER + : EXCEPTION_CONTINUE_SEARCH ) { + + NOTHING; + } + + NpReleaseVcb(); + FsRtlExitFileSystem(); + return Results; + +#else + + FsRtlEnterFileSystem(); + NpAcquireSharedVcb(); + + try { + + Results = NpCommonRead( FileObject, + Buffer, + Length, + IoStatus, + NULL ); + + } except( FsRtlIsNtstatusExpected(GetExceptionCode()) + ? EXCEPTION_EXECUTE_HANDLER + : EXCEPTION_CONTINUE_SEARCH ) { + + NOTHING; + } + + NpReleaseVcb(); + FsRtlExitFileSystem(); + return Results; + +#endif +} + + +// +// Internal support routine +// + +BOOLEAN +NpCommonRead ( + IN PFILE_OBJECT FileObject, + OUT PVOID ReadBuffer, + IN ULONG ReadLength, + OUT PIO_STATUS_BLOCK Iosb, + IN PIRP Irp OPTIONAL + ) + +/*++ + +Routine Description: + + This is the common routine for reading a named pipe both via the fast + path and with an Irp + +Arguments: + + FileObject - Supplies the file object used in this operation + + ReadBuffer - Supplies the buffer where data is to be written + + ReadLength - Supplies the length of read buffer in bytes + + Iosb - Receives the final completion status of this operation + + Irp - Optionally supplies an Irp to be used in this operation + +Return Value: + + BOOLEAN - TRUE if the operation was successful and FALSE if the caller + needs to take the longer Irp based route. + +--*/ + +{ + NODE_TYPE_CODE NodeTypeCode; + PCCB Ccb; + PNONPAGED_CCB NonpagedCcb; + NAMED_PIPE_END NamedPipeEnd; + + NAMED_PIPE_CONFIGURATION NamedPipeConfiguration; + + ULONG ReadRemaining; + READ_MODE ReadMode; + COMPLETION_MODE CompletionMode; + PDATA_QUEUE ReadQueue; + PEVENT_TABLE_ENTRY Event; + BOOLEAN Status; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpCommonRead\n", 0); + DebugTrace( 0, Dbg, "FileObject = %08lx\n", FileObject); + DebugTrace( 0, Dbg, "ReadBuffer = %08lx\n", ReadBuffer); + DebugTrace( 0, Dbg, "ReadLength = %08lx\n", ReadLength); + DebugTrace( 0, Dbg, "Iosb = %08lx\n", Iosb); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); + + Iosb->Information = 0; + + // + // Get the Ccb and figure out who we are, and make sure we're not + // disconnected + // + + if ((NodeTypeCode = NpDecodeFileObject( FileObject, + NULL, + &Ccb, + &NamedPipeEnd )) == NTC_UNDEFINED) { + + DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0); + + Iosb->Status = STATUS_PIPE_DISCONNECTED; + if (ARGUMENT_PRESENT(Irp)) { NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); } + + return TRUE; + } + + // + // Now we only will allow Read operations on the pipe and not a directory + // or the device + // + + if (NodeTypeCode != NPFS_NTC_CCB) { + + DebugTrace(0, Dbg, "FileObject is not for a named pipe\n", 0); + + Iosb->Status = STATUS_INVALID_PARAMETER; + if (ARGUMENT_PRESENT(Irp)) { NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); } + + return TRUE; + } + + NpAcquireExclusiveCcb(Ccb); + + NonpagedCcb = Ccb->NonpagedCcb; + + try { + // + // Check if the pipe is not in the connected state. + // + + if ((Ccb->NamedPipeState == FILE_PIPE_DISCONNECTED_STATE) || + (Ccb->NamedPipeState == FILE_PIPE_LISTENING_STATE)) { + + DebugTrace(0, Dbg, "Pipe in disconnected or listening state\n", 0); + + if (Ccb->NamedPipeState == FILE_PIPE_DISCONNECTED_STATE) { + + Iosb->Status = STATUS_PIPE_DISCONNECTED; + + } else { + + Iosb->Status = STATUS_PIPE_LISTENING; + } + + if (ARGUMENT_PRESENT(Irp)) { NpCompleteRequest( Irp, Iosb->Status ); } + + try_return(Status = TRUE); + } + + ASSERT((Ccb->NamedPipeState == FILE_PIPE_CONNECTED_STATE) || + (Ccb->NamedPipeState == FILE_PIPE_CLOSING_STATE)); + + // + // We only allow a read by the server on a non outbound only pipe + // and by the client on a non inbound only pipe + // + + NamedPipeConfiguration = Ccb->Fcb->Specific.Fcb.NamedPipeConfiguration; + + if (((NamedPipeEnd == FILE_PIPE_SERVER_END) && + (NamedPipeConfiguration == FILE_PIPE_OUTBOUND)) + + || + + ((NamedPipeEnd == FILE_PIPE_CLIENT_END) && + (NamedPipeConfiguration == FILE_PIPE_INBOUND))) { + + DebugTrace(0, Dbg, "Trying to read to the wrong pipe configuration\n", 0); + + Iosb->Status = STATUS_INVALID_PARAMETER; + if (ARGUMENT_PRESENT(Irp)) { NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); } + + try_return (Status = TRUE); + } + + // + // Reference our input parameters to make things easier, and + // initialize our main variables that describe the Read command + // + + ReadRemaining = ReadLength; + ReadMode = Ccb->ReadMode[ NamedPipeEnd ]; + CompletionMode = Ccb->CompletionMode[ NamedPipeEnd ]; + + // + // Now the data queue that we read from into and the event that we signal + // are based on the named pipe end. The server read from the inbound + // queue and signals the client event. The client does just the + // opposite. + // + + if (NamedPipeEnd == FILE_PIPE_SERVER_END) { + + ReadQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + + Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ]; + + } else { + + ReadQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + + Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_SERVER_END ]; + } + + DebugTrace(0, Dbg, "ReadBuffer = %08lx\n", ReadBuffer); + DebugTrace(0, Dbg, "ReadLength = %08lx\n", ReadLength); + DebugTrace(0, Dbg, "ReadMode = %08lx\n", ReadMode); + DebugTrace(0, Dbg, "CompletionMode = %08lx\n", CompletionMode); + DebugTrace(0, Dbg, "ReadQueue = %08lx\n", ReadQueue); + DebugTrace(0, Dbg, "Event = %08lx\n", Event); + + // + // if the read queue does not contain any write entries + // then we either need to enqueue this operation or + // fail immediately + // + + if (!NpIsDataQueueWriters( ReadQueue )) { + + // + // Check if the other end of the pipe is closing, and if + // so then we complete it with end of file. + // Otherwise check to see if we should enqueue the irp + // or complete the operation and tell the user the pipe is empty. + // + + if (Ccb->NamedPipeState == FILE_PIPE_CLOSING_STATE) { + + DebugTrace(0, Dbg, "Complete the irp with eof\n", 0); + + Iosb->Status = STATUS_PIPE_BROKEN; + if (ARGUMENT_PRESENT(Irp)) { NpCompleteRequest( Irp, STATUS_PIPE_BROKEN ); } + + } else if (CompletionMode == FILE_PIPE_QUEUE_OPERATION) { + + if (!ARGUMENT_PRESENT(Irp)) { + + DebugTrace(0, Dbg, "Need to supply Irp\n", 0); + + try_return(Status = FALSE); + } + + DebugTrace(0, Dbg, "Put the irp into the read queue\n", 0); + + (VOID)NpAddDataQueueEntry( ReadQueue, + ReadEntries, + Buffered, + ReadLength, + Irp, + NULL ); + + IoMarkIrpPending( Irp ); + + Iosb->Status = STATUS_PENDING; + + } else { + + DebugTrace(0, Dbg, "Complete the irp with pipe empty\n", 0); + + Iosb->Status = STATUS_PIPE_EMPTY; + if (ARGUMENT_PRESENT(Irp)) { NpCompleteRequest( Irp, STATUS_PIPE_EMPTY ); } + } + + } else { + + // + // otherwise there we have a read irp against a read queue + // that contains one or more write entries. + // + + *Iosb = NpReadDataQueue( ReadQueue, + FALSE, + FALSE, + ReadBuffer, + ReadLength, + ReadMode, + Ccb ); + + // + // Finish up the read irp. + // + + if (ARGUMENT_PRESENT(Irp)) { Irp->IoStatus = *Iosb; NpCompleteRequest( Irp, Iosb->Status ); } + } + + // + // Now we need to advance the read queue to the next write irp to + // skip over flushes and closes + // + + (VOID)NpGetNextRealDataQueueEntry( ReadQueue ); + + Status = TRUE; + // + // And because we've done something we need to signal the + // other ends event + // + + NpSignalEventTableEntry( Event ); + + + try_exit: NOTHING; + } finally { + NpReleaseCcb(Ccb); + } + + + DebugTrace(-1, Dbg, "NpCommonRead -> TRUE\n", 0); + return Status; +} diff --git a/private/ntos/npfs/readsup.c b/private/ntos/npfs/readsup.c new file mode 100644 index 000000000..0e1367c82 --- /dev/null +++ b/private/ntos/npfs/readsup.c @@ -0,0 +1,293 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + ReadSup.c + +Abstract: + + This module implements the Read support routine. This is a common + read function that is called to do read, unbuffered read, peek, and + transceive. + +Author: + + Gary Kimura [GaryKi] 20-Sep-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_READSUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpReadDataQueue) +#endif + + +IO_STATUS_BLOCK +NpReadDataQueue ( + IN PDATA_QUEUE ReadQueue, + IN BOOLEAN PeekOperation, + IN BOOLEAN ReadOverflowOperation, + IN PUCHAR ReadBuffer, + IN ULONG ReadLength, + IN READ_MODE ReadMode, + IN PCCB Ccb + ) + +/*++ + +Routine Description: + + This procedure reads data from the read queue and fills up the + read buffer. It will also dequeue the queue or leave it alone based + on an input parameter. + +Arguments: + + ReadQueue - Provides the read queue to examine. Its state must + already be set to WriteEntries. + + PeekOperation - Indicates if the operation is to dequeue information + off of the queue as it is being read or leave the queue alone. + TRUE means to leave the queue alone. + + ReadOverflowOperation - Indicates if this is a read overflow operation. + With read overflow we will not alter the named pipe if the data + will overflow the read buffer. + + ReadBuffer - Supplies a buffer to receive the data + + ReadLength - Supplies the length, in bytes, of ReadBuffer. + + ReadMode - Indicates if the read operation is message mode or + byte stream mode. + + NamedPipeEnd - Supplies the end of the named pipe doing the read + + Ccb - Supplies the ccb for the pipe + +Return Value: + + IO_STATUS_BLOCK - Indicates the result of the operation. + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + + PDATA_ENTRY DataEntry; + + ULONG ReadRemaining; + ULONG AmountRead; + + PUCHAR WriteBuffer; + ULONG WriteLength; + ULONG WriteRemaining; + + ULONG AmountToCopy; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpReadDataQueue\n", 0); + DebugTrace( 0, Dbg, "ReadQueue = %08lx\n", ReadQueue); + DebugTrace( 0, Dbg, "PeekOperation = %08lx\n", PeekOperation); + DebugTrace( 0, Dbg, "ReadOverflowOperation = %08lx\n", ReadOverflowOperation); + DebugTrace( 0, Dbg, "ReadBuffer = %08lx\n", ReadBuffer); + DebugTrace( 0, Dbg, "ReadLength = %08lx\n", ReadLength); + DebugTrace( 0, Dbg, "ReadMode = %08lx\n", ReadMode); + DebugTrace( 0, Dbg, "Ccb = %08lx\n", Ccb); + + // + // If this is an overflow operation then we will force us to do peeks. + // Later when are determine that the opreation succeeded we will complete + // the write irp. + // + + if (ReadOverflowOperation) { + + PeekOperation = TRUE; + } + + // + // Now for every real data entry we loop until either we run out + // of data entries or until the read buffer is full + // + + ReadRemaining = ReadLength; + Iosb.Status = STATUS_SUCCESS; + AmountRead = 0; + + for (DataEntry = (PeekOperation ? NpGetNextDataQueueEntry( ReadQueue, NULL ) + : NpGetNextRealDataQueueEntry( ReadQueue )); + + (DataEntry != NULL) && (ReadRemaining > 0); + + DataEntry = (PeekOperation ? NpGetNextDataQueueEntry( ReadQueue, DataEntry ) + : NpGetNextRealDataQueueEntry( ReadQueue ))) { + + DebugTrace(0, Dbg, "Top of Loop\n", 0); + DebugTrace(0, Dbg, "ReadRemaining = %08lx\n", ReadRemaining); + + // + // If this is a peek operation then make sure we got a real + // data entry and not a close or flush + // + + if (!PeekOperation || + (DataEntry->DataEntryType == Buffered) || + (DataEntry->DataEntryType == Unbuffered)) { + + // + // Calculate how much data is in this entry. The write + // remaining is based on whether this is the first entry + // in the queue or a later data entry + // + + WriteBuffer = DataEntry->DataPointer; + WriteLength = DataEntry->DataSize; + WriteRemaining = WriteLength; + + if (DataEntry == NpGetNextDataQueueEntry( ReadQueue, NULL )) { + + WriteRemaining -= ReadQueue->NextByteOffset; + } + + DebugTrace(0, Dbg, "WriteBuffer = %08lx\n", WriteBuffer); + DebugTrace(0, Dbg, "WriteLength = %08lx\n", WriteLength); + DebugTrace(0, Dbg, "WriteRemaining = %08lx\n", WriteRemaining); + + // + // copy data from the write buffer at write offset to the + // read buffer at read offset by the mininum of write + // remaining or read remaining + // + + AmountToCopy = (WriteRemaining < ReadRemaining ? WriteRemaining + : ReadRemaining); + + try { + + RtlCopyMemory( &ReadBuffer[ ReadLength - ReadRemaining ], + &WriteBuffer[ WriteLength - WriteRemaining ], + AmountToCopy ); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + + // + // Update the Read and Write remaining counts, the total + // amount we've read and the next byte offset field in the + // read queue + // + + ReadRemaining -= AmountToCopy; + WriteRemaining -= AmountToCopy; + AmountRead += AmountToCopy; + + if (!PeekOperation) { + + ReadQueue->NextByteOffset = WriteLength - WriteRemaining; + } + + // + // Now update the security fields in the ccb + // + + NpCopyClientContext( Ccb, DataEntry ); + + // + // If the remaining write length is greater than zero + // then we've filled up the read buffer so we need to + // figure out if its an overflow error + // + + if (WriteRemaining > 0 || + (ReadOverflowOperation && (AmountRead == 0))) { + + DebugTrace(0, Dbg, "Write remaining is > 0\n", 0); + + if (ReadMode == FILE_PIPE_MESSAGE_MODE) { + + DebugTrace(0, Dbg, "Overflow message mode read\n", 0); + + // + // Set the status field and break out of the for-loop. + // + + Iosb.Status = STATUS_BUFFER_OVERFLOW; + break; + } + + } else { + + DebugTrace(0, Dbg, "Remaining Write is zero\n", 0); + + // + // The write entry is done so remove it from the read + // queue, if this is not a peek operation. This might + // also have an Irp that needs to be completed + // + + if (!PeekOperation || ReadOverflowOperation) { + + PIRP WriteIrp; + + // + // For a read overflow operation we need to get the read data + // queue entry and remove it. + // + + if (ReadOverflowOperation) { + PDATA_ENTRY TempDataEntry; + TempDataEntry = NpGetNextRealDataQueueEntry( ReadQueue ); + ASSERT(TempDataEntry == DataEntry); + } + + if ((WriteIrp = NpRemoveDataQueueEntry( ReadQueue )) != NULL) { + + NpCompleteRequest( WriteIrp, STATUS_SUCCESS ); + } + } + + // + // And if we are doing message mode reads then we'll + // work on completing this irp without going back + // to the top of the loop + // + + if (ReadMode == FILE_PIPE_MESSAGE_MODE) { + + DebugTrace(0, Dbg, "Successful message mode read\n", 0); + + // + // Set the status field and break out of the for-loop. + // + + Iosb.Status = STATUS_SUCCESS; + break; + } + + ASSERTMSG("Srv cannot use read overflow on a byte stream pipe ", !ReadOverflowOperation); + } + } + } + + DebugTrace(0, Dbg, "End of loop, AmountRead = %08lx\n", AmountRead); + + Iosb.Information = AmountRead; + + DebugTrace(-1, Dbg, "NpReadDataQueue -> Iosb.Status = %08lx\n", Iosb.Status); + return Iosb; +} diff --git a/private/ntos/npfs/resrcsup.c b/private/ntos/npfs/resrcsup.c new file mode 100644 index 000000000..b0c36ac6b --- /dev/null +++ b/private/ntos/npfs/resrcsup.c @@ -0,0 +1,234 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + ResrcSup.c + +Abstract: + + This module implements the NamedPipe Resource acquisition routines + +Author: + + Gary Kimura [GaryKi] 22-Mar-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_RESRCSUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpAcquireExclusiveCcb) +#pragma alloc_text(PAGE, NpAcquireExclusiveVcb) +#pragma alloc_text(PAGE, NpAcquireSharedCcb) +#pragma alloc_text(PAGE, NpAcquireSharedVcb) +#pragma alloc_text(PAGE, NpReleaseCcb) +#pragma alloc_text(PAGE, NpReleaseVcb) +#endif + + +VOID +NpAcquireExclusiveVcb ( + ) + +/*++ + +Routine Description: + + This routine acquires exclusive access to the Vcb + +Arguments: + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpAcquireExclusiveVcb\n", 0); + + ExAcquireResourceExclusive( &(NpVcb->Resource), TRUE ); + + DebugTrace(-1, Dbg, "NpAcquireExclusiveVcb -> (VOID)\n", 0); + + return; +} + + +VOID +NpAcquireSharedVcb ( + ) + +/*++ + +Routine Description: + + This routine acquires shared access to the Vcb + +Arguments: + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpAcquireSharedVcb\n", 0); + + ExAcquireResourceShared( &(NpVcb->Resource), TRUE ); + + DebugTrace(-1, Dbg, "NpAcquireSharedVcb -> (VOID)\n", 0); + + return; +} + + +VOID +NpAcquireExclusiveCcb ( + IN PNONPAGED_CCB NonpagedCcb + ) + +/*++ + +Routine Description: + + This routine acquires exclusive access to the Ccb by first getting + shared access to the Fcb. + +Arguments: + + NonpagedCcb - Supplies the Ccb to acquire + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpAcquireExclusiveCcb, NonpagedCcb = %08lx\n", NonpagedCcb); + + (VOID)ExAcquireResourceShared( &(NpVcb->Resource), TRUE ); + + (VOID)ExAcquireResourceExclusive( &(NonpagedCcb->Resource), TRUE ); + + DebugTrace(-1, Dbg, "NpAcquireExclusiveCcb -> (VOID)\n", 0); + + return; +} + + +VOID +NpAcquireSharedCcb ( + IN PNONPAGED_CCB NonpagedCcb + ) + +/*++ + +Routine Description: + + This routine acquires shared access to the Ccb by first getting + shared access to the Fcb. + +Arguments: + + NonpagedCcb - Supplies the Ccb to acquire + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpAcquireSharedCcb, NonpagedCcb = %08lx\n", NonpagedCcb); + + (VOID)ExAcquireResourceShared( &(NpVcb->Resource), TRUE ); + + (VOID)ExAcquireResourceShared( &(NonpagedCcb->Resource), TRUE ); + + DebugTrace(-1, Dbg, "NpAcquireSharedCcb -> (VOID)\n", 0); + + return; +} + + +VOID +NpReleaseVcb ( + ) + +/*++ + +Routine Description: + + This routine releases access to the Vcb + +Arguments: + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(0, Dbg, "NpReleaseVcb\n", 0); + + ExReleaseResource( &(NpVcb->Resource) ); + + return; +} + + +VOID +NpReleaseCcb ( + IN PNONPAGED_CCB NonpagedCcb + ) + +/*++ + +Routine Description: + + This routine releases access to the Ccb + +Arguments: + + Ccb - Supplies the Ccb being released + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(0, Dbg, "NpReleaseCcb, NonpagedCcb = %08lx\n", NonpagedCcb); + + ExReleaseResource( &(NonpagedCcb->Resource) ); + ExReleaseResource( &(NpVcb->Resource) ); + + return; +} diff --git a/private/ntos/npfs/secursup.c b/private/ntos/npfs/secursup.c new file mode 100644 index 000000000..c6869a55b --- /dev/null +++ b/private/ntos/npfs/secursup.c @@ -0,0 +1,387 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + SecurSup.c + +Abstract: + + This module implements the Named Pipe Security support routines + +Author: + + Gary Kimura [GaryKi] 06-May-1991 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_SECURSUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpCopyClientContext) +#pragma alloc_text(PAGE, NpImpersonateClientContext) +#pragma alloc_text(PAGE, NpInitializeSecurity) +#pragma alloc_text(PAGE, NpSetDataEntryClientContext) +#pragma alloc_text(PAGE, NpUninitializeSecurity) +#endif + + +NTSTATUS +NpInitializeSecurity ( + IN PCCB Ccb, + IN PSECURITY_QUALITY_OF_SERVICE SecurityQos, + IN PETHREAD UserThread + ) + +/*++ + +Routine Description: + + This routine initializes the security (impersonation) fields + in the ccb. It is called when the client end gets opened. + +Arguments: + + Ccb - Supplies the ccb being initialized + + SecurityQos - Supplies the clients quality of service parameter + + UserThread - Supplise the client's user thread + +Return Value: + + NTSTATUS - Returns the result of the operation + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpInitializeSecurity, Ccb = %08lx\n", Ccb); + + // + // Either copy the security qos parameter, if it is not null or + // create a dummy qos + // + + if (SecurityQos != NULL) { + + RtlCopyMemory( &Ccb->SecurityQos, + SecurityQos, + sizeof(SECURITY_QUALITY_OF_SERVICE) ); + + } else { + + Ccb->SecurityQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); + Ccb->SecurityQos.ImpersonationLevel = SecurityImpersonation; + Ccb->SecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; + Ccb->SecurityQos.EffectiveOnly = TRUE; + } + + // + // Because we might be asked to reinitialize the ccb we need + // to first check if the security client context is not null and if so then + // free its pool + // + + if (Ccb->SecurityClientContext != NULL) { + + SeDeleteClientSecurity( Ccb->SecurityClientContext ); + ExFreePool( Ccb->SecurityClientContext ); + } + + // + // If the tracking mode is static then we need to capture the + // client context now otherwise we set the client context field + // to null + // + + if (Ccb->SecurityQos.ContextTrackingMode == SECURITY_STATIC_TRACKING) { + + // + // Allocate a client context record, and then initialize it + // + + Ccb->SecurityClientContext = FsRtlAllocatePool( PagedPool, + sizeof(SECURITY_CLIENT_CONTEXT) ); + + DebugTrace(0, Dbg, "Static tracking, ClientContext = %08lx\n", Ccb->SecurityClientContext); + + if (!NT_SUCCESS(Status = SeCreateClientSecurity( UserThread, + &Ccb->SecurityQos, + FALSE, + Ccb->SecurityClientContext ))) { + + DebugTrace(0, Dbg, "Not successful at creating client security, %08lx\n", Status); + + ExFreePool( Ccb->SecurityClientContext ); + Ccb->SecurityClientContext = NULL; + } + + } else { + + DebugTrace(0, Dbg, "Dynamic tracking\n", 0); + + Ccb->SecurityClientContext = NULL; + Status = STATUS_SUCCESS; + } + + DebugTrace(-1, Dbg, "NpInitializeSecurity -> %08lx\n", Status); + + return Status; +} + + +VOID +NpUninitializeSecurity ( + IN PCCB Ccb + ) + +/*++ + +Routine Description: + + This routine deletes the client context referenced by the ccb + +Arguments: + + Ccb - Supplies the ccb being uninitialized + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpUninitializeSecurity, Ccb = %08lx\n", Ccb); + + // + // We only have work to do if the client context field is not null + // and then we need to delete the client context, and free the memory. + // + + if (Ccb->SecurityClientContext != NULL) { + + DebugTrace(0, Dbg, "Delete client context, %08lx\n", Ccb->SecurityClientContext); + + SeDeleteClientSecurity( Ccb->SecurityClientContext ); + + ExFreePool( Ccb->SecurityClientContext ); + Ccb->SecurityClientContext = NULL; + } + + DebugTrace(-1, Dbg, "NpUninitializeSecurity -> VOID\n", 0); + + return; +} + + +NTSTATUS +NpSetDataEntryClientContext ( + IN NAMED_PIPE_END NamedPipeEnd, + IN PCCB Ccb, + IN PDATA_ENTRY DataEntry, + IN PETHREAD UserThread + ) + +/*++ + +Routine Description: + + This routine captures a new client context and stores it in the indicated + data entry, but only if the tracking mode is dynamic and only for the + client end of the named pipe. + +Arguments: + + NamedPipeEnd - Indicates the client or server end of the named pipe. + Only the client end does anything. + + Ccb - Supplies the ccb for this instance of the named pipe. + + DataEntry - Supplies the data entry to use to store the client context + + UserThread - Supplies the thread of the client + +Return Value: + + NTSTATUS - Returns our success code. + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpSetDataEntryClientContext, Ccb = %08lx\n", Ccb); + + // + // Only do the work if this is the client end and tracking is dynamic + // + + if ((NamedPipeEnd == FILE_PIPE_CLIENT_END) && + (Ccb->SecurityQos.ContextTrackingMode == SECURITY_DYNAMIC_TRACKING)) { + + // + // Allocate a client context record, and then initialize it + // + + DataEntry->SecurityClientContext = FsRtlAllocatePool( NonPagedPoolMustSucceed, + sizeof(SECURITY_CLIENT_CONTEXT) ); + + DebugTrace(0, Dbg, "Client End, Dynamic Tracking, ClientContext = %08lx\n", DataEntry->SecurityClientContext); + + if (!NT_SUCCESS(Status = SeCreateClientSecurity( UserThread, + &Ccb->SecurityQos, + FALSE, + DataEntry->SecurityClientContext ))) { + + DebugTrace(0, Dbg, "Not successful at creating client security, %08lx\n", Status); + + ExFreePool( DataEntry->SecurityClientContext ); + DataEntry->SecurityClientContext = NULL; + } + + } else { + + DebugTrace(0, Dbg, "Static Tracking or Not Client End\n", 0); + + DataEntry->SecurityClientContext = NULL; + Status = STATUS_SUCCESS; + } + + DebugTrace(-1, Dbg, "NpSetDataEntryClientContext -> %08lx\n", Status); + + return Status; +} + + +VOID +NpCopyClientContext ( + IN PCCB Ccb, + IN PDATA_ENTRY DataEntry + ) + +/*++ + +Routine Description: + + This routine copies the client context stored in the data entry into + the ccb, but only for dynamic tracking. + +Arguments: + + Ccb - Supplies the ccb to update. + + DataEntry - Supplies the DataEntry to copy from. + +Return Value: + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpCopyClientContext, Ccb = %08lx\n", Ccb); + + // + // Only do the copy if the data entries client context field is not null + // which means that we are doing dynamic tracking. Note we will + // not be called with a server write data entry that has a non null + // client context. + // + + if (DataEntry->SecurityClientContext != NULL) { + + DebugTrace(0, Dbg, "have something to copy %08lx\n", DataEntry->SecurityClientContext); + + // + // First check if we need to delete and deallocate the client + // context in the nonpaged ccb + // + + if (Ccb->SecurityClientContext != NULL) { + + DebugTrace(0, Dbg, "Remove current client context %08lx\n", Ccb->SecurityClientContext); + + SeDeleteClientSecurity( Ccb->SecurityClientContext ); + + ExFreePool( Ccb->SecurityClientContext ); + } + + // + // Now copy over the reference to the client context, and zero + // out the reference in the data entry. + // + + Ccb->SecurityClientContext = DataEntry->SecurityClientContext; + DataEntry->SecurityClientContext = NULL; + } + + DebugTrace(-1, Dbg, "NpCopyClientContext -> VOID\n", 0 ); + + return; +} + + +NTSTATUS +NpImpersonateClientContext ( + IN PCCB Ccb + ) + +/*++ + +Routine Description: + + This routine impersonates the current client context stored in the + ccb + +Arguments: + + Ccb - Supplies the ccb for the named pipe + +Return Value: + + NTSTATUS - returns our status code. + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpImpersonateClientContext, Ccb = %08lx\n", Ccb); + + if (Ccb->SecurityClientContext == NULL) { + + DebugTrace(0, Dbg, "Cannot impersonate\n", 0); + + Status = STATUS_CANNOT_IMPERSONATE; + + } else { + + SeImpersonateClient( Ccb->SecurityClientContext, NULL ); + + Status = STATUS_SUCCESS; + } + + DebugTrace(-1, Dbg, "NpImpersonateClientContext -> %08lx\n", Status); + + return Status; +} diff --git a/private/ntos/npfs/seinfo.c b/private/ntos/npfs/seinfo.c new file mode 100644 index 000000000..3031dd43d --- /dev/null +++ b/private/ntos/npfs/seinfo.c @@ -0,0 +1,416 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + SeInfo.c + +Abstract: + + This module implements the Security Info routines for NPFS called by the + dispatch driver. There are two entry points NpFsdQueryInformation + and NpFsdSetInformation. + +Author: + + Gary Kimura [GaryKi] 21-Aug-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_SEINFO) + +// +// local procedure prototypes +// + +NTSTATUS +NpCommonQuerySecurityInfo ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + + +NTSTATUS +NpCommonSetSecurityInfo ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpCommonQuerySecurityInfo) +#pragma alloc_text(PAGE, NpCommonSetSecurityInfo) +#pragma alloc_text(PAGE, NpFsdQuerySecurityInfo) +#pragma alloc_text(PAGE, NpFsdSetSecurityInfo) +#endif + + +NTSTATUS +NpFsdQuerySecurityInfo ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the Query Security Information API + calls. + +Arguments: + + NpfsDeviceObject - Supplies the device object to use. + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The Fsd status for the Irp + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpFsdQuerySecurityInfo\n", 0); + + // + // Call the common Query Information routine. + // + + FsRtlEnterFileSystem(); + + NpAcquireExclusiveVcb(); + + try { + + Status = NpCommonQuerySecurityInfo( NpfsDeviceObject, Irp ); + + } except(NpExceptionFilter( GetExceptionCode() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = NpProcessException( NpfsDeviceObject, Irp, GetExceptionCode() ); + } + + NpReleaseVcb(); + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpFsdQuerySecurityInfo -> %08lx\n", Status ); + + return Status; +} + + +NTSTATUS +NpFsdSetSecurityInfo ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the Set Security Information API + calls. + +Arguments: + + NpfsDeviceObject - Supplies the device object to use. + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The Fsd status for the Irp + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpFsdSetSecurityInfo\n", 0); + + // + // Call the common Set Information routine. + // + + FsRtlEnterFileSystem(); + + NpAcquireExclusiveVcb(); + + try { + + Status = NpCommonSetSecurityInfo( NpfsDeviceObject, Irp ); + + } except(NpExceptionFilter( GetExceptionCode() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = NpProcessException( NpfsDeviceObject, Irp, GetExceptionCode() ); + } + + NpReleaseVcb(); + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpFsdSetSecurityInfo -> %08lx\n", Status ); + + return Status; +} + + +// +// Internal support routine +// + +NTSTATUS +NpCommonQuerySecurityInfo ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for querying security information. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - the return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + NTSTATUS Status; + + NODE_TYPE_CODE NodeTypeCode; + PFCB Fcb; + PCCB Ccb; + NAMED_PIPE_END NamedPipeEnd; + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpCommonQuerySecurityInfo...\n", 0); + DebugTrace( 0, Dbg, " Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg, " ->SecurityInformation = %08lx\n", IrpSp->Parameters.QuerySecurity.SecurityInformation); + DebugTrace( 0, Dbg, " ->Length = %08lx\n", IrpSp->Parameters.QuerySecurity.Length); + DebugTrace( 0, Dbg, " ->UserBuffer = %08lx\n", Irp->UserBuffer); + + // + // Get the ccb and figure out who we are, and make sure we're not + // disconnected. + // + + if ((NodeTypeCode = NpDecodeFileObject( IrpSp->FileObject, + &Fcb, + &Ccb, + &NamedPipeEnd )) == NTC_UNDEFINED) { + + DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + Status = STATUS_PIPE_DISCONNECTED; + + DebugTrace(-1, Dbg, "NpCommonQueryInformation -> %08lx\n", Status ); + return Status; + } + + // + // Now we only will allow write operations on the pipe and not a directory + // or the device + // + + if (NodeTypeCode != NPFS_NTC_CCB) { + + DebugTrace(0, Dbg, "FileObject is not for a named pipe\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); + Status = STATUS_INVALID_PARAMETER; + + DebugTrace(-1, Dbg, "NpCommonQueryInformation -> %08lx\n", Status ); + return Status; + } + + // + // Call the security routine to do the actual query + // + + try { + + Status = SeQuerySecurityDescriptorInfo( &IrpSp->Parameters.QuerySecurity.SecurityInformation, + Irp->UserBuffer, + &IrpSp->Parameters.QuerySecurity.Length, + &Fcb->SecurityDescriptor ); + + if ( Status == STATUS_BUFFER_TOO_SMALL ) { + + Irp->IoStatus.Information = IrpSp->Parameters.QuerySecurity.Length; + + Status = STATUS_BUFFER_OVERFLOW; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + + NpCompleteRequest( Irp, Status ); + + DebugTrace(-1, Dbg, "NpCommonQuerySecurityInfo -> %08lx\n", Status ); + return Status; +} + + +// +// Internal support routine +// + +NTSTATUS +NpCommonSetSecurityInfo ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for Setting security information. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - the return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + NTSTATUS Status; + + NODE_TYPE_CODE NodeTypeCode; + PFCB Fcb; + PCCB Ccb; + NAMED_PIPE_END NamedPipeEnd; + + PSECURITY_DESCRIPTOR OldSecurityDescriptor; + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NpCommonSetSecurityInfo...\n", 0); + DebugTrace( 0, Dbg, " Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg, " ->SecurityInformation = %08lx\n", IrpSp->Parameters.SetSecurity.SecurityInformation); + DebugTrace( 0, Dbg, " ->SecurityDescriptor = %08lx\n", IrpSp->Parameters.SetSecurity.SecurityDescriptor); + + // + // Get the ccb and figure out who we are, and make sure we're not + // disconnected. + // + + if ((NodeTypeCode = NpDecodeFileObject( IrpSp->FileObject, + &Fcb, + &Ccb, + &NamedPipeEnd )) == NTC_UNDEFINED) { + + DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + Status = STATUS_PIPE_DISCONNECTED; + + DebugTrace(-1, Dbg, "NpCommonQueryInformation -> %08lx\n", Status ); + return Status; + } + + // + // Now we only will allow write operations on the pipe and not a directory + // or the device + // + + if (NodeTypeCode != NPFS_NTC_CCB) { + + DebugTrace(0, Dbg, "FileObject is not for a named pipe\n", 0); + + NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); + Status = STATUS_INVALID_PARAMETER; + + DebugTrace(-1, Dbg, "NpCommonQueryInformation -> %08lx\n", Status ); + return Status; + } + + // + // Call the security routine to do the actual set + // + + OldSecurityDescriptor = Fcb->SecurityDescriptor; + + Status = SeSetSecurityDescriptorInfo( NULL, + &IrpSp->Parameters.SetSecurity.SecurityInformation, + IrpSp->Parameters.SetSecurity.SecurityDescriptor, + &Fcb->SecurityDescriptor, + PagedPool, + IoGetFileObjectGenericMapping() ); + + if (NT_SUCCESS(Status)) { + ExFreePool( OldSecurityDescriptor ); + } + + NpCompleteRequest( Irp, Status ); + + DebugTrace(-1, Dbg, "NpCommonSetSecurityInfo -> %08lx\n", Status ); + return Status; +} diff --git a/private/ntos/npfs/sources b/private/ntos/npfs/sources new file mode 100644 index 000000000..063d6f34b --- /dev/null +++ b/private/ntos/npfs/sources @@ -0,0 +1,65 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=npfs + +TARGETNAME=npfs +TARGETPATH=\nt\public\sdk\lib +TARGETTYPE=DRIVER + +INCLUDES=..\inc + +NTPROFILEINPUT=yes + +MSC_WARNING_LEVEL=/W3 /WX + +SOURCES= \ + AliasSup.c \ + Cleanup.c \ + Close.c \ + Create.c \ + CreateNp.c \ + DataSup.c \ + Dir.c \ + DumpSup.c \ + EventSup.c \ + FileInfo.c \ + FilObSup.c \ + FlushBuf.c \ + FsCtrl.c \ + NpData.c \ + Npfs.rc \ + NpInit.c \ + PrefxSup.c \ + Read.c \ + ReadSup.c \ + SecurSup.c \ + SeInfo.c \ + StateSup.c \ + StrucSup.c \ + VolInfo.c \ + WaitSup.c \ + Write.c \ + WriteSup.c diff --git a/private/ntos/npfs/statesup.c b/private/ntos/npfs/statesup.c new file mode 100644 index 000000000..d7fbb7d2f --- /dev/null +++ b/private/ntos/npfs/statesup.c @@ -0,0 +1,1179 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + StateSup.c + +Abstract: + + This module implements the Named Pipe State Support routines + +Author: + + Gary Kimura [GaryKi] 30-Aug-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (NPFS_BUG_CHECK_STATESUP) + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_STATESUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpInitializePipeState) +#pragma alloc_text(PAGE, NpUninitializePipeState) +#endif + +VOID +NpCancelListeningQueueIrp ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + + +VOID +NpInitializePipeState ( + IN PCCB Ccb, + IN PFILE_OBJECT ServerFileObject + ) + +/*++ + +Routine Description: + + This routine initialize a named pipe instance to the disconnected state. + +Arguments: + + Ccb - Supplies a pointer to the Ccb representing the pipe state + + ServerFileObject - Supplies a pointer to the server file object + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpInitializePipeState, Ccb = %08lx\n", Ccb); + + // + // Set the ccb and nonpaged ccb fields + // + + Ccb->FileObject[ FILE_PIPE_SERVER_END ] = ServerFileObject; + Ccb->NamedPipeState = FILE_PIPE_DISCONNECTED_STATE; + + // + // The file object contexts pointers. + // + + NpSetFileObject( ServerFileObject, + Ccb, + Ccb->NonpagedCcb, + FILE_PIPE_SERVER_END ); + + // + // and return to our caller + // + + DebugTrace(-1, Dbg, "NpInitializePipeState -> VOID\n", 0); + + return; +} + + +VOID +NpUninitializePipeState ( + IN PCCB Ccb + ) + +/*++ + +Routine Description: + + This routine initialize a named pipe instance to the disconnected state. + +Arguments: + + Ccb - Supplies a pointer to the Ccb representing the pipe state + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpUninitializePipeState, Ccb = %08lx\n", Ccb); + + // + // The file object contexts pointers for our server to null + // + + NpSetFileObject( Ccb->FileObject[ FILE_PIPE_SERVER_END ], + NULL, + NULL, + FILE_PIPE_SERVER_END ); + Ccb->FileObject[ FILE_PIPE_SERVER_END ] = NULL; + + + // + // The file object contexts pointers for our client to null + // + + NpSetFileObject( Ccb->FileObject[ FILE_PIPE_CLIENT_END ], + NULL, + NULL, + FILE_PIPE_CLIENT_END ); + Ccb->FileObject[ FILE_PIPE_CLIENT_END ] = NULL; + + // + // Set to null both pointers to file object. + // + + Ccb->FileObject[ FILE_PIPE_SERVER_END ] = NULL; + Ccb->FileObject[ FILE_PIPE_CLIENT_END ] = NULL; + + // + // and return to our caller + // + + DebugTrace(-1, Dbg, "NpUninitializePipeState -> VOID\n", 0); + + return; +} + + +NTSTATUS +NpSetListeningPipeState ( + IN PCCB Ccb, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine sets a named pipe to the listening state. This routine + will either complete the IRP right away or put in the listening queue + to be completed later. + +Arguments: + + Ccb - Supplies a pointer to the Ccb representing the pipe state + + Irp - Supplies the Irp doing the listening operation + +Return Value: + + NTSTATUS + +--*/ + +{ + NTSTATUS Status; + + DebugTrace(+1, Dbg, "NpSetListeningPipeState, Ccb = %08lx\n", Ccb); + + // + // Case on the current state of the named pipe + // + + switch (Ccb->NamedPipeState) { + + case FILE_PIPE_DISCONNECTED_STATE: + + DebugTrace(0, Dbg, "Pipe was disconnected\n", 0); + + // + // Set the state to listening and check for any wait for named + // pipe requests. + // + + Ccb->NamedPipeState = FILE_PIPE_LISTENING_STATE; + + NpCancelWaiter( &NpVcb->WaitQueue, + &Ccb->Fcb->FullFileName ); + + // + // If the completion mode is complete operation then we can + // complete this irp otherwise we need to enqueue the irp + // into the listening queue, and mark it pending. + // + + if (Ccb->CompletionMode[ FILE_PIPE_SERVER_END ] == FILE_PIPE_COMPLETE_OPERATION) { + + NpCompleteRequest( Irp, STATUS_PIPE_LISTENING ); + Status = STATUS_SUCCESS; + + } else { + + IoMarkIrpPending( Irp ); + + InsertTailList( &Ccb->NonpagedCcb->ListeningQueue, &Irp->Tail.Overlay.ListEntry ); + + // + // Set the cancel routine and also check if the irp is already cancelled + // + + IoAcquireCancelSpinLock( &Irp->CancelIrql ); + Irp->IoStatus.Status = (ULONG)Ccb; + + if (Irp->Cancel) { + + IoReleaseCancelSpinLock( Irp->CancelIrql ); + RemoveEntryList( &Irp->Tail.Overlay.ListEntry ); + NpCompleteRequest( Irp, STATUS_CANCELLED ); + Status = STATUS_SUCCESS; + + } else { + + IoSetCancelRoutine( Irp, NpCancelListeningQueueIrp ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + Status = STATUS_PENDING; + } + } + + break; + + case FILE_PIPE_LISTENING_STATE: + + DebugTrace(0, Dbg, "Pipe was listening\n", 0); + + // + // If the completion mode is complete operation then we can + // complete this irp otherwise we need to enqueue the irp + // into the listening queue, and mark it pending. + // + + if (Ccb->CompletionMode[ FILE_PIPE_SERVER_END ] == FILE_PIPE_COMPLETE_OPERATION) { + + NpCompleteRequest( Irp, STATUS_PIPE_LISTENING ); + Status = STATUS_PIPE_LISTENING; + + } else { + + IoMarkIrpPending( Irp ); + + InsertTailList( &Ccb->NonpagedCcb->ListeningQueue, &Irp->Tail.Overlay.ListEntry ); + + // + // Set the cancel routine and also check if the irp is already cancelled + // + + IoAcquireCancelSpinLock( &Irp->CancelIrql ); + Irp->IoStatus.Status = (ULONG)Ccb; + + if (Irp->Cancel) { + + IoReleaseCancelSpinLock( Irp->CancelIrql ); + RemoveEntryList( &Irp->Tail.Overlay.ListEntry ); + NpCompleteRequest( Irp, STATUS_CANCELLED ); + Status = STATUS_SUCCESS; + + } else { + + IoSetCancelRoutine( Irp, NpCancelListeningQueueIrp ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + Status = STATUS_PENDING; + } + } + + break; + + case FILE_PIPE_CONNECTED_STATE: + + DebugTrace(0, Dbg, "Pipe was connected\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_CONNECTED ); + Status = STATUS_PIPE_CONNECTED; + + break; + + case FILE_PIPE_CLOSING_STATE: + + DebugTrace(0, Dbg, "Pipe was closing\n", 0); + + NpCompleteRequest( Irp, STATUS_PIPE_CLOSING ); + Status = STATUS_PIPE_CLOSING; + + break; + + default: + + NpBugCheck( Ccb->NamedPipeState, 0, 0 ); + } + + // + // and return to our caller + // + + DebugTrace(-1, Dbg, "NpSetListeningPipeState -> %08lx\n", Status); + + return Status; +} + + +NTSTATUS +NpSetConnectedPipeState ( + IN PCCB Ccb, + IN PFILE_OBJECT ClientFileObject + ) + +/*++ + +Routine Description: + + This routine sets the state of a named pipe to connected. + +Arguments: + + Ccb - Supplies a pointer to the Ccb representing the pipe state + + ClientFileObject - Supplies the file object for the client that is + doing the connect. + +Return Value: + + NTSTATUS + +--*/ + +{ + NTSTATUS Status; + PNONPAGED_CCB NonpagedCcb; + PIRP LocalIrp; + KIRQL CancelIrql; + + DebugTrace(+1, Dbg, "NpSetConnectedPipeState, Ccb = %08lx\n", Ccb); + + // + // Save a pointer to the nonpaged ccb, we really need to do this now so when we + // complete our listening waiters we won't touch paged pool + // + + NonpagedCcb = Ccb->NonpagedCcb; + + // + // Case on the current state of the named pipe + // + + switch (Ccb->NamedPipeState) { + + case FILE_PIPE_DISCONNECTED_STATE: + + DebugTrace(0, Dbg, "Pipe was disconnected\n", 0); + + NpBugCheck( 0, 0, 0 ); + + case FILE_PIPE_LISTENING_STATE: + + DebugTrace(0, Dbg, "Pipe was listening\n", 0); + + // + // Set the state of the pipe to connected and adjust the + // appropriate read mode and completion mode values + // + + Ccb->NamedPipeState = FILE_PIPE_CONNECTED_STATE; + Ccb->ReadMode[ FILE_PIPE_CLIENT_END ] = FILE_PIPE_BYTE_STREAM_MODE; + Ccb->CompletionMode[ FILE_PIPE_CLIENT_END ] = FILE_PIPE_QUEUE_OPERATION; + + // + // Set our back pointer to the client file object and set the + // client file object context pointers + // + + Ccb->FileObject[ FILE_PIPE_CLIENT_END ] = ClientFileObject; + + NpSetFileObject( ClientFileObject, + Ccb, + NonpagedCcb, + FILE_PIPE_CLIENT_END ); + + // + // And complete any listening waiters + // + + IoAcquireCancelSpinLock( &CancelIrql ); + + while (!IsListEmpty( &NonpagedCcb->ListeningQueue )) { + PLIST_ENTRY Links; + + Links = RemoveHeadList( &NonpagedCcb->ListeningQueue ); + + LocalIrp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry ); + + // + // Disable the cancel routine in the irp before completing the request + // + + LocalIrp->CancelIrql = CancelIrql; + + IoSetCancelRoutine( LocalIrp, NULL ); + + IoReleaseCancelSpinLock( CancelIrql ); + + NpCompleteRequest( LocalIrp, STATUS_SUCCESS ); + + IoAcquireCancelSpinLock( &CancelIrql ); + } + + IoReleaseCancelSpinLock( CancelIrql ); + + Status = STATUS_SUCCESS; + + break; + + case FILE_PIPE_CONNECTED_STATE: + + DebugTrace(0, Dbg, "Pipe was connected\n", 0); + + NpBugCheck( 0, 0, 0 ); + + case FILE_PIPE_CLOSING_STATE: + + DebugTrace(0, Dbg, "Pipe was closing\n", 0); + + NpBugCheck( 0, 0, 0 ); + + default: + + NpBugCheck( Ccb->NamedPipeState, 0, 0 ); + } + + // + // and return to our caller + // + + DebugTrace(-1, Dbg, "NpSetConnectedPipeState -> %08lx\n", Status); + + return Status; +} + + +NTSTATUS +NpSetClosingPipeState ( + IN PCCB Ccb, + IN PIRP Irp, + IN NAMED_PIPE_END NamedPipeEnd + ) + +/*++ + +Routine Description: + + This routine sets a pipe state to closing. This routine will + either complete the irp right away or put in on the data queue + to be completed later. + +Arguments: + + Ccb - Supplies a pointer to the Ccb representing the pipe state + + Irp - Supplies the Irp trying to do the close operation + + NamedPipeEnd - Indicates if the server or client is doing the transition + +Return Value: + + NTSTATUS - + +--*/ + +{ + NTSTATUS Status; + + PNONPAGED_CCB NonpagedCcb; + + PFCB Fcb; + PIRP LocalIrp; + KIRQL CancelIrql; + + PDATA_QUEUE ReadQueue; + PDATA_QUEUE WriteQueue; + + PEVENT_TABLE_ENTRY Event; + + DebugTrace(+1, Dbg, "NpSetClosingPipeState, Ccb = %08lx\n", Ccb); + + Fcb = Ccb->Fcb; + + // + // Save a pointer to the nonpaged ccb, we really need to do this now so when we + // complete our listening waiters we won't touch paged pool + // + + NonpagedCcb = Ccb->NonpagedCcb; + + // + // Case on the current state of the named pipe + // + + switch (Ccb->NamedPipeState) { + + case FILE_PIPE_DISCONNECTED_STATE: + + DebugTrace(0, Dbg, "Pipe was disconnected\n", 0); + + ASSERT( NamedPipeEnd == FILE_PIPE_SERVER_END ); + + // + // Pipe is disconnected, for safety sake we'll zero out the + // file objects context pointers to use + // + + NpSetFileObject( Ccb->FileObject[ FILE_PIPE_SERVER_END ], + NULL, + NULL, + FILE_PIPE_SERVER_END ); + Ccb->FileObject[ FILE_PIPE_SERVER_END ] = NULL; + + NpSetFileObject( Ccb->FileObject[ FILE_PIPE_CLIENT_END ], + NULL, + NULL, + FILE_PIPE_CLIENT_END ); + Ccb->FileObject[ FILE_PIPE_CLIENT_END ] = NULL; + + // + // Close it we delete the instance, and possibly the Fcb if its + // open count is now zero. + // + + NpDeleteCcb( Ccb ); + if (Fcb->OpenCount == 0) { + + NpDeleteFcb( Fcb ); + } + + // + // And now complete the irp + // + + NpCompleteRequest( Irp, STATUS_SUCCESS ); + Status = STATUS_SUCCESS; + + break; + + case FILE_PIPE_LISTENING_STATE: + + DebugTrace(0, Dbg, "Pipe was listening\n", 0); + + ASSERT( NamedPipeEnd == FILE_PIPE_SERVER_END ); + + // + // Pipe in listening state, so complete all IRPs that are in the + // listening queue with a closing status, and then delete the + // instance and possibly the Fcb if its open count is now zero + // + + IoAcquireCancelSpinLock( &CancelIrql ); + + while (!IsListEmpty( &NonpagedCcb->ListeningQueue )) { + PLIST_ENTRY Links; + + Links = RemoveHeadList( &NonpagedCcb->ListeningQueue ); + + LocalIrp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry ); + + // + // Disable the cancel routine in the irp before completing the request + // + + LocalIrp->CancelIrql = CancelIrql; + + IoSetCancelRoutine( LocalIrp, NULL ); + + IoReleaseCancelSpinLock( CancelIrql ); + + NpCompleteRequest( LocalIrp, STATUS_PIPE_BROKEN ); + + IoAcquireCancelSpinLock( &CancelIrql ); + } + + IoReleaseCancelSpinLock( CancelIrql ); + + // + // For safety sake we'll zero out the file objects context + // pointers to use + // + + NpSetFileObject( Ccb->FileObject[ FILE_PIPE_SERVER_END ], + NULL, + NULL, + FILE_PIPE_SERVER_END ); + Ccb->FileObject[ FILE_PIPE_SERVER_END ] = NULL; + + NpSetFileObject( Ccb->FileObject[ FILE_PIPE_CLIENT_END ], + NULL, + NULL, + FILE_PIPE_CLIENT_END ); + Ccb->FileObject[ FILE_PIPE_CLIENT_END ] = NULL; + + // + // Remove the ccb and possibly the Fcb + // + + NpDeleteCcb( Ccb ); + if (Fcb->OpenCount == 0) { + + NpDeleteFcb( Fcb ); + } + + // + // And now complete the irp + // + + NpCompleteRequest( Irp, STATUS_SUCCESS ); + Status = STATUS_SUCCESS; + + break; + + case FILE_PIPE_CONNECTED_STATE: + + // + // The pipe is connected so decide who is trying to do the close + // and then fall into common code + // + + if (NamedPipeEnd == FILE_PIPE_SERVER_END) { + + DebugTrace(0, Dbg, "Pipe was connected, server doing close\n", 0); + + ReadQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + WriteQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + + Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ]; + + // + // For safety sake we'll zero out the file objects context + // pointers to use + // + + NpSetFileObject( Ccb->FileObject[ FILE_PIPE_SERVER_END ], + NULL, + NULL, + FILE_PIPE_SERVER_END ); + Ccb->FileObject[ FILE_PIPE_SERVER_END ] = NULL; + + } else { + + DebugTrace(0, Dbg, "Pipe was connected, client doing close\n", 0); + + ReadQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + WriteQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + + Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_SERVER_END ]; + + // + // For safety sake we'll zero out the file objects context + // pointers to use + // + + NpSetFileObject( Ccb->FileObject[ FILE_PIPE_CLIENT_END ], + NULL, + NULL, + FILE_PIPE_CLIENT_END ); + Ccb->FileObject[ FILE_PIPE_CLIENT_END ] = NULL; + } + + // + // To do a close on a connected pipe we set its state to closing + // drain the read queue and drain reads on the write queue. + // + // + // Closing <---ReadQueue---- [ Remove all entries ] + // End + // ---WriteQueue---> [ Remove only read entries ] + // + + Ccb->NamedPipeState = FILE_PIPE_CLOSING_STATE; + + while (!NpIsDataQueueEmpty( ReadQueue )) { + + if ((LocalIrp = NpRemoveDataQueueEntry( ReadQueue )) != NULL) { + + NpCompleteRequest( LocalIrp, STATUS_PIPE_BROKEN ); + } + } + + while (!NpIsDataQueueEmpty( WriteQueue ) && + (WriteQueue->QueueState == ReadEntries)) { + + if ((LocalIrp = NpRemoveDataQueueEntry( WriteQueue )) != NULL) { + + NpCompleteRequest( LocalIrp, STATUS_PIPE_BROKEN ); + } + } + + // + // Now if the write queue is empty then we complete this + // close request with success otherwise we need to enqueue + // the irp to the write queue and set our return status to pending + // also the mark the irp pending + // + + if ( TRUE || NpIsDataQueueEmpty( WriteQueue ) ) { + + NpCompleteRequest( Irp, STATUS_SUCCESS ); + Status = STATUS_SUCCESS; + + } else { + + IoMarkIrpPending( Irp ); + + (VOID)NpAddDataQueueEntry( WriteQueue, WriteEntries, Close, 0, Irp, NULL ); + Status = STATUS_PENDING; + } + + // + // Now signal the other sides event to show that something has + // happened + // + + NpSignalEventTableEntry( Event ); + + break; + + case FILE_PIPE_CLOSING_STATE: + + // + // The pipe is closing so decide who is trying to complete the close + // and then fall into common code + // + + if (NamedPipeEnd == FILE_PIPE_SERVER_END) { + + DebugTrace(0, Dbg, "Pipe was closing, server doing close\n", 0); + + ReadQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + + // + // For safety sake we'll zero out the file objects context + // pointers to use + // + + NpSetFileObject( Ccb->FileObject[ FILE_PIPE_SERVER_END ], + NULL, + NULL, + FILE_PIPE_SERVER_END ); + Ccb->FileObject[ FILE_PIPE_SERVER_END ] = NULL; + + NpSetFileObject( Ccb->FileObject[ FILE_PIPE_CLIENT_END ], + NULL, + NULL, + FILE_PIPE_CLIENT_END ); + Ccb->FileObject[ FILE_PIPE_CLIENT_END ] = NULL; + + } else { + + DebugTrace(0, Dbg, "Pipe was closing, client doing close\n", 0); + + ReadQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + + // + // For safety sake we'll zero out the file objects context + // pointers to use + // + + NpSetFileObject( Ccb->FileObject[ FILE_PIPE_SERVER_END ], + NULL, + NULL, + FILE_PIPE_SERVER_END ); + Ccb->FileObject[ FILE_PIPE_SERVER_END ] = NULL; + + NpSetFileObject( Ccb->FileObject[ FILE_PIPE_CLIENT_END ], + NULL, + NULL, + FILE_PIPE_CLIENT_END ); + Ccb->FileObject[ FILE_PIPE_CLIENT_END ] = NULL; + } + + // + // To do a close on a closing pipe we drain the read queue of + // all its entries, delete the instance, and possibly delete the + // Fcb if its open count is now zero. + // + // + // Previously <-----Closed----- Closing + // Closed End + // End ----ReadQueue---> + // + + while (!NpIsDataQueueEmpty( ReadQueue )) { + + if ((LocalIrp = NpRemoveDataQueueEntry( ReadQueue )) != NULL) { + + NpCompleteRequest( LocalIrp, STATUS_PIPE_BROKEN ); + } + } + + NpDeleteCcb( Ccb ); + if (Fcb->OpenCount == 0) { + + NpDeleteFcb( Fcb ); + } + + // + // And now complete the irp + // + + NpCompleteRequest( Irp, STATUS_SUCCESS ); + Status = STATUS_SUCCESS; + + break; + + default: + + NpBugCheck( Ccb->NamedPipeState, 0, 0 ); + } + + // + // and return to our caller + // + + DebugTrace(-1, Dbg, "NpSetClosingPipeState -> %08lx\n", Status); + + return Status; +} + + +NTSTATUS +NpSetDisconnectedPipeState ( + IN PCCB Ccb + ) + +/*++ + +Routine Description: + + This routine sets a pipe state to disconnected, only the server is + allowed to do this transition + +Arguments: + + Ccb - Supplies a pointer to the Ccb representing the pipe instance + +Return Value: + + NTSTATUS + +--*/ + +{ + NTSTATUS Status; + + PNONPAGED_CCB NonpagedCcb; + + PIRP Irp; + + PDATA_QUEUE Inbound; + PDATA_QUEUE Outbound; + PEVENT_TABLE_ENTRY ClientEvent; + + DebugTrace(+1, Dbg, "NpSetDisconnectedPipeState, Ccb = %08lx\n", Ccb); + + + // + // Save a pointer to the nonpaged ccb, we really need to do this now so when we + // complete our listening waiters we won't touch paged pool + // + + NonpagedCcb = Ccb->NonpagedCcb; + + // + // Case on the current state of the named pipe + // + + switch (Ccb->NamedPipeState) { + + case FILE_PIPE_DISCONNECTED_STATE: + + DebugTrace(0, Dbg, "Pipe already disconnected\n", 0); + + // + // pipe already disconnected so there is no work for us to do + // + + Status = STATUS_PIPE_DISCONNECTED; + + break; + + case FILE_PIPE_LISTENING_STATE: + + DebugTrace(0, Dbg, "Pipe was listening\n", 0); + + // + // Pipe in listening state, so complete all IRPs that are in the + // listening queue with a disconnected status + // + + while (!IsListEmpty( &NonpagedCcb->ListeningQueue )) { + PLIST_ENTRY Links; + + Links = RemoveHeadList( &NonpagedCcb->ListeningQueue ); + + Irp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry ); + + // + // Disable the cancel routine in the irp before completing the request + // + + IoAcquireCancelSpinLock( &Irp->CancelIrql ); + IoSetCancelRoutine( Irp, NULL ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + } + + Status = STATUS_SUCCESS; + + break; + + case FILE_PIPE_CONNECTED_STATE: + + DebugTrace(0, Dbg, "Pipe was connected\n", 0); + + Inbound = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + Outbound = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + ClientEvent = NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ]; + + // + // Pipe is connected so we need to discard all of the data queues + // and complete any of their IRPs with status disconnected. + // + + while (!NpIsDataQueueEmpty( Inbound )) { + + if ((Irp = NpRemoveDataQueueEntry( Inbound )) != NULL) { + + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + } + } + + while (!NpIsDataQueueEmpty( Outbound )) { + + if ((Irp = NpRemoveDataQueueEntry( Outbound )) != NULL) { + + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + } + } + + // + // Signal the client event and then remove it from the pipe + // + + NpSignalEventTableEntry( ClientEvent ); + + NpDeleteEventTableEntry( &NpVcb->EventTable, ClientEvent ); + NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ] = NULL; + + // + // Disable the client's file object + // + + NpSetFileObject( Ccb->FileObject[ FILE_PIPE_CLIENT_END ], + NULL, + NULL, + FILE_PIPE_CLIENT_END ); + Ccb->FileObject[ FILE_PIPE_CLIENT_END ] = NULL; + + Status = STATUS_SUCCESS; + + break; + + case FILE_PIPE_CLOSING_STATE: + + DebugTrace(0, Dbg, "Pipe was closing\n", 0); + + Inbound = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + Outbound = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + ClientEvent = NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ]; + + // + // Pipe is closing (this had to have been done by the client) we + // need to discard all of the data queues (only the inbound can have + // entries) and complete any of their IRPs with status disconnected. + // + // + // Server <----Inbound---- Client + // End End + // ----Closed-----> + // + + while (!NpIsDataQueueEmpty( Inbound )) { + + if ((Irp = NpRemoveDataQueueEntry( Inbound )) != NULL) { + + NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); + } + } + + ASSERT( NpIsDataQueueEmpty( Outbound ) ); + + // + // The client event should already be gone but for safety sake + // we'll make sure its gone. + // + + NpDeleteEventTableEntry( &NpVcb->EventTable, ClientEvent ); + NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ] = NULL; + + // + // Also if it's still connected, disable the client's file object + // + + NpSetFileObject( Ccb->FileObject[ FILE_PIPE_CLIENT_END ], + NULL, + NULL, + FILE_PIPE_CLIENT_END ); + Ccb->FileObject[ FILE_PIPE_CLIENT_END ] = NULL; + + Status = STATUS_SUCCESS; + + break; + + default: + + NpBugCheck( Ccb->NamedPipeState, 0, 0 ); + } + + // + // Set the state to disconnected + // + + Ccb->NamedPipeState = FILE_PIPE_DISCONNECTED_STATE; + + // + // and return to our caller + // + + DebugTrace(-1, Dbg, "NpSetDisconnectedPipeState -> %08lx\n", Status); + + return Status; +} + + +// +// Local support routine +// + +VOID +NpCancelListeningQueueIrp ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the cancel function for an IRP saved in a listening + queue + +Arguments: + + DeviceObject - ignored + + Irp - Supplies the Irp being cancelled. A pointer to the ccb + structure is stored in the information field of the Irp Iosb + field. + +Return Value: + + None. + +--*/ + +{ + PCCB Ccb; + + UNREFERENCED_PARAMETER( DeviceObject ); + + // + // The status field is used to store a pointer to the Ccb + // containing this irp + // + + Ccb = (PCCB)Irp->IoStatus.Status; + + // + // We now need to void the cancel routine and release the io cancel spinlock + // + + IoSetCancelRoutine( Irp, NULL ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + // + // Get exclusive access to the named pipe vcb so we can now do our work + // + + NpAcquireExclusiveVcb(); + + try { + + PNONPAGED_CCB NonPagedCcb = Ccb->NonpagedCcb; + PLIST_ENTRY Links; + BOOLEAN RemoveEntry = FALSE; + + if (NodeType( NonPagedCcb ) == NPFS_NTC_NONPAGED_CCB) { + + // + // We need to check that the Irp is still in the listening queue. + // + + for (Links = NonPagedCcb->ListeningQueue.Flink; + Links != &NonPagedCcb->ListeningQueue; + Links = Links->Flink) { + + if (CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry ) == Irp) { + + RemoveEntry = TRUE; + break; + } + } + + if (RemoveEntry) { + + RemoveEntryList( &Irp->Tail.Overlay.ListEntry ); + NpCompleteRequest( Irp, STATUS_CANCELLED ); + } + } + + // + // Catch all exceptions. It's possible that the Ccb has gone away in + // the meantime and there is now no Irp to complete. + // + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + NOTHING; + } + + NpReleaseVcb(); + + // + // And return to our caller + // + + return; +} diff --git a/private/ntos/npfs/strucsup.c b/private/ntos/npfs/strucsup.c new file mode 100644 index 000000000..d3e05d079 --- /dev/null +++ b/private/ntos/npfs/strucsup.c @@ -0,0 +1,1022 @@ +/*++ + +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) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpCreateCcb) +#pragma alloc_text(PAGE, NpCreateFcb) +#pragma alloc_text(PAGE, NpCreateRootDcb) +#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) +#pragma alloc_text(PAGE, NpInitializeVcb) +#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. + +--*/ + +{ + // + // The following variables are used for abnormal unwinding + // + + BOOLEAN UnwindResource = FALSE; + BOOLEAN UnwindEventTable = FALSE; + BOOLEAN UnwindWaitQueue = FALSE; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpInitializeVcb, Vcb = %08lx\n", NpVcb); + + try { + + // + // 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; + NpVcb->NodeByteSize = sizeof(VCB); + + // + // Initialize the Prefix table + // + + RtlInitializeUnicodePrefix( &NpVcb->PrefixTable ); + + // + // Initialize the resource variable for the Vcb + // + + ExInitializeResource( &NpVcb->Resource ); + UnwindResource = TRUE; + + // + // Initialize the event table + // + + NpInitializeEventTable( &NpVcb->EventTable ); + UnwindEventTable = TRUE; + + // + // Initialize the wait queue + // + + NpInitializeWaitQueue( &NpVcb->WaitQueue ); + UnwindWaitQueue = TRUE; + + } finally { + + // + // If this is an abnormal termination then check if we need + // to undo any initialization we've already done. + // + + if (AbnormalTermination()) { + + if (UnwindResource) { ExDeleteResource( &NpVcb->Resource ); } + if (UnwindEventTable) { NpUninitializeEventTable( &NpVcb->EventTable ); } + if (UnwindWaitQueue) { NpUninitializeWaitQueue( &NpVcb->WaitQueue ); } + } + + DebugTrace(-1, Dbg, "NpInitializeVcb -> VOID\n", 0); + } + + // + // return and tell the caller + // + + return; +} + + +VOID +NpDeleteVcb ( + VOID + ) + +/*++ + +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: + + None. + +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) || (NpVcb->OpenUnderlingCount != 0)) { + + DebugDump("Error deleting Vcb\n", 0, NpVcb); + NpBugCheck( 0, 0, 0 ); + } + + // + // Remove the Root Dcb + // + + if (NpVcb->RootDcb != NULL) { + + NpDeleteFcb( NpVcb->RootDcb ); + } + + // + // Uninitialize the resource variable for the Vcb + // + + ExDeleteResource( &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; +} + + +VOID +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. + +--*/ + +{ + // + // The following variables are used for abnormal unwinding + // + + PVOID UnwindStorage = NULL; + BOOLEAN UnwindPrefix = FALSE; + + 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 ); + } + + try { + + // + // Allocate a new DCB and zero it out + // + + NpVcb->RootDcb = UnwindStorage = FsRtlAllocatePool( PagedPool, sizeof(DCB) ); + + RtlZeroMemory( NpVcb->RootDcb, sizeof(DCB)); + + // + // Set the proper node type code and node byte size + // + + NpVcb->RootDcb->NodeTypeCode = NPFS_NTC_ROOT_DCB; + NpVcb->RootDcb->NodeByteSize = sizeof(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 ); + + // + // set the full file name + // + // **** Use good string routines when available **** + // + + { + static PWCH Name = L"\\\0"; + + RtlInitUnicodeString( &NpVcb->RootDcb->FullFileName, Name ); + RtlInitUnicodeString( &NpVcb->RootDcb->LastFileName, Name ); + } + + // + // 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 ); + } + UnwindPrefix = TRUE; + + } finally { + + // + // If this is an abnormal termination then undo our work + // + + if (AbnormalTermination()) { + + if (UnwindPrefix) { RtlRemoveUnicodePrefix( &NpVcb->PrefixTable, &NpVcb->RootDcb->PrefixTableEntry ); } + + if (UnwindStorage != NULL) { ExFreePool( UnwindStorage ); } + } + + DebugTrace(-1, Dbg, "NpCreateRootDcb -> %8lx\n", NpVcb->RootDcb); + } + + return; +} + + +VOID +NpDeleteRootDcb ( + IN PROOT_DCB RootDcb + ) + +/*++ + +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 + +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 ); + + NpCompleteRequest( Irp, STATUS_FILE_FORCED_CLOSED ); + } + + while (!IsListEmpty(&RootDcb->Specific.Dcb.NotifyPartialQueue)) { + + Links = RemoveHeadList( &RootDcb->Specific.Dcb.NotifyPartialQueue ); + + Irp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry ); + + NpCompleteRequest( Irp, STATUS_FILE_FORCED_CLOSED ); + } + + // + // 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 ); + ExFreePool( RootDcb->FullFileName.Buffer ); + + // + // Finally deallocate the Dcb record + // + + ExFreePool( RootDcb ); + + // + // and return to our caller + // + + DebugTrace(-1, Dbg, "NpDeleteRootDcb -> VOID\n", 0); + + return; +} + + +PFCB +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 + ) + +/*++ + +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; + + // + // The following variables are used for abnormal unwinding + // + + PVOID UnwindStorage[2] = { NULL, NULL }; + BOOLEAN UnwindEntryList = FALSE; + BOOLEAN UnwindPrefix = FALSE; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpCreateFcb\n", 0); + + try { + + // + // Allocate a new FCB record and zero it out + // + + Fcb = UnwindStorage[0] = FsRtlAllocatePool( PagedPool, sizeof(FCB) ); + + RtlZeroMemory( Fcb, sizeof(FCB) ); + + // + // Set the proper node type code and node byte size + // + + Fcb->NodeTypeCode = NPFS_NTC_FCB; + Fcb->NodeByteSize = sizeof(FCB); + + // + // Insert this fcb into our parent dcb's queue + // + + InsertTailList( &ParentDcb->Specific.Dcb.ParentDcbQueue, + &Fcb->ParentDcbLinks ); + UnwindEntryList = TRUE; + + // + // 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 + // + // **** Use good string routines when available + // + + { + PWCH Name; + ULONG Length; + + Length = FileName->Length; + + Name = UnwindStorage[1] = FsRtlAllocatePool( NonPagedPool, Length + 2 ); + + RtlCopyMemory( Name, FileName->Buffer, Length ); + Name[ Length / sizeof(WCHAR) ] = L'\0'; + + Fcb->FullFileName.Length = (USHORT)Length; + Fcb->FullFileName.MaximumLength = (USHORT)Length + 2; + Fcb->FullFileName.Buffer = &Name[0]; + + Fcb->LastFileName.Length = (USHORT)Length - 2; + Fcb->LastFileName.MaximumLength = (USHORT)Length + 2 - 2; + 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 ); + } + UnwindPrefix = TRUE; + + // + // Set the configuration and pipe type + // + + Fcb->Specific.Fcb.NamedPipeConfiguration = NamedPipeConfiguration; + Fcb->Specific.Fcb.NamedPipeType = NamedPipeType; + + } finally { + + // + // If this is an abnormal unwind then undo our work + // + + if (AbnormalTermination()) { + + ULONG i; + + if (UnwindPrefix) { RtlRemoveUnicodePrefix( &NpVcb->PrefixTable, &Fcb->PrefixTableEntry ); } + if (UnwindEntryList) { RemoveEntryList( &Fcb->ParentDcbLinks ); } + + for (i = 0; i < 2; i += 1) { + if (UnwindStorage[i] != NULL) { ExFreePool( UnwindStorage[i] ); } + } + } + + DebugTrace(-1, Dbg, "NpCreateFcb -> %08lx\n", Fcb); + } + + // + // return and tell the caller + // + + return Fcb; +} + + +VOID +NpDeleteFcb ( + IN PFCB Fcb + ) + +/*++ + +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 + +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 ); + } + + // + // 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) { + + SeDeassignSecurity( &Fcb->SecurityDescriptor ); + } + + // + // Remove the entry from the prefix table, and then remove the full + // file name + // + + RtlRemoveUnicodePrefix( &NpVcb->PrefixTable, &Fcb->PrefixTableEntry ); + ExFreePool( Fcb->FullFileName.Buffer ); + + // + // Finally deallocate the Fcb record + // + + ExFreePool( Fcb ); + + // + // Check for any outstanding notify irps + // + + NpCheckForNotify( ParentDcb, TRUE ); + + // + // and return to our caller + // + + DebugTrace(-1, Dbg, "NpDeleteFcb -> VOID\n", 0); + + return; +} + + +PCCB +NpCreateCcb ( + IN PFCB Fcb, + IN PFILE_OBJECT ServerFileObject, + IN NAMED_PIPE_STATE NamedPipeState, + IN READ_MODE ServerReadMode, + IN COMPLETION_MODE ServerCompletionMode, + IN PEPROCESS CreatorProcess, + IN ULONG InBoundQuota, + IN ULONG OutBoundQuota + ) + +/*++ + +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; + + // + // The following variables are used for abnormal unwinding + // + + PVOID UnwindStorage[2] = { NULL, NULL }; + BOOLEAN UnwindEntryList = FALSE; + BOOLEAN UnwindDataQueue[2] = { FALSE, FALSE }; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpCreateCcb\n", 0); + + try { + + // + // Allocate a new CCB record (paged and nonpaged), and zero them out + // + + Ccb = UnwindStorage[0] = FsRtlAllocatePool( PagedPool, sizeof(CCB) ); + + RtlZeroMemory( Ccb, sizeof(CCB) ); + + Ccb->NonpagedCcb = UnwindStorage[1] = FsRtlAllocatePool( NonPagedPool, sizeof(NONPAGED_CCB) ); + + RtlZeroMemory( Ccb->NonpagedCcb, sizeof(NONPAGED_CCB) ); + + // + // Set the proper node type code and node byte size + // + + Ccb->NodeTypeCode = NPFS_NTC_CCB; + Ccb->NodeByteSize = sizeof(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 ); + UnwindEntryList = TRUE; + Ccb->Fcb = Fcb; + + Fcb->OpenCount += 1; + Fcb->ServerOpenCount += 1; + NpVcb->OpenUnderlingCount += 1; + + // + // 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; + Ccb->NonpagedCcb->NodeByteSize = sizeof(NONPAGED_CCB); + + // + // Set the pipe state, read mode, completion mode, and creator process + // + + Ccb->NamedPipeState = NamedPipeState; + Ccb->ReadMode[ FILE_PIPE_SERVER_END ] = ServerReadMode; + Ccb->CompletionMode[ FILE_PIPE_SERVER_END ] = ServerCompletionMode; + Ccb->CreatorProcess = CreatorProcess; + + // + // Initialize the data queues + // + + NpInitializeDataQueue( &Ccb->DataQueue[ FILE_PIPE_INBOUND ], + CreatorProcess, + InBoundQuota ); + UnwindDataQueue[ FILE_PIPE_INBOUND ] = TRUE; + + NpInitializeDataQueue( &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ], + CreatorProcess, + OutBoundQuota ); + UnwindDataQueue[ FILE_PIPE_OUTBOUND ] = TRUE; + + // + // Initialize the listening queue + // + + InitializeListHead( &Ccb->NonpagedCcb->ListeningQueue ); + + ExInitializeResource(&Ccb->NonpagedCcb->Resource); + + } finally { + + // + // If this is an abnormal termination then undo our work + // + + if (AbnormalTermination()) { + + ULONG i; + + if (UnwindEntryList) { RemoveEntryList( &Ccb->CcbLinks ); } + if (UnwindDataQueue[FILE_PIPE_INBOUND]) { NpUninitializeDataQueue( &Ccb->DataQueue[FILE_PIPE_INBOUND], CreatorProcess ); } + if (UnwindDataQueue[FILE_PIPE_OUTBOUND]) { NpUninitializeDataQueue( &Ccb->DataQueue[FILE_PIPE_OUTBOUND], CreatorProcess ); } + + for (i = 0; i < 2; i += 1) { + if (UnwindStorage[i] != NULL) { ExFreePool( UnwindStorage[i] ); } + } + } + + DebugTrace(-1, Dbg, "NpCreateCcb -> %08lx\n", Ccb); + } + + // + // return and tell the caller + // + + return Ccb; +} + + +PROOT_DCB_CCB +NpCreateRootDcbCcb ( + ) + +/*++ + +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 = FsRtlAllocatePool( PagedPool, sizeof(ROOT_DCB_CCB) ); + + RtlZeroMemory( Ccb, sizeof(ROOT_DCB_CCB) ); + + // + // Set the proper node type code and node byte size + // + + Ccb->NodeTypeCode = NPFS_NTC_ROOT_DCB_CCB; + Ccb->NodeByteSize = sizeof(ROOT_DCB_CCB); + + // + // return and tell the caller + // + + DebugTrace(-1, Dbg, "NpCreateRootDcbCcb -> %08lx\n", Ccb); + + return Ccb; +} + + +VOID +NpDeleteCcb ( + IN PCCB Ccb + ) + +/*++ + +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 + +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 ); + NpVcb->OpenUnderlingCount -= 1; + 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 ], + Ccb->CreatorProcess ); + + NpUninitializeDataQueue( &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ], + Ccb->CreatorProcess ); + + // + // Check for any outstanding notify irps + // + + NpCheckForNotify( Ccb->Fcb->ParentDcb, FALSE ); + + // + // Delete the resource + // + ExDeleteResource(&Ccb->NonpagedCcb->Resource); + + // + // Free up the security fields in the ccb and then free the nonpaged + // ccb + // + + NpUninitializeSecurity( Ccb ); + ExFreePool( Ccb->NonpagedCcb ); + + break; + + case NPFS_NTC_ROOT_DCB_CCB: + + if (((PROOT_DCB_CCB)Ccb)->QueryTemplate != NULL) { + + ExFreePool( ((PROOT_DCB_CCB)Ccb)->QueryTemplate ); + } + break; + } + + // Deallocate the Ccb record + // + + ExFreePool( Ccb ); + + // + // return and tell the caller + // + + DebugTrace(-1, Dbg, "NpDeleteCcb -> VOID\n", 0); + + return; +} diff --git a/private/ntos/npfs/volinfo.c b/private/ntos/npfs/volinfo.c new file mode 100644 index 000000000..b3010c1d2 --- /dev/null +++ b/private/ntos/npfs/volinfo.c @@ -0,0 +1,360 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + VolInfo.c + +Abstract: + + This module implements the volume information routines for NPFS called by + the dispatch driver. + +Author: + + Gary Kimura [GaryKi] 12-Apr-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_VOLINFO) + +// +// Local procedure prototypes +// + +NTSTATUS +NpCommonQueryVolumeInformation ( + IN PIRP Irp + ); + +NTSTATUS +NpQueryFsDeviceInfo ( + IN PFILE_FS_DEVICE_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +NpQueryFsAttributeInfo ( + IN PFILE_FS_ATTRIBUTE_INFORMATION Buffer, + IN OUT PULONG Length + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpCommonQueryVolumeInformation) +#pragma alloc_text(PAGE, NpFsdQueryVolumeInformation) +#pragma alloc_text(PAGE, NpQueryFsAttributeInfo) +#pragma alloc_text(PAGE, NpQueryFsDeviceInfo) +#endif + + +NTSTATUS +NpFsdQueryVolumeInformation ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the Fsd part of the NtQueryVolumeInformation API + call. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the file + being queried exists. + + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The FSD status for the Irp. + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpFsdQueryVolumeInformation\n", 0); + + // + // Call the common query routine, with blocking allowed if synchronous + // + + FsRtlEnterFileSystem(); + + try { + + Status = NpCommonQueryVolumeInformation( Irp ); + + } except(NpExceptionFilter( GetExceptionCode() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = NpProcessException( NpfsDeviceObject, Irp, GetExceptionCode() ); + } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpFsdQueryVolumeInformation -> %08lx\n", Status); + + return Status; +} + +// +// Internal support routine +// + +NTSTATUS +NpCommonQueryVolumeInformation ( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for querying volume information. + +Arguments: + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + ULONG Length; + FS_INFORMATION_CLASS FsInformationClass; + PVOID Buffer; + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NptCommonQueryVolumeInfo...\n", 0); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp ); + DebugTrace( 0, Dbg, "Length = %08lx\n", IrpSp->Parameters.QueryVolume.Length); + DebugTrace( 0, Dbg, "FsInformationClass = %08lx\n", IrpSp->Parameters.QueryVolume.FsInformationClass); + DebugTrace( 0, Dbg, "Buffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer); + + // + // Reference our input parameters to make things easier + // + + Length = IrpSp->Parameters.QueryVolume.Length; + FsInformationClass = IrpSp->Parameters.QueryVolume.FsInformationClass; + Buffer = Irp->AssociatedIrp.SystemBuffer; + + switch (FsInformationClass) { + + case FileFsDeviceInformation: + + Status = NpQueryFsDeviceInfo( Buffer, &Length ); + break; + + case FileFsAttributeInformation: + + Status = NpQueryFsAttributeInfo( Buffer, &Length ); + break; + + default: + + Status = STATUS_NOT_SUPPORTED; + break; + } + + // + // Set the information field to the number of bytes actually filled in + // + + Irp->IoStatus.Information = IrpSp->Parameters.QueryVolume.Length - Length; + + // + // Complete the request + + NpCompleteRequest( Irp, Status ); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpCommonQueryVolumeInformation -> %08lx\n", Status); + + return Status; +} + + +// +// Internal support routine +// + +NTSTATUS +NpQueryFsDeviceInfo ( + IN PFILE_FS_DEVICE_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query volume device call + +Arguments: + + Buffer - Supplies a pointer to the output buffer where the information + is to be returned + + Length - Supplies the length of the buffer in byte. This variable + upon return recieves the remaining bytes free in the buffer + +Return Value: + + Status - Returns the status for the query + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(0, Dbg, "NpQueryFsDeviceInfo...\n", 0); + + // + // Make sure the buffer is large enough + // + + if (*Length < sizeof(FILE_FS_DEVICE_INFORMATION)) { + + return STATUS_BUFFER_OVERFLOW; + } + + RtlZeroMemory( Buffer, sizeof(FILE_FS_DEVICE_INFORMATION) ); + + // + // Set the output buffer + // + + Buffer->DeviceType = FILE_DEVICE_NAMED_PIPE; + + // + // Adjust the length variable + // + + *Length -= sizeof(FILE_FS_DEVICE_INFORMATION); + + // + // And return success to our caller + // + + return STATUS_SUCCESS; +} + + +// +// Internal support routine +// + +NTSTATUS +NpQueryFsAttributeInfo ( + IN PFILE_FS_ATTRIBUTE_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query volume attribute call + +Arguments: + + Buffer - Supplies a pointer to the output buffer where the information + is to be returned + + Length - Supplies the length of the buffer in byte. This variable + upon return recieves the remaining bytes free in the buffer + +Return Value: + + Status - Returns the status for the query + +--*/ + +{ + ULONG BytesToCopy; + + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(0, Dbg, "NpQueryFsAttributeInfo...\n", 0); + + // + // Determine how much of the file system name will fit. + // + + if ( (*Length - FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, + FileSystemName[0] )) >= 8 ) { + + BytesToCopy = 8; + *Length -= FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, + FileSystemName[0] ) + 8; + Status = STATUS_SUCCESS; + + } else { + + BytesToCopy = *Length - FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, + FileSystemName[0]); + *Length = 0; + + Status = STATUS_BUFFER_OVERFLOW; + } + + // + // Set the output buffer + // + + Buffer->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES; + Buffer->MaximumComponentNameLength = MAXULONG; + Buffer->FileSystemNameLength = BytesToCopy; + + RtlCopyMemory( &Buffer->FileSystemName[0], L"NPFS", BytesToCopy ); + + // + // And return success to our caller + // + + return Status; +} diff --git a/private/ntos/npfs/waitsup.c b/private/ntos/npfs/waitsup.c new file mode 100644 index 000000000..fa03790e2 --- /dev/null +++ b/private/ntos/npfs/waitsup.c @@ -0,0 +1,626 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + WaitSup.c + +Abstract: + + This module implements the Wait for Named Pipe support routines. + +Author: + + Gary Kimura [GaryKi] 30-Aug-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_WAITSUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpInitializeWaitQueue) +#pragma alloc_text(PAGE, NpUninitializeWaitQueue) +#endif + + +// +// Local procedures and structures +// + +typedef struct _WAIT_CONTEXT { + KDPC Dpc; + KTIMER Timer; + PWAIT_QUEUE WaitQueue; +} WAIT_CONTEXT; +typedef WAIT_CONTEXT *PWAIT_CONTEXT; + +VOID +NpTimerDispatch( + IN PKDPC Dpc, + IN PVOID Contxt, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +VOID +NpCancelWaitQueueIrp( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + + +VOID +NpInitializeWaitQueue ( + IN PWAIT_QUEUE WaitQueue + ) + +/*++ + +Routine Description: + + This routine initializes the wait for named pipe queue. + +Arguments: + + WaitQueue - Supplies a pointer to the list head being initialized + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpInitializeWaitQueue, WaitQueue = %08lx\n", WaitQueue); + + // + // Initialize the List head + // + + InitializeListHead( &WaitQueue->Queue ); + + // + // Initialize the Wait Queue's spinlock + // + + KeInitializeSpinLock( &WaitQueue->SpinLock ); + + // + // and return to our caller + // + + DebugTrace(-1, Dbg, "NpInitializeWaitQueue -> VOID\n", 0); + + return; +} + + +VOID +NpUninitializeWaitQueue ( + IN PWAIT_QUEUE WaitQueue + ) + +/*++ + +Routine Description: + + This routine uninitializes the wait for named pipe queue. + +Arguments: + + WaitQueue - Supplies a pointer to the list head being uninitialized + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpInitializeWaitQueue, WaitQueue = %08lx\n", WaitQueue); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpInitializeWaitQueue -> VOID\n", 0); + + return; +} + + +VOID +NpAddWaiter ( + IN PWAIT_QUEUE WaitQueue, + IN LARGE_INTEGER DefaultTimeOut, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine adds a new "wait for named pipe" IRP to the wait queue. + After calling this function the caller nolonger can access the IRP + +Arguments: + + WaitQueue - Supplies the wait queue being used + + DefaultTimeOut - Supplies the default time out to use if one is + not supplied in the Irp + + Irp - Supplies a pointer to the wait Irp + +Return Value: + + None. + +--*/ + +{ + KIRQL OldIrql; + PWAIT_CONTEXT Context; + PFILE_PIPE_WAIT_FOR_BUFFER WaitForBuffer; + LARGE_INTEGER Timeout; + ULONG i; + + DebugTrace(+1, Dbg, "NpAddWaiter, WaitQueue = %08lx\n", WaitQueue); + + // + // Allocate a dpc and timer structure and initialize them + // + + Context = FsRtlAllocatePool( NonPagedPool, sizeof(WAIT_CONTEXT) ); + + KeInitializeDpc( &Context->Dpc, NpTimerDispatch, Irp ); + + KeInitializeTimer( &Context->Timer ); + + Context->WaitQueue = WaitQueue; + + // + // Have the information of the irp point to the context buffer + // + + Irp->IoStatus.Information = (ULONG)Context; + + // + // Figure out our timeout value + // + + WaitForBuffer = (PFILE_PIPE_WAIT_FOR_BUFFER)Irp->AssociatedIrp.SystemBuffer; + + if (WaitForBuffer->TimeoutSpecified) { + + Timeout = WaitForBuffer->Timeout; + + } else { + + Timeout = DefaultTimeOut; + } + + // + // Upcase the name of the pipe we are waiting for + // + + for (i = 0; i < WaitForBuffer->NameLength/sizeof(WCHAR); i += 1) { + + WaitForBuffer->Name[i] = RtlUpcaseUnicodeChar(WaitForBuffer->Name[i]); + } + + // + // Acquire the spinlock + // + + KeAcquireSpinLock( &WaitQueue->SpinLock, &OldIrql ); + + try { + + // + // Now insert this new entry into the Wait Queue + // + + InsertTailList( &WaitQueue->Queue, &Irp->Tail.Overlay.ListEntry ); + + // + // And set the timer to go off + // + + (VOID)KeSetTimer( &Context->Timer, Timeout, &Context->Dpc ); + + // + // Now set the cancel routine for the irp and check if it has been cancelled. + // + + IoAcquireCancelSpinLock( &Irp->CancelIrql ); + Irp->IoStatus.Status = (ULONG)WaitQueue; + + if (Irp->Cancel) { + + NpCancelWaitQueueIrp( ((PVOID)0x1), Irp ); + + } else { + + IoSetCancelRoutine( Irp, NpCancelWaitQueueIrp ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + } + + } finally { + + // + // Release the spinlock + // + + KeReleaseSpinLock( &WaitQueue->SpinLock, OldIrql ); + } + + // + // And now return to our caller + // + + DebugTrace(-1, Dbg, "NpAddWaiter -> VOID\n", 0); + + return; +} + + +VOID +NpCancelWaiter ( + IN PWAIT_QUEUE WaitQueue, + IN PUNICODE_STRING NameOfPipe + ) + +/*++ + +Routine Description: + + This procedure cancels all waiters that are waiting for the named + pipe to reach the listening state. The corresponding IRPs are completed + with STATUS_SUCCESS. + +Arguments: + + WaitQueue - Supplies the wait queue being modified + + NameOfPipe - Supplies the name of the named pipe (device relative) + that has just reached the listening state. + +Return Value: + + None. + +--*/ + +{ + KIRQL OldIrql; + PLIST_ENTRY Links; + PIRP Irp; + PFILE_PIPE_WAIT_FOR_BUFFER WaitForBuffer; + PWAIT_CONTEXT Context; + ULONG i; + + UNICODE_STRING NonPagedNameOfPipe; + + DebugTrace(+1, Dbg, "NpCancelWaiter, WaitQueue = %08lx\n", WaitQueue); + + // + // Capture the name of pipe before we grab the spinlock, and upcase it + // + + NonPagedNameOfPipe.Buffer = FsRtlAllocatePool( NonPagedPool, NameOfPipe->Length ); + NonPagedNameOfPipe.Length = 0; + NonPagedNameOfPipe.MaximumLength = NameOfPipe->Length; + + (VOID) RtlUpcaseUnicodeString( &NonPagedNameOfPipe, NameOfPipe, FALSE ); + + // + // Acquire the spinlock + // + + KeAcquireSpinLock( &WaitQueue->SpinLock, &OldIrql ); + + try { + + // + // For each waiting irp check if the name matches + // + + for (Links = WaitQueue->Queue.Flink; + Links != &WaitQueue->Queue; + Links = Links->Flink) { + + Irp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry ); + WaitForBuffer = (PFILE_PIPE_WAIT_FOR_BUFFER)Irp->AssociatedIrp.SystemBuffer; + Context = (PWAIT_CONTEXT)Irp->IoStatus.Information; + + // + // Check if this Irp matches the one we've been waiting for + // First check the lengths for equality, and then compare + // the strings. They match if we exit the inner loop with + // i >= name length. + // + + if (((USHORT)(WaitForBuffer->NameLength + sizeof(WCHAR))) == NonPagedNameOfPipe.Length) { + + for (i = 0; i < WaitForBuffer->NameLength/sizeof(WCHAR); i += 1) { + + if (WaitForBuffer->Name[i] != NonPagedNameOfPipe.Buffer[i+1]) { + + break; + } + } + + if (i >= WaitForBuffer->NameLength/sizeof(WCHAR)) { + + // + // We need to complete this irp so we first + // stop the timer, dequeue it from the wait queue + // (be sure to keep links correct), disable the cancel routine + // and then complete the Irp. + // + + if (KeCancelTimer( &Context->Timer )) { + + Links = Links->Blink; + + RemoveEntryList( &Irp->Tail.Overlay.ListEntry ); + + IoAcquireCancelSpinLock( &Irp->CancelIrql ); + IoSetCancelRoutine( Irp, NULL ); + Irp->IoStatus.Information = 0; + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + NpCompleteRequest( Irp, STATUS_SUCCESS ); + + ExFreePool( Context ); + } + } + } + } + + } finally { + + // + // Release the spinlock + // + + KeReleaseSpinLock( &WaitQueue->SpinLock, OldIrql ); + + ExFreePool( NonPagedNameOfPipe.Buffer ); + + DebugTrace(-1, Dbg, "NpCancelWaiter -> VOID\n", 0); + } + + // + // And now return to our caller + // + + return; +} + + +// +// Local support routine +// + +VOID +NpTimerDispatch( + IN PKDPC Dpc, + IN PVOID Contxt, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This routine is called whenever a timer on a wait queue Irp goes off + +Arguments: + + Dpc - Ignored + + Contxt - Supplies a pointer to the irp whose timer went off + + SystemArgument1 - Ignored + + SystemArgument2 - Ignored + +Return Value: + + none. + +--*/ + +{ + PIRP Irp = Contxt; + KIRQL OldIrql; + PLIST_ENTRY Links; + PWAIT_CONTEXT Context; + PWAIT_QUEUE WaitQueue; + + UNREFERENCED_PARAMETER( Dpc ); + UNREFERENCED_PARAMETER( SystemArgument1 ); + UNREFERENCED_PARAMETER( SystemArgument2 ); + + Context = (PWAIT_CONTEXT)Irp->IoStatus.Information; + WaitQueue = Context->WaitQueue; + + KeAcquireSpinLock( &WaitQueue->SpinLock, &OldIrql ); + + try { + + // + // Check if the Irp is still in the waiting queue. We need to do + // this because we might be in the middle of canceling the entry + // when the timer went off. + // + + for (Links = WaitQueue->Queue.Flink; + Links != &WaitQueue->Queue; + Links = Links->Flink) { + + if (Irp == CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry )) { + + // + // Remove the IRP, and complete it with a result of timeout + // + + RemoveEntryList( &Irp->Tail.Overlay.ListEntry ); + + NpCompleteRequest( Irp, STATUS_IO_TIMEOUT ); + + // + // Deallocate the context + // + + ExFreePool( Context ); + + // + // And exit from the loop because we found our match + // + + break; + } + } + + } finally { + + // + // Release the spinlock + // + + KeReleaseSpinLock( &WaitQueue->SpinLock, OldIrql ); + } + + // + // And now return to our caller + // + + return; +} + + +// +// Local Support routine +// + +VOID +NpCancelWaitQueueIrp( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called to cancel a wait queue irp + +Arguments: + + DeviceObject - Ignored + + Irp - Supplies the Irp being cancelled. The Iosb.Status field in the irp + points to the wait queue + +Return Value: + + none. + +--*/ + +{ + PWAIT_QUEUE WaitQueue; + KIRQL OldIrql; + PLIST_ENTRY Links; + + UNREFERENCED_PARAMETER( DeviceObject ); + + // + // The status field is used to store a pointer to the wait queue + // containing this irp + // + + WaitQueue = (PWAIT_QUEUE)Irp->IoStatus.Status; + + // + // We now need to void the cancel routine and release the io cancel spinlock + // + + IoSetCancelRoutine( Irp, NULL ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + // + // Get the spinlock proctecting the wait queue + // + + if (DeviceObject != (PVOID)0x1) { KeAcquireSpinLock( &WaitQueue->SpinLock, &OldIrql ); } + + try { + + // + // For each waiting irp check if it has been cancelled + // + + for (Links = WaitQueue->Queue.Flink; + Links != &WaitQueue->Queue; + Links = Links->Flink) { + + PIRP LocalIrp; + PWAIT_CONTEXT Context; + + LocalIrp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry ); + Context = (PWAIT_CONTEXT)LocalIrp->IoStatus.Information; + + if (LocalIrp->Cancel) { + + // + // We need to complete this irp so we first + // stop the timer, dequeue it from the wait queue + // (be sure to keep links correct), and then complete the Irp. + // + + if (KeCancelTimer( &Context->Timer )) { + + Links = Links->Blink; + + RemoveEntryList( &LocalIrp->Tail.Overlay.ListEntry ); + + LocalIrp->IoStatus.Information = 0; + + NpCompleteRequest( LocalIrp, STATUS_CANCELLED ); + ExFreePool( Context ); + } + } + } + + } finally { + + if (DeviceObject != (PVOID)0x1) { KeReleaseSpinLock( &WaitQueue->SpinLock, OldIrql ); } + } + + // + // And return to our caller + // + + return; + + + +} diff --git a/private/ntos/npfs/write.c b/private/ntos/npfs/write.c new file mode 100644 index 000000000..34356ee02 --- /dev/null +++ b/private/ntos/npfs/write.c @@ -0,0 +1,608 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + Write.c + +Abstract: + + This module implements the File Write routine for NPFS called by the + dispatch driver. + +Author: + + Gary Kimura [GaryKi] 21-Aug-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_WRITE) + +#if DBG +ULONG NpFastWriteTrue = 0; +ULONG NpFastWriteFalse = 0; +ULONG NpSlowWriteCalls = 0; +#endif + +// +// local procedure prototypes +// + +BOOLEAN +NpCommonWrite ( + IN PFILE_OBJECT FileObject, + IN PVOID WriteBuffer, + IN ULONG WriteLength, + IN PETHREAD UserThread, + OUT PIO_STATUS_BLOCK Iosb, + IN PIRP Irp OPTIONAL + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpCommonWrite) +#pragma alloc_text(PAGE, NpFastWrite) +#pragma alloc_text(PAGE, NpFsdWrite) +#endif + + +NTSTATUS +NpFsdWrite ( + IN PNPFS_DEVICE_OBJECT NpfsDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtWriteFile API calls. + +Arguments: + + NpfsDeviceObject - Supplies the device object to use. + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The Fsd status for the Irp + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + PIO_STACK_LOCATION IrpSp; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpFsdWrite\n", 0); + DbgDoit( NpSlowWriteCalls += 1 ); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + FsRtlEnterFileSystem(); + + NpAcquireSharedVcb(); + + try { + + (VOID) NpCommonWrite( IrpSp->FileObject, + Irp->UserBuffer, + IrpSp->Parameters.Write.Length, + Irp->Tail.Overlay.Thread, + &Iosb, + Irp ); + + } except(NpExceptionFilter( GetExceptionCode() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Iosb.Status = NpProcessException( NpfsDeviceObject, Irp, GetExceptionCode() ); + } + + NpReleaseVcb(); + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NpFsdWrite -> %08lx\n", Iosb.Status ); + + return Iosb.Status; +} + + +BOOLEAN +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 + ) + +/*++ + +Routine Description: + + This routine does a fast write bypassing the usual file system + entry routine (i.e., without the Irp). + +Arguments: + + FileObject - Pointer to the file object being read. + + FileOffset - Byte offset in file for desired data. + + Length - Length of desired data in bytes. + + Wait - FALSE if caller may not block, TRUE otherwise + + LockKey - Supplies the Key used to use if the byte range being read is locked. + + Buffer - Pointer to output buffer to which data should be copied. + + IoStatus - Pointer to standard I/O status block to receive the status + for the transfer. + +Return Value: + + BOOLEAN - TRUE if the operation completed successfully and FALSE if the + caller needs to take the long IRP based route. + +--*/ + +{ + BOOLEAN Results = FALSE; + UNREFERENCED_PARAMETER( FileOffset ); + UNREFERENCED_PARAMETER( Wait ); + UNREFERENCED_PARAMETER( LockKey ); + UNREFERENCED_PARAMETER( DeviceObject ); + + PAGED_CODE(); + + FsRtlEnterFileSystem(); + + NpAcquireSharedVcb(); + + try { + + if (NpCommonWrite( FileObject, + Buffer, + Length, + PsGetCurrentThread(), + IoStatus, + NULL )) { + + DbgDoit( NpFastWriteTrue += 1 ); + + if (IoStatus->Status == STATUS_PENDING) { + + IoStatus->Status = STATUS_SUCCESS; + } + + Results = TRUE; + + } else { + + DbgDoit( NpFastWriteFalse += 1 ); + } + + } except( FsRtlIsNtstatusExpected(GetExceptionCode()) + ? EXCEPTION_EXECUTE_HANDLER + : EXCEPTION_CONTINUE_SEARCH ) { + + NOTHING; + } + + NpReleaseVcb(); + FsRtlExitFileSystem(); + return Results; +} + + +// +// Internal support routine +// + +BOOLEAN +NpCommonWrite ( + IN PFILE_OBJECT FileObject, + IN PVOID WriteBuffer, + IN ULONG WriteLength, + IN PETHREAD UserThread, + OUT PIO_STATUS_BLOCK Iosb, + IN PIRP Irp OPTIONAL + ) + +/*++ + +Routine Description: + + This is the common routine for writing data to a named pipe both via the + fast path and with an Irp. + +Arguments: + + FileObject - Supplies the file object used in this operation + + WriteBuffer - Supplies the buffer where data from which data is to be read + + WriteLength - Supplies the length of the write buffer in bytes + + UserThread - Supplies the thread id of the caller + + Iosb - Receives the final completion status of this operation + + Irp - Optionally supplies an Irp to be used in this operation + +Return Value: + + NTSTATUS - the return status for the operation + +--*/ + +{ + NODE_TYPE_CODE NodeTypeCode; + PCCB Ccb; + PNONPAGED_CCB NonpagedCcb; + NAMED_PIPE_END NamedPipeEnd; + + NAMED_PIPE_CONFIGURATION NamedPipeConfiguration; + + ULONG WriteRemaining; + PDATA_QUEUE WriteQueue; + + PEVENT_TABLE_ENTRY Event; + READ_MODE ReadMode; + BOOLEAN Status; + + PDATA_ENTRY DataEntry; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpCommonWrite\n", 0); + DebugTrace( 0, Dbg, "FileObject = %08lx\n", FileObject); + DebugTrace( 0, Dbg, "WriteBuffer = %08lx\n", WriteBuffer); + DebugTrace( 0, Dbg, "WriteLength = %08lx\n", WriteLength); + DebugTrace( 0, Dbg, "UserThread = %08lx\n", UserThread); + DebugTrace( 0, Dbg, "Iosb = %08lx\n", Iosb); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); + + Iosb->Information = 0; + if (ARGUMENT_PRESENT(Irp)) { Irp->IoStatus.Information = 0; } + + // + // Get the Ccb and figure out who we are, and make sure we're not + // disconnected + // + + if ((NodeTypeCode = NpDecodeFileObject( FileObject, + NULL, + &Ccb, + &NamedPipeEnd )) == NTC_UNDEFINED) { + + DebugTrace(0, Dbg, "Pipe is disconnected from us\n", 0); + + Iosb->Status = STATUS_PIPE_DISCONNECTED; + if (ARGUMENT_PRESENT(Irp)) { NpCompleteRequest( Irp, STATUS_PIPE_DISCONNECTED ); } + + return TRUE; + } + + // + // Now we only will allow write operations on the pipe and not a directory + // or the device + // + + if (NodeTypeCode != NPFS_NTC_CCB) { + + DebugTrace(0, Dbg, "FileObject is not for a named pipe\n", 0); + + Iosb->Status = STATUS_INVALID_PARAMETER; + if (ARGUMENT_PRESENT(Irp)) { NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); } + + return TRUE; + } + + NpAcquireExclusiveCcb(Ccb); + + NonpagedCcb = Ccb->NonpagedCcb; + + try { + // + // Check if the pipe is not in the connected state. + // + + if ((Ccb->NamedPipeState == FILE_PIPE_DISCONNECTED_STATE) || + (Ccb->NamedPipeState == FILE_PIPE_LISTENING_STATE) || + (Ccb->NamedPipeState == FILE_PIPE_CLOSING_STATE)) { + + DebugTrace(0, Dbg, "Pipe in disconnected or listening or closing state\n", 0); + + if (Ccb->NamedPipeState == FILE_PIPE_DISCONNECTED_STATE) { + + Iosb->Status = STATUS_PIPE_DISCONNECTED; + + } else if (Ccb->NamedPipeState == FILE_PIPE_LISTENING_STATE) { + + Iosb->Status = STATUS_PIPE_LISTENING; + + } else { + + Iosb->Status = STATUS_PIPE_CLOSING; + } + + if (ARGUMENT_PRESENT(Irp)) { NpCompleteRequest( Irp, Iosb->Status ); } + + try_return(Status = TRUE); + } + + ASSERT(Ccb->NamedPipeState == FILE_PIPE_CONNECTED_STATE); + + // + // We only allow a write by the server on a non inbound only pipe + // and by the client on a non outbound only pipe + // + + NamedPipeConfiguration = Ccb->Fcb->Specific.Fcb.NamedPipeConfiguration; + + if (((NamedPipeEnd == FILE_PIPE_SERVER_END) && + (NamedPipeConfiguration == FILE_PIPE_INBOUND)) + + || + + ((NamedPipeEnd == FILE_PIPE_CLIENT_END) && + (NamedPipeConfiguration == FILE_PIPE_OUTBOUND))) { + + DebugTrace(0, Dbg, "Trying to write to the wrong pipe configuration\n", 0); + + Iosb->Status = STATUS_INVALID_PARAMETER; + if (ARGUMENT_PRESENT(Irp)) { NpCompleteRequest( Irp, STATUS_INVALID_PARAMETER ); } + + try_return(Status = TRUE); + } + + // + // Set up the amount of data we will have written by the time this + // operation gets completed and indicate success until we set it otherwise. + // + + Iosb->Status = STATUS_SUCCESS; + Iosb->Information = WriteLength; + if (ARGUMENT_PRESENT(Irp)) { Irp->IoStatus.Information = WriteLength; } + + // + // Now the data queue that we write into and the event that we signal + // are based on the named pipe end. The server writes to the outbound + // queue and signals the client event. The client does just the + // opposite. We also need to figure out the read mode for the opposite + // end of the pipe. + // + + if (NamedPipeEnd == FILE_PIPE_SERVER_END) { + + WriteQueue = &Ccb->DataQueue[ FILE_PIPE_OUTBOUND ]; + + Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_CLIENT_END ]; + ReadMode = Ccb->ReadMode[ FILE_PIPE_CLIENT_END ]; + + } else { + + WriteQueue = &Ccb->DataQueue[ FILE_PIPE_INBOUND ]; + + Event = NonpagedCcb->EventTableEntry[ FILE_PIPE_SERVER_END ]; + ReadMode = Ccb->ReadMode[ FILE_PIPE_SERVER_END ]; + } + + // + // The next section checks if we should continue with the write operation. + // The reasons why we will not continue are if we recongnize that the + // pipe quota will not support this write and it is a message mode type + // with complete operations. We will also bail out now if the quota will + // not support the write and this is a fast I/O write request. + // + // If the pipe contains readers and amount to read plus pipe quota is less + // than the write length then we need to do some additional checks. + // Or if pipe does not contain reads and the amount of quota left is less + // than the write length then we need to do some additional checks. + // + + if ((NpIsDataQueueReaders( WriteQueue ) && + (WriteQueue->BytesInQueue < WriteLength) && + (WriteQueue->Quota < (WriteLength + sizeof(DATA_ENTRY)))) + + || + + (!NpIsDataQueueReaders( WriteQueue ) && + ((WriteQueue->Quota - WriteQueue->QuotaUsed) < (WriteLength + sizeof(DATA_ENTRY))))) { + + DebugTrace(0, Dbg, "Quota is not sufficient for the request\n", 0); + + // + // If this is a message mode pipe with complete operations then we + // complete without writing the message + // + + if ((Ccb->Fcb->Specific.Fcb.NamedPipeType == FILE_PIPE_MESSAGE_TYPE) && + (Ccb->CompletionMode[NamedPipeEnd] == FILE_PIPE_COMPLETE_OPERATION)) { + + Iosb->Information = 0; + if (ARGUMENT_PRESENT(Irp)) { Irp->IoStatus = *Iosb; NpCompleteRequest( Irp, STATUS_SUCCESS ); } + + try_return(Status = TRUE); + } + + // + // If this is a fast I/O pipe then we tell the call to take the long + // Irp based route + // + + if (!ARGUMENT_PRESENT(Irp)) { + + DebugTrace(0, Dbg, "Need to supply Irp\n", 0); + + try_return(Status = FALSE); + } + } + + // + // Now we'll call our common write data queue routine to + // transfer data out of our write buffer into the data queue. + // If the result of the call is FALSE then we still have some + // write data to put into the write queue. + // + + if (!NpWriteDataQueue( WriteQueue, + ReadMode, + WriteBuffer, + WriteLength, + Ccb->Fcb->Specific.Fcb.NamedPipeType, + &WriteRemaining, + Ccb, + NamedPipeEnd, + UserThread )) { + + ASSERT( !NpIsDataQueueReaders( WriteQueue )); + + ASSERT((Ccb->Fcb->Specific.Fcb.NamedPipeType == FILE_PIPE_BYTE_STREAM_TYPE) || + (Ccb->CompletionMode[NamedPipeEnd] == FILE_PIPE_QUEUE_OPERATION) || + (WriteRemaining <= (WriteQueue->Quota - WriteQueue->QuotaUsed))); + + // + // Check if the operation is not to block and if so then we + // will complete the operation now with what we're written, if what is + // left will not fit in the quota for the file + // + + if ((Ccb->CompletionMode[NamedPipeEnd] == FILE_PIPE_COMPLETE_OPERATION) && + ((WriteQueue->Quota - WriteQueue->QuotaUsed) < (sizeof(DATA_ENTRY) + WriteLength))) { + + DebugTrace(0, Dbg, "Complete the byte stream write immediately\n", 0); + + Iosb->Information = WriteLength - WriteRemaining; + if (ARGUMENT_PRESENT(Irp)) { Irp->IoStatus = *Iosb; NpCompleteRequest( Irp, STATUS_SUCCESS ); } + + } else { + + DebugTrace(0, Dbg, "Add write to data queue\n", 0); + + // + // Add this write request to the write queue + // + + ASSERT( !NpIsDataQueueReaders( WriteQueue )); + + if (ARGUMENT_PRESENT(Irp)) { + + IoMarkIrpPending( Irp ); + + try { + + DataEntry = NpAddDataQueueEntry( WriteQueue, + WriteEntries, + Buffered, + WriteLength, + Irp, + WriteBuffer ); + + } finally { + + if (AbnormalTermination()) { + + IoGetCurrentIrpStackLocation((Irp))->Control &= ~SL_PENDING_RETURNED; + } + } + + } else { + + DataEntry = NpAddDataQueueEntry( WriteQueue, + WriteEntries, + Buffered, + WriteLength, + Irp, + WriteBuffer ); + } + + // + // And set the security part of the data entry + // + + NpSetDataEntryClientContext( NamedPipeEnd, + Ccb, + DataEntry, + UserThread ); + + // + // Now if the remaining length is not equal to the original + // write length then this must have been the first write entry + // into the data queue and we need to set the Next Byte + // field + // + + if (WriteLength > WriteRemaining) { + + WriteQueue->NextByteOffset = WriteLength - WriteRemaining; + } + + // + // Set our status for the write irp to pending + // + + Iosb->Status = STATUS_PENDING; + } + + } else { + + DebugTrace(0, Dbg, "Complete the Write Irp\n", 0); + + // + // The write irp is finished so we can complete it now + // + + if (ARGUMENT_PRESENT(Irp)) { Irp->IoStatus = *Iosb; NpCompleteRequest( Irp, STATUS_SUCCESS ); } + } + + // + // Now we need to advance the write queue to the next read irp to + // skip over flushes and closes + // + + //****NpWriteDataQueue does this for us**** (VOID)NpGetNextRealDataQueueEntry( WriteQueue ); + + // + // And because we've done something we need to signal the + // other ends event + // + + NpSignalEventTableEntry( Event ); + + Status = TRUE; + + try_exit: NOTHING; + } finally { + NpReleaseCcb(Ccb); + } + + + DebugTrace(-1, Dbg, "NpCommonWrite -> TRUE\n", 0); + return Status; +} diff --git a/private/ntos/npfs/writesup.c b/private/ntos/npfs/writesup.c new file mode 100644 index 000000000..5272b53fd --- /dev/null +++ b/private/ntos/npfs/writesup.c @@ -0,0 +1,329 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + WriteSup.c + +Abstract: + + This module implements the Write support routine. This is a common + write function that is called by write, unbuffered write, and transceive. + +Author: + + Gary Kimura [GaryKi] 21-Sep-1990 + +Revision History: + +--*/ + +#include "NpProcs.h" + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_WRITESUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NpWriteDataQueue) +#endif + + +BOOLEAN +NpWriteDataQueue ( + IN PDATA_QUEUE WriteQueue, + IN READ_MODE ReadMode, + IN PUCHAR WriteBuffer, + IN ULONG WriteLength, + IN NAMED_PIPE_TYPE PipeType, + OUT PULONG WriteRemaining, + IN PCCB Ccb, + IN NAMED_PIPE_END NamedPipeEnd, + IN PETHREAD UserThread + ) + +/*++ + +Routine Description: + + This procedure writes data from the write buffer into read entries in + the write queue. It will also dequeue entries in the queue as necessary. + +Arguments: + + WriteQueue - Provides the write queue to process. + + ReadMode - Supplies the read mode of read entries in the write queue. + + WriteBuffer - Provides the buffer from which to read the data. + + WriteLength - Provides the length, in bytes, of WriteBuffer. + + PipeType - Indicates if type of pipe (i.e., message or byte stream). + + WriteRemaining - Receives the number of bytes remaining to be transfered + that were not completed by this call. If the operation wrote + everything then is value is set to zero. + + Ccb - Supplies the ccb for the operation + + NamedPipeEnd - Supplies the end of the pipe doing the write + + UserThread - Supplies the user thread + +Return Value: + + BOOLEAN - TRUE if the operation wrote everything and FALSE otherwise. + Note that a zero byte message that hasn't been written will return + a function result of FALSE and WriteRemaining of zero. + +--*/ + +{ + BOOLEAN Result; + + BOOLEAN WriteZeroMessage; + + PDATA_ENTRY DataEntry; + + PUCHAR ReadBuffer; + ULONG ReadLength; + ULONG ReadRemaining; + + ULONG AmountToCopy; + + PIRP ReadIrp; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NpWriteDataQueue\n", 0); + DebugTrace( 0, Dbg, "WriteQueue = %08lx\n", WriteQueue); + DebugTrace( 0, Dbg, "WriteBuffer = %08lx\n", WriteBuffer); + DebugTrace( 0, Dbg, "WriteLength = %08lx\n", WriteLength); + DebugTrace( 0, Dbg, "PipeType = %08lx\n", PipeType); + DebugTrace( 0, Dbg, "Ccb = %08lx\n", Ccb); + DebugTrace( 0, Dbg, "NamedPipeEnd = %08lx\n", NamedPipeEnd); + DebugTrace( 0, Dbg, "UserThread = %08lx\n", UserThread); + + // + // Determine if we are to write a zero byte message, and initialize + // WriteRemaining + // + + *WriteRemaining = WriteLength; + + if ((PipeType == FILE_PIPE_MESSAGE_TYPE) && (WriteLength == 0)) { + + WriteZeroMessage = TRUE; + + } else { + + WriteZeroMessage = FALSE; + } + + // + // Now while the write queue has some read entries in it and + // there is some remaining write data or this is a write zero message + // then we'll do the following main loop + // + + for (DataEntry = NpGetNextRealDataQueueEntry( WriteQueue ); + + (NpIsDataQueueReaders(WriteQueue) && + ((*WriteRemaining > 0) || WriteZeroMessage)); + + DataEntry = NpGetNextRealDataQueueEntry( WriteQueue )) { + + ReadBuffer = DataEntry->DataPointer; + ReadLength = DataEntry->DataSize; + ReadRemaining = ReadLength - WriteQueue->NextByteOffset; + + DebugTrace(0, Dbg, "Top of main loop...\n", 0); + DebugTrace(0, Dbg, "ReadBuffer = %08lx\n", ReadBuffer); + DebugTrace(0, Dbg, "ReadLength = %08lx\n", ReadLength); + DebugTrace(0, Dbg, "ReadRemaining = %08lx\n", ReadRemaining); + DebugTrace(0, Dbg, "*WriteRemaining = %08lx\n", *WriteRemaining); + + // + // Check if this is a ReadOverflow Operation and if so then also check + // that the read will succeed otherwise complete this read with + // buffer overflow and continue on. + // + + { + PIO_STACK_LOCATION IrpSp; + + IrpSp = IoGetCurrentIrpStackLocation( DataEntry->Irp ); + + if (IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_PIPE_INTERNAL_READ_OVFLOW) { + + if ((ReadLength < WriteLength) || WriteZeroMessage) { + + ReadIrp = NpRemoveDataQueueEntry( WriteQueue ); + NpCompleteRequest( ReadIrp, STATUS_BUFFER_OVERFLOW ); + continue; + } + } + } + + // + // copy data from the write buffer at write offset to the + // read buffer at read offset by the mininum of write remaining + // or read remaining + // + + AmountToCopy = (*WriteRemaining < ReadRemaining ? *WriteRemaining + : ReadRemaining); + + try { + + RtlCopyMemory( &ReadBuffer[ ReadLength - ReadRemaining ], + &WriteBuffer[ WriteLength - *WriteRemaining ], + AmountToCopy ); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + + // + // Update the Read and Write remaining counts + // + + ReadRemaining -= AmountToCopy; + *WriteRemaining -= AmountToCopy; + + // + // Now update the security fields in the nonpaged ccb, we'll + // just use the two routines supplied in the security support + // routines + // + + if ((NamedPipeEnd == FILE_PIPE_CLIENT_END) && + (Ccb->SecurityQos.ContextTrackingMode == SECURITY_DYNAMIC_TRACKING)) { + + NTSTATUS Status; + + if (!NT_SUCCESS( Status = NpSetDataEntryClientContext( NamedPipeEnd, + Ccb, + DataEntry, + UserThread ))) { + + ExRaiseStatus( Status ); + } + + NpCopyClientContext( Ccb, DataEntry ); + + } else { + + DataEntry->SecurityClientContext = NULL; + } + + // + // Now we've done with the read entry so remove it from the + // write queue, get its irp, and fill in the information field + // to be the bytes that we've transferred into the read buffer. + // + + ReadIrp = NpRemoveDataQueueEntry( WriteQueue ); + + ASSERT( ReadIrp != NULL ); + + ReadIrp->IoStatus.Information = ReadLength - ReadRemaining; + + // + // Now we need to check if this is an internal (unbuffered) read + // operation and if so then we need to also update the allocation + // size stored in the Irp to be the bytes remaining in the write + // queue. We can decide if this is an internal operation by + // checking where the data entry would have been kept in the Irp. + // RemoveDataQueueEntry makes sure this field is set properly on its + // return. + // + + { + PDATA_ENTRY DataEntry; + + DataEntry = (PDATA_ENTRY)IoGetNextIrpStackLocation( ReadIrp ); + + if (DataEntry->DataEntryType == Unbuffered) { + + ReadIrp->Overlay.AllocationSize.QuadPart = WriteQueue->BytesInQueue; + } + } + + // + // Now if the write remaining is zero then we've completed + // both the write and read successfully. We'll complete the + // read irp at this time, and set write zero message to false + // to guarantee that we'll complete the write irp in the + // following if-statement after this main loop. + // + + if (*WriteRemaining == 0) { + + DebugTrace(0, Dbg, "Finished up the write remaining\n", 0); + + //**** ASSERT( ReadIrp->IoStatus.Information != 0 ); + + NpCompleteRequest( ReadIrp, STATUS_SUCCESS ); + + WriteZeroMessage = FALSE; + + } else { + + // + // There is still some space in the write buffer to be + // written out, but before we can handle that (in the + // following if statement) we need to finish the read. + // If the read is message mode then we've overflowed the + // buffer otherwise we completed successfully + // + + if (ReadMode == FILE_PIPE_MESSAGE_MODE) { + + DebugTrace(0, Dbg, "Read buffer Overflow\n", 0); + + NpCompleteRequest( ReadIrp, STATUS_BUFFER_OVERFLOW ); + + } else { + + DebugTrace(0, Dbg, "Read buffer byte stream done\n", 0); + + //**** ASSERT( ReadIrp->IoStatus.Information != 0 ); + + NpCompleteRequest( ReadIrp, STATUS_SUCCESS ); + } + } + } + + DebugTrace(0, Dbg, "Finished loop...\n", 0); + DebugTrace(0, Dbg, "*WriteRemaining = %08lx\n", *WriteRemaining); + DebugTrace(0, Dbg, "WriteZeroMessage = %08lx\n", WriteZeroMessage); + + // + // At this point we've finished off all of the read entries in the + // queue and we might still have something left to write. If that + // is the case then we'll set our result to FALSE otherwise we're + // done so we'll return TRUE. + // + + if ((*WriteRemaining > 0) || (WriteZeroMessage)) { + + ASSERT( !NpIsDataQueueReaders( WriteQueue )); + + Result = FALSE; + + } else { + + + Result = TRUE; + } + + DebugTrace(-1, Dbg, "NpWriteDataQueue -> %08lx\n", Result); + return Result; +} + |