[SHELL32] Improve UI of drive formatting (#2048)
[reactos.git] / ntoskrnl / ob / obdir.c
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 */