Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379/* * PROJECT: ReactOS API Tests * LICENSE: LGPL - See COPYING.LIB in the top level directory * PURPOSE: Tests for SHIM engine caching. * PROGRAMMER: Mark Jansen */
#include "precomp.h"
#include <winsvc.h>#include <versionhelpers.h>
enum ServiceCommands{ RegisterShimCacheWithHandle = 128, RegisterShimCacheWithoutHandle = 129,};
static NTSTATUS (NTAPI *pNtApphelpCacheControl)(APPHELPCACHESERVICECLASS, PAPPHELP_CACHE_SERVICE_LOOKUP);
NTSTATUS CallCacheControl(UNICODE_STRING* PathName, BOOLEAN WithMapping, APPHELPCACHESERVICECLASS Service){ APPHELP_CACHE_SERVICE_LOOKUP CacheEntry = { {0} }; NTSTATUS Status; CacheEntry.ImageName = *PathName; if (WithMapping) { OBJECT_ATTRIBUTES LocalObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; InitializeObjectAttributes(&LocalObjectAttributes, PathName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenFile(&CacheEntry.ImageHandle, SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_READ_DATA | FILE_EXECUTE, &LocalObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE); ok_ntstatus(Status, STATUS_SUCCESS); } else { CacheEntry.ImageHandle = INVALID_HANDLE_VALUE; } Status = pNtApphelpCacheControl(Service, &CacheEntry); if (CacheEntry.ImageHandle != INVALID_HANDLE_VALUE) NtClose(CacheEntry.ImageHandle); return Status;}
int InitEnv(UNICODE_STRING* PathName){ NTSTATUS Status = CallCacheControl(PathName, FALSE, ApphelpCacheServiceRemove); if (Status == STATUS_INVALID_PARAMETER) { /* Windows Vista+ has a different layout for APPHELP_CACHE_SERVICE_LOOKUP */ return 0; } ok(Status == STATUS_SUCCESS || Status == STATUS_NOT_FOUND, "Wrong value for Status, expected: SUCCESS or NOT_FOUND, got: 0x%lx\n", Status); return 1;}
void CheckValidation(UNICODE_STRING* PathName){ APPHELP_CACHE_SERVICE_LOOKUP CacheEntry = { {0} }; NTSTATUS Status;
/* Validate the handling of a NULL pointer */ Status = pNtApphelpCacheControl(ApphelpCacheServiceRemove, NULL); ok_ntstatus(Status, STATUS_INVALID_PARAMETER); Status = pNtApphelpCacheControl(ApphelpCacheServiceLookup, NULL); ok_ntstatus(Status, STATUS_INVALID_PARAMETER);
/* Validate the handling of a NULL pointer inside the struct */ Status = pNtApphelpCacheControl(ApphelpCacheServiceRemove, &CacheEntry); ok_ntstatus(Status, STATUS_INVALID_PARAMETER); Status = pNtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry); ok_ntstatus(Status, STATUS_INVALID_PARAMETER);
/* Just call the dump function */ Status = pNtApphelpCacheControl(ApphelpCacheServiceDump, NULL); ok_ntstatus(Status, STATUS_SUCCESS);
/* Validate the handling of an invalid handle inside the struct */ CacheEntry.ImageName = *PathName; CacheEntry.ImageHandle = (HANDLE)2; Status = pNtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry); ok_ntstatus(Status, IsWindows7OrGreater() ? STATUS_NOT_FOUND : STATUS_INVALID_PARAMETER);
/* Validate the handling of an invalid service number */ Status = pNtApphelpCacheControl(999, NULL); ok_ntstatus(Status, STATUS_INVALID_PARAMETER); Status = pNtApphelpCacheControl(999, &CacheEntry); ok_ntstatus(Status, STATUS_INVALID_PARAMETER);}
static BOOLEAN RequestAddition(SC_HANDLE service_handle, BOOLEAN WithMapping){ SERVICE_STATUS Status; ControlService(service_handle, WithMapping ? RegisterShimCacheWithHandle : RegisterShimCacheWithoutHandle, &Status); /* TODO: how to get a return code from the service? */ return TRUE;}
static void RunApphelpCacheControlTests(SC_HANDLE service_handle){ WCHAR szPath[MAX_PATH]; UNICODE_STRING ntPath; BOOLEAN Result; NTSTATUS Status; APPHELP_CACHE_SERVICE_LOOKUP CacheEntry;
GetModuleFileNameW(NULL, szPath, sizeof(szPath) / sizeof(szPath[0])); Result = RtlDosPathNameToNtPathName_U(szPath, &ntPath, NULL, NULL); ok(Result == TRUE, "RtlDosPathNameToNtPathName_U\n"); if (!InitEnv(&ntPath)) { skip("NtApphelpCacheControl expects a different structure layout\n"); return; } /* At this point we have made sure that our binary is not present in the cache, and that the NtApphelpCacheControl function expects the struct layout we use. */ CheckValidation(&ntPath);
/* We expect not to find it */ Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup); ok_ntstatus(Status, STATUS_NOT_FOUND); Status = CallCacheControl(&ntPath, FALSE, ApphelpCacheServiceLookup); ok_ntstatus(Status, STATUS_NOT_FOUND);
/* First we add our process without a file handle (so it will be registered without file info) */ RequestAddition(service_handle, FALSE);
/* now we try to find it without validating file info */ Status = CallCacheControl(&ntPath, FALSE, ApphelpCacheServiceLookup); ok_ntstatus(Status, STATUS_SUCCESS); /* when validating file info the cache notices the file is wrong, so it is dropped from the cache */ Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup); ok_ntstatus(Status, STATUS_NOT_FOUND); /* making the second check without info also fail. */ Status = CallCacheControl(&ntPath, FALSE, ApphelpCacheServiceLookup); ok_ntstatus(Status, STATUS_NOT_FOUND);
/* Now we add the file with file info */ RequestAddition(service_handle, TRUE);
/* so both checks should succeed */ Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup); ok_ntstatus(Status, STATUS_SUCCESS); Status = CallCacheControl(&ntPath, FALSE, ApphelpCacheServiceLookup); ok_ntstatus(Status, STATUS_SUCCESS);
/* We know the file is in the cache now (assuming previous tests succeeded, let's test invalid handle behavior */ CacheEntry.ImageName = ntPath; CacheEntry.ImageHandle = 0; Status = pNtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry); ok_ntstatus(Status, IsWindows7OrGreater() ? STATUS_NOT_FOUND : STATUS_INVALID_PARAMETER);
/* re-add it for the next test */ RequestAddition(service_handle, TRUE); Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup); ok_ntstatus(Status, STATUS_SUCCESS); CacheEntry.ImageHandle = (HANDLE)1; Status = pNtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry); ok_ntstatus(Status, IsWindows7OrGreater() ? STATUS_NOT_FOUND : STATUS_INVALID_PARAMETER);
/* and again */ RequestAddition(service_handle, TRUE); Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup); ok_ntstatus(Status, STATUS_SUCCESS);#ifdef _WIN64 CacheEntry.ImageHandle = (HANDLE)0x8000000000000000ULL;#else CacheEntry.ImageHandle = (HANDLE)0x80000000;#endif Status = pNtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry); ok_ntstatus(Status, IsWindows7OrGreater() ? STATUS_NOT_FOUND : STATUS_INVALID_PARAMETER);
RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath.Buffer);}
/* Most service related code was taken from services_winetest:service and modified for usage here The rest came from MSDN */
static SERVICE_STATUS_HANDLE (WINAPI *pRegisterServiceCtrlHandlerExA)(LPCSTR,LPHANDLER_FUNCTION_EX,LPVOID);static char service_name[100] = "apphelp_test_service";static HANDLE service_stop_event;static SERVICE_STATUS_HANDLE service_status;
static BOOLEAN RegisterInShimCache(BOOLEAN WithMapping){ WCHAR szPath[MAX_PATH]; UNICODE_STRING ntPath; BOOLEAN Result; NTSTATUS Status; GetModuleFileNameW(NULL, szPath, sizeof(szPath) / sizeof(szPath[0])); Result = RtlDosPathNameToNtPathName_U(szPath, &ntPath, NULL, NULL); if (!Result) { DbgPrint("RegisterInShimCache: RtlDosPathNameToNtPathName_U failed\n"); return FALSE; }
Status = CallCacheControl(&ntPath, WithMapping, ApphelpCacheServiceUpdate); if (!NT_SUCCESS(Status)) { DbgPrint("RegisterInShimCache: CallCacheControl failed\n"); RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath.Buffer); return FALSE; } RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath.Buffer); return TRUE;}
static DWORD WINAPI service_handler(DWORD ctrl, DWORD event_type, void *event_data, void *context){ SERVICE_STATUS status = {0}; status.dwServiceType = SERVICE_WIN32; status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
switch(ctrl) { case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: status.dwCurrentState = SERVICE_STOP_PENDING; status.dwControlsAccepted = 0; SetServiceStatus(service_status, &status); SetEvent(service_stop_event); return NO_ERROR; case RegisterShimCacheWithHandle: if (!RegisterInShimCache(TRUE)) { /* TODO: how should we communicate a failure? */ } break; case RegisterShimCacheWithoutHandle: if (!RegisterInShimCache(FALSE)) { /* TODO: how should we communicate a failure? */ } break; default: DbgPrint("Unhandled: %d\n", ctrl); break; } status.dwCurrentState = SERVICE_RUNNING; SetServiceStatus(service_status, &status); return NO_ERROR;}
static void WINAPI service_main(DWORD argc, char **argv){ SERVICE_STATUS status = {0}; service_status = pRegisterServiceCtrlHandlerExA(service_name, service_handler, NULL); if(!service_status) return;
status.dwServiceType = SERVICE_WIN32; status.dwCurrentState = SERVICE_RUNNING; status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; SetServiceStatus(service_status, &status);
WaitForSingleObject(service_stop_event, INFINITE);
status.dwCurrentState = SERVICE_STOPPED; status.dwControlsAccepted = 0; SetServiceStatus(service_status, &status);}
static SC_HANDLE InstallService(SC_HANDLE scm_handle){ char service_cmd[MAX_PATH+150], *ptr; SC_HANDLE service;
ptr = service_cmd + GetModuleFileNameA(NULL, service_cmd, MAX_PATH); strcpy(ptr, " NtApphelpCacheControl service"); ptr += strlen(ptr);
service = CreateServiceA(scm_handle, service_name, service_name, GENERIC_ALL, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, service_cmd, NULL, NULL, NULL, NULL, NULL); if (!service) { skip("Could not create helper service\n"); return NULL; } return service;}
static void WaitService(SC_HANDLE service_handle, DWORD Status, SERVICE_STATUS_PROCESS* ssp){ DWORD dwBytesNeeded; DWORD dwStartTime = GetTickCount(); while (ssp->dwCurrentState != Status) { Sleep(40); if (!QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, (LPBYTE)ssp, sizeof(SERVICE_STATUS_PROCESS), &dwBytesNeeded )) { ok(0, "QueryServiceStatusEx failed waiting for %lu\n", Status); break; } if ((GetTickCount() - dwStartTime) > 1000) { ok(0, "Timeout waiting for (%lu) from service, is: %lu.\n", Status, ssp->dwCurrentState); break; } }}
static void RunTest(){ SC_HANDLE scm_handle = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS); SC_HANDLE service_handle = InstallService(scm_handle); if (service_handle) { SERVICE_STATUS_PROCESS ssp = {0}; BOOL res = StartServiceA(service_handle, 0, NULL); if (res) { WaitService(service_handle, SERVICE_RUNNING, &ssp); RunApphelpCacheControlTests(service_handle); ControlService(service_handle, SERVICE_CONTROL_STOP, (LPSERVICE_STATUS)&ssp); WaitService(service_handle, SERVICE_STOPPED, &ssp); } else { skip("Could not start helper service\n"); } DeleteService(service_handle); } CloseServiceHandle(scm_handle);}
START_TEST(NtApphelpCacheControl){ char **argv; int argc;
pRegisterServiceCtrlHandlerExA = (void*)GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegisterServiceCtrlHandlerExA"); if (!pRegisterServiceCtrlHandlerExA) { win_skip("RegisterServiceCtrlHandlerExA not available, skipping tests\n"); return; }
pNtApphelpCacheControl = (void*)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtApphelpCacheControl"); if (!pNtApphelpCacheControl) { win_skip("NtApphelpCacheControl not available, skipping tests\n"); return; }
argc = winetest_get_mainargs(&argv); if(argc < 3) { RunTest(); } else { SERVICE_TABLE_ENTRYA servtbl[] = { {service_name, service_main}, {NULL, NULL} }; service_stop_event = CreateEventA(NULL, TRUE, FALSE, NULL); StartServiceCtrlDispatcherA(servtbl); Sleep(50); CloseHandle(service_stop_event); }}