1 /* 2 * PROJECT: ReactOS API Tests 3 * LICENSE: LGPL - See COPYING.LIB in the top level directory 4 * PURPOSE: Tests for SHIM engine caching. 5 * PROGRAMMER: Mark Jansen 6 */ 7 8 #include "precomp.h" 9 10 #include <winsvc.h> 11 12 enum ServiceCommands 13 { 14 RegisterShimCacheWithHandle = 128, 15 RegisterShimCacheWithoutHandle = 129, 16 }; 17 18 static NTSTATUS (NTAPI *pNtApphelpCacheControl)(APPHELPCACHESERVICECLASS, PAPPHELP_CACHE_SERVICE_LOOKUP); 19 20 NTSTATUS CallCacheControl(UNICODE_STRING* PathName, BOOLEAN WithMapping, APPHELPCACHESERVICECLASS Service) 21 { 22 APPHELP_CACHE_SERVICE_LOOKUP CacheEntry = { {0} }; 23 NTSTATUS Status; 24 CacheEntry.ImageName = *PathName; 25 if (WithMapping) 26 { 27 OBJECT_ATTRIBUTES LocalObjectAttributes; 28 IO_STATUS_BLOCK IoStatusBlock; 29 InitializeObjectAttributes(&LocalObjectAttributes, PathName, 30 OBJ_CASE_INSENSITIVE, NULL, NULL); 31 Status = NtOpenFile(&CacheEntry.ImageHandle, 32 SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_READ_DATA | FILE_EXECUTE, 33 &LocalObjectAttributes, &IoStatusBlock, 34 FILE_SHARE_READ | FILE_SHARE_DELETE, 35 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE); 36 ok_ntstatus(Status, STATUS_SUCCESS); 37 } 38 else 39 { 40 CacheEntry.ImageHandle = INVALID_HANDLE_VALUE; 41 } 42 Status = pNtApphelpCacheControl(Service, &CacheEntry); 43 if (CacheEntry.ImageHandle != INVALID_HANDLE_VALUE) 44 NtClose(CacheEntry.ImageHandle); 45 return Status; 46 } 47 48 int InitEnv(UNICODE_STRING* PathName) 49 { 50 NTSTATUS Status = CallCacheControl(PathName, FALSE, ApphelpCacheServiceRemove); 51 if (Status == STATUS_INVALID_PARAMETER) 52 { 53 /* Windows Vista+ has a different layout for APPHELP_CACHE_SERVICE_LOOKUP */ 54 return 0; 55 } 56 ok(Status == STATUS_SUCCESS || Status == STATUS_NOT_FOUND, 57 "Wrong value for Status, expected: SUCCESS or NOT_FOUND, got: 0x%lx\n", 58 Status); 59 return 1; 60 } 61 62 void CheckValidation(UNICODE_STRING* PathName) 63 { 64 APPHELP_CACHE_SERVICE_LOOKUP CacheEntry = { {0} }; 65 NTSTATUS Status; 66 67 /* Validate the handling of a NULL pointer */ 68 Status = pNtApphelpCacheControl(ApphelpCacheServiceRemove, NULL); 69 ok_ntstatus(Status, STATUS_INVALID_PARAMETER); 70 Status = pNtApphelpCacheControl(ApphelpCacheServiceLookup, NULL); 71 ok_ntstatus(Status, STATUS_INVALID_PARAMETER); 72 73 /* Validate the handling of a NULL pointer inside the struct */ 74 Status = pNtApphelpCacheControl(ApphelpCacheServiceRemove, &CacheEntry); 75 ok_ntstatus(Status, STATUS_INVALID_PARAMETER); 76 Status = pNtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry); 77 ok_ntstatus(Status, STATUS_INVALID_PARAMETER); 78 79 /* Just call the dump function */ 80 Status = pNtApphelpCacheControl(ApphelpCacheServiceDump, NULL); 81 ok_ntstatus(Status, STATUS_SUCCESS); 82 83 /* Validate the handling of an invalid handle inside the struct */ 84 CacheEntry.ImageName = *PathName; 85 CacheEntry.ImageHandle = (HANDLE)2; 86 Status = pNtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry); 87 ok_ntstatus(Status, STATUS_NOT_FOUND); 88 89 /* Validate the handling of an invalid service number */ 90 Status = pNtApphelpCacheControl(999, NULL); 91 ok_ntstatus(Status, STATUS_INVALID_PARAMETER); 92 Status = pNtApphelpCacheControl(999, &CacheEntry); 93 ok_ntstatus(Status, STATUS_INVALID_PARAMETER); 94 } 95 96 static BOOLEAN RequestAddition(SC_HANDLE service_handle, BOOLEAN WithMapping) 97 { 98 SERVICE_STATUS Status; 99 ControlService(service_handle, WithMapping ? RegisterShimCacheWithHandle : 100 RegisterShimCacheWithoutHandle, &Status); 101 /* TODO: how to get a return code from the service? */ 102 return TRUE; 103 } 104 105 static void RunApphelpCacheControlTests(SC_HANDLE service_handle) 106 { 107 WCHAR szPath[MAX_PATH]; 108 UNICODE_STRING ntPath; 109 BOOLEAN Result; 110 NTSTATUS Status; 111 APPHELP_CACHE_SERVICE_LOOKUP CacheEntry; 112 113 GetModuleFileNameW(NULL, szPath, sizeof(szPath) / sizeof(szPath[0])); 114 Result = RtlDosPathNameToNtPathName_U(szPath, &ntPath, NULL, NULL); 115 ok(Result == TRUE, "RtlDosPathNameToNtPathName_U\n"); 116 if (!InitEnv(&ntPath)) 117 { 118 skip("NtApphelpCacheControl expects a different structure layout\n"); 119 return; 120 } 121 /* At this point we have made sure that our binary is not present in the cache, 122 and that the NtApphelpCacheControl function expects the struct layout we use. */ 123 CheckValidation(&ntPath); 124 125 /* We expect not to find it */ 126 Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup); 127 ok_ntstatus(Status, STATUS_NOT_FOUND); 128 Status = CallCacheControl(&ntPath, FALSE, ApphelpCacheServiceLookup); 129 ok_ntstatus(Status, STATUS_NOT_FOUND); 130 131 /* First we add our process without a file handle (so it will be registered without file info) */ 132 RequestAddition(service_handle, FALSE); 133 134 /* now we try to find it without validating file info */ 135 Status = CallCacheControl(&ntPath, FALSE, ApphelpCacheServiceLookup); 136 ok_ntstatus(Status, STATUS_SUCCESS); 137 /* when validating file info the cache notices the file is wrong, so it is dropped from the cache */ 138 Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup); 139 ok_ntstatus(Status, STATUS_NOT_FOUND); 140 /* making the second check without info also fail. */ 141 Status = CallCacheControl(&ntPath, FALSE, ApphelpCacheServiceLookup); 142 ok_ntstatus(Status, STATUS_NOT_FOUND); 143 144 145 /* Now we add the file with file info */ 146 RequestAddition(service_handle, TRUE); 147 148 /* so both checks should succeed */ 149 Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup); 150 ok_ntstatus(Status, STATUS_SUCCESS); 151 Status = CallCacheControl(&ntPath, FALSE, ApphelpCacheServiceLookup); 152 ok_ntstatus(Status, STATUS_SUCCESS); 153 154 /* We know the file is in the cache now (assuming previous tests succeeded, 155 let's test invalid handle behavior */ 156 CacheEntry.ImageName = ntPath; 157 CacheEntry.ImageHandle = 0; 158 Status = pNtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry); 159 ok_ntstatus(Status, STATUS_NOT_FOUND); 160 161 /* re-add it for the next test */ 162 RequestAddition(service_handle, TRUE); 163 Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup); 164 ok_ntstatus(Status, STATUS_SUCCESS); 165 CacheEntry.ImageHandle = (HANDLE)1; 166 Status = pNtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry); 167 ok_ntstatus(Status, STATUS_NOT_FOUND); 168 169 /* and again */ 170 RequestAddition(service_handle, TRUE); 171 Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup); 172 ok_ntstatus(Status, STATUS_SUCCESS); 173 CacheEntry.ImageHandle = (HANDLE)0x80000000; 174 Status = pNtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry); 175 ok_ntstatus(Status, STATUS_NOT_FOUND); 176 177 RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath.Buffer); 178 } 179 180 181 /* Most service related code was taken from services_winetest:service and modified for usage here 182 The rest came from MSDN */ 183 184 static SERVICE_STATUS_HANDLE (WINAPI *pRegisterServiceCtrlHandlerExA)(LPCSTR,LPHANDLER_FUNCTION_EX,LPVOID); 185 static char service_name[100] = "apphelp_test_service"; 186 static HANDLE service_stop_event; 187 static SERVICE_STATUS_HANDLE service_status; 188 189 static BOOLEAN RegisterInShimCache(BOOLEAN WithMapping) 190 { 191 WCHAR szPath[MAX_PATH]; 192 UNICODE_STRING ntPath; 193 BOOLEAN Result; 194 NTSTATUS Status; 195 GetModuleFileNameW(NULL, szPath, sizeof(szPath) / sizeof(szPath[0])); 196 Result = RtlDosPathNameToNtPathName_U(szPath, &ntPath, NULL, NULL); 197 if (!Result) 198 { 199 DbgPrint("RegisterInShimCache: RtlDosPathNameToNtPathName_U failed\n"); 200 return FALSE; 201 } 202 203 Status = CallCacheControl(&ntPath, WithMapping, ApphelpCacheServiceUpdate); 204 if (!NT_SUCCESS(Status)) 205 { 206 DbgPrint("RegisterInShimCache: CallCacheControl failed\n"); 207 RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath.Buffer); 208 return FALSE; 209 } 210 RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath.Buffer); 211 return TRUE; 212 } 213 214 215 static DWORD WINAPI service_handler(DWORD ctrl, DWORD event_type, void *event_data, void *context) 216 { 217 SERVICE_STATUS status = {0}; 218 status.dwServiceType = SERVICE_WIN32; 219 status.dwControlsAccepted = SERVICE_ACCEPT_STOP; 220 221 switch(ctrl) 222 { 223 case SERVICE_CONTROL_STOP: 224 case SERVICE_CONTROL_SHUTDOWN: 225 status.dwCurrentState = SERVICE_STOP_PENDING; 226 status.dwControlsAccepted = 0; 227 SetServiceStatus(service_status, &status); 228 SetEvent(service_stop_event); 229 return NO_ERROR; 230 case RegisterShimCacheWithHandle: 231 if (!RegisterInShimCache(TRUE)) 232 { 233 /* TODO: how should we communicate a failure? */ 234 } 235 break; 236 case RegisterShimCacheWithoutHandle: 237 if (!RegisterInShimCache(FALSE)) 238 { 239 /* TODO: how should we communicate a failure? */ 240 } 241 break; 242 default: 243 DbgPrint("Unhandled: %d\n", ctrl); 244 break; 245 } 246 status.dwCurrentState = SERVICE_RUNNING; 247 SetServiceStatus(service_status, &status); 248 return NO_ERROR; 249 } 250 251 static void WINAPI service_main(DWORD argc, char **argv) 252 { 253 SERVICE_STATUS status = {0}; 254 service_status = pRegisterServiceCtrlHandlerExA(service_name, service_handler, NULL); 255 if(!service_status) 256 return; 257 258 status.dwServiceType = SERVICE_WIN32; 259 status.dwCurrentState = SERVICE_RUNNING; 260 status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; 261 SetServiceStatus(service_status, &status); 262 263 WaitForSingleObject(service_stop_event, INFINITE); 264 265 status.dwCurrentState = SERVICE_STOPPED; 266 status.dwControlsAccepted = 0; 267 SetServiceStatus(service_status, &status); 268 } 269 270 static SC_HANDLE InstallService(SC_HANDLE scm_handle) 271 { 272 char service_cmd[MAX_PATH+150], *ptr; 273 SC_HANDLE service; 274 275 ptr = service_cmd + GetModuleFileNameA(NULL, service_cmd, MAX_PATH); 276 strcpy(ptr, " NtApphelpCacheControl service"); 277 ptr += strlen(ptr); 278 279 service = CreateServiceA(scm_handle, service_name, service_name, GENERIC_ALL, 280 SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, 281 service_cmd, NULL, NULL, NULL, NULL, NULL); 282 if (!service) 283 { 284 skip("Could not create helper service\n"); 285 return NULL; 286 } 287 return service; 288 } 289 290 static void WaitService(SC_HANDLE service_handle, DWORD Status, SERVICE_STATUS_PROCESS* ssp) 291 { 292 DWORD dwBytesNeeded; 293 DWORD dwStartTime = GetTickCount(); 294 while (ssp->dwCurrentState != Status) 295 { 296 Sleep(40); 297 if (!QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, 298 (LPBYTE)ssp, sizeof(SERVICE_STATUS_PROCESS), &dwBytesNeeded )) 299 { 300 ok(0, "QueryServiceStatusEx failed waiting for %lu\n", Status); 301 break; 302 } 303 if ((GetTickCount() - dwStartTime) > 1000) 304 { 305 ok(0, "Timeout waiting for (%lu) from service, is: %lu.\n", 306 Status, ssp->dwCurrentState); 307 break; 308 } 309 } 310 } 311 312 static void RunTest() 313 { 314 SC_HANDLE scm_handle = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS); 315 SC_HANDLE service_handle = InstallService(scm_handle); 316 if (service_handle) 317 { 318 SERVICE_STATUS_PROCESS ssp = {0}; 319 BOOL res = StartServiceA(service_handle, 0, NULL); 320 if (res) 321 { 322 WaitService(service_handle, SERVICE_RUNNING, &ssp); 323 RunApphelpCacheControlTests(service_handle); 324 ControlService(service_handle, SERVICE_CONTROL_STOP, (LPSERVICE_STATUS)&ssp); 325 WaitService(service_handle, SERVICE_STOPPED, &ssp); 326 } 327 else 328 { 329 skip("Could not start helper service\n"); 330 } 331 DeleteService(service_handle); 332 } 333 CloseServiceHandle(scm_handle); 334 } 335 336 START_TEST(NtApphelpCacheControl) 337 { 338 char **argv; 339 int argc; 340 341 pRegisterServiceCtrlHandlerExA = (void*)GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegisterServiceCtrlHandlerExA"); 342 if (!pRegisterServiceCtrlHandlerExA) 343 { 344 win_skip("RegisterServiceCtrlHandlerExA not available, skipping tests\n"); 345 return; 346 } 347 348 pNtApphelpCacheControl = (void*)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtApphelpCacheControl"); 349 if (!pNtApphelpCacheControl) 350 { 351 win_skip("NtApphelpCacheControl not available, skipping tests\n"); 352 return; 353 } 354 355 argc = winetest_get_mainargs(&argv); 356 if(argc < 3) 357 { 358 RunTest(); 359 } 360 else 361 { 362 SERVICE_TABLE_ENTRYA servtbl[] = { 363 {service_name, service_main}, 364 {NULL, NULL} 365 }; 366 service_stop_event = CreateEventA(NULL, TRUE, FALSE, NULL); 367 StartServiceCtrlDispatcherA(servtbl); 368 Sleep(50); 369 CloseHandle(service_stop_event); 370 } 371 } 372 373 374