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