xref: /reactos/ntoskrnl/ob/obdir.c (revision 4d3df0da)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/ob/obdir.c
5  * PURPOSE:         Manages the Object Manager's Directory Implementation,
6  *                  such as functions for addition, deletion and lookup into
7  *                  the Object Manager's namespace. These routines are separate
8  *                  from the Namespace Implementation because they are largely
9  *                  independent and could be used for other namespaces.
10  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
11  *                  Thomas Weidenmueller (w3seek@reactos.org)
12  */
13 
14 /* INCLUDES ***************************************************************/
15 
16 #include <ntoskrnl.h>
17 #define NDEBUG
18 #include <debug.h>
19 
20 POBJECT_TYPE ObpDirectoryObjectType = NULL;
21 
22 /* PRIVATE FUNCTIONS ******************************************************/
23 
24 /*++
25 * @name ObpInsertEntryDirectory
26 *
27 *     The ObpInsertEntryDirectory routine <FILLMEIN>.
28 *
29 * @param Parent
30 *        <FILLMEIN>.
31 *
32 * @param Context
33 *        <FILLMEIN>.
34 *
35 * @param ObjectHeader
36 *        <FILLMEIN>.
37 *
38 * @return TRUE if the object was inserted, FALSE otherwise.
39 *
40 * @remarks None.
41 *
42 *--*/
43 BOOLEAN
44 NTAPI
45 ObpInsertEntryDirectory(IN POBJECT_DIRECTORY Parent,
46                         IN POBP_LOOKUP_CONTEXT Context,
47                         IN POBJECT_HEADER ObjectHeader)
48 {
49     POBJECT_DIRECTORY_ENTRY *AllocatedEntry;
50     POBJECT_DIRECTORY_ENTRY NewEntry;
51     POBJECT_HEADER_NAME_INFO HeaderNameInfo;
52 
53     /* Make sure we have a name */
54     ASSERT(ObjectHeader->NameInfoOffset != 0);
55 
56     /* Validate the context */
57     if ((Context->Object) ||
58         !(Context->DirectoryLocked) ||
59         (Parent != Context->Directory))
60     {
61         /* Invalid context */
62         DPRINT1("OB: ObpInsertEntryDirectory - invalid context %p %u\n",
63                 Context, Context->DirectoryLocked);
64         ASSERT(FALSE);
65         return FALSE;
66     }
67 
68     /* Allocate a new Directory Entry */
69     NewEntry = ExAllocatePoolWithTag(PagedPool,
70                                      sizeof(OBJECT_DIRECTORY_ENTRY),
71                                      OB_DIR_TAG);
72     if (!NewEntry) return FALSE;
73 
74     /* Save the hash */
75     NewEntry->HashValue = Context->HashValue;
76 
77     /* Get the Object Name Information */
78     HeaderNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
79 
80     /* Get the Allocated entry */
81     AllocatedEntry = &Parent->HashBuckets[Context->HashIndex];
82 
83     /* Set it */
84     NewEntry->ChainLink = *AllocatedEntry;
85     *AllocatedEntry = NewEntry;
86 
87     /* Associate the Object */
88     NewEntry->Object = &ObjectHeader->Body;
89 
90     /* Associate the Directory */
91     HeaderNameInfo->Directory = Parent;
92     return TRUE;
93 }
94 
95 /*++
96 * @name ObpGetShadowDirectory
97 *
98 *     The ObpGetShadowDirectory routine <FILLMEIN>.
99 *
100 * @param Directory
101 *        <FILLMEIN>.
102 *
103 * @return Pointer to the global DOS directory if any, or NULL otherwise.
104 *
105 * @remarks None.
106 *
107 *--*/
108 POBJECT_DIRECTORY
109 NTAPI
110 ObpGetShadowDirectory(IN POBJECT_DIRECTORY Directory)
111 {
112     PDEVICE_MAP DeviceMap;
113     POBJECT_DIRECTORY GlobalDosDirectory = NULL;
114 
115     /* Acquire the device map lock */
116     KeAcquireGuardedMutex(&ObpDeviceMapLock);
117 
118     /* Get the global DOS directory if any */
119     DeviceMap = Directory->DeviceMap;
120     if (DeviceMap != NULL)
121     {
122         GlobalDosDirectory = DeviceMap->GlobalDosDevicesDirectory;
123     }
124 
125     /* Release the devicemap lock */
126     KeReleaseGuardedMutex(&ObpDeviceMapLock);
127 
128     return GlobalDosDirectory;
129 }
130 
131 /*++
132 * @name ObpLookupEntryDirectory
133 *
134 *     The ObpLookupEntryDirectory routine <FILLMEIN>.
135 *
136 * @param Directory
137 *        <FILLMEIN>.
138 *
139 * @param Name
140 *        <FILLMEIN>.
141 *
142 * @param Attributes
143 *        <FILLMEIN>.
144 *
145 * @param SearchShadow
146 *        <FILLMEIN>.
147 *
148 * @param Context
149 *        <FILLMEIN>.
150 *
151 * @return Pointer to the object which was found, or NULL otherwise.
152 *
153 * @remarks None.
154 *
155 *--*/
156 PVOID
157 NTAPI
158 ObpLookupEntryDirectory(IN POBJECT_DIRECTORY Directory,
159                         IN PUNICODE_STRING Name,
160                         IN ULONG Attributes,
161                         IN UCHAR SearchShadow,
162                         IN POBP_LOOKUP_CONTEXT Context)
163 {
164     BOOLEAN CaseInsensitive = FALSE;
165     POBJECT_HEADER_NAME_INFO HeaderNameInfo;
166     POBJECT_HEADER ObjectHeader;
167     ULONG HashValue;
168     ULONG HashIndex;
169     LONG TotalChars;
170     WCHAR CurrentChar;
171     POBJECT_DIRECTORY_ENTRY *AllocatedEntry;
172     POBJECT_DIRECTORY_ENTRY *LookupBucket;
173     POBJECT_DIRECTORY_ENTRY CurrentEntry;
174     PVOID FoundObject = NULL;
175     PWSTR Buffer;
176     POBJECT_DIRECTORY ShadowDirectory;
177     PAGED_CODE();
178 
179     /* Check if we should search the shadow directory */
180     if (ObpLUIDDeviceMapsEnabled == 0) SearchShadow = FALSE;
181 
182     /* Fail if we don't have a directory or name */
183     if (!(Directory) || !(Name)) goto Quickie;
184 
185     /* Get name information */
186     TotalChars = Name->Length / sizeof(WCHAR);
187     Buffer = Name->Buffer;
188 
189     /* Set up case-sensitivity */
190     if (Attributes & OBJ_CASE_INSENSITIVE) CaseInsensitive = TRUE;
191 
192     /* Fail if the name is empty */
193     if (!(Buffer) || !(TotalChars)) goto Quickie;
194 
195     /* Create the Hash */
196     for (HashValue = 0; TotalChars; TotalChars--)
197     {
198         /* Go to the next Character */
199         CurrentChar = *Buffer++;
200 
201         /* Prepare the Hash */
202         HashValue += (HashValue << 1) + (HashValue >> 1);
203 
204         /* Create the rest based on the name */
205         if (CurrentChar < 'a') HashValue += CurrentChar;
206         else if (CurrentChar > 'z') HashValue += RtlUpcaseUnicodeChar(CurrentChar);
207         else HashValue += (CurrentChar - ('a'-'A'));
208     }
209 
210     /* Merge it with our number of hash buckets */
211     HashIndex = HashValue % 37;
212 
213     /* Save the result */
214     Context->HashValue = HashValue;
215     Context->HashIndex = (USHORT)HashIndex;
216 
217 DoItAgain:
218     /* Get the root entry and set it as our lookup bucket */
219     AllocatedEntry = &Directory->HashBuckets[HashIndex];
220     LookupBucket = AllocatedEntry;
221 
222     /* Check if the directory is already locked */
223     if (!Context->DirectoryLocked)
224     {
225         /* Lock it */
226         ObpAcquireDirectoryLockShared(Directory, Context);
227     }
228 
229     /* Start looping */
230     while ((CurrentEntry = *AllocatedEntry))
231     {
232         /* Do the hashes match? */
233         if (CurrentEntry->HashValue == HashValue)
234         {
235             /* Make sure that it has a name */
236             ObjectHeader = OBJECT_TO_OBJECT_HEADER(CurrentEntry->Object);
237 
238             /* Get the name information */
239             ASSERT(ObjectHeader->NameInfoOffset != 0);
240             HeaderNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
241 
242             /* Do the names match? */
243             if ((Name->Length == HeaderNameInfo->Name.Length) &&
244                 (RtlEqualUnicodeString(Name, &HeaderNameInfo->Name, CaseInsensitive)))
245             {
246                 break;
247             }
248         }
249 
250         /* Move to the next entry */
251         AllocatedEntry = &CurrentEntry->ChainLink;
252     }
253 
254     /* Check if we still have an entry */
255     if (CurrentEntry)
256     {
257         /* Set this entry as the first, to speed up incoming insertion */
258         if (AllocatedEntry != LookupBucket)
259         {
260             /* Check if the directory was locked or convert the lock */
261             if ((Context->DirectoryLocked) ||
262                 (ExConvertPushLockSharedToExclusive(&Directory->Lock)))
263             {
264                 /* Set the Current Entry */
265                 *AllocatedEntry = CurrentEntry->ChainLink;
266 
267                 /* Link to the old Hash Entry */
268                 CurrentEntry->ChainLink = *LookupBucket;
269 
270                 /* Set the new Hash Entry */
271                 *LookupBucket = CurrentEntry;
272             }
273         }
274 
275         /* Save the found object */
276         FoundObject = CurrentEntry->Object;
277         goto Quickie;
278     }
279     else
280     {
281         /* Check if the directory was locked */
282         if (!Context->DirectoryLocked)
283         {
284             /* Release the lock */
285             ObpReleaseDirectoryLock(Directory, Context);
286         }
287 
288         /* Check if we should scan the shadow directory */
289         if ((SearchShadow) && (Directory->DeviceMap))
290         {
291             ShadowDirectory = ObpGetShadowDirectory(Directory);
292             /* A global DOS directory was found, loop it again */
293             if (ShadowDirectory != NULL)
294             {
295                 Directory = ShadowDirectory;
296                 goto DoItAgain;
297             }
298         }
299     }
300 
301 Quickie:
302     /* Check if we inserted an object */
303     if (FoundObject)
304     {
305         /* Get the object name information */
306         ObjectHeader = OBJECT_TO_OBJECT_HEADER(FoundObject);
307         ObpReferenceNameInfo(ObjectHeader);
308 
309         /* Reference the object being looked up */
310         ObReferenceObject(FoundObject);
311 
312         /* Check if the directory was locked */
313         if (!Context->DirectoryLocked)
314         {
315             /* Release the lock */
316             ObpReleaseDirectoryLock(Directory, Context);
317         }
318     }
319 
320     /* Check if we found an object already */
321     if (Context->Object)
322     {
323         /* We already did a lookup, so remove this object's query reference */
324         ObjectHeader = OBJECT_TO_OBJECT_HEADER(Context->Object);
325         HeaderNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
326         ObpDereferenceNameInfo(HeaderNameInfo);
327 
328         /* Also dereference the object itself */
329         ObDereferenceObject(Context->Object);
330     }
331 
332     /* Return the object we found */
333     Context->Object = FoundObject;
334     return FoundObject;
335 }
336 
337 /*++
338 * @name ObpDeleteEntryDirectory
339 *
340 *     The ObpDeleteEntryDirectory routine <FILLMEIN>.
341 *
342 * @param Context
343 *        <FILLMEIN>.
344 *
345 * @return TRUE if the object was deleted, FALSE otherwise.
346 *
347 * @remarks None.
348 *
349 *--*/
350 BOOLEAN
351 NTAPI
352 ObpDeleteEntryDirectory(POBP_LOOKUP_CONTEXT Context)
353 {
354     POBJECT_DIRECTORY Directory;
355     POBJECT_DIRECTORY_ENTRY *AllocatedEntry;
356     POBJECT_DIRECTORY_ENTRY CurrentEntry;
357 
358     /* Get the Directory */
359     Directory = Context->Directory;
360     if (!Directory) return FALSE;
361 
362     /* Get the Entry */
363     AllocatedEntry = &Directory->HashBuckets[Context->HashIndex];
364     CurrentEntry = *AllocatedEntry;
365 
366     /* Unlink the Entry */
367     *AllocatedEntry = CurrentEntry->ChainLink;
368     CurrentEntry->ChainLink = NULL;
369 
370     /* Free it */
371     ExFreePoolWithTag(CurrentEntry, OB_DIR_TAG);
372 
373     /* Return */
374     return TRUE;
375 }
376 
377 /* FUNCTIONS **************************************************************/
378 
379 /*++
380 * @name NtOpenDirectoryObject
381 * @implemented NT4
382 *
383 *     The NtOpenDirectoryObject routine opens a namespace directory object.
384 *
385 * @param DirectoryHandle
386 *        Variable which receives the directory handle.
387 *
388 * @param DesiredAccess
389 *        Desired access to the directory.
390 *
391 * @param ObjectAttributes
392 *        Structure describing the directory.
393 *
394 * @return STATUS_SUCCESS or appropriate error value.
395 *
396 * @remarks None.
397 *
398 *--*/
399 NTSTATUS
400 NTAPI
401 NtOpenDirectoryObject(OUT PHANDLE DirectoryHandle,
402                       IN ACCESS_MASK DesiredAccess,
403                       IN POBJECT_ATTRIBUTES ObjectAttributes)
404 {
405     HANDLE Directory;
406     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
407     NTSTATUS Status;
408     PAGED_CODE();
409 
410     /* Check if we need to do any probing */
411     if (PreviousMode != KernelMode)
412     {
413         _SEH2_TRY
414         {
415             /* Probe the return handle */
416             ProbeForWriteHandle(DirectoryHandle);
417         }
418         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
419         {
420             /* Return the exception code */
421             _SEH2_YIELD(return _SEH2_GetExceptionCode());
422         }
423         _SEH2_END;
424     }
425 
426     /* Open the directory object */
427     Status = ObOpenObjectByName(ObjectAttributes,
428                                 ObpDirectoryObjectType,
429                                 PreviousMode,
430                                 NULL,
431                                 DesiredAccess,
432                                 NULL,
433                                 &Directory);
434     if (NT_SUCCESS(Status))
435     {
436         _SEH2_TRY
437         {
438             /* Write back the handle to the caller */
439             *DirectoryHandle = Directory;
440         }
441         _SEH2_EXCEPT(ExSystemExceptionFilter())
442         {
443             /* Get the exception code */
444             Status = _SEH2_GetExceptionCode();
445         }
446         _SEH2_END;
447     }
448 
449     /* Return the status to the caller */
450     return Status;
451 }
452 
453 /*++
454 * @name NtQueryDirectoryObject
455 * @implemented NT4
456 *
457 *     The NtQueryDirectoryObject routine reads information from a directory in
458 *     the system namespace.
459 *
460 * @param DirectoryHandle
461 *        Handle obtained with NtOpenDirectoryObject which
462 *        must grant DIRECTORY_QUERY access to the directory object.
463 *
464 * @param Buffer
465 *        Buffer to hold the data read.
466 *
467 * @param BufferLength
468 *        Size of the buffer in bytes.
469 *
470 * @param ReturnSingleEntry
471 *        When TRUE, only 1 entry is written in DirObjInformation;
472 *        otherwise as many as will fit in the buffer.
473 *
474 * @param RestartScan
475 *        If TRUE start reading at index 0.
476 *        If FALSE start reading at the index specified by *ObjectIndex.
477 *
478 * @param Context
479 *        Zero based index into the directory, interpretation
480 *        depends on RestartScan.
481 *
482 * @param ReturnLength
483 *        Caller supplied storage for the number of bytes
484 *        written (or NULL).
485 *
486 * @return STATUS_SUCCESS or appropriate error value.
487 *
488 * @remarks Although you can iterate over the directory by calling this
489 *          function multiple times, the directory is unlocked between
490 *          calls. This means that another thread can change the directory
491 *          and so iterating doesn't guarantee a consistent picture of the
492 *          directory. Best thing is to retrieve all directory entries in
493 *          one call.
494 *
495 *--*/
496 NTSTATUS
497 NTAPI
498 NtQueryDirectoryObject(IN HANDLE DirectoryHandle,
499                        OUT PVOID Buffer,
500                        IN ULONG BufferLength,
501                        IN BOOLEAN ReturnSingleEntry,
502                        IN BOOLEAN RestartScan,
503                        IN OUT PULONG Context,
504                        OUT PULONG ReturnLength OPTIONAL)
505 {
506     POBJECT_DIRECTORY Directory;
507     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
508     ULONG SkipEntries = 0;
509     NTSTATUS Status;
510     PVOID LocalBuffer;
511     POBJECT_DIRECTORY_INFORMATION DirectoryInfo;
512     ULONG Length, TotalLength;
513     ULONG Count, CurrentEntry;
514     ULONG Hash;
515     POBJECT_DIRECTORY_ENTRY Entry;
516     POBJECT_HEADER ObjectHeader;
517     POBJECT_HEADER_NAME_INFO ObjectNameInfo;
518     UNICODE_STRING Name;
519     PWSTR p;
520     OBP_LOOKUP_CONTEXT LookupContext;
521     PAGED_CODE();
522 
523     /* Initialize lookup */
524     ObpInitializeLookupContext(&LookupContext);
525 
526     /* Check if we need to do any probing */
527     if (PreviousMode != KernelMode)
528     {
529         _SEH2_TRY
530         {
531             /* Probe the buffer (assuming it will hold Unicode characters) */
532             ProbeForWrite(Buffer, BufferLength, sizeof(WCHAR));
533 
534             /* Probe the context and copy it unless scan-restart was requested */
535             ProbeForWriteUlong(Context);
536             if (!RestartScan) SkipEntries = *Context;
537 
538             /* Probe the return length if the caller specified one */
539             if (ReturnLength) ProbeForWriteUlong(ReturnLength);
540         }
541         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
542         {
543             /* Return the exception code */
544             _SEH2_YIELD(return _SEH2_GetExceptionCode());
545         }
546         _SEH2_END;
547     }
548     else if (!RestartScan)
549     {
550         /* This is kernel mode, save the context without probing, if needed */
551         SkipEntries = *Context;
552     }
553 
554     /* Allocate a buffer */
555     LocalBuffer = ExAllocatePoolWithTag(PagedPool,
556                                         sizeof(OBJECT_DIRECTORY_INFORMATION) +
557                                         BufferLength,
558                                         OB_NAME_TAG);
559     if (!LocalBuffer) return STATUS_INSUFFICIENT_RESOURCES;
560     RtlZeroMemory(LocalBuffer, BufferLength);
561 
562     /* Get a reference to directory */
563     Status = ObReferenceObjectByHandle(DirectoryHandle,
564                                        DIRECTORY_QUERY,
565                                        ObpDirectoryObjectType,
566                                        PreviousMode,
567                                        (PVOID*)&Directory,
568                                        NULL);
569     if (!NT_SUCCESS(Status))
570     {
571         /* Free the buffer and fail */
572         ExFreePoolWithTag(LocalBuffer, OB_NAME_TAG);
573         return Status;
574     }
575 
576     /* Lock directory in shared mode */
577     ObpAcquireDirectoryLockShared(Directory, &LookupContext);
578 
579     /* Start at position 0 */
580     DirectoryInfo = (POBJECT_DIRECTORY_INFORMATION)LocalBuffer;
581     TotalLength = sizeof(OBJECT_DIRECTORY_INFORMATION);
582 
583     /* Start with 0 entries */
584     Count = 0;
585     CurrentEntry = 0;
586 
587     /* Set default status and start looping */
588     Status = STATUS_NO_MORE_ENTRIES;
589     for (Hash = 0; Hash < 37; Hash++)
590     {
591         /* Get this entry and loop all of them */
592         Entry = Directory->HashBuckets[Hash];
593         while (Entry)
594         {
595             /* Check if we should process this entry */
596             if (SkipEntries == CurrentEntry++)
597             {
598                 /* Get the header data */
599                 ObjectHeader = OBJECT_TO_OBJECT_HEADER(Entry->Object);
600                 ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
601 
602                 /* Get the object name */
603                 if (ObjectNameInfo)
604                 {
605                     /* Use the one we have */
606                     Name = ObjectNameInfo->Name;
607                 }
608                 else
609                 {
610                     /* Otherwise, use an empty one */
611                     RtlInitEmptyUnicodeString(&Name, NULL, 0);
612                 }
613 
614                 /* Calculate the length for this entry */
615                 Length = sizeof(OBJECT_DIRECTORY_INFORMATION) +
616                          Name.Length + sizeof(UNICODE_NULL) +
617                          ObjectHeader->Type->Name.Length + sizeof(UNICODE_NULL);
618 
619                 /* Make sure this entry won't overflow */
620                 if ((TotalLength + Length) > BufferLength)
621                 {
622                     /* Check if the caller wanted only an entry */
623                     if (ReturnSingleEntry)
624                     {
625                         /* Then we'll fail and ask for more buffer */
626                         TotalLength += Length;
627                         Status = STATUS_BUFFER_TOO_SMALL;
628                     }
629                     else
630                     {
631                         /* Otherwise, we'll say we're done for now */
632                         Status = STATUS_MORE_ENTRIES;
633                     }
634 
635                     /* Decrease the entry since we didn't process */
636                     CurrentEntry--;
637                     goto Quickie;
638                 }
639 
640                 /* Now fill in the buffer */
641                 DirectoryInfo->Name.Length = Name.Length;
642                 DirectoryInfo->Name.MaximumLength = Name.Length +
643                                                     sizeof(UNICODE_NULL);
644                 DirectoryInfo->Name.Buffer = Name.Buffer;
645                 DirectoryInfo->TypeName.Length = ObjectHeader->
646                                                  Type->Name.Length;
647                 DirectoryInfo->TypeName.MaximumLength = ObjectHeader->
648                                                         Type->Name.Length +
649                                                         sizeof(UNICODE_NULL);
650                 DirectoryInfo->TypeName.Buffer = ObjectHeader->
651                                                  Type->Name.Buffer;
652 
653                 /* Set success */
654                 Status = STATUS_SUCCESS;
655 
656                 /* Increase statistics */
657                 TotalLength += Length;
658                 DirectoryInfo++;
659                 Count++;
660 
661                 /* If the caller only wanted an entry, bail out */
662                 if (ReturnSingleEntry) goto Quickie;
663 
664                 /* Increase the key by one */
665                 SkipEntries++;
666             }
667 
668             /* Move to the next directory */
669             Entry = Entry->ChainLink;
670         }
671     }
672 
673 Quickie:
674     /* Make sure we got success */
675     if (NT_SUCCESS(Status))
676     {
677         /* Clear the current pointer and set it */
678         RtlZeroMemory(DirectoryInfo, sizeof(OBJECT_DIRECTORY_INFORMATION));
679         DirectoryInfo++;
680 
681         /* Set the buffer here now and loop entries */
682         p = (PWSTR)DirectoryInfo;
683         DirectoryInfo = LocalBuffer;
684         while (Count--)
685         {
686             /* Copy the name buffer */
687             RtlCopyMemory(p,
688                           DirectoryInfo->Name.Buffer,
689                           DirectoryInfo->Name.Length);
690 
691             /* Now fixup the pointers */
692             DirectoryInfo->Name.Buffer = (PVOID)((ULONG_PTR)Buffer +
693                                                  ((ULONG_PTR)p -
694                                                   (ULONG_PTR)LocalBuffer));
695 
696             /* Advance in buffer and NULL-terminate */
697             p = (PVOID)((ULONG_PTR)p + DirectoryInfo->Name.Length);
698             *p++ = UNICODE_NULL;
699 
700             /* Now copy the type name buffer */
701             RtlCopyMemory(p,
702                           DirectoryInfo->TypeName.Buffer,
703                           DirectoryInfo->TypeName.Length);
704 
705             /* Now fixup the pointers */
706             DirectoryInfo->TypeName.Buffer = (PVOID)((ULONG_PTR)Buffer +
707                                                      ((ULONG_PTR)p -
708                                                      (ULONG_PTR)LocalBuffer));
709 
710             /* Advance in buffer and NULL-terminate */
711             p = (PVOID)((ULONG_PTR)p + DirectoryInfo->TypeName.Length);
712             *p++ = UNICODE_NULL;
713 
714             /* Move to the next entry */
715             DirectoryInfo++;
716         }
717 
718         /* Set the key */
719         *Context = CurrentEntry;
720     }
721 
722     _SEH2_TRY
723     {
724         /* Copy the buffer */
725         RtlCopyMemory(Buffer,
726                       LocalBuffer,
727                       (TotalLength <= BufferLength) ?
728                       TotalLength : BufferLength);
729 
730         /* Check if the caller requested the return length and return it*/
731         if (ReturnLength) *ReturnLength = TotalLength;
732     }
733     _SEH2_EXCEPT(ExSystemExceptionFilter())
734     {
735         /* Get the exception code */
736         Status = _SEH2_GetExceptionCode();
737     }
738     _SEH2_END;
739 
740     /* Unlock the directory */
741     ObpReleaseDirectoryLock(Directory, &LookupContext);
742 
743     /* Dereference the directory and free our buffer */
744     ObDereferenceObject(Directory);
745     ExFreePoolWithTag(LocalBuffer, OB_NAME_TAG);
746 
747     /* Return status to caller */
748     return Status;
749 }
750 
751 /*++
752 * @name NtCreateDirectoryObject
753 * @implemented NT4
754 *
755 *     The NtOpenDirectoryObject routine creates or opens a directory object.
756 *
757 * @param DirectoryHandle
758 *        Variable which receives the directory handle.
759 *
760 * @param DesiredAccess
761 *        Desired access to the directory.
762 *
763 * @param ObjectAttributes
764 *        Structure describing the directory.
765 *
766 * @return STATUS_SUCCESS or appropriate error value.
767 *
768 * @remarks None.
769 *
770 *--*/
771 NTSTATUS
772 NTAPI
773 NtCreateDirectoryObject(OUT PHANDLE DirectoryHandle,
774                         IN ACCESS_MASK DesiredAccess,
775                         IN POBJECT_ATTRIBUTES ObjectAttributes)
776 {
777     POBJECT_DIRECTORY Directory;
778     HANDLE NewHandle;
779     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
780     NTSTATUS Status;
781     PAGED_CODE();
782 
783     /* Check if we need to do any probing */
784     if (PreviousMode != KernelMode)
785     {
786         _SEH2_TRY
787         {
788             /* Probe the return handle */
789             ProbeForWriteHandle(DirectoryHandle);
790         }
791         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
792         {
793             /* Return the exception code */
794             _SEH2_YIELD(return _SEH2_GetExceptionCode());
795         }
796         _SEH2_END;
797     }
798 
799     /* Create the object */
800     Status = ObCreateObject(PreviousMode,
801                             ObpDirectoryObjectType,
802                             ObjectAttributes,
803                             PreviousMode,
804                             NULL,
805                             sizeof(OBJECT_DIRECTORY),
806                             0,
807                             0,
808                             (PVOID*)&Directory);
809     if (!NT_SUCCESS(Status)) return Status;
810 
811     /* Setup the object */
812     RtlZeroMemory(Directory, sizeof(OBJECT_DIRECTORY));
813     ExInitializePushLock(&Directory->Lock);
814     Directory->SessionId = -1;
815 
816     /* Insert it into the handle table */
817     Status = ObInsertObject((PVOID)Directory,
818                             NULL,
819                             DesiredAccess,
820                             0,
821                             NULL,
822                             &NewHandle);
823 
824     /* Enter SEH to protect write */
825     _SEH2_TRY
826     {
827         /* Return the handle back to the caller */
828         *DirectoryHandle = NewHandle;
829     }
830     _SEH2_EXCEPT(ExSystemExceptionFilter())
831     {
832         /* Get the exception code */
833         Status = _SEH2_GetExceptionCode();
834     }
835     _SEH2_END;
836 
837     /* Return status to caller */
838     return Status;
839 }
840 
841 /* EOF */
842