Create a branch for working on csrss and co.
[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 BOOLEAN ObpLUIDDeviceMapsEnabled;
21 POBJECT_TYPE ObDirectoryType = 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 %ld\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 ObDirectoryType,
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 ObDirectoryType,
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 ObDirectoryType,
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 */