Cleanup isn't necessary after calling the driver in NtQueryDirectoryFile.
[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 ObpReleaseCapturedAttributes(&ObjectCreateInfo);
187 if (ObjectName.Buffer) ExFreePool(ObjectName.Buffer);
188 if (!NT_SUCCESS(Status))
189 {
190 DPRINT("ObFindObject() failed (Status %lx)\n", Status);
191 return Status;
192 }
193
194 DPRINT("OBject: %x, Remaining Path: %wZ\n", Object, &RemainingPath);
195 if (Object == NULL)
196 {
197 RtlFreeUnicodeString(&RemainingPath);
198 return STATUS_UNSUCCESSFUL;
199 }
200 if (RemainingPath.Buffer != NULL)
201 {
202 if (wcschr(RemainingPath.Buffer + 1, L'\\') == NULL)
203 Status = STATUS_OBJECT_NAME_NOT_FOUND;
204 else
205 Status =STATUS_OBJECT_PATH_NOT_FOUND;
206 RtlFreeUnicodeString(&RemainingPath);
207 ObDereferenceObject(Object);
208 return Status;
209 }
210
211 Status = ObpCreateHandle(PsGetCurrentProcess(),
212 Object,
213 DesiredAccess,
214 FALSE,
215 Handle);
216
217 ObDereferenceObject(Object);
218 RtlFreeUnicodeString(&RemainingPath);
219
220 return Status;
221 }
222
223 VOID
224 STDCALL
225 ObQueryDeviceMapInformation(PEPROCESS Process,
226 PPROCESS_DEVICEMAP_INFORMATION DeviceMapInfo)
227 {
228 //KIRQL OldIrql ;
229
230 /*
231 * FIXME: This is an ugly hack for now, to always return the System Device Map
232 * instead of returning the Process Device Map. Not important yet since we don't use it
233 */
234
235 /* FIXME: Acquire the DeviceMap Spinlock */
236 // KeAcquireSpinLock(DeviceMap->Lock, &OldIrql);
237
238 /* Make a copy */
239 DeviceMapInfo->Query.DriveMap = ObSystemDeviceMap->DriveMap;
240 RtlMoveMemory(DeviceMapInfo->Query.DriveType, ObSystemDeviceMap->DriveType, sizeof(ObSystemDeviceMap->DriveType));
241
242 /* FIXME: Release the DeviceMap Spinlock */
243 // KeReleasepinLock(DeviceMap->Lock, OldIrql);
244 }
245
246 VOID
247 NTAPI
248 ObpAddEntryDirectory(PDIRECTORY_OBJECT Parent,
249 POBJECT_HEADER Header,
250 PWSTR Name)
251 /*
252 * FUNCTION: Add an entry to a namespace directory
253 * ARGUMENTS:
254 * Parent = directory to add in
255 * Header = Header of the object to add the entry for
256 * Name = Name to give the entry
257 */
258 {
259 KIRQL oldlvl;
260
261 ASSERT(HEADER_TO_OBJECT_NAME(Header));
262 HEADER_TO_OBJECT_NAME(Header)->Directory = Parent;
263
264 KeAcquireSpinLock(&Parent->Lock, &oldlvl);
265 InsertTailList(&Parent->head, &Header->Entry);
266 KeReleaseSpinLock(&Parent->Lock, oldlvl);
267 }
268
269
270 VOID
271 NTAPI
272 ObpRemoveEntryDirectory(POBJECT_HEADER Header)
273 /*
274 * FUNCTION: Remove an entry from a namespace directory
275 * ARGUMENTS:
276 * Header = Header of the object to remove
277 */
278 {
279 KIRQL oldlvl;
280
281 DPRINT("ObpRemoveEntryDirectory(Header %x)\n",Header);
282
283 KeAcquireSpinLock(&(HEADER_TO_OBJECT_NAME(Header)->Directory->Lock),&oldlvl);
284 if (Header->Entry.Flink && Header->Entry.Blink)
285 {
286 RemoveEntryList(&(Header->Entry));
287 Header->Entry.Flink = Header->Entry.Blink = NULL;
288 }
289 KeReleaseSpinLock(&(HEADER_TO_OBJECT_NAME(Header)->Directory->Lock),oldlvl);
290 }
291
292 NTSTATUS
293 STDCALL
294 ObpCreateDirectory(OB_OPEN_REASON Reason,
295 PVOID ObjectBody,
296 PEPROCESS Process,
297 ULONG HandleCount,
298 ACCESS_MASK GrantedAccess)
299 {
300 PDIRECTORY_OBJECT Directory = ObjectBody;
301
302 if (Reason == ObCreateHandle)
303 {
304 InitializeListHead(&Directory->head);
305 KeInitializeSpinLock(&Directory->Lock);
306 }
307
308 return STATUS_SUCCESS;
309 }
310
311 PVOID
312 ObpFindEntryDirectory(PDIRECTORY_OBJECT DirectoryObject,
313 PWSTR Name,
314 ULONG Attributes)
315 {
316 PLIST_ENTRY current = DirectoryObject->head.Flink;
317 POBJECT_HEADER current_obj;
318
319 DPRINT("ObFindEntryDirectory(dir %x, name %S)\n",DirectoryObject, Name);
320
321 if (Name[0]==0)
322 {
323 return(DirectoryObject);
324 }
325 if (Name[0]=='.' && Name[1]==0)
326 {
327 return(DirectoryObject);
328 }
329 if (Name[0]=='.' && Name[1]=='.' && Name[2]==0)
330 {
331 return(HEADER_TO_OBJECT_NAME(BODY_TO_HEADER(DirectoryObject))->Directory);
332 }
333 while (current!=(&(DirectoryObject->head)))
334 {
335 current_obj = CONTAINING_RECORD(current,OBJECT_HEADER,Entry);
336 DPRINT(" Scanning: %S for: %S\n",HEADER_TO_OBJECT_NAME(current_obj)->Name.Buffer, Name);
337 if (Attributes & OBJ_CASE_INSENSITIVE)
338 {
339 if (_wcsicmp(HEADER_TO_OBJECT_NAME(current_obj)->Name.Buffer, Name)==0)
340 {
341 DPRINT("Found it %x\n",&current_obj->Body);
342 return(&current_obj->Body);
343 }
344 }
345 else
346 {
347 if ( wcscmp(HEADER_TO_OBJECT_NAME(current_obj)->Name.Buffer, Name)==0)
348 {
349 DPRINT("Found it %x\n",&current_obj->Body);
350 return(&current_obj->Body);
351 }
352 }
353 current = current->Flink;
354 }
355 DPRINT(" Not Found: %s() = NULL\n",__FUNCTION__);
356 return(NULL);
357 }
358
359
360 NTSTATUS STDCALL
361 ObpParseDirectory(PVOID Object,
362 PVOID * NextObject,
363 PUNICODE_STRING FullPath,
364 PWSTR * Path,
365 ULONG Attributes)
366 {
367 PWSTR Start;
368 PWSTR End;
369 PVOID FoundObject;
370 KIRQL oldlvl;
371
372 DPRINT("ObpParseDirectory(Object %x, Path %x, *Path %S)\n",
373 Object,Path,*Path);
374
375 *NextObject = NULL;
376
377 if ((*Path) == NULL)
378 {
379 return STATUS_UNSUCCESSFUL;
380 }
381
382 Start = *Path;
383 if (*Start == L'\\')
384 Start++;
385
386 End = wcschr(Start, L'\\');
387 if (End != NULL)
388 {
389 *End = 0;
390 }
391
392 KeAcquireSpinLock(&(((PDIRECTORY_OBJECT)Object)->Lock), &oldlvl);
393 FoundObject = ObpFindEntryDirectory(Object, Start, Attributes);
394 if (FoundObject == NULL)
395 {
396 KeReleaseSpinLock(&(((PDIRECTORY_OBJECT)Object)->Lock), oldlvl);
397 if (End != NULL)
398 {
399 *End = L'\\';
400 }
401 return STATUS_UNSUCCESSFUL;
402 }
403
404 ObReferenceObjectByPointer(FoundObject,
405 STANDARD_RIGHTS_REQUIRED,
406 NULL,
407 UserMode);
408 KeReleaseSpinLock(&(((PDIRECTORY_OBJECT)Object)->Lock), oldlvl);
409 if (End != NULL)
410 {
411 *End = L'\\';
412 *Path = End;
413 }
414 else
415 {
416 *Path = NULL;
417 }
418
419 *NextObject = FoundObject;
420
421 return STATUS_SUCCESS;
422 }
423
424 VOID
425 INIT_FUNCTION
426 ObInit(VOID)
427 {
428 OBJECT_ATTRIBUTES ObjectAttributes;
429 UNICODE_STRING Name;
430 SECURITY_DESCRIPTOR SecurityDescriptor;
431 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
432
433 /* Initialize the security descriptor cache */
434 ObpInitSdCache();
435
436 /* Initialize the Default Event */
437 KeInitializeEvent(&ObpDefaultObject, NotificationEvent, TRUE );
438
439 /* Create the Type Type */
440 DPRINT("Creating Type Type\n");
441 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
442 RtlInitUnicodeString(&Name, L"Type");
443 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
444 ObjectTypeInitializer.ValidAccessMask = OBJECT_TYPE_ALL_ACCESS;
445 ObjectTypeInitializer.UseDefaultObject = TRUE;
446 ObjectTypeInitializer.MaintainTypeList = TRUE;
447 ObjectTypeInitializer.PoolType = NonPagedPool;
448 ObjectTypeInitializer.GenericMapping = ObpTypeMapping;
449 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(OBJECT_TYPE);
450 ObpCreateTypeObject(&ObjectTypeInitializer, &Name, &ObTypeObjectType);
451
452 /* Create the Directory Type */
453 DPRINT("Creating Directory Type\n");
454 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
455 RtlInitUnicodeString(&Name, L"Directory");
456 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
457 ObjectTypeInitializer.ValidAccessMask = DIRECTORY_ALL_ACCESS;
458 ObjectTypeInitializer.UseDefaultObject = FALSE;
459 ObjectTypeInitializer.OpenProcedure = ObpCreateDirectory;
460 ObjectTypeInitializer.ParseProcedure = ObpParseDirectory;
461 ObjectTypeInitializer.MaintainTypeList = FALSE;
462 ObjectTypeInitializer.GenericMapping = ObpDirectoryMapping;
463 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(DIRECTORY_OBJECT);
464 ObpCreateTypeObject(&ObjectTypeInitializer, &Name, &ObDirectoryType);
465
466 /* Create security descriptor */
467 RtlCreateSecurityDescriptor(&SecurityDescriptor,
468 SECURITY_DESCRIPTOR_REVISION1);
469 RtlSetOwnerSecurityDescriptor(&SecurityDescriptor,
470 SeAliasAdminsSid,
471 FALSE);
472 RtlSetGroupSecurityDescriptor(&SecurityDescriptor,
473 SeLocalSystemSid,
474 FALSE);
475 RtlSetDaclSecurityDescriptor(&SecurityDescriptor,
476 TRUE,
477 SePublicDefaultDacl,
478 FALSE);
479
480 /* Create root directory */
481 DPRINT("Creating Root Directory\n");
482 InitializeObjectAttributes(&ObjectAttributes,
483 NULL,
484 OBJ_PERMANENT,
485 NULL,
486 &SecurityDescriptor);
487 ObCreateObject(KernelMode,
488 ObDirectoryType,
489 &ObjectAttributes,
490 KernelMode,
491 NULL,
492 sizeof(DIRECTORY_OBJECT),
493 0,
494 0,
495 (PVOID*)&NameSpaceRoot);
496 ObInsertObject((PVOID)NameSpaceRoot,
497 NULL,
498 DIRECTORY_ALL_ACCESS,
499 0,
500 NULL,
501 NULL);
502
503 /* Create '\ObjectTypes' directory */
504 RtlInitUnicodeString(&Name, L"\\ObjectTypes");
505 InitializeObjectAttributes(&ObjectAttributes,
506 &Name,
507 OBJ_PERMANENT,
508 NULL,
509 &SecurityDescriptor);
510 ObCreateObject(KernelMode,
511 ObDirectoryType,
512 &ObjectAttributes,
513 KernelMode,
514 NULL,
515 sizeof(DIRECTORY_OBJECT),
516 0,
517 0,
518 (PVOID*)&ObpTypeDirectoryObject);
519 ObInsertObject((PVOID)ObpTypeDirectoryObject,
520 NULL,
521 DIRECTORY_ALL_ACCESS,
522 0,
523 NULL,
524 NULL);
525
526 /* Insert the two objects we already created but couldn't add */
527 /* NOTE: Uses TypeList & Creator Info in OB 2.0 */
528 ObpAddEntryDirectory(ObpTypeDirectoryObject, BODY_TO_HEADER(ObTypeObjectType), NULL);
529 ObpAddEntryDirectory(ObpTypeDirectoryObject, BODY_TO_HEADER(ObDirectoryType), NULL);
530
531 /* Create 'symbolic link' object type */
532 ObInitSymbolicLinkImplementation();
533
534 /* FIXME: Hack Hack! */
535 ObSystemDeviceMap = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ObSystemDeviceMap), TAG('O', 'b', 'D', 'm'));
536 RtlZeroMemory(ObSystemDeviceMap, sizeof(*ObSystemDeviceMap));
537 }
538
539 NTSTATUS
540 STDCALL
541 ObpCreateTypeObject(POBJECT_TYPE_INITIALIZER ObjectTypeInitializer,
542 PUNICODE_STRING TypeName,
543 POBJECT_TYPE *ObjectType)
544 {
545 POBJECT_HEADER Header;
546 POBJECT_TYPE LocalObjectType;
547 ULONG HeaderSize;
548 NTSTATUS Status;
549
550 DPRINT("ObpCreateTypeObject(ObjectType: %wZ)\n", TypeName);
551
552 /* Allocate the Object */
553 Status = ObpAllocateObject(NULL,
554 TypeName,
555 ObTypeObjectType,
556 OBJECT_ALLOC_SIZE(sizeof(OBJECT_TYPE)),
557 &Header);
558 if (!NT_SUCCESS(Status))
559 {
560 DPRINT1("ObpAllocateObject failed!\n");
561 return Status;
562 }
563
564 LocalObjectType = (POBJECT_TYPE)&Header->Body;
565 DPRINT("Local ObjectType: %p Header: %p \n", LocalObjectType, Header);
566
567 /* Check if this is the first Object Type */
568 if (!ObTypeObjectType)
569 {
570 ObTypeObjectType = LocalObjectType;
571 Header->Type = ObTypeObjectType;
572 LocalObjectType->Key = TAG('O', 'b', 'j', 'T');
573 }
574 else
575 {
576 CHAR Tag[4];
577 Tag[0] = TypeName->Buffer[0];
578 Tag[1] = TypeName->Buffer[1];
579 Tag[2] = TypeName->Buffer[2];
580 Tag[3] = TypeName->Buffer[3];
581
582 /* Set Tag */
583 DPRINT("Convert: %s \n", Tag);
584 LocalObjectType->Key = *(PULONG)Tag;
585 }
586
587 /* Set it up */
588 LocalObjectType->TypeInfo = *ObjectTypeInitializer;
589 LocalObjectType->Name = *TypeName;
590 LocalObjectType->TypeInfo.PoolType = ObjectTypeInitializer->PoolType;
591
592 /* These two flags need to be manually set up */
593 Header->Flags |= OB_FLAG_KERNEL_MODE | OB_FLAG_PERMANENT;
594
595 /* Check if we have to maintain a type list */
596 if (NtGlobalFlag & FLG_MAINTAIN_OBJECT_TYPELIST)
597 {
598 /* Enable support */
599 LocalObjectType->TypeInfo.MaintainTypeList = TRUE;
600 }
601
602 /* Calculate how much space our header'll take up */
603 HeaderSize = sizeof(OBJECT_HEADER) + sizeof(OBJECT_HEADER_NAME_INFO) +
604 (ObjectTypeInitializer->MaintainHandleCount ?
605 sizeof(OBJECT_HEADER_HANDLE_INFO) : 0);
606
607 /* Update the Pool Charges */
608 if (ObjectTypeInitializer->PoolType == NonPagedPool)
609 {
610 LocalObjectType->TypeInfo.DefaultNonPagedPoolCharge += HeaderSize;
611 }
612 else
613 {
614 LocalObjectType->TypeInfo.DefaultPagedPoolCharge += HeaderSize;
615 }
616
617 /* All objects types need a security procedure */
618 if (!ObjectTypeInitializer->SecurityProcedure)
619 {
620 LocalObjectType->TypeInfo.SecurityProcedure = SeDefaultObjectMethod;
621 }
622
623 /* Select the Wait Object */
624 if (LocalObjectType->TypeInfo.UseDefaultObject)
625 {
626 /* Add the SYNCHRONIZE access mask since it's waitable */
627 LocalObjectType->TypeInfo.ValidAccessMask |= SYNCHRONIZE;
628
629 /* Use the "Default Object", a simple event */
630 LocalObjectType->DefaultObject = &ObpDefaultObject;
631 }
632 /* Special system objects get an optimized hack so they can be waited on */
633 else if (TypeName->Length == 8 && !wcscmp(TypeName->Buffer, L"File"))
634 {
635 LocalObjectType->DefaultObject = (PVOID)FIELD_OFFSET(FILE_OBJECT, Event);
636 }
637 /* FIXME: When LPC stops sucking, add a hack for Waitable Ports */
638 else
639 {
640 /* No default Object */
641 LocalObjectType->DefaultObject = NULL;
642 }
643
644 /* Initialize Object Type components */
645 ExInitializeResourceLite(&LocalObjectType->Mutex);
646 InitializeListHead(&LocalObjectType->TypeList);
647
648 /* Insert it into the Object Directory */
649 if (ObpTypeDirectoryObject)
650 {
651 ObpAddEntryDirectory(ObpTypeDirectoryObject, Header, TypeName->Buffer);
652 ObReferenceObject(ObpTypeDirectoryObject);
653 }
654
655 *ObjectType = LocalObjectType;
656 return Status;
657 }
658
659 /* EOF */