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