[NTDLL_APITEST] Show that registry strings can be longer than MAXUSHORT.
[reactos.git] / modules / rostests / apitests / ntdll / NtApphelpCacheControl.c
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 #ifdef _WIN64
174 CacheEntry.ImageHandle = (HANDLE)0x8000000000000000ULL;
175 #else
176 CacheEntry.ImageHandle = (HANDLE)0x80000000;
177 #endif
178 Status = pNtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry);
179 ok_ntstatus(Status, STATUS_NOT_FOUND);
180
181 RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath.Buffer);
182 }
183
184
185 /* Most service related code was taken from services_winetest:service and modified for usage here
186 The rest came from MSDN */
187
188 static SERVICE_STATUS_HANDLE (WINAPI *pRegisterServiceCtrlHandlerExA)(LPCSTR,LPHANDLER_FUNCTION_EX,LPVOID);
189 static char service_name[100] = "apphelp_test_service";
190 static HANDLE service_stop_event;
191 static SERVICE_STATUS_HANDLE service_status;
192
193 static BOOLEAN RegisterInShimCache(BOOLEAN WithMapping)
194 {
195 WCHAR szPath[MAX_PATH];
196 UNICODE_STRING ntPath;
197 BOOLEAN Result;
198 NTSTATUS Status;
199 GetModuleFileNameW(NULL, szPath, sizeof(szPath) / sizeof(szPath[0]));
200 Result = RtlDosPathNameToNtPathName_U(szPath, &ntPath, NULL, NULL);
201 if (!Result)
202 {
203 DbgPrint("RegisterInShimCache: RtlDosPathNameToNtPathName_U failed\n");
204 return FALSE;
205 }
206
207 Status = CallCacheControl(&ntPath, WithMapping, ApphelpCacheServiceUpdate);
208 if (!NT_SUCCESS(Status))
209 {
210 DbgPrint("RegisterInShimCache: CallCacheControl failed\n");
211 RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath.Buffer);
212 return FALSE;
213 }
214 RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath.Buffer);
215 return TRUE;
216 }
217
218
219 static DWORD WINAPI service_handler(DWORD ctrl, DWORD event_type, void *event_data, void *context)
220 {
221 SERVICE_STATUS status = {0};
222 status.dwServiceType = SERVICE_WIN32;
223 status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
224
225 switch(ctrl)
226 {
227 case SERVICE_CONTROL_STOP:
228 case SERVICE_CONTROL_SHUTDOWN:
229 status.dwCurrentState = SERVICE_STOP_PENDING;
230 status.dwControlsAccepted = 0;
231 SetServiceStatus(service_status, &status);
232 SetEvent(service_stop_event);
233 return NO_ERROR;
234 case RegisterShimCacheWithHandle:
235 if (!RegisterInShimCache(TRUE))
236 {
237 /* TODO: how should we communicate a failure? */
238 }
239 break;
240 case RegisterShimCacheWithoutHandle:
241 if (!RegisterInShimCache(FALSE))
242 {
243 /* TODO: how should we communicate a failure? */
244 }
245 break;
246 default:
247 DbgPrint("Unhandled: %d\n", ctrl);
248 break;
249 }
250 status.dwCurrentState = SERVICE_RUNNING;
251 SetServiceStatus(service_status, &status);
252 return NO_ERROR;
253 }
254
255 static void WINAPI service_main(DWORD argc, char **argv)
256 {
257 SERVICE_STATUS status = {0};
258 service_status = pRegisterServiceCtrlHandlerExA(service_name, service_handler, NULL);
259 if(!service_status)
260 return;
261
262 status.dwServiceType = SERVICE_WIN32;
263 status.dwCurrentState = SERVICE_RUNNING;
264 status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
265 SetServiceStatus(service_status, &status);
266
267 WaitForSingleObject(service_stop_event, INFINITE);
268
269 status.dwCurrentState = SERVICE_STOPPED;
270 status.dwControlsAccepted = 0;
271 SetServiceStatus(service_status, &status);
272 }
273
274 static SC_HANDLE InstallService(SC_HANDLE scm_handle)
275 {
276 char service_cmd[MAX_PATH+150], *ptr;
277 SC_HANDLE service;
278
279 ptr = service_cmd + GetModuleFileNameA(NULL, service_cmd, MAX_PATH);
280 strcpy(ptr, " NtApphelpCacheControl service");
281 ptr += strlen(ptr);
282
283 service = CreateServiceA(scm_handle, service_name, service_name, GENERIC_ALL,
284 SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE,
285 service_cmd, NULL, NULL, NULL, NULL, NULL);
286 if (!service)
287 {
288 skip("Could not create helper service\n");
289 return NULL;
290 }
291 return service;
292 }
293
294 static void WaitService(SC_HANDLE service_handle, DWORD Status, SERVICE_STATUS_PROCESS* ssp)
295 {
296 DWORD dwBytesNeeded;
297 DWORD dwStartTime = GetTickCount();
298 while (ssp->dwCurrentState != Status)
299 {
300 Sleep(40);
301 if (!QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO,
302 (LPBYTE)ssp, sizeof(SERVICE_STATUS_PROCESS), &dwBytesNeeded ))
303 {
304 ok(0, "QueryServiceStatusEx failed waiting for %lu\n", Status);
305 break;
306 }
307 if ((GetTickCount() - dwStartTime) > 1000)
308 {
309 ok(0, "Timeout waiting for (%lu) from service, is: %lu.\n",
310 Status, ssp->dwCurrentState);
311 break;
312 }
313 }
314 }
315
316 static void RunTest()
317 {
318 SC_HANDLE scm_handle = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
319 SC_HANDLE service_handle = InstallService(scm_handle);
320 if (service_handle)
321 {
322 SERVICE_STATUS_PROCESS ssp = {0};
323 BOOL res = StartServiceA(service_handle, 0, NULL);
324 if (res)
325 {
326 WaitService(service_handle, SERVICE_RUNNING, &ssp);
327 RunApphelpCacheControlTests(service_handle);
328 ControlService(service_handle, SERVICE_CONTROL_STOP, (LPSERVICE_STATUS)&ssp);
329 WaitService(service_handle, SERVICE_STOPPED, &ssp);
330 }
331 else
332 {
333 skip("Could not start helper service\n");
334 }
335 DeleteService(service_handle);
336 }
337 CloseServiceHandle(scm_handle);
338 }
339
340 START_TEST(NtApphelpCacheControl)
341 {
342 char **argv;
343 int argc;
344
345 pRegisterServiceCtrlHandlerExA = (void*)GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegisterServiceCtrlHandlerExA");
346 if (!pRegisterServiceCtrlHandlerExA)
347 {
348 win_skip("RegisterServiceCtrlHandlerExA not available, skipping tests\n");
349 return;
350 }
351
352 pNtApphelpCacheControl = (void*)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtApphelpCacheControl");
353 if (!pNtApphelpCacheControl)
354 {
355 win_skip("NtApphelpCacheControl not available, skipping tests\n");
356 return;
357 }
358
359 argc = winetest_get_mainargs(&argv);
360 if(argc < 3)
361 {
362 RunTest();
363 }
364 else
365 {
366 SERVICE_TABLE_ENTRYA servtbl[] = {
367 {service_name, service_main},
368 {NULL, NULL}
369 };
370 service_stop_event = CreateEventA(NULL, TRUE, FALSE, NULL);
371 StartServiceCtrlDispatcherA(servtbl);
372 Sleep(50);
373 CloseHandle(service_stop_event);
374 }
375 }
376
377