Index: ntoskrnl/fsrtl/fsrtlpc.c =================================================================== --- ntoskrnl/fsrtl/fsrtlpc.c (revision 67882) +++ ntoskrnl/fsrtl/fsrtlpc.c (working copy) @@ -17,6 +17,7 @@ PERESOURCE FsRtlPagingIoResources; ULONG FsRtlPagingIoResourceSelector; NTSTATUS NTAPI INIT_FUNCTION FsRtlInitializeWorkerThread(VOID); +VOID NTAPI INIT_FUNCTION FsRtlInitializeTunnels(); static const UCHAR LegalAnsiCharacterArray[] = { @@ -172,6 +173,9 @@ FsRtlInitializeLargeMcbs(); + /* Initialize the tunnels */ + FsRtlInitializeTunnels(); + /* Allocate the Resource Buffer */ FsRtlPagingIoResources = FsRtlAllocatePoolWithTag(NonPagedPool, FSRTL_MAX_RESOURCES * Index: ntoskrnl/fsrtl/tunnel.c =================================================================== --- ntoskrnl/fsrtl/tunnel.c (revision 67882) +++ ntoskrnl/fsrtl/tunnel.c (working copy) @@ -12,6 +12,193 @@ #define NDEBUG #include +typedef struct { + RTL_SPLAY_LINKS SplayInfo; + LIST_ENTRY TimerQueueEntry; + ULONGLONG DirectoryKey; + LARGE_INTEGER Time; + ULONG Flags; + UNICODE_STRING ShortName; + UNICODE_STRING LongName; + PVOID Data; + ULONG DataLength; +}TUNNEL_NODE_ENTRY, *PTUNNEL_NODE_ENTRY; + +ULONG TunnelMaxEntries = 256; +ULONG TunnelMaxAge = 15; +PAGED_LOOKASIDE_LIST TunnelLookasideList; + +#define DEFAULT_EXTRA_SIZE (72) +#define DEFAULT_ENTRY_SIZE (sizeof(TUNNEL_NODE_ENTRY) + DEFAULT_EXTRA_SIZE) + +#define TUNNEL_FLAG_POOL 0x2 +#define TUNNEL_FLAG_KEY_SHORT_NAME 0x1 + +VOID +NTAPI +FsRtlGetTunnelParameterValue(IN PUNICODE_STRING ParameterName, + OUT PULONG Value) +{ + UNICODE_STRING Root = RTL_CONSTANT_STRING(L"Registry\\Machine\\System\\CurrentControlSet\\Control\\FileSystem"); + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE hKey; + NTSTATUS Status; + ULONG Length; + PKEY_VALUE_FULL_INFORMATION Info; + + /* initialize object attributes */ + InitializeObjectAttributes(&ObjectAttributes, &Root, OBJ_CASE_INSENSITIVE, NULL, NULL); + + /* open registry key */ + Status = ZwOpenKey(&hKey, KEY_READ, &ObjectAttributes); + + if (!NT_SUCCESS(Status)) + { + /* failed to open key */ + return; + } + + /* query value size */ + Status = ZwQueryValueKey(hKey, ParameterName, KeyValueFullInformation, NULL, 0, &Length); + + if (Status != STATUS_BUFFER_TOO_SMALL) + { + /* failed to query size */ + ZwClose(hKey); + return; + } + + /* allocate buffer */ + Info = ExAllocatePool(PagedPool, Length); + + if (!Info) + { + /* out of memory */ + ZwClose(hKey); + return; + } + + /* query value */ + Status = ZwQueryValueKey(hKey, ParameterName, KeyValueFullInformation, NULL, 0, &Length); + + if (NT_SUCCESS(Status)) + { + if (Info->DataLength) + { + /* store result */ + *Value = (ULONG)((ULONG_PTR)Info + Info->DataOffset); + } + } + + /* free buffer */ + ExFreePool(Info); + + /* close key */ + ZwClose(hKey); +} + + +VOID +NTAPI +FsRtlInitializeTunnels() +{ + ULONG TunnelEntries; + UNICODE_STRING MaximumTunnelEntryAgeInSeconds = RTL_CONSTANT_STRING(L"MaximumTunnelEntryAgeInSeconds"); + UNICODE_STRING MaximumTunnelEntries = RTL_CONSTANT_STRING( L"MaximumTunnelEntries"); + + /* check for nt */ + if (MmIsThisAnNtAsSystem()) + { + /* default */ + TunnelMaxEntries = 1024; + } + + /* check for custom override of max entries*/ + FsRtlGetTunnelParameterValue(&MaximumTunnelEntries, &TunnelMaxEntries); + + /* check for custom override of age*/ + FsRtlGetTunnelParameterValue(&MaximumTunnelEntryAgeInSeconds, &TunnelMaxAge); + + if (!TunnelMaxAge) + { + /* no age means no entries */ + TunnelMaxEntries = 0; + } + + /* get max entries */ + TunnelEntries = TunnelMaxEntries; + + /* convert to ticks */ + TunnelMaxAge *= 10000000; + + if(TunnelMaxEntries <= 65535) + { + /* use max 256 entries */ + TunnelEntries = TunnelMaxEntries / 16; + } + + if(!TunnelEntries && TunnelMaxEntries ) + { + /* max tunnel entries was too small */ + TunnelEntries = TunnelMaxEntries + 1; + } + + if (TunnelEntries > 0xFFFF) + { + /* max entries is 256 */ + TunnelEntries = 256; + } + + /* initialize look aside list */ + ExInitializePagedLookasideList(&TunnelLookasideList, NULL, NULL, 0, DEFAULT_ENTRY_SIZE, 0 /*FIXME*/, TunnelEntries); +} + +LONG +FsRtlCompareNodeAndKey( + IN PTUNNEL_NODE_ENTRY CurEntry, + IN ULONGLONG DirectoryKey, + IN PUNICODE_STRING KeyString) +{ + PUNICODE_STRING String; + LONG Ret; + + if (DirectoryKey > CurEntry->DirectoryKey) + { + Ret = 1; + } + else if (DirectoryKey < CurEntry->DirectoryKey) + { + Ret = -1; + } + else + { + if (CurEntry->Flags & TUNNEL_FLAG_KEY_SHORT_NAME) + { + /* use short name as key */ + String = &CurEntry->ShortName; + } + else + { + /* use long name as key */ + String = &CurEntry->LongName; + } + + Ret = RtlCompareUnicodeString(KeyString, String, TRUE); + } + + return Ret; +} + +VOID +FsRtlFreeTunnelNode( + IN PTUNNEL_NODE_ENTRY CurEntry) +{ + if (CurEntry->Flags & TUNNEL_FLAG_POOL) + ExFreePool(CurEntry); + else + ExFreeToPagedLookasideList(&TunnelLookasideList, CurEntry); +} + /* PUBLIC FUNCTIONS **********************************************************/ /*++ @@ -56,8 +243,218 @@ IN ULONG DataLength, IN PVOID Data) { - /* Unimplemented */ - KeBugCheck(FILE_SYSTEM); + PTUNNEL_NODE_ENTRY NodeEntry; + PRTL_SPLAY_LINKS CurEntry, LastEntry; + ULONG Length; + LONG Result = 0; + BOOLEAN AllocatedFromPool = FALSE; + PUNICODE_STRING KeyString; + + /* check if tunnel cache is enabled */ + if (!TunnelMaxEntries) + { + /* entries are disabled */ + return; + } + + /* calculate node length */ + Length = sizeof(TUNNEL_NODE_ENTRY); + + /* add data size */ + Length += DataLength; + + if (ShortName) + { + /* add short name length */ + Length += ShortName->Length; + } + + if (LongName) + { + /* add short name length */ + Length += LongName->Length; + } + + if (Length > DEFAULT_ENTRY_SIZE) + { + /* bigger than default entry */ + NodeEntry = ExAllocatePool(NonPagedPool, Length); + AllocatedFromPool = TRUE; + } + else + { + /* get standard entry */ + NodeEntry = ExAllocateFromPagedLookasideList(&TunnelLookasideList); + } + + /* check for success */ + if (!NodeEntry) + { + /* out of memory */ + return; + } + + /* acquire lock */ + ExAcquireFastMutex(&Cache->Mutex); + + /* now search cache for existing entries */ + CurEntry = Cache->Cache; + + /* check which key should be used for search */ + KeyString = (KeyByShortName ? ShortName : LongName); + + /* initialize last entry */ + LastEntry = NULL; + + while(CurEntry) + { + /* compare current node */ + Result = FsRtlCompareNodeAndKey((PTUNNEL_NODE_ENTRY)CurEntry, DirectoryKey, KeyString); + + /* backup last entry */ + LastEntry = CurEntry; + + if (Result > 0) + { + /* current directory key is bigger */ + CurEntry = CurEntry->LeftChild; + } + else + { + if (Result == 0) + { + /* found equal entry */ + break; + } + + /* current directory key is smaller */ + CurEntry = CurEntry->RightChild; + } + } + + /* initialize node entry */ + RtlInitializeSplayLinks(&NodeEntry->SplayInfo); + + if (CurEntry != NULL) + { + /* found existing item */ + NodeEntry->SplayInfo.LeftChild = (struct _RTL_SPLAY_LINKS*)CurEntry->LeftChild; + NodeEntry->SplayInfo.RightChild = (struct _RTL_SPLAY_LINKS*)CurEntry->RightChild; + + if (CurEntry->LeftChild) + { + /* update parent */ + CurEntry->LeftChild->Parent = (struct _RTL_SPLAY_LINKS*)NodeEntry; + } + + if (CurEntry->RightChild) + { + /* update parent */ + CurEntry->RightChild->Parent = (struct _RTL_SPLAY_LINKS*)NodeEntry; + } + + if (CurEntry->Parent == CurEntry) + { + /* cur entry was root */ + Cache->Cache = (struct _RTL_SPLAY_LINKS*)NodeEntry; + } + else + { + /* update parent */ + NodeEntry->SplayInfo.Parent = CurEntry->Parent; + + /* update parent node */ + if (LastEntry->LeftChild == CurEntry) + LastEntry->LeftChild = (struct _RTL_SPLAY_LINKS*)NodeEntry; + else + LastEntry->RightChild = (struct _RTL_SPLAY_LINKS*)NodeEntry; + } + + /* remove entry */ + RemoveEntryList(&((PTUNNEL_NODE_ENTRY)LastEntry)->TimerQueueEntry); + + /* free node entry */ + FsRtlFreeTunnelNode((PTUNNEL_NODE_ENTRY)LastEntry); + + /* decrement node count */ + Cache->NumEntries--; + } + else + { + if (LastEntry == NULL) + { + /* first entry in tunnel cache */ + Cache->Cache = (struct _RTL_SPLAY_LINKS*)NodeEntry; + } + else + { + if (Result > 0) + { + /* new left node */ + LastEntry->LeftChild = (struct _RTL_SPLAY_LINKS*)NodeEntry; + NodeEntry->SplayInfo.Parent = LastEntry; + } + else + { + /* new right node */ + LastEntry->RightChild = (struct _RTL_SPLAY_LINKS*)NodeEntry; + NodeEntry->SplayInfo.Parent = LastEntry; + } + } + } + + /* initialize entry */ + KeQuerySystemTime(&NodeEntry->Time); + + NodeEntry->DirectoryKey = DirectoryKey; + NodeEntry->Flags = (AllocatedFromPool ? TUNNEL_FLAG_POOL : 0x0); + NodeEntry->Flags |= (KeyByShortName ? TUNNEL_FLAG_KEY_SHORT_NAME : 0x0); + + if (ShortName) + { + /* copy short name */ + NodeEntry->ShortName.Length = ShortName->Length; + NodeEntry->ShortName.MaximumLength = ShortName->Length; + NodeEntry->ShortName.Buffer = (LPWSTR)((ULONG_PTR)NodeEntry + sizeof(TUNNEL_NODE_ENTRY)); + + RtlMoveMemory(NodeEntry->ShortName.Buffer, ShortName->Buffer, ShortName->Length); + } + else + { + NodeEntry->ShortName.Length = NodeEntry->ShortName.MaximumLength = 0; + NodeEntry->ShortName.Buffer = NULL; + } + + if (LongName) + { + /* copy long name */ + NodeEntry->LongName.Length = LongName->Length; + NodeEntry->LongName.MaximumLength = LongName->Length; + NodeEntry->LongName.Buffer = (LPWSTR)((ULONG_PTR)NodeEntry + sizeof(TUNNEL_NODE_ENTRY) + NodeEntry->ShortName.Length); + + RtlMoveMemory(NodeEntry->LongName.Buffer, LongName->Buffer, LongName->Length); + } + else + { + NodeEntry->LongName.Length = NodeEntry->LongName.MaximumLength = 0; + NodeEntry->LongName.Buffer = NULL; + } + + NodeEntry->DataLength = DataLength; + NodeEntry->Data = (PVOID)((ULONG_PTR)NodeEntry + sizeof(TUNNEL_NODE_ENTRY) + NodeEntry->ShortName.Length + NodeEntry->LongName.Length); + RtlMoveMemory(NodeEntry->Data, Data, DataLength); + + /* increment node count */ + Cache->NumEntries++; + + /* insert into list */ + InsertTailList(&Cache->TimerQueue, &NodeEntry->TimerQueueEntry); + + + /* TODO: remove expired entries */ + + /* release lock */ + ExReleaseFastMutex(&Cache->Mutex); } /*++ @@ -82,8 +479,17 @@ FsRtlDeleteKeyFromTunnelCache(IN PTUNNEL Cache, IN ULONGLONG DirectoryKey) { - /* Unimplemented */ - KeBugCheck(FILE_SYSTEM); + /* initialize mutex */ + ExInitializeFastMutex(&Cache->Mutex); + + /* initialize node tree */ + Cache->Cache = NULL; + + /* initialize timer list */ + InitializeListHead(&Cache->TimerQueue); + + /* initialize node count */ + Cache->NumEntries = 0; } /*++ @@ -149,13 +555,100 @@ IN OUT PULONG DataLength, OUT PVOID Data) { - /* Unimplemented */ - KeBugCheck(FILE_SYSTEM); - return FALSE; + BOOLEAN Ret = FALSE; + PTUNNEL_NODE_ENTRY CurEntry; + NTSTATUS Status = STATUS_SUCCESS; + LONG Result; + + /* check if tunnel cache is enabled */ + if (!TunnelMaxEntries) + { + /* entries are disabled */ + return FALSE; + } + + /* acquire tunnel lock */ + ExAcquireFastMutex(&Cache->Mutex); + + /* now search cache for existing entries */ + CurEntry = (PTUNNEL_NODE_ENTRY)Cache->Cache; + + while(CurEntry) + { + /* compare current node */ + Result = FsRtlCompareNodeAndKey(CurEntry, DirectoryKey, Name); + + if (Result > 0) + { + /* current directory key is bigger */ + CurEntry = (PTUNNEL_NODE_ENTRY)CurEntry->SplayInfo.LeftChild; + } + else + { + if (Result == 0) + { + /* found equal entry */ + break; + } + + /* current directory key is smaller */ + CurEntry = (PTUNNEL_NODE_ENTRY)CurEntry->SplayInfo.RightChild; + } + } + + if (CurEntry != NULL) + { + _SEH2_TRY + { + /* copy short name */ + RtlCopyUnicodeString(ShortName, &CurEntry->ShortName); + + /* check size */ + if (LongName->MaximumLength < CurEntry->LongName.Length) + { + /* buffer is too small */ + LongName->Buffer = ExAllocatePool(PagedPool, CurEntry->LongName.Length); + if (LongName->Buffer) + { + LongName->Length = CurEntry->LongName.Length; + LongName->MaximumLength = CurEntry->LongName.MaximumLength; + RtlMoveMemory(LongName->Buffer, CurEntry->LongName.Buffer, CurEntry->LongName.Length); + } + } + else + { + /* buffer is big enough */ + RtlCopyUnicodeString(LongName, &CurEntry->LongName); + } + + /* copy data */ + RtlMoveMemory(Data, CurEntry->Data, CurEntry->DataLength); + + /* store size */ + *DataLength = CurEntry->DataLength; + + /* done */ + Ret = TRUE; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + /* Get the status */ + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + + /* FIXME handle error */ + ASSERT(Status == STATUS_SUCCESS); + } + + /* release tunnel lock */ + ExReleaseFastMutex(&Cache->Mutex); + + return Ret; } /*++ - * @name FsRtlDeleteTunnelCache + * @name FsRtlInitializeTunnelCache * @unimplemented * * FILLME