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
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;
|
|
}
|
|
|