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