- Implemented InterlockedBitTestAndReset, InterlockedBitTestAndSet, InterlockedExchan...
[reactos.git] / reactos / ntoskrnl / ob / namespc.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ob/namespc.c
6 * PURPOSE: Manages the system namespace
7 *
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
9 */
10
11 /* INCLUDES ***************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 #if defined (ALLOC_PRAGMA)
18 #pragma alloc_text(INIT, ObInit)
19 #endif
20
21
22 extern ULONG NtGlobalFlag;
23
24 /* GLOBALS ****************************************************************/
25
26 POBJECT_TYPE ObDirectoryType = NULL;
27 POBJECT_TYPE ObTypeObjectType = NULL;
28
29 PDIRECTORY_OBJECT NameSpaceRoot = NULL;
30 PDIRECTORY_OBJECT ObpTypeDirectoryObject = NULL;
31 /* FIXME: Move this somewhere else once devicemap support is in */
32 PDEVICE_MAP ObSystemDeviceMap = NULL;
33 KEVENT ObpDefaultObject;
34
35 static GENERIC_MAPPING ObpDirectoryMapping = {
36 STANDARD_RIGHTS_READ|DIRECTORY_QUERY|DIRECTORY_TRAVERSE,
37 STANDARD_RIGHTS_WRITE|DIRECTORY_CREATE_OBJECT|DIRECTORY_CREATE_SUBDIRECTORY,
38 STANDARD_RIGHTS_EXECUTE|DIRECTORY_QUERY|DIRECTORY_TRAVERSE,
39 DIRECTORY_ALL_ACCESS};
40
41 static GENERIC_MAPPING ObpTypeMapping = {
42 STANDARD_RIGHTS_READ,
43 STANDARD_RIGHTS_WRITE,
44 STANDARD_RIGHTS_EXECUTE,
45 0x000F0001};
46
47 NTSTATUS
48 STDCALL
49 ObpAllocateObject(POBJECT_CREATE_INFORMATION ObjectCreateInfo,
50 PUNICODE_STRING ObjectName,
51 POBJECT_TYPE ObjectType,
52 ULONG ObjectSize,
53 POBJECT_HEADER *ObjectHeader);
54
55 /* FUNCTIONS **************************************************************/
56
57 /*
58 * @implemented
59 */
60 NTSTATUS STDCALL
61 ObReferenceObjectByName(PUNICODE_STRING ObjectPath,
62 ULONG Attributes,
63 PACCESS_STATE PassedAccessState,
64 ACCESS_MASK DesiredAccess,
65 POBJECT_TYPE ObjectType,
66 KPROCESSOR_MODE AccessMode,
67 PVOID ParseContext,
68 PVOID* ObjectPtr)
69 {
70 PVOID Object = NULL;
71 UNICODE_STRING RemainingPath;
72 UNICODE_STRING ObjectName;
73 OBJECT_CREATE_INFORMATION ObjectCreateInfo;
74 NTSTATUS Status;
75
76 PAGED_CODE();
77
78 /* Capture the name */
79 DPRINT("Capturing Name\n");
80 Status = ObpCaptureObjectName(&ObjectName, ObjectPath, AccessMode);
81 if (!NT_SUCCESS(Status))
82 {
83 DPRINT("ObpCaptureObjectName() failed (Status %lx)\n", Status);
84 return Status;
85 }
86
87 /*
88 * Create a fake ObjectCreateInfo structure. Note that my upcoming
89 * ObFindObject refactoring will remove the need for this hack.
90 */
91 ObjectCreateInfo.RootDirectory = NULL;
92 ObjectCreateInfo.Attributes = Attributes;
93
94 Status = ObFindObject(&ObjectCreateInfo,
95 &ObjectName,
96 &Object,
97 &RemainingPath,
98 ObjectType);
99
100 if (ObjectName.Buffer) ExFreePool(ObjectName.Buffer);
101
102 if (!NT_SUCCESS(Status))
103 {
104 return(Status);
105 }
106 DPRINT("RemainingPath.Buffer '%S' Object %p\n", RemainingPath.Buffer, Object);
107
108 if (RemainingPath.Buffer != NULL || Object == NULL)
109 {
110 DPRINT("Object %p\n", Object);
111 *ObjectPtr = NULL;
112 RtlFreeUnicodeString (&RemainingPath);
113 return(STATUS_OBJECT_NAME_NOT_FOUND);
114 }
115 *ObjectPtr = Object;
116 RtlFreeUnicodeString (&RemainingPath);
117 return(STATUS_SUCCESS);
118 }
119
120
121 /**********************************************************************
122 * NAME EXPORTED
123 * ObOpenObjectByName
124 *
125 * DESCRIPTION
126 * Obtain a handle to an existing object.
127 *
128 * ARGUMENTS
129 * ObjectAttributes
130 * ...
131 * ObjectType
132 * ...
133 * ParseContext
134 * ...
135 * AccessMode
136 * ...
137 * DesiredAccess
138 * ...
139 * PassedAccessState
140 * ...
141 * Handle
142 * Handle to close.
143 *
144 * RETURN VALUE
145 * Status.
146 *
147 * @implemented
148 */
149 NTSTATUS STDCALL
150 ObOpenObjectByName(IN POBJECT_ATTRIBUTES ObjectAttributes,
151 IN POBJECT_TYPE ObjectType,
152 IN OUT PVOID ParseContext,
153 IN KPROCESSOR_MODE AccessMode,
154 IN ACCESS_MASK DesiredAccess,
155 IN PACCESS_STATE PassedAccessState,
156 OUT PHANDLE Handle)
157 {
158 UNICODE_STRING RemainingPath;
159 PVOID Object = NULL;
160 UNICODE_STRING ObjectName;
161 OBJECT_CREATE_INFORMATION ObjectCreateInfo;
162 NTSTATUS Status;
163
164 PAGED_CODE();
165
166 DPRINT("ObOpenObjectByName(...)\n");
167
168 /* Capture all the info */
169 DPRINT("Capturing Create Info\n");
170 Status = ObpCaptureObjectAttributes(ObjectAttributes,
171 AccessMode,
172 ObjectType,
173 &ObjectCreateInfo,
174 &ObjectName);
175 if (!NT_SUCCESS(Status))
176 {
177 DPRINT("ObpCaptureObjectAttributes() failed (Status %lx)\n", Status);
178 return Status;
179 }
180
181 Status = ObFindObject(&ObjectCreateInfo,
182 &ObjectName,
183 &Object,
184 &RemainingPath,
185 ObjectType);
186 if (ObjectName.Buffer) ExFreePool(ObjectName.Buffer);
187 if (!NT_SUCCESS(Status))
188 {
189 DPRINT("ObFindObject() failed (Status %lx)\n", Status);
190 goto Cleanup;
191 }
192
193 DPRINT("OBject: %p, Remaining Path: %wZ\n", Object, &RemainingPath);
194 if (Object == NULL)
195 {
196 Status = STATUS_UNSUCCESSFUL;
197 goto Cleanup;
198 }
199 if (RemainingPath.Buffer != NULL)
200 {
201 if (wcschr(RemainingPath.Buffer + 1, L'\\') == NULL)
202 Status = STATUS_OBJECT_NAME_NOT_FOUND;
203 else
204 Status =STATUS_OBJECT_PATH_NOT_FOUND;
205 goto Cleanup;
206 }
207
208 Status = ObpCreateHandle(Object,
209 DesiredAccess,
210 ObjectCreateInfo.Attributes,
211 Handle);
212
213 Cleanup:
214 if (Object != NULL)
215 {
216 ObDereferenceObject(Object);
217 }
218 RtlFreeUnicodeString(&RemainingPath);
219 ObpReleaseCapturedAttributes(&ObjectCreateInfo);
220
221 return Status;
222 }
223
224 VOID
225 STDCALL
226 ObQueryDeviceMapInformation(PEPROCESS Process,
227 PPROCESS_DEVICEMAP_INFORMATION DeviceMapInfo)
228 {
229 //KIRQL OldIrql ;
230
231 /*
232 * FIXME: This is an ugly hack for now, to always return the System Device Map
233 * instead of returning the Process Device Map. Not important yet since we don't use it
234 */
235
236 /* FIXME: Acquire the DeviceMap Spinlock */
237 // KeAcquireSpinLock(DeviceMap->Lock, &OldIrql);
238
239 /* Make a copy */
240 DeviceMapInfo->Query.DriveMap = ObSystemDeviceMap->DriveMap;
241 RtlMoveMemory(DeviceMapInfo->Query.DriveType, ObSystemDeviceMap->DriveType, sizeof(ObSystemDeviceMap->DriveType));
242
243 /* FIXME: Release the DeviceMap Spinlock */
244 // KeReleasepinLock(DeviceMap->Lock, OldIrql);
245 }
246
247 VOID
248 NTAPI
249 ObpAddEntryDirectory(PDIRECTORY_OBJECT Parent,
250 POBJECT_HEADER Header,
251 PWSTR Name)
252 /*
253 * FUNCTION: Add an entry to a namespace directory
254 * ARGUMENTS:
255 * Parent = directory to add in
256 * Header = Header of the object to add the entry for
257 * Name = Name to give the entry
258 */
259 {
260 KIRQL oldlvl;
261
262 ASSERT(HEADER_TO_OBJECT_NAME(Header));
263 HEADER_TO_OBJECT_NAME(Header)->Directory = Parent;
264
265 KeAcquireSpinLock(&Parent->Lock, &oldlvl);
266 InsertTailList(&Parent->head, &Header->Entry);
267 KeReleaseSpinLock(&Parent->Lock, oldlvl);
268 }
269
270
271 VOID
272 NTAPI
273 ObpRemoveEntryDirectory(POBJECT_HEADER Header)
274 /*
275 * FUNCTION: Remove an entry from a namespace directory
276 * ARGUMENTS:
277 * Header = Header of the object to remove
278 */
279 {
280 KIRQL oldlvl;
281
282 DPRINT("ObpRemoveEntryDirectory(Header %x)\n",Header);
283
284 KeAcquireSpinLock(&(HEADER_TO_OBJECT_NAME(Header)->Directory->Lock),&oldlvl);
285 if (Header->Entry.Flink && Header->Entry.Blink)
286 {
287 RemoveEntryList(&(Header->Entry));
288 Header->Entry.Flink = Header->Entry.Blink = NULL;
289 }
290 KeReleaseSpinLock(&(HEADER_TO_OBJECT_NAME(Header)->Directory->Lock),oldlvl);
291 }
292
293 NTSTATUS
294 STDCALL
295 ObpCreateDirectory(OB_OPEN_REASON Reason,
296 PVOID ObjectBody,
297 PEPROCESS Process,
298 ULONG HandleCount,
299 ACCESS_MASK GrantedAccess)
300 {
301 PDIRECTORY_OBJECT Directory = ObjectBody;
302
303 if (Reason == ObCreateHandle)
304 {
305 InitializeListHead(&Directory->head);
306 KeInitializeSpinLock(&Directory->Lock);
307 }
308
309 return STATUS_SUCCESS;
310 }
311
312 PVOID
313 ObpFindEntryDirectory(PDIRECTORY_OBJECT DirectoryObject,
314 PWSTR Name,
315 ULONG Attributes)
316 {
317 PLIST_ENTRY current = DirectoryObject->head.Flink;
318 POBJECT_HEADER current_obj;
319
320 DPRINT("ObFindEntryDirectory(dir %x, name %S)\n",DirectoryObject, Name);
321
322 if (Name[0]==0)
323 {
324 return(DirectoryObject);
325 }
326 if (Name[0]=='.' && Name[1]==0)
327 {
328 return(DirectoryObject);
329 }
330 if (Name[0]=='.' && Name[1]=='.' && Name[2]==0)
331 {
332 return(HEADER_TO_OBJECT_NAME(BODY_TO_HEADER(DirectoryObject))->Directory);
333 }
334 while (current!=(&(DirectoryObject->head)))
335 {
336 current_obj = CONTAINING_RECORD(current,OBJECT_HEADER,Entry);
337 DPRINT(" Scanning: %S for: %S\n",HEADER_TO_OBJECT_NAME(current_obj)->Name.Buffer, Name);
338 if (Attributes & OBJ_CASE_INSENSITIVE)
339 {
340 if (_wcsicmp(HEADER_TO_OBJECT_NAME(current_obj)->Name.Buffer, Name)==0)
341 {
342 DPRINT("Found it %x\n",&current_obj->Body);
343 return(&current_obj->Body);
344 }
345 }
346 else
347 {
348 if ( wcscmp(HEADER_TO_OBJECT_NAME(current_obj)->Name.Buffer, Name)==0)
349 {
350 DPRINT("Found it %x\n",&current_obj->Body);
351 return(&current_obj->Body);
352 }
353 }
354 current = current->Flink;
355 }
356 DPRINT(" Not Found: %s() = NULL\n",__FUNCTION__);
357 return(NULL);
358 }
359
360
361 NTSTATUS STDCALL
362 ObpParseDirectory(PVOID Object,
363 PVOID * NextObject,
364 PUNICODE_STRING FullPath,
365 PWSTR * Path,
366 ULONG Attributes)
367 {
368 PWSTR Start;
369 PWSTR End;
370 PVOID FoundObject;
371 KIRQL oldlvl;
372
373 DPRINT("ObpParseDirectory(Object %x, Path %x, *Path %S)\n",
374 Object,Path,*Path);
375
376 *NextObject = NULL;
377
378 if ((*Path) == NULL)
379 {
380 return STATUS_UNSUCCESSFUL;
381 }
382
383 Start = *Path;
384 if (*Start == L'\\')
385 Start++;
386
387 End = wcschr(Start, L'\\');
388 if (End != NULL)
389 {
390 *End = 0;
391 }
392
393 KeAcquireSpinLock(&(((PDIRECTORY_OBJECT)Object)->Lock), &oldlvl);
394 FoundObject = ObpFindEntryDirectory(Object, Start, Attributes);
395 if (FoundObject == NULL)
396 {
397 KeReleaseSpinLock(&(((PDIRECTORY_OBJECT)Object)->Lock), oldlvl);
398 if (End != NULL)
399 {
400 *End = L'\\';
401 }
402 return STATUS_UNSUCCESSFUL;
403 }
404
405 ObReferenceObjectByPointer(FoundObject,
406 STANDARD_RIGHTS_REQUIRED,
407 NULL,
408 UserMode);
409 KeReleaseSpinLock(&(((PDIRECTORY_OBJECT)Object)->Lock), oldlvl);
410 if (End != NULL)
411 {
412 *End = L'\\';
413 *Path = End;
414 }
415 else
416 {
417 *Path = NULL;
418 }
419
420 *NextObject = FoundObject;
421
422 return STATUS_SUCCESS;
423 }
424
425 VOID
426 INIT_FUNCTION
427 ObInit(VOID)
428 {
429 OBJECT_ATTRIBUTES ObjectAttributes;
430 UNICODE_STRING Name;
431 SECURITY_DESCRIPTOR SecurityDescriptor;
432 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
433
434 /* Initialize the security descriptor cache */
435 ObpInitSdCache();
436
437 /* Initialize the Default Event */
438 KeInitializeEvent(&ObpDefaultObject, NotificationEvent, TRUE );
439
440 /* Create the Type Type */
441 DPRINT("Creating Type Type\n");
442 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
443 RtlInitUnicodeString(&Name, L"Type");
444 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
445 ObjectTypeInitializer.ValidAccessMask = OBJECT_TYPE_ALL_ACCESS;
446 ObjectTypeInitializer.UseDefaultObject = TRUE;
447 ObjectTypeInitializer.MaintainTypeList = TRUE;
448 ObjectTypeInitializer.PoolType = NonPagedPool;
449 ObjectTypeInitializer.GenericMapping = ObpTypeMapping;
450 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(OBJECT_TYPE);
451 ObpCreateTypeObject(&ObjectTypeInitializer, &Name, &ObTypeObjectType);
452
453 /* Create the Directory Type */
454 DPRINT("Creating Directory Type\n");
455 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
456 RtlInitUnicodeString(&Name, L"Directory");
457 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
458 ObjectTypeInitializer.ValidAccessMask = DIRECTORY_ALL_ACCESS;
459 ObjectTypeInitializer.UseDefaultObject = FALSE;
460 ObjectTypeInitializer.OpenProcedure = ObpCreateDirectory;
461 ObjectTypeInitializer.ParseProcedure = ObpParseDirectory;
462 ObjectTypeInitializer.MaintainTypeList = FALSE;
463 ObjectTypeInitializer.GenericMapping = ObpDirectoryMapping;
464 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(DIRECTORY_OBJECT);
465 ObpCreateTypeObject(&ObjectTypeInitializer, &Name, &ObDirectoryType);
466
467 /* Create security descriptor */
468 RtlCreateSecurityDescriptor(&SecurityDescriptor,
469 SECURITY_DESCRIPTOR_REVISION1);
470 RtlSetOwnerSecurityDescriptor(&SecurityDescriptor,
471 SeAliasAdminsSid,
472 FALSE);
473 RtlSetGroupSecurityDescriptor(&SecurityDescriptor,
474 SeLocalSystemSid,
475 FALSE);
476 RtlSetDaclSecurityDescriptor(&SecurityDescriptor,
477 TRUE,
478 SePublicDefaultDacl,
479 FALSE);
480
481 /* Create root directory */
482 DPRINT("Creating Root Directory\n");
483 InitializeObjectAttributes(&ObjectAttributes,
484 NULL,
485 OBJ_PERMANENT,
486 NULL,
487 &SecurityDescriptor);
488 ObCreateObject(KernelMode,
489 ObDirectoryType,
490 &ObjectAttributes,
491 KernelMode,
492 NULL,
493 sizeof(DIRECTORY_OBJECT),
494 0,
495 0,
496 (PVOID*)&NameSpaceRoot);
497 ObInsertObject((PVOID)NameSpaceRoot,
498 NULL,
499 DIRECTORY_ALL_ACCESS,
500 0,
501 NULL,
502 NULL);
503
504 /* Create '\ObjectTypes' directory */
505 RtlInitUnicodeString(&Name, L"\\ObjectTypes");
506 InitializeObjectAttributes(&ObjectAttributes,
507 &Name,
508 OBJ_PERMANENT,
509 NULL,
510 &SecurityDescriptor);
511 ObCreateObject(KernelMode,
512 ObDirectoryType,
513 &ObjectAttributes,
514 KernelMode,
515 NULL,
516 sizeof(DIRECTORY_OBJECT),
517 0,
518 0,
519 (PVOID*)&ObpTypeDirectoryObject);
520 ObInsertObject((PVOID)ObpTypeDirectoryObject,
521 NULL,
522 DIRECTORY_ALL_ACCESS,
523 0,
524 NULL,
525 NULL);
526
527 /* Insert the two objects we already created but couldn't add */
528 /* NOTE: Uses TypeList & Creator Info in OB 2.0 */
529 ObpAddEntryDirectory(ObpTypeDirectoryObject, BODY_TO_HEADER(ObTypeObjectType), NULL);
530 ObpAddEntryDirectory(ObpTypeDirectoryObject, BODY_TO_HEADER(ObDirectoryType), NULL);
531
532 /* Create 'symbolic link' object type */
533 ObInitSymbolicLinkImplementation();
534
535 /* FIXME: Hack Hack! */
536 ObSystemDeviceMap = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ObSystemDeviceMap), TAG('O', 'b', 'D', 'm'));
537 RtlZeroMemory(ObSystemDeviceMap, sizeof(*ObSystemDeviceMap));
538 }
539
540 NTSTATUS
541 STDCALL
542 ObpCreateTypeObject(POBJECT_TYPE_INITIALIZER ObjectTypeInitializer,
543 PUNICODE_STRING TypeName,
544 POBJECT_TYPE *ObjectType)
545 {
546 POBJECT_HEADER Header;
547 POBJECT_TYPE LocalObjectType;
548 ULONG HeaderSize;
549 NTSTATUS Status;
550
551 DPRINT("ObpCreateTypeObject(ObjectType: %wZ)\n", TypeName);
552
553 /* Allocate the Object */
554 Status = ObpAllocateObject(NULL,
555 TypeName,
556 ObTypeObjectType,
557 OBJECT_ALLOC_SIZE(sizeof(OBJECT_TYPE)),
558 &Header);
559 if (!NT_SUCCESS(Status))
560 {
561 DPRINT1("ObpAllocateObject failed!\n");
562 return Status;
563 }
564
565 LocalObjectType = (POBJECT_TYPE)&Header->Body;
566 DPRINT("Local ObjectType: %p Header: %p \n", LocalObjectType, Header);
567
568 /* Check if this is the first Object Type */
569 if (!ObTypeObjectType)
570 {
571 ObTypeObjectType = LocalObjectType;
572 Header->Type = ObTypeObjectType;
573 LocalObjectType->Key = TAG('O', 'b', 'j', 'T');
574 }
575 else
576 {
577 CHAR Tag[4];
578 Tag[0] = TypeName->Buffer[0];
579 Tag[1] = TypeName->Buffer[1];
580 Tag[2] = TypeName->Buffer[2];
581 Tag[3] = TypeName->Buffer[3];
582
583 /* Set Tag */
584 DPRINT("Convert: %s \n", Tag);
585 LocalObjectType->Key = *(PULONG)Tag;
586 }
587
588 /* Set it up */
589 LocalObjectType->TypeInfo = *ObjectTypeInitializer;
590 LocalObjectType->Name = *TypeName;
591 LocalObjectType->TypeInfo.PoolType = ObjectTypeInitializer->PoolType;
592
593 /* These two flags need to be manually set up */
594 Header->Flags |= OB_FLAG_KERNEL_MODE | OB_FLAG_PERMANENT;
595
596 /* Check if we have to maintain a type list */
597 if (NtGlobalFlag & FLG_MAINTAIN_OBJECT_TYPELIST)
598 {
599 /* Enable support */
600 LocalObjectType->TypeInfo.MaintainTypeList = TRUE;
601 }
602
603 /* Calculate how much space our header'll take up */
604 HeaderSize = sizeof(OBJECT_HEADER) + sizeof(OBJECT_HEADER_NAME_INFO) +
605 (ObjectTypeInitializer->MaintainHandleCount ?
606 sizeof(OBJECT_HEADER_HANDLE_INFO) : 0);
607
608 /* Update the Pool Charges */
609 if (ObjectTypeInitializer->PoolType == NonPagedPool)
610 {
611 LocalObjectType->TypeInfo.DefaultNonPagedPoolCharge += HeaderSize;
612 }
613 else
614 {
615 LocalObjectType->TypeInfo.DefaultPagedPoolCharge += HeaderSize;
616 }
617
618 /* All objects types need a security procedure */
619 if (!ObjectTypeInitializer->SecurityProcedure)
620 {
621 LocalObjectType->TypeInfo.SecurityProcedure = SeDefaultObjectMethod;
622 }
623
624 /* Select the Wait Object */
625 if (LocalObjectType->TypeInfo.UseDefaultObject)
626 {
627 /* Add the SYNCHRONIZE access mask since it's waitable */
628 LocalObjectType->TypeInfo.ValidAccessMask |= SYNCHRONIZE;
629
630 /* Use the "Default Object", a simple event */
631 LocalObjectType->DefaultObject = &ObpDefaultObject;
632 }
633 /* Special system objects get an optimized hack so they can be waited on */
634 else if (TypeName->Length == 8 && !wcscmp(TypeName->Buffer, L"File"))
635 {
636 LocalObjectType->DefaultObject = (PVOID)FIELD_OFFSET(FILE_OBJECT, Event);
637 }
638 /* FIXME: When LPC stops sucking, add a hack for Waitable Ports */
639 else
640 {
641 /* No default Object */
642 LocalObjectType->DefaultObject = NULL;
643 }
644
645 /* Initialize Object Type components */
646 ExInitializeResourceLite(&LocalObjectType->Mutex);
647 InitializeListHead(&LocalObjectType->TypeList);
648
649 /* Insert it into the Object Directory */
650 if (ObpTypeDirectoryObject)
651 {
652 ObpAddEntryDirectory(ObpTypeDirectoryObject, Header, TypeName->Buffer);
653 ObReferenceObject(ObpTypeDirectoryObject);
654 }
655
656 *ObjectType = LocalObjectType;
657 return Status;
658 }
659
660 /* EOF */