Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1349 lines
36 KiB

/*
Cache handling functions for use in kernel32.dll
VadimB
*/
#include "sdbp.h"
#define _APPHELP_CACHE_INIT_
#include "ahcache.h"
#include <safeboot.h>
NTSTATUS
ApphelpCacheControlValidateParameters(
IN PAHCACHESERVICEDATA pServiceData,
OUT PUNICODE_STRING pFileName,
OUT HANDLE* pFileHandle
);
NTSYSCALLAPI
NTSTATUS
NtApphelpCacheControl(
IN APPHELPCACHESERVICECLASS Service,
IN OUT PVOID ServiceData
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, ApphelpCacheInitialize) // INIT ?
#pragma alloc_text(PAGE, ApphelpDuplicateUnicodeString)
#pragma alloc_text(PAGE, ApphelpFreeUnicodeString)
#pragma alloc_text(PAGE, ApphelpCacheQueryFileInformation)
#pragma alloc_text(PAGE, ApphelpCacheCompareEntries)
#pragma alloc_text(PAGE, ApphelpAVLTableAllocate)
#pragma alloc_text(PAGE, ApphelpAVLTableFree)
#pragma alloc_text(PAGE, _ApphelpCacheFreeEntry)
#pragma alloc_text(PAGE, _ApphelpCacheDeleteEntry)
#pragma alloc_text(PAGE, ApphelpCacheRemoveEntry)
#pragma alloc_text(PAGE, ApphelpCacheInsertEntry)
#pragma alloc_text(PAGE, ApphelpCacheLookupEntry)
#pragma alloc_text(PAGE, ApphelpCacheParseBuffer)
#pragma alloc_text(PAGE, ApphelpCacheCreateBuffer)
#pragma alloc_text(PAGE, ApphelpCacheWrite)
#pragma alloc_text(PAGE, ApphelpCacheRead)
#pragma alloc_text(PAGE, ApphelpCacheVerifyContext)
#pragma alloc_text(PAGE, ApphelpCacheReleaseLock)
#pragma alloc_text(PAGE, ApphelpCacheLockExclusive)
#pragma alloc_text(PAGE, ApphelpCacheLockExclusiveNoWait)
#pragma alloc_text(PAGE, ApphelpCacheFlush)
#pragma alloc_text(PAGE, ApphelpCacheControlValidateParameters)
#pragma alloc_text(PAGE, ApphelpCacheShutdown)
#pragma alloc_text(PAGE, ApphelpCacheDump)
#pragma alloc_text(PAGE, NtApphelpCacheControl)
#endif // ALLOC_PRAGMA
#define APPCOMPAT_CACHE_KEY_NAME \
L"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager\\AppCompatCache"
#define APPCOMPAT_CACHE_VALUE_NAME \
L"AppCompatCache"
static UNICODE_STRING AppcompatKeyPathLayers =
RTL_CONSTANT_STRING(L"\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers");
static UNICODE_STRING AppcompatKeyPathCustom =
RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Custom\\");
//
// The default cache timeout. This timeout affects the maximum delay
// that we can incur due to congestion for the shared mutex.
//
#define SHIM_CACHE_TIMEOUT 100
//
// Cache entries as they are stored in the registry
//
typedef struct tagSTOREDCACHEENTRY {
UNICODE_STRING FileName; // length, maximum length and buffer that is an offset
LONGLONG FileTime;
LONGLONG FileSize;
} STOREDCACHEENTRY, *PSTOREDCACHEENTRY;
typedef struct tagSTOREDCACHEHEADER {
DWORD dwMagic; // cache identifier
DWORD dwCount; // entry count
} STOREDCACHEHEADER, *PSTOREDCACHEHEADER;
//
// Global cache data
//
typedef struct tagSHIMCACHEHEADER {
RTL_AVL_TABLE Table; // cache
LIST_ENTRY ListHead; // cache nodes, lru list
} SHIMCACHEHEADER, *PSHIMCACHEHEADER;
SHIMCACHEHEADER g_ShimCache; // global header
ERESOURCE g_SharedLock;
BOOL g_bCacheEnabled = FALSE;
//
// Magic DWORD that allows us to validate the cache quickly
// we do not however limit validation to this dword check
// and we are prepared for data being completely invalid
//
#define SHIM_CACHE_MAGIC_NEW 0xBADC0FFE
//
// Maximum number of cache entries
//
#define MAX_SHIM_CACHE_ENTRIES 0x200
///////////////////////////////////////////////////////////////////////////////////////
//
// Utility functions
//
//
///////////////////////////////////////////////////////////////////////////////////////
NTSTATUS
ApphelpDuplicateUnicodeString(
PUNICODE_STRING pStrDest,
PUNICODE_STRING pStrSrc
)
{
USHORT Length = pStrSrc->Length;
if (Length == 0) {
pStrDest->Length =
pStrDest->MaximumLength = 0;
pStrDest->Buffer = NULL;
return STATUS_SUCCESS;
}
pStrDest->MaximumLength = Length + sizeof(UNICODE_NULL);
pStrDest->Buffer = (PWCHAR)SdbAlloc(pStrDest->MaximumLength);
if (pStrDest->Buffer == NULL) {
return STATUS_NO_MEMORY;
}
RtlCopyMemory(pStrDest->Buffer, pStrSrc->Buffer, Length);
*(pStrDest->Buffer + Length/sizeof(WCHAR)) = L'\0';
pStrDest->Length = Length;
return STATUS_SUCCESS;
}
VOID
ApphelpFreeUnicodeString(
PUNICODE_STRING pStr
)
{
if (pStr != NULL) {
if (pStr->Buffer != NULL) {
// #if DBG
RtlFillMemory(pStr->Buffer, pStr->MaximumLength, 'B');
// #endif
SdbFree(pStr->Buffer);
}
RtlZeroMemory(pStr, sizeof(*pStr));
}
}
NTSTATUS
ApphelpCacheQueryFileInformation(
HANDLE FileHandle,
PLONGLONG pFileSize,
PLONGLONG pFileTime
)
/*++
Return: Status indicating success or failure
Desc: Queries for file size and timestamp.
--*/
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
FILE_BASIC_INFORMATION BasicFileInfo;
FILE_STANDARD_INFORMATION StdFileInfo;
LONGLONG FileTime;
Status = NtQueryInformationFile(FileHandle,
&IoStatusBlock,
&BasicFileInfo,
sizeof(BasicFileInfo),
FileBasicInformation);
if (!NT_SUCCESS(Status)) {
DBGPRINT((sdlError,
"ShimQueryFileInformation",
"NtQueryInformationFile/BasicInfo failed 0x%x\n",
Status));
goto Cleanup;
}
FileTime = BasicFileInfo.LastWriteTime.QuadPart;
Status = NtQueryInformationFile(FileHandle,
&IoStatusBlock,
&StdFileInfo,
sizeof(StdFileInfo),
FileStandardInformation);
if (!NT_SUCCESS(Status)) {
DBGPRINT((sdlError,
"ShimQueryFileInformation",
"NtQueryInformationFile/StdInfo failed 0x%x\n",
Status));
goto Cleanup;
}
*pFileSize = StdFileInfo.EndOfFile.QuadPart;
*pFileTime = FileTime;
Cleanup:
return Status;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Table handling routines
//
///////////////////////////////////////////////////////////////////////////////////
RTL_GENERIC_COMPARE_RESULTS
NTAPI
ApphelpCacheCompareEntries(
IN PRTL_AVL_TABLE pTable,
IN PVOID pFirstStruct,
IN PVOID pSecondStruct
)
{
PSHIMCACHEENTRY pFirstEntry = (PSHIMCACHEENTRY)pFirstStruct;
PSHIMCACHEENTRY pSecondEntry = (PSHIMCACHEENTRY)pSecondStruct;
LONG lResult;
UNREFERENCED_PARAMETER(pTable);
lResult = RtlCompareUnicodeString(&pFirstEntry->FileName,
&pSecondEntry->FileName,
TRUE);
if (lResult < 0) {
return GenericLessThan;
}
if (lResult > 0) {
return GenericGreaterThan;
}
// match
return GenericEqual;
}
PVOID
NTAPI
ApphelpAVLTableAllocate(
struct _RTL_AVL_TABLE *Table,
CLONG ByteSize
)
{
UNREFERENCED_PARAMETER(Table);
return SdbAlloc(ByteSize);
}
VOID
NTAPI
ApphelpAVLTableFree(
struct _RTL_AVL_TABLE *Table,
PVOID pBuffer
)
{
UNREFERENCED_PARAMETER(Table);
if (pBuffer != NULL) {
SdbFree(pBuffer);
}
}
//////////////////////////////////////////////////////////////////////////////////////
//
// Delete cache entry - by filename or using a pointer
//
//////////////////////////////////////////////////////////////////////////////////////
NTSTATUS
_ApphelpCacheFreeEntry(
IN PSHIMCACHEENTRY pEntry
)
{
PWCHAR pBuffer;
BOOL bDeleted;
NTSTATUS Status = STATUS_NOT_FOUND;
ULONG BufferLength;
ASSERT(ExIsResourceAcquiredExclusiveLite(&g_SharedLock) == TRUE);
RemoveEntryList(&pEntry->ListEntry);
pBuffer = pEntry->FileName.Buffer;
BufferLength = pEntry->FileName.MaximumLength;
bDeleted = RtlDeleteElementGenericTableAvl(&g_ShimCache.Table,
pEntry);
if (bDeleted) {
// #if DBG
RtlFillMemory(pBuffer, BufferLength, 'C');
// #endif
SdbFree(pBuffer);
Status = STATUS_SUCCESS;
}
return Status;
}
//
// Delete Cache entry, no lock
//
//
NTSTATUS
_ApphelpCacheDeleteEntry(
IN PUNICODE_STRING pFileName
)
{
SHIMCACHEENTRY ShimCacheEntry;
PSHIMCACHEENTRY pEntryFound;
NTSTATUS Status;
ASSERT(ExIsResourceAcquiredExclusiveLite(&g_SharedLock) == TRUE);
ShimCacheEntry.FileName = *pFileName;
//
// First find the element and unlink it.
//
pEntryFound = RtlLookupElementGenericTableAvl(&g_ShimCache.Table,
&ShimCacheEntry);
if (pEntryFound) {
Status = _ApphelpCacheFreeEntry(pEntryFound);
} else {
Status = STATUS_NOT_FOUND;
}
return Status;
}
//
// Delete cache entry under the lock
//
NTSTATUS
ApphelpCacheRemoveEntry(
IN PUNICODE_STRING FileName
)
{
NTSTATUS Status;
Status = ApphelpCacheLockExclusive();
if (!NT_SUCCESS(Status)) {
return Status;
}
Status = _ApphelpCacheDeleteEntry(FileName);
ApphelpCacheReleaseLock();
return Status;
}
//////////////////////////////////////////////////////////////////////////////////////
//
// Update the cache by inserting a new entry
//
//////////////////////////////////////////////////////////////////////////////////////
//
// Uses Lock
//
NTSTATUS
ApphelpCacheInsertEntry(
IN PUNICODE_STRING pFileName,
IN HANDLE FileHandle
)
{
PSHIMCACHEENTRY pEntryFound;
SHIMCACHEENTRY ShimCacheEntry = { 0 };
PVOID pNodeOrParent;
TABLE_SEARCH_RESULT SearchResult;
NTSTATUS Status;
ULONG nElements;
LONGLONG FileTime = 0;
LONGLONG FileSize = 0;
Status = ApphelpCacheLockExclusive();
if (!NT_SUCCESS(Status)) {
return Status;
}
if (FileHandle != INVALID_HANDLE_VALUE) {
Status = ApphelpCacheQueryFileInformation(FileHandle, &FileSize, &FileTime);
if (!NT_SUCCESS(Status)) {
DBGPRINT((sdlError,
"ApphelpCacheInsertEntry",
"Failed to query file information for \"%ls\" status 0x%lx\n",
pFileName->Buffer,
Status));
goto Cleanup;
}
}
ShimCacheEntry.FileName = *pFileName; // note that we take it as-is for lookup
pEntryFound = (PSHIMCACHEENTRY)RtlLookupElementGenericTableFullAvl(&g_ShimCache.Table,
&ShimCacheEntry,
&pNodeOrParent,
&SearchResult);
if (SearchResult == TableFoundNode) {
//
// pEntryFound is valid and points to our node
//
//
// update the data
//
pEntryFound->FileTime = FileTime;
pEntryFound->FileSize = FileSize;
//
// update the lru index - exclude the element from it's place and insert
// at the front of the list
//
RemoveEntryList(&pEntryFound->ListEntry);
InsertHeadList(&g_ShimCache.ListHead, &pEntryFound->ListEntry);
Status = STATUS_SUCCESS;
goto Cleanup;
}
//
// allocate the entry
//
Status = ApphelpDuplicateUnicodeString(&ShimCacheEntry.FileName, pFileName);
if (!NT_SUCCESS(Status)) {
goto Cleanup;
}
ShimCacheEntry.FileTime = FileTime;
ShimCacheEntry.FileSize = FileSize;
pEntryFound = RtlInsertElementGenericTableFullAvl(&g_ShimCache.Table,
&ShimCacheEntry,
sizeof(ShimCacheEntry),
NULL,
pNodeOrParent,
SearchResult);
InsertHeadList(&g_ShimCache.ListHead, &pEntryFound->ListEntry);
nElements = RtlNumberGenericTableElementsAvl(&g_ShimCache.Table);
if (nElements > MAX_SHIM_CACHE_ENTRIES) {
//
// remove an element -- check for list being empty, just in case
//
pEntryFound = (PSHIMCACHEENTRY)RemoveTailList(&g_ShimCache.ListHead);
//
// remove this entry
//
Status = _ApphelpCacheFreeEntry(pEntryFound);
if (!NT_SUCCESS(Status)) {
DBGPRINT((sdlError,
"ApphelpCacheInsertEntry",
"Failed to remove cache entry\n"));
goto Cleanup;
}
}
Status = STATUS_SUCCESS;
Cleanup:
ApphelpCacheReleaseLock();
return Status;
}
//
// returns STATUS_SUCCESS if the entry was found in the cache
// Procedure is using locking mechanism
//
//
NTSTATUS
ApphelpCacheLookupEntry(
IN PUNICODE_STRING pFileName,
IN HANDLE FileHandle
)
{
PSHIMCACHEENTRY pEntryFound;
SHIMCACHEENTRY ShimCacheEntry;
NTSTATUS Status = STATUS_NOT_FOUND;
LONGLONG FileTime;
LONGLONG FileSize;
//
// lock for exclusive access yet we do not wait
//
Status = ApphelpCacheLockExclusiveNoWait();
if (!NT_SUCCESS(Status)) {
DBGPRINT((sdlInfo,
"ApphelpCacheLookupEntry",
"Failed to lock cache\n"));
return STATUS_NOT_FOUND;
}
ShimCacheEntry.FileName = *pFileName;
pEntryFound = RtlLookupElementGenericTableAvl(&g_ShimCache.Table,
&ShimCacheEntry);
if (pEntryFound == NULL) {
Status = STATUS_NOT_FOUND;
goto Cleanup;
}
if (FileHandle != INVALID_HANDLE_VALUE) {
Status = ApphelpCacheQueryFileInformation(FileHandle, &FileSize, &FileTime);
if (!NT_SUCCESS(Status) ||
pEntryFound->FileTime != FileTime ||
pEntryFound->FileSize != FileSize) {
//
// most likely the file is gone
//
Status = _ApphelpCacheDeleteEntry(pFileName);
if (!NT_SUCCESS(Status)) {
DBGPRINT((sdlWarning,
"ApphelpCacheLookupEntry",
"Entry \"%ls\" was found then disappeared 0x%lx\n",
pFileName->Buffer,
Status));
}
Status = STATUS_NOT_FOUND;
goto Cleanup;
}
}
//
// if we have not removed the entry -- move it to the head of the lru
//
RemoveEntryList(&pEntryFound->ListEntry);
InsertHeadList(&g_ShimCache.ListHead, &pEntryFound->ListEntry);
Status = STATUS_SUCCESS;
Cleanup:
ApphelpCacheReleaseLock();
return Status;
}
//
// Verify apphelp cache caller's context
//
//
NTSTATUS
ApphelpCacheVerifyContext(
VOID
)
{
//
// verify that the cache is good to operate on
//
KPROCESSOR_MODE PreviousMode;
NTSTATUS Status = STATUS_SUCCESS;
PreviousMode = ExGetPreviousMode();
if (PreviousMode != KernelMode) {
//
// Does the caller have "trusted computer base" privilge?
//
if (!SeSinglePrivilegeCheck(SeTcbPrivilege, UserMode)) {
DBGPRINT((sdlError, "ApphelpCacheVerifyContext", "Security check failed\n"));
Status = STATUS_ACCESS_DENIED;
}
}
return Status;
}
//////////////////////////////////////////////////////////////////////////////////////
//
// Cache persistance routines
//
//////////////////////////////////////////////////////////////////////////////////////
NTSTATUS
ApphelpCacheParseBuffer(
PVOID pBuffer,
ULONG lBufferSize
)
{
PSTOREDCACHEHEADER pStoredHeader;
PSTOREDCACHEENTRY pStoredEntry;
SHIMCACHEENTRY ShimCacheEntry;
ULONG nElement;
NTSTATUS Status;
PSHIMCACHEENTRY pCacheEntry;
PVOID pBufferEnd;
STOREDCACHEENTRY StoredEntry;
STOREDCACHEHEADER StoredHeader;
if (lBufferSize < sizeof(STOREDCACHEHEADER)) {
return STATUS_INVALID_PARAMETER;
}
pBufferEnd = (PVOID)((PBYTE)pBuffer + lBufferSize);
RtlCopyMemory(&StoredHeader, pBuffer, sizeof(StoredHeader));
if (StoredHeader.dwMagic != SHIM_CACHE_MAGIC_NEW) {
return STATUS_INVALID_PARAMETER;
}
pStoredHeader = (PSTOREDCACHEHEADER)pBuffer;
pStoredEntry = (PSTOREDCACHEENTRY)(pStoredHeader + 1);
for (nElement = 0; nElement < StoredHeader.dwCount; ++nElement, ++pStoredEntry) {
if ((ULONG_PTR)(pStoredEntry + 1) > (ULONG_PTR)pBufferEnd) {
//
// invalid data
//
break;
}
//
// once we determined that the entry has a valid size, copy it
//
RtlCopyMemory(&StoredEntry, pStoredEntry, sizeof(StoredEntry));
if ((ULONG_PTR)StoredEntry.FileName.Buffer >= (ULONG_PTR)lBufferSize) {
//
// also invalid entry -- offset is greater than the size of the buffer
//
break;
}
//
// fixup the buffer please
//
StoredEntry.FileName.Buffer = (PWCHAR)((ULONG_PTR)pBuffer +
(ULONG_PTR)StoredEntry.FileName.Buffer);
if (StoredEntry.FileName.Length > StoredEntry.FileName.MaximumLength) {
break;
}
if ((ULONG_PTR)(StoredEntry.FileName.Buffer +
StoredEntry.FileName.MaximumLength/sizeof(WCHAR)) > (ULONG_PTR)pBufferEnd) {
//
// invalid data
//
break;
}
Status = ApphelpDuplicateUnicodeString(&ShimCacheEntry.FileName,
&StoredEntry.FileName);
if (!NT_SUCCESS(Status)) {
return Status;
}
ShimCacheEntry.FileTime = StoredEntry.FileTime;
ShimCacheEntry.FileSize = StoredEntry.FileSize;
//
// insert the entry
//
pCacheEntry = (PSHIMCACHEENTRY)RtlInsertElementGenericTableAvl(&g_ShimCache.Table,
&ShimCacheEntry,
sizeof(ShimCacheEntry),
NULL);
if (pCacheEntry == NULL) {
//
// the entry has not been inserted
// clean it up now
//
ApphelpFreeUnicodeString(&ShimCacheEntry.FileName);
return STATUS_NO_MEMORY;
}
InsertTailList(&g_ShimCache.ListHead, &pCacheEntry->ListEntry);
}
return STATUS_SUCCESS;
}
NTSTATUS
ApphelpCacheCreateBuffer(
PVOID* ppBuffer,
PULONG pBufferSize
)
{
ULONG nElements = 0;
ULONG lBufferSize; // size of the buffer we need
PLIST_ENTRY pListEntry;
PSHIMCACHEENTRY pEntry;
PVOID pBuffer;
PSTOREDCACHEENTRY pStoredEntry;
PSTOREDCACHEHEADER pStoredHeader;
PWCHAR pStringBuffer;
//
// Calculate total size
//
lBufferSize = sizeof(STOREDCACHEHEADER);
pListEntry = g_ShimCache.ListHead.Flink;
while (pListEntry != &g_ShimCache.ListHead) {
pEntry = (PSHIMCACHEENTRY)pListEntry;
lBufferSize += sizeof(STOREDCACHEENTRY) +
pEntry->FileName.MaximumLength;
nElements++;
pListEntry = pListEntry->Flink;
}
lBufferSize = ROUND_UP_COUNT(lBufferSize, sizeof(ULONGLONG));
//
// once we have all the entries, allocate the cache
//
pBuffer = SdbAlloc(lBufferSize);
if (pBuffer == NULL) {
return STATUS_NO_MEMORY;
}
pStoredHeader = (PSTOREDCACHEHEADER)pBuffer;
pStoredHeader->dwMagic = SHIM_CACHE_MAGIC_NEW;
pStoredHeader->dwCount = nElements;
pStoredEntry = (PSTOREDCACHEENTRY)(pStoredHeader + 1);
pStringBuffer = (PWCHAR)((PBYTE)pBuffer + lBufferSize);
//
// flatten the data
//
pListEntry = g_ShimCache.ListHead.Flink;
while (pListEntry != &g_ShimCache.ListHead) {
pEntry = (PSHIMCACHEENTRY)pListEntry;
//
// store the entry first
//
pStoredEntry->FileTime = pEntry->FileTime;
pStoredEntry->FileSize = pEntry->FileSize;
//
// Filename is more interesting
//
pStoredEntry->FileName.Length = pEntry->FileName.Length;
pStoredEntry->FileName.MaximumLength = pEntry->FileName.MaximumLength;
//
// now the buffer
//
pStringBuffer = (PWCHAR)((PBYTE)pStringBuffer - pEntry->FileName.MaximumLength);
RtlCopyMemory(pStringBuffer, pEntry->FileName.Buffer, pEntry->FileName.MaximumLength);
//
// fixup the pointer
//
pStoredEntry->FileName.Buffer = (PWCHAR)((ULONG_PTR)pStringBuffer - (ULONG_PTR)pBuffer);
pListEntry = pListEntry->Flink;
pStoredEntry++;
}
//
// done with the buffer
//
*ppBuffer = pBuffer;
*pBufferSize = lBufferSize;
return STATUS_SUCCESS;
}
NTSTATUS
ApphelpCacheWrite(
VOID
)
{
static UNICODE_STRING ustrAppcompatCacheKeyName =
RTL_CONSTANT_STRING(APPCOMPAT_CACHE_KEY_NAME);
static OBJECT_ATTRIBUTES objaAppcompatCacheKeyName =
RTL_CONSTANT_OBJECT_ATTRIBUTES(&ustrAppcompatCacheKeyName, OBJ_CASE_INSENSITIVE);
static UNICODE_STRING ustrAppcompatCacheValueName =
RTL_CONSTANT_STRING(APPCOMPAT_CACHE_VALUE_NAME);
HANDLE hKey = NULL;
ULONG BufferSize = 0;
NTSTATUS Status;
ULONG CreateDisposition;
PVOID pBuffer = NULL;
Status = ApphelpCacheCreateBuffer(&pBuffer, &BufferSize);
if (!NT_SUCCESS(Status)) {
return Status;
}
Status = NtCreateKey(&hKey,
STANDARD_RIGHTS_WRITE |
KEY_QUERY_VALUE |
KEY_ENUMERATE_SUB_KEYS |
KEY_SET_VALUE |
KEY_CREATE_SUB_KEY,
&objaAppcompatCacheKeyName,
0,
NULL,
REG_OPTION_NON_VOLATILE,
&CreateDisposition);
if (!NT_SUCCESS(Status)) {
DBGPRINT((sdlError, "ApphelpCacheWrite", "Failed to create key 0x%lx\n", Status));
goto Cleanup;
}
Status = NtSetValueKey(hKey,
&ustrAppcompatCacheValueName,
0,
REG_BINARY,
pBuffer,
BufferSize);
if (!NT_SUCCESS(Status)) {
DBGPRINT((sdlError, "ApphelpCacheWrite", "Failed to create key 0x%lx\n", Status));
}
NtClose(hKey);
Cleanup:
if (pBuffer != NULL) {
SdbFree(pBuffer);
}
return Status;
}
NTSTATUS
ApphelpCacheRead(
VOID
)
{
//
static UNICODE_STRING ustrAppcompatCacheKeyName =
RTL_CONSTANT_STRING(APPCOMPAT_CACHE_KEY_NAME);
static OBJECT_ATTRIBUTES objaAppcompatCacheKeyName =
RTL_CONSTANT_OBJECT_ATTRIBUTES(&ustrAppcompatCacheKeyName, OBJ_CASE_INSENSITIVE);
static UNICODE_STRING ustrAppcompatCacheValueName =
RTL_CONSTANT_STRING(APPCOMPAT_CACHE_VALUE_NAME);
HANDLE KeyHandle = NULL;
KEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo;
PKEY_VALUE_PARTIAL_INFORMATION pKeyValueInfo = &KeyValuePartialInfo;
ULONG KeyValueLength = 0;
ULONG BufferSize = sizeof(KeyValuePartialInformation);
NTSTATUS Status;
Status = NtOpenKey(&KeyHandle,
KEY_QUERY_VALUE,
(POBJECT_ATTRIBUTES)&objaAppcompatCacheKeyName);
if (!NT_SUCCESS(Status)) {
return FALSE;
}
Status = NtQueryValueKey(KeyHandle,
&ustrAppcompatCacheValueName,
KeyValuePartialInformation,
pKeyValueInfo,
BufferSize,
&KeyValueLength);
if (!NT_SUCCESS(Status)) {
if (Status != STATUS_BUFFER_TOO_SMALL) {
DBGPRINT((sdlError,
"ApphelpCacheRead",
"NtQueryValueKey fails for \"%s\", status 0x%lx\n",
ustrAppcompatCacheValueName.Buffer,
Status));
goto Cleanup;
}
BufferSize = KeyValueLength;
pKeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)SdbAlloc(BufferSize);
if (pKeyValueInfo == NULL) {
DBGPRINT((sdlError,
"ApphelpCacheRead",
"Failed to allocate cache buffer 0x%lx bytes\n",
BufferSize));
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
Status = NtQueryValueKey(KeyHandle,
&ustrAppcompatCacheValueName,
KeyValuePartialInformation,
pKeyValueInfo,
BufferSize,
&KeyValueLength);
if (!NT_SUCCESS(Status)) {
DBGPRINT((sdlError,
"ApphelpCacheRead",
"NtQueryValueKey fails(2) for \"%s\", status 0x%lx\n",
ustrAppcompatCacheValueName.Buffer,
Status));
goto Cleanup;
}
}
if (pKeyValueInfo->Type != REG_BINARY) {
DBGPRINT((sdlError,
"ApphelpCacheRead",
"Bad value type for apphelp cache 0x%lx\n",
pKeyValueInfo->Type));
Status = STATUS_OBJECT_TYPE_MISMATCH;
goto Cleanup;
}
//
// Now go through the cache (flat part) and parse all the entries into the table.
// Make sure that we are able to parse old entries (after system upgrade).
//
Status = ApphelpCacheParseBuffer((PVOID)pKeyValueInfo->Data,
pKeyValueInfo->DataLength);
Cleanup:
if (pKeyValueInfo != &KeyValuePartialInfo) {
SdbFree(pKeyValueInfo);
}
if (KeyHandle != NULL) {
NtClose(KeyHandle);
}
return Status;
}
//////////////////////////////////////////////////////////////////////////////////////
//
// Cache Initialization routine
//
//////////////////////////////////////////////////////////////////////////////////////
NTSTATUS
ApphelpCacheInitialize(
IN PLOADER_PARAMETER_BLOCK pLoaderBlock,
IN ULONG BootPhase
)
{
NTSTATUS Status;
//
// Check for safe mode.
//
if (pLoaderBlock->LoadOptions != NULL &&
strstr(pLoaderBlock->LoadOptions, SAFEBOOT_LOAD_OPTION_A) != NULL) {
g_bCacheEnabled = FALSE;
return STATUS_SUCCESS;
}
//
// Create the cache lock.
//
Status = ExInitializeResourceLite(&g_SharedLock);
if (!NT_SUCCESS(Status)) {
DBGPRINT((sdlError,
"ApphelpCacheInitialize",
"Failed to initialize the cache lock\n",
Status));
g_bCacheEnabled = FALSE;
return Status;
}
RtlInitializeGenericTableAvl(&g_ShimCache.Table,
ApphelpCacheCompareEntries,
ApphelpAVLTableAllocate,
ApphelpAVLTableFree,
NULL);
InitializeListHead(&g_ShimCache.ListHead);
//
// Once we initialize - load the cache from the registry
//
Status = ApphelpCacheRead();
if (!NT_SUCCESS(Status)) {
DBGPRINT((sdlError,
"ApphelpCacheInitialize",
"Failed to retrieve apphelp cache 0x%lx\n",
Status));
}
g_bCacheEnabled = TRUE;
return STATUS_SUCCESS;
UNREFERENCED_PARAMETER(pLoaderBlock);
UNREFERENCED_PARAMETER(BootPhase);
}
NTSTATUS
ApphelpCacheShutdown(
IN ULONG ShutdownPhase
)
{
NTSTATUS Status = STATUS_SUCCESS;
if (g_bCacheEnabled) {
Status = ApphelpCacheWrite();
}
return Status;
UNREFERENCED_PARAMETER(ShutdownPhase);
}
//////////////////////////////////////////////////////////////////////////////////////
//
// Locking
//
//////////////////////////////////////////////////////////////////////////////////////
NTSTATUS
ApphelpCacheLockExclusiveNoWait(
VOID
)
{
BOOLEAN bLock;
NTSTATUS Status = STATUS_SUCCESS;
KeEnterCriticalRegion();
bLock = ExTryToAcquireResourceExclusiveLite(&g_SharedLock);
if (!bLock) {
KeLeaveCriticalRegion();
DBGPRINT((sdlWarning,
"ApphelpCacheLockExclusiveNoWait",
"Failed to lock apphelp cache\n"));
Status = STATUS_ACCESS_DENIED;
}
return Status;
}
NTSTATUS
ApphelpCacheLockExclusive(
VOID
)
{
BOOLEAN bLock;
KeEnterCriticalRegion();
bLock = ExAcquireResourceExclusiveLite(&g_SharedLock, TRUE);
ASSERT(bLock);
return STATUS_SUCCESS;
}
NTSTATUS
ApphelpCacheReleaseLock(
VOID
)
{
ExReleaseResourceLite(&g_SharedLock);
KeLeaveCriticalRegion();
return STATUS_SUCCESS;
}
//////////////////////////////////////////////////////////////////////////////////////
//
// Routine to flush the cache
//
//////////////////////////////////////////////////////////////////////////////////////
NTSTATUS
ApphelpCacheFlush(
VOID
)
{
PWCHAR pBuffer;
BOOL bDeleted;
NTSTATUS Status;
PSHIMCACHEENTRY pEntry;
ULONG BufferLength;
Status = ApphelpCacheLockExclusive();
if (!NT_SUCCESS(Status)) {
return Status;
}
//
// Enumerate the entries and remove them from the cache
//
for (pEntry = (PSHIMCACHEENTRY)RtlEnumerateGenericTableAvl(&g_ShimCache.Table, TRUE);
pEntry != NULL;
pEntry = (PSHIMCACHEENTRY)RtlEnumerateGenericTableAvl(&g_ShimCache.Table, TRUE)) {
pBuffer = pEntry->FileName.Buffer;
BufferLength = pEntry->FileName.MaximumLength;
RemoveEntryList(&pEntry->ListEntry);
bDeleted = RtlDeleteElementGenericTableAvl(&g_ShimCache.Table,
pEntry);
if (bDeleted) {
// #if DBG
RtlFillMemory(pBuffer, BufferLength, 'A');
// #endif
SdbFree(pBuffer);
} else {
DBGPRINT((sdlError,
"ApphelpCacheFlush",
"Failed to remove the entry from cache %ls\n",
pBuffer));
}
}
ASSERT(RtlIsGenericTableEmptyAvl(&g_ShimCache.Table));
ASSERT(IsListEmpty(&g_ShimCache.ListHead));
ApphelpCacheReleaseLock();
return STATUS_SUCCESS;
}
NTSTATUS
ApphelpCacheDump(
VOID
)
{
PLIST_ENTRY pListEntry;
int nElement = 0;
PSHIMCACHEENTRY pEntry;
NTSTATUS Status;
Status = ApphelpCacheLockExclusive();
if (!NT_SUCCESS(Status)) {
return Status;
}
DBGPRINT((sdlInfo, "ApphelpCacheDump", "(LRU) (Name)\n"));
pListEntry = g_ShimCache.ListHead.Flink;
while (pListEntry != &g_ShimCache.ListHead) {
pEntry = (PSHIMCACHEENTRY)pListEntry;
++nElement;
DBGPRINT((sdlInfo,
"ApphelpCacheDump",
"%2d. %ls\n",
nElement,
pEntry->FileName.Buffer));
pListEntry = pListEntry->Flink;
}
DBGPRINT((sdlInfo, "ApphelpCacheDump", "----\n"));
ApphelpCacheReleaseLock();
return STATUS_SUCCESS;
}
//////////////////////////////////////////////////////////////////////////////////////
//
// Control function calls
//
//////////////////////////////////////////////////////////////////////////////////////
NTSTATUS
ApphelpCacheControlValidateParameters(
IN PAHCACHESERVICEDATA pServiceData,
OUT PUNICODE_STRING pFileName,
OUT HANDLE* pFileHandle
)
{
NTSTATUS Status;
UNICODE_STRING FileName;
if (pServiceData == NULL) {
return STATUS_INVALID_PARAMETER;
}
RtlZeroMemory(pFileName, sizeof(*pFileName));
try {
ProbeForRead(pServiceData, sizeof(AHCACHESERVICEDATA), sizeof(ULONG));
*pFileHandle = pServiceData->FileHandle;
FileName = ProbeAndReadUnicodeString(&pServiceData->FileName);
ProbeForRead(FileName.Buffer, FileName.Length, sizeof(UCHAR));
Status = ApphelpDuplicateUnicodeString(pFileName, &FileName);
if (!NT_SUCCESS(Status)) {
return Status;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
ApphelpFreeUnicodeString(pFileName);
return GetExceptionCode();
}
return STATUS_SUCCESS;
}
NTSYSCALLAPI
NTSTATUS
NtApphelpCacheControl(
IN APPHELPCACHESERVICECLASS Service,
IN OUT PVOID ServiceData
)
{
UNICODE_STRING FileName = { 0 };
HANDLE FileHandle = INVALID_HANDLE_VALUE;
NTSTATUS Status;
PAHCACHESERVICEDATA pServiceData = (PAHCACHESERVICEDATA)ServiceData;
if (!g_bCacheEnabled) {
return STATUS_INVALID_PARAMETER;
}
//
// This is the parameter validation code
//
switch (Service) {
case ApphelpCacheServiceLookup:
//
// Lookup the entry specified in ServiceData.
// Potentially may remove an entry from the cache.
//
Status = ApphelpCacheControlValidateParameters(pServiceData, &FileName, &FileHandle);
if (!NT_SUCCESS(Status)) {
goto Done;
}
Status = ApphelpCacheLookupEntry(&FileName, FileHandle);
break;
case ApphelpCacheServiceUpdate:
Status = ApphelpCacheVerifyContext();
if (!NT_SUCCESS(Status)) {
goto Done;
}
Status = ApphelpCacheControlValidateParameters(pServiceData, &FileName, &FileHandle);
if (!NT_SUCCESS(Status)) {
goto Done;
}
Status = ApphelpCacheInsertEntry(&FileName, FileHandle);
break;
case ApphelpCacheServiceRemove:
Status = ApphelpCacheControlValidateParameters(pServiceData, &FileName, &FileHandle);
if (!NT_SUCCESS(Status)) {
goto Done;
}
Status = ApphelpCacheRemoveEntry(&FileName);
break;
case ApphelpCacheServiceFlush:
Status = ApphelpCacheFlush();
break;
case ApphelpCacheServiceDump:
#if DBG
Status = ApphelpCacheDump();
#else
Status = STATUS_SUCCESS;
#endif
break;
default:
Status = STATUS_INVALID_PARAMETER;
break;
}
Done:
ApphelpFreeUnicodeString(&FileName);
return Status;
}