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