1ec3e249d0c2246ddc4ffd546b9c500a9a6be55a
[reactos.git] / rostests / kmtests / ntos_ob / ObType.c
1 /*
2 * PROJECT: ReactOS kernel-mode tests
3 * LICENSE: LGPLv2+ - See COPYING.LIB in the top level directory
4 * PURPOSE: Kernel-Mode Test Suite Ob Regressions KM-Test
5 * PROGRAMMER: Aleksey Bragin <aleksey@reactos.org>
6 * Thomas Faber <thomas.faber@reactos.org>
7 */
8
9 /* TODO: split this into multiple tests! ObLife, ObHandle, ObName, ... */
10
11 #include <kmt_test.h>
12
13 #define NDEBUG
14 #include <debug.h>
15
16 #define CheckObject(Handle, Pointers, Handles) do \
17 { \
18 PUBLIC_OBJECT_BASIC_INFORMATION ObjectInfo; \
19 Status = ZwQueryObject(Handle, ObjectBasicInformation, \
20 &ObjectInfo, sizeof ObjectInfo, NULL); \
21 ok_eq_hex(Status, STATUS_SUCCESS); \
22 ok_eq_ulong(ObjectInfo.PointerCount, Pointers); \
23 ok_eq_ulong(ObjectInfo.HandleCount, Handles); \
24 } while (0)
25
26 #define NUM_OBTYPES 5
27
28 typedef struct _MY_OBJECT1
29 {
30 ULONG Something1;
31 } MY_OBJECT1, *PMY_OBJECT1;
32
33 typedef struct _MY_OBJECT2
34 {
35 ULONG Something1;
36 ULONG SomeLong[10];
37 } MY_OBJECT2, *PMY_OBJECT2;
38
39 static POBJECT_TYPE ObTypes[NUM_OBTYPES];
40 static UNICODE_STRING ObTypeName[NUM_OBTYPES];
41 static UNICODE_STRING ObName[NUM_OBTYPES];
42 static OBJECT_TYPE_INITIALIZER ObTypeInitializer[NUM_OBTYPES];
43 static UNICODE_STRING ObDirectoryName;
44 static OBJECT_ATTRIBUTES ObDirectoryAttributes;
45 static OBJECT_ATTRIBUTES ObAttributes[NUM_OBTYPES];
46 static PVOID ObBody[NUM_OBTYPES];
47 static HANDLE ObHandle1[NUM_OBTYPES];
48 static HANDLE DirectoryHandle;
49
50 typedef struct _COUNTS
51 {
52 USHORT Dump;
53 USHORT Open;
54 USHORT Close;
55 USHORT Delete;
56 USHORT Parse;
57 USHORT OkayToClose;
58 USHORT QueryName;
59 } COUNTS, *PCOUNTS;
60 static COUNTS Counts;
61
62 static
63 VOID
64 NTAPI
65 DumpProc(
66 IN PVOID Object,
67 IN POB_DUMP_CONTROL DumpControl)
68 {
69 DPRINT("DumpProc() called\n");
70 ++Counts.Dump;
71 }
72
73 static
74 NTSTATUS
75 NTAPI
76 OpenProc(
77 IN OB_OPEN_REASON OpenReason,
78 IN PEPROCESS Process,
79 IN PVOID Object,
80 IN ACCESS_MASK GrantedAccess,
81 IN ULONG HandleCount)
82 {
83 DPRINT("OpenProc() 0x%p, OpenReason %d, HandleCount %lu, AccessMask 0x%lX\n",
84 Object, OpenReason, HandleCount, GrantedAccess);
85 ++Counts.Open;
86 return STATUS_SUCCESS;
87 }
88
89 static
90 VOID
91 NTAPI
92 CloseProc(
93 IN PEPROCESS Process,
94 IN PVOID Object,
95 IN ACCESS_MASK GrantedAccess,
96 IN ULONG ProcessHandleCount,
97 IN ULONG SystemHandleCount)
98 {
99 DPRINT("CloseProc() 0x%p, ProcessHandleCount %lu, SystemHandleCount %lu, AccessMask 0x%lX\n",
100 Object, ProcessHandleCount, SystemHandleCount, GrantedAccess);
101 ++Counts.Close;
102 }
103
104 static
105 VOID
106 NTAPI
107 DeleteProc(
108 IN PVOID Object)
109 {
110 DPRINT("DeleteProc() 0x%p\n", Object);
111 ++Counts.Delete;
112 }
113
114 static
115 NTSTATUS
116 NTAPI
117 ParseProc(
118 IN PVOID ParseObject,
119 IN PVOID ObjectType,
120 IN OUT PACCESS_STATE AccessState,
121 IN KPROCESSOR_MODE AccessMode,
122 IN ULONG Attributes,
123 IN OUT PUNICODE_STRING CompleteName,
124 IN OUT PUNICODE_STRING RemainingName,
125 IN OUT PVOID Context OPTIONAL,
126 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
127 OUT PVOID *Object)
128 {
129 DPRINT("ParseProc() called\n");
130 *Object = NULL;
131
132 ++Counts.Parse;
133 return STATUS_OBJECT_NAME_NOT_FOUND;//STATUS_SUCCESS;
134 }
135
136 static
137 BOOLEAN
138 NTAPI
139 OkayToCloseProc(
140 IN PEPROCESS Process OPTIONAL,
141 IN PVOID Object,
142 IN HANDLE Handle,
143 IN KPROCESSOR_MODE AccessMode)
144 {
145 DPRINT("OkayToCloseProc() 0x%p, Handle 0x%p, AccessMask 0x%lX\n",
146 Object, Handle, AccessMode);
147 ++Counts.OkayToClose;
148 return TRUE;
149 }
150
151 static
152 NTSTATUS
153 NTAPI
154 QueryNameProc(
155 IN PVOID Object,
156 IN BOOLEAN HasObjectName,
157 OUT POBJECT_NAME_INFORMATION ObjectNameInfo,
158 IN ULONG Length,
159 OUT PULONG ReturnLength,
160 IN KPROCESSOR_MODE AccessMode)
161 {
162 DPRINT("QueryNameProc() 0x%p, HasObjectName %d, Len %lu, AccessMask 0x%lX\n",
163 Object, HasObjectName, Length, AccessMode);
164 ++Counts.QueryName;
165
166 ObjectNameInfo = NULL;
167 ReturnLength = 0;
168 return STATUS_OBJECT_NAME_NOT_FOUND;
169 }
170
171 static
172 VOID
173 ObtCreateObjectTypes(VOID)
174 {
175 INT i;
176 NTSTATUS Status;
177 struct
178 {
179 WCHAR DirectoryName[sizeof "\\ObjectTypes\\" - 1];
180 WCHAR TypeName[15];
181 } Name;
182 OBJECT_ATTRIBUTES ObjectAttributes;
183 HANDLE ObjectTypeHandle;
184 UNICODE_STRING ObjectPath;
185
186 RtlCopyMemory(&Name.DirectoryName, L"\\ObjectTypes\\", sizeof Name.DirectoryName);
187
188 for (i = 0; i < NUM_OBTYPES; ++i)
189 {
190 Status = RtlStringCbPrintfW(Name.TypeName, sizeof Name.TypeName, L"MyObjectType%x", i);
191 ASSERT(NT_SUCCESS(Status));
192 RtlInitUnicodeString(&ObTypeName[i], Name.TypeName);
193 DPRINT("Creating object type %wZ\n", &ObTypeName[i]);
194
195 RtlZeroMemory(&ObTypeInitializer[i], sizeof ObTypeInitializer[i]);
196 ObTypeInitializer[i].Length = sizeof ObTypeInitializer[i];
197 ObTypeInitializer[i].PoolType = NonPagedPool;
198 ObTypeInitializer[i].MaintainHandleCount = TRUE;
199 ObTypeInitializer[i].ValidAccessMask = OBJECT_TYPE_ALL_ACCESS;
200
201 // Test for invalid parameter
202 // FIXME: Make it more exact, to see which params Win2k3 checks
203 // existence of
204 Status = ObCreateObjectType(&ObTypeName[i], &ObTypeInitializer[i], NULL, &ObTypes[i]);
205 ok_eq_hex(Status, STATUS_INVALID_PARAMETER);
206
207 ObTypeInitializer[i].CloseProcedure = CloseProc;
208 ObTypeInitializer[i].DeleteProcedure = DeleteProc;
209 ObTypeInitializer[i].DumpProcedure = DumpProc;
210 ObTypeInitializer[i].OpenProcedure = OpenProc;
211 ObTypeInitializer[i].ParseProcedure = ParseProc;
212 ObTypeInitializer[i].OkayToCloseProcedure = OkayToCloseProc;
213 ObTypeInitializer[i].QueryNameProcedure = QueryNameProc;
214 //ObTypeInitializer[i].SecurityProcedure = SecurityProc;
215
216 Status = ObCreateObjectType(&ObTypeName[i], &ObTypeInitializer[i], NULL, &ObTypes[i]);
217 if (Status == STATUS_OBJECT_NAME_COLLISION)
218 {
219 /* as we cannot delete the object types, get a pointer if they
220 * already exist */
221 RtlInitUnicodeString(&ObjectPath, Name.DirectoryName);
222 InitializeObjectAttributes(&ObjectAttributes, &ObjectPath, OBJ_KERNEL_HANDLE, NULL, NULL);
223 Status = ObOpenObjectByName(&ObjectAttributes, NULL, KernelMode, NULL, 0, NULL, &ObjectTypeHandle);
224 ok_eq_hex(Status, STATUS_SUCCESS);
225 ok(ObjectTypeHandle != NULL, "ObjectTypeHandle = NULL\n");
226 if (!skip(Status == STATUS_SUCCESS && ObjectTypeHandle, "No handle\n"))
227 {
228 Status = ObReferenceObjectByHandle(ObjectTypeHandle, 0, NULL, KernelMode, (PVOID)&ObTypes[i], NULL);
229 ok_eq_hex(Status, STATUS_SUCCESS);
230 if (!skip(Status == STATUS_SUCCESS && ObTypes[i], "blah\n"))
231 {
232 ObTypes[i]->TypeInfo.CloseProcedure = CloseProc;
233 ObTypes[i]->TypeInfo.DeleteProcedure = DeleteProc;
234 ObTypes[i]->TypeInfo.DumpProcedure = DumpProc;
235 ObTypes[i]->TypeInfo.OpenProcedure = OpenProc;
236 ObTypes[i]->TypeInfo.ParseProcedure = ParseProc;
237 ObTypes[i]->TypeInfo.OkayToCloseProcedure = OkayToCloseProc;
238 ObTypes[i]->TypeInfo.QueryNameProcedure = QueryNameProc;
239 }
240 Status = ZwClose(ObjectTypeHandle);
241 }
242 }
243
244 ok_eq_hex(Status, STATUS_SUCCESS);
245 ok(ObTypes[i] != NULL, "ObType = NULL\n");
246 }
247 }
248
249 static
250 VOID
251 ObtCreateDirectory(VOID)
252 {
253 NTSTATUS Status;
254
255 RtlInitUnicodeString(&ObDirectoryName, L"\\ObtDirectory");
256 InitializeObjectAttributes(&ObDirectoryAttributes, &ObDirectoryName, OBJ_KERNEL_HANDLE | OBJ_PERMANENT | OBJ_CASE_INSENSITIVE, NULL, NULL);
257 Status = ZwCreateDirectoryObject(&DirectoryHandle, DELETE, &ObDirectoryAttributes);
258 ok_eq_hex(Status, STATUS_SUCCESS);
259 CheckObject(DirectoryHandle, 3LU, 1LU);
260 }
261
262 #define CheckCounts(OpenCount, CloseCount, DeleteCount, ParseCount, \
263 OkayToCloseCount, QueryNameCount) do \
264 { \
265 ok_eq_uint(Counts.Open, OpenCount); \
266 ok_eq_uint(Counts.Close, CloseCount); \
267 ok_eq_uint(Counts.Delete, DeleteCount); \
268 ok_eq_uint(Counts.Parse, ParseCount); \
269 ok_eq_uint(Counts.OkayToClose, OkayToCloseCount); \
270 ok_eq_uint(Counts.QueryName, QueryNameCount); \
271 } while (0)
272
273 #define SaveCounts(Save) memcpy(&Save, &Counts, sizeof Counts)
274
275 /* TODO: make this the same as NUM_OBTYPES */
276 #define NUM_OBTYPES2 2
277 static
278 VOID
279 ObtCreateObjects(VOID)
280 {
281 NTSTATUS Status;
282 WCHAR Name[NUM_OBTYPES2][MAX_PATH];
283 COUNTS SaveCounts;
284 INT i;
285 ACCESS_MASK Access[NUM_OBTYPES2] = { STANDARD_RIGHTS_ALL, GENERIC_ALL };
286 ULONG ObjectSize[NUM_OBTYPES2] = { sizeof(MY_OBJECT1), sizeof(MY_OBJECT2) };
287
288 // Create two objects
289 for (i = 0; i < NUM_OBTYPES2; ++i)
290 {
291 ASSERT(sizeof Name[i] == MAX_PATH * sizeof(WCHAR));
292 Status = RtlStringCbPrintfW(Name[i], sizeof Name[i], L"\\ObtDirectory\\MyObject%d", i + 1);
293 ASSERT(Status == STATUS_SUCCESS);
294 RtlInitUnicodeString(&ObName[i], Name[i]);
295 InitializeObjectAttributes(&ObAttributes[i], &ObName[i], OBJ_CASE_INSENSITIVE, NULL, NULL);
296 }
297 CheckObject(DirectoryHandle, 3LU, 1LU);
298
299 for (i = 0; i < NUM_OBTYPES2; ++i)
300 {
301 Status = ObCreateObject(KernelMode, ObTypes[i], &ObAttributes[i], KernelMode, NULL, ObjectSize[i], 0L, 0L, &ObBody[i]);
302 ok_eq_hex(Status, STATUS_SUCCESS);
303 }
304
305 SaveCounts(SaveCounts);
306
307 // Insert them
308 for (i = 0; i < NUM_OBTYPES2; ++i)
309 {
310 CheckObject(DirectoryHandle, 3LU + i, 1LU);
311 Status = ObInsertObject(ObBody[i], NULL, Access[i], 0, &ObBody[i], &ObHandle1[i]);
312 ok_eq_hex(Status, STATUS_SUCCESS);
313 ok(ObBody[i] != NULL, "Object body = NULL\n");
314 ok(ObHandle1[i] != NULL, "Handle = NULL\n");
315 CheckObject(ObHandle1[i], 3LU, 1LU);
316 CheckCounts(SaveCounts.Open + 1, SaveCounts.Close, SaveCounts.Delete, SaveCounts.Parse, SaveCounts.OkayToClose, SaveCounts.QueryName);
317 SaveCounts(SaveCounts);
318 CheckObject(DirectoryHandle, 4LU + i, 1LU);
319 }
320
321 //DPRINT1("%d %d %d %d %d %d %d\n", DumpCount, OpenCount, CloseCount, DeleteCount, ParseCount, OkayToCloseCount, QueryNameCount);
322 CheckCounts(SaveCounts.Open, SaveCounts.Close, SaveCounts.Delete, SaveCounts.Parse, SaveCounts.OkayToClose, SaveCounts.QueryName);
323 }
324
325 static
326 VOID
327 ObtClose(
328 BOOLEAN Clean,
329 BOOLEAN AlternativeMethod)
330 {
331 NTSTATUS Status;
332 LONG_PTR Ret;
333 PVOID TypeObject;
334 INT i;
335 UNICODE_STRING ObPathName[NUM_OBTYPES];
336 WCHAR Name[MAX_PATH];
337
338 // Close what we have opened and free what we allocated
339 for (i = 0; i < NUM_OBTYPES2; ++i)
340 {
341 if (!skip(ObBody[i] != NULL, "Nothing to dereference\n"))
342 {
343 if (ObHandle1[i]) CheckObject(ObHandle1[i], 3LU, 1LU);
344 Ret = ObDereferenceObject(ObBody[i]);
345 ok_eq_longptr(Ret, (LONG_PTR)1);
346 if (ObHandle1[i]) CheckObject(ObHandle1[i], 2LU, 1LU);
347 ObBody[i] = NULL;
348 }
349 if (!skip(ObHandle1[i] != NULL, "Nothing to close\n"))
350 {
351 Status = ZwClose(ObHandle1[i]);
352 ok_eq_hex(Status, STATUS_SUCCESS);
353 ObHandle1[i] = NULL;
354 }
355 }
356
357 if (skip(Clean, "Not cleaning up, as requested. Use ObTypeClean to clean up\n"))
358 return;
359
360 // Now we have to get rid of a directory object
361 // Since it is permanent, we have to firstly make it temporary
362 // and only then kill
363 // (this procedure is described in DDK)
364 if (!skip(DirectoryHandle != NULL, "No directory handle\n"))
365 {
366 CheckObject(DirectoryHandle, 3LU, 1LU);
367
368 Status = ZwMakeTemporaryObject(DirectoryHandle);
369 ok_eq_hex(Status, STATUS_SUCCESS);
370 CheckObject(DirectoryHandle, 3LU, 1LU);
371
372 Status = ZwClose(DirectoryHandle);
373 ok_eq_hex(Status, STATUS_SUCCESS);
374 }
375
376 /* we don't delete the object types we created. It makes Windows unstable.
377 * TODO: perhaps make it work in ROS anyway */
378 return;
379 if (!AlternativeMethod)
380 {
381 for (i = 0; i < NUM_OBTYPES; ++i)
382 if (!skip(ObTypes[i] != NULL, "No object type to delete\n"))
383 {
384 Ret = ObDereferenceObject(ObTypes[i]);
385 ok_eq_longptr(Ret, (LONG_PTR)0);
386 ObTypes[i] = NULL;
387 }
388 }
389 else
390 {
391 for (i = 0; i < NUM_OBTYPES; ++i)
392 {
393 if (!skip(ObTypes[i] != NULL, "No object type to delete\n"))
394 {
395 Status = RtlStringCbPrintfW(Name, sizeof Name, L"\\ObjectTypes\\MyObjectType%d", i);
396 RtlInitUnicodeString(&ObPathName[i], Name);
397 Status = ObReferenceObjectByName(&ObPathName[i], OBJ_CASE_INSENSITIVE, NULL, 0L, NULL, KernelMode, NULL, &TypeObject);
398
399 Ret = ObDereferenceObject(TypeObject);
400 ok_eq_longptr(Ret, (LONG_PTR)2);
401 Ret = ObDereferenceObject(TypeObject);
402 ok_eq_longptr(Ret, (LONG_PTR)1);
403 DPRINT("Reference Name %wZ = %p, ObTypes[%d] = %p\n",
404 ObPathName[i], TypeObject, i, ObTypes[i]);
405 ObTypes[i] = NULL;
406 }
407 }
408 }
409 }
410
411 static
412 VOID
413 TestObjectType(
414 IN BOOLEAN Clean)
415 {
416 RtlZeroMemory(&Counts, sizeof Counts);
417
418 ObtCreateObjectTypes();
419 DPRINT("ObtCreateObjectTypes() done\n");
420
421 ObtCreateDirectory();
422 DPRINT("ObtCreateDirectory() done\n");
423
424 if (!skip(ObTypes[0] != NULL, "No object types!\n"))
425 ObtCreateObjects();
426 DPRINT("ObtCreateObjects() done\n");
427
428 ObtClose(Clean, FALSE);
429 }
430
431 START_TEST(ObType)
432 {
433 TestObjectType(TRUE);
434 }
435
436 /* run this to see the objects created in user mode */
437 START_TEST(ObTypeNoClean)
438 {
439 TestObjectType(FALSE);
440 }
441
442 /* run this to clean up after ObTypeNoClean */
443 START_TEST(ObTypeClean)
444 {
445 ObtClose(TRUE, FALSE);
446 }