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)
14 /* INCLUDES ***************************************************************/
20 BOOLEAN ObpLUIDDeviceMapsEnabled
;
21 POBJECT_TYPE ObDirectoryType
= NULL
;
23 /* PRIVATE FUNCTIONS ******************************************************/
26 * @name ObpInsertEntryDirectory
28 * The ObpInsertEntryDirectory routine <FILLMEIN>.
39 * @return TRUE if the object was inserted, FALSE otherwise.
46 ObpInsertEntryDirectory(IN POBJECT_DIRECTORY Parent
,
47 IN POBP_LOOKUP_CONTEXT Context
,
48 IN POBJECT_HEADER ObjectHeader
)
50 POBJECT_DIRECTORY_ENTRY
*AllocatedEntry
;
51 POBJECT_DIRECTORY_ENTRY NewEntry
;
52 POBJECT_HEADER_NAME_INFO HeaderNameInfo
;
54 /* Make sure we have a name */
55 ASSERT(ObjectHeader
->NameInfoOffset
!= 0);
57 /* Validate the context */
58 if ((Context
->Object
) ||
59 !(Context
->DirectoryLocked
) ||
60 (Parent
!= Context
->Directory
))
63 DPRINT1("OB: ObpInsertEntryDirectory - invalid context %p %u\n",
64 Context
, Context
->DirectoryLocked
);
69 /* Allocate a new Directory Entry */
70 NewEntry
= ExAllocatePoolWithTag(PagedPool
,
71 sizeof(OBJECT_DIRECTORY_ENTRY
),
73 if (!NewEntry
) return FALSE
;
76 NewEntry
->HashValue
= Context
->HashValue
;
78 /* Get the Object Name Information */
79 HeaderNameInfo
= OBJECT_HEADER_TO_NAME_INFO(ObjectHeader
);
81 /* Get the Allocated entry */
82 AllocatedEntry
= &Parent
->HashBuckets
[Context
->HashIndex
];
85 NewEntry
->ChainLink
= *AllocatedEntry
;
86 *AllocatedEntry
= NewEntry
;
88 /* Associate the Object */
89 NewEntry
->Object
= &ObjectHeader
->Body
;
91 /* Associate the Directory */
92 HeaderNameInfo
->Directory
= Parent
;
97 * @name ObpLookupEntryDirectory
99 * The ObpLookupEntryDirectory routine <FILLMEIN>.
110 * @param SearchShadow
116 * @return Pointer to the object which was found, or NULL otherwise.
123 ObpLookupEntryDirectory(IN POBJECT_DIRECTORY Directory
,
124 IN PUNICODE_STRING Name
,
126 IN UCHAR SearchShadow
,
127 IN POBP_LOOKUP_CONTEXT Context
)
129 BOOLEAN CaseInsensitive
= FALSE
;
130 POBJECT_HEADER_NAME_INFO HeaderNameInfo
;
131 POBJECT_HEADER ObjectHeader
;
136 POBJECT_DIRECTORY_ENTRY
*AllocatedEntry
;
137 POBJECT_DIRECTORY_ENTRY
*LookupBucket
;
138 POBJECT_DIRECTORY_ENTRY CurrentEntry
;
139 PVOID FoundObject
= NULL
;
143 /* Check if we should search the shadow directory */
144 if (!ObpLUIDDeviceMapsEnabled
) SearchShadow
= FALSE
;
146 /* Fail if we don't have a directory or name */
147 if (!(Directory
) || !(Name
)) goto Quickie
;
149 /* Get name information */
150 TotalChars
= Name
->Length
/ sizeof(WCHAR
);
151 Buffer
= Name
->Buffer
;
153 /* Set up case-sensitivity */
154 if (Attributes
& OBJ_CASE_INSENSITIVE
) CaseInsensitive
= TRUE
;
156 /* Fail if the name is empty */
157 if (!(Buffer
) || !(TotalChars
)) goto Quickie
;
159 /* Create the Hash */
160 for (HashValue
= 0; TotalChars
; TotalChars
--)
162 /* Go to the next Character */
163 CurrentChar
= *Buffer
++;
165 /* Prepare the Hash */
166 HashValue
+= (HashValue
<< 1) + (HashValue
>> 1);
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'));
174 /* Merge it with our number of hash buckets */
175 HashIndex
= HashValue
% 37;
177 /* Save the result */
178 Context
->HashValue
= HashValue
;
179 Context
->HashIndex
= (USHORT
)HashIndex
;
181 /* Get the root entry and set it as our lookup bucket */
182 AllocatedEntry
= &Directory
->HashBuckets
[HashIndex
];
183 LookupBucket
= AllocatedEntry
;
185 /* Check if the directory is already locked */
186 if (!Context
->DirectoryLocked
)
189 ObpAcquireDirectoryLockShared(Directory
, Context
);
193 while ((CurrentEntry
= *AllocatedEntry
))
195 /* Do the hashes match? */
196 if (CurrentEntry
->HashValue
== HashValue
)
198 /* Make sure that it has a name */
199 ObjectHeader
= OBJECT_TO_OBJECT_HEADER(CurrentEntry
->Object
);
201 /* Get the name information */
202 ASSERT(ObjectHeader
->NameInfoOffset
!= 0);
203 HeaderNameInfo
= OBJECT_HEADER_TO_NAME_INFO(ObjectHeader
);
205 /* Do the names match? */
206 if ((Name
->Length
== HeaderNameInfo
->Name
.Length
) &&
207 (RtlEqualUnicodeString(Name
, &HeaderNameInfo
->Name
, CaseInsensitive
)))
213 /* Move to the next entry */
214 AllocatedEntry
= &CurrentEntry
->ChainLink
;
217 /* Check if we still have an entry */
220 /* Set this entry as the first, to speed up incoming insertion */
221 if (AllocatedEntry
!= LookupBucket
)
223 /* Check if the directory was locked or convert the lock */
224 if ((Context
->DirectoryLocked
) ||
225 (ExConvertPushLockSharedToExclusive(&Directory
->Lock
)))
227 /* Set the Current Entry */
228 *AllocatedEntry
= CurrentEntry
->ChainLink
;
230 /* Link to the old Hash Entry */
231 CurrentEntry
->ChainLink
= *LookupBucket
;
233 /* Set the new Hash Entry */
234 *LookupBucket
= CurrentEntry
;
238 /* Save the found object */
239 FoundObject
= CurrentEntry
->Object
;
244 /* Check if the directory was locked */
245 if (!Context
->DirectoryLocked
)
247 /* Release the lock */
248 ObpReleaseDirectoryLock(Directory
, Context
);
251 /* Check if we should scan the shadow directory */
252 if ((SearchShadow
) && (Directory
->DeviceMap
))
254 /* FIXME: We don't support this yet */
260 /* Check if we inserted an object */
263 /* Get the object name information */
264 ObjectHeader
= OBJECT_TO_OBJECT_HEADER(FoundObject
);
265 ObpReferenceNameInfo(ObjectHeader
);
267 /* Reference the object being looked up */
268 ObReferenceObject(FoundObject
);
270 /* Check if the directory was locked */
271 if (!Context
->DirectoryLocked
)
273 /* Release the lock */
274 ObpReleaseDirectoryLock(Directory
, Context
);
278 /* Check if we found an object already */
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
);
286 /* Also dereference the object itself */
287 ObDereferenceObject(Context
->Object
);
290 /* Return the object we found */
291 Context
->Object
= FoundObject
;
296 * @name ObpDeleteEntryDirectory
298 * The ObpDeleteEntryDirectory routine <FILLMEIN>.
303 * @return TRUE if the object was deleted, FALSE otherwise.
310 ObpDeleteEntryDirectory(POBP_LOOKUP_CONTEXT Context
)
312 POBJECT_DIRECTORY Directory
;
313 POBJECT_DIRECTORY_ENTRY
*AllocatedEntry
;
314 POBJECT_DIRECTORY_ENTRY CurrentEntry
;
316 /* Get the Directory */
317 Directory
= Context
->Directory
;
318 if (!Directory
) return FALSE
;
321 AllocatedEntry
= &Directory
->HashBuckets
[Context
->HashIndex
];
322 CurrentEntry
= *AllocatedEntry
;
324 /* Unlink the Entry */
325 *AllocatedEntry
= CurrentEntry
->ChainLink
;
326 CurrentEntry
->ChainLink
= NULL
;
329 ExFreePoolWithTag(CurrentEntry
, OB_DIR_TAG
);
335 /* FUNCTIONS **************************************************************/
338 * @name NtOpenDirectoryObject
341 * The NtOpenDirectoryObject routine opens a namespace directory object.
343 * @param DirectoryHandle
344 * Variable which receives the directory handle.
346 * @param DesiredAccess
347 * Desired access to the directory.
349 * @param ObjectAttributes
350 * Structure describing the directory.
352 * @return STATUS_SUCCESS or appropriate error value.
359 NtOpenDirectoryObject(OUT PHANDLE DirectoryHandle
,
360 IN ACCESS_MASK DesiredAccess
,
361 IN POBJECT_ATTRIBUTES ObjectAttributes
)
364 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
368 /* Check if we need to do any probing */
369 if (PreviousMode
!= KernelMode
)
373 /* Probe the return handle */
374 ProbeForWriteHandle(DirectoryHandle
);
376 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
378 /* Return the exception code */
379 _SEH2_YIELD(return _SEH2_GetExceptionCode());
384 /* Open the directory object */
385 Status
= ObOpenObjectByName(ObjectAttributes
,
392 if (NT_SUCCESS(Status
))
396 /* Write back the handle to the caller */
397 *DirectoryHandle
= Directory
;
399 _SEH2_EXCEPT(ExSystemExceptionFilter())
401 /* Get the exception code */
402 Status
= _SEH2_GetExceptionCode();
407 /* Return the status to the caller */
412 * @name NtQueryDirectoryObject
415 * The NtQueryDirectoryObject routine reads information from a directory in
416 * the system namespace.
418 * @param DirectoryHandle
419 * Handle obtained with NtOpenDirectoryObject which
420 * must grant DIRECTORY_QUERY access to the directory object.
423 * Buffer to hold the data read.
425 * @param BufferLength
426 * Size of the buffer in bytes.
428 * @param ReturnSingleEntry
429 * When TRUE, only 1 entry is written in DirObjInformation;
430 * otherwise as many as will fit in the buffer.
433 * If TRUE start reading at index 0.
434 * If FALSE start reading at the index specified by *ObjectIndex.
437 * Zero based index into the directory, interpretation
438 * depends on RestartScan.
440 * @param ReturnLength
441 * Caller supplied storage for the number of bytes
444 * @return STATUS_SUCCESS or appropriate error value.
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
456 NtQueryDirectoryObject(IN HANDLE DirectoryHandle
,
458 IN ULONG BufferLength
,
459 IN BOOLEAN ReturnSingleEntry
,
460 IN BOOLEAN RestartScan
,
461 IN OUT PULONG Context
,
462 OUT PULONG ReturnLength OPTIONAL
)
464 POBJECT_DIRECTORY Directory
;
465 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
466 ULONG SkipEntries
= 0;
469 POBJECT_DIRECTORY_INFORMATION DirectoryInfo
;
470 ULONG Length
, TotalLength
;
471 ULONG Count
, CurrentEntry
;
473 POBJECT_DIRECTORY_ENTRY Entry
;
474 POBJECT_HEADER ObjectHeader
;
475 POBJECT_HEADER_NAME_INFO ObjectNameInfo
;
478 OBP_LOOKUP_CONTEXT LookupContext
;
481 /* Initialize lookup */
482 ObpInitializeLookupContext(&LookupContext
);
484 /* Check if we need to do any probing */
485 if (PreviousMode
!= KernelMode
)
489 /* Probe the buffer (assuming it will hold Unicode characters) */
490 ProbeForWrite(Buffer
, BufferLength
, sizeof(WCHAR
));
492 /* Probe the context and copy it unless scan-restart was requested */
493 ProbeForWriteUlong(Context
);
494 if (!RestartScan
) SkipEntries
= *Context
;
496 /* Probe the return length if the caller specified one */
497 if (ReturnLength
) ProbeForWriteUlong(ReturnLength
);
499 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
501 /* Return the exception code */
502 _SEH2_YIELD(return _SEH2_GetExceptionCode());
506 else if (!RestartScan
)
508 /* This is kernel mode, save the context without probing, if needed */
509 SkipEntries
= *Context
;
512 /* Allocate a buffer */
513 LocalBuffer
= ExAllocatePoolWithTag(PagedPool
,
514 sizeof(OBJECT_DIRECTORY_INFORMATION
) +
517 if (!LocalBuffer
) return STATUS_INSUFFICIENT_RESOURCES
;
518 RtlZeroMemory(LocalBuffer
, BufferLength
);
520 /* Get a reference to directory */
521 Status
= ObReferenceObjectByHandle(DirectoryHandle
,
527 if (!NT_SUCCESS(Status
))
529 /* Free the buffer and fail */
530 ExFreePoolWithTag(LocalBuffer
, OB_NAME_TAG
);
534 /* Lock directory in shared mode */
535 ObpAcquireDirectoryLockShared(Directory
, &LookupContext
);
537 /* Start at position 0 */
538 DirectoryInfo
= (POBJECT_DIRECTORY_INFORMATION
)LocalBuffer
;
539 TotalLength
= sizeof(OBJECT_DIRECTORY_INFORMATION
);
541 /* Start with 0 entries */
545 /* Set default status and start looping */
546 Status
= STATUS_NO_MORE_ENTRIES
;
547 for (Hash
= 0; Hash
< 37; Hash
++)
549 /* Get this entry and loop all of them */
550 Entry
= Directory
->HashBuckets
[Hash
];
553 /* Check if we should process this entry */
554 if (SkipEntries
== CurrentEntry
++)
556 /* Get the header data */
557 ObjectHeader
= OBJECT_TO_OBJECT_HEADER(Entry
->Object
);
558 ObjectNameInfo
= OBJECT_HEADER_TO_NAME_INFO(ObjectHeader
);
560 /* Get the object name */
563 /* Use the one we have */
564 Name
= ObjectNameInfo
->Name
;
568 /* Otherwise, use an empty one */
569 RtlInitEmptyUnicodeString(&Name
, NULL
, 0);
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
);
577 /* Make sure this entry won't overflow */
578 if ((TotalLength
+ Length
) > BufferLength
)
580 /* Check if the caller wanted only an entry */
581 if (ReturnSingleEntry
)
583 /* Then we'll fail and ask for more buffer */
584 TotalLength
+= Length
;
585 Status
= STATUS_BUFFER_TOO_SMALL
;
589 /* Otherwise, we'll say we're done for now */
590 Status
= STATUS_MORE_ENTRIES
;
593 /* Decrease the entry since we didn't process */
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
->
605 DirectoryInfo
->TypeName
.MaximumLength
= ObjectHeader
->
607 sizeof(UNICODE_NULL
);
608 DirectoryInfo
->TypeName
.Buffer
= ObjectHeader
->
612 Status
= STATUS_SUCCESS
;
614 /* Increase statistics */
615 TotalLength
+= Length
;
619 /* If the caller only wanted an entry, bail out */
620 if (ReturnSingleEntry
) goto Quickie
;
622 /* Increase the key by one */
626 /* Move to the next directory */
627 Entry
= Entry
->ChainLink
;
632 /* Make sure we got success */
633 if (NT_SUCCESS(Status
))
635 /* Clear the current pointer and set it */
636 RtlZeroMemory(DirectoryInfo
, sizeof(OBJECT_DIRECTORY_INFORMATION
));
639 /* Set the buffer here now and loop entries */
640 p
= (PWSTR
)DirectoryInfo
;
641 DirectoryInfo
= LocalBuffer
;
644 /* Copy the name buffer */
646 DirectoryInfo
->Name
.Buffer
,
647 DirectoryInfo
->Name
.Length
);
649 /* Now fixup the pointers */
650 DirectoryInfo
->Name
.Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+
652 (ULONG_PTR
)LocalBuffer
));
654 /* Advance in buffer and NULL-terminate */
655 p
= (PVOID
)((ULONG_PTR
)p
+ DirectoryInfo
->Name
.Length
);
658 /* Now copy the type name buffer */
660 DirectoryInfo
->TypeName
.Buffer
,
661 DirectoryInfo
->TypeName
.Length
);
663 /* Now fixup the pointers */
664 DirectoryInfo
->TypeName
.Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+
666 (ULONG_PTR
)LocalBuffer
));
668 /* Advance in buffer and NULL-terminate */
669 p
= (PVOID
)((ULONG_PTR
)p
+ DirectoryInfo
->TypeName
.Length
);
672 /* Move to the next entry */
677 *Context
= CurrentEntry
;
682 /* Copy the buffer */
683 RtlCopyMemory(Buffer
,
685 (TotalLength
<= BufferLength
) ?
686 TotalLength
: BufferLength
);
688 /* Check if the caller requested the return length and return it*/
689 if (ReturnLength
) *ReturnLength
= TotalLength
;
691 _SEH2_EXCEPT(ExSystemExceptionFilter())
693 /* Get the exception code */
694 Status
= _SEH2_GetExceptionCode();
698 /* Unlock the directory */
699 ObpReleaseDirectoryLock(Directory
, &LookupContext
);
701 /* Dereference the directory and free our buffer */
702 ObDereferenceObject(Directory
);
703 ExFreePoolWithTag(LocalBuffer
, OB_NAME_TAG
);
705 /* Return status to caller */
710 * @name NtCreateDirectoryObject
713 * The NtOpenDirectoryObject routine creates or opens a directory object.
715 * @param DirectoryHandle
716 * Variable which receives the directory handle.
718 * @param DesiredAccess
719 * Desired access to the directory.
721 * @param ObjectAttributes
722 * Structure describing the directory.
724 * @return STATUS_SUCCESS or appropriate error value.
731 NtCreateDirectoryObject(OUT PHANDLE DirectoryHandle
,
732 IN ACCESS_MASK DesiredAccess
,
733 IN POBJECT_ATTRIBUTES ObjectAttributes
)
735 POBJECT_DIRECTORY Directory
;
737 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
741 /* Check if we need to do any probing */
742 if (PreviousMode
!= KernelMode
)
746 /* Probe the return handle */
747 ProbeForWriteHandle(DirectoryHandle
);
749 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
751 /* Return the exception code */
752 _SEH2_YIELD(return _SEH2_GetExceptionCode());
757 /* Create the object */
758 Status
= ObCreateObject(PreviousMode
,
763 sizeof(OBJECT_DIRECTORY
),
767 if (!NT_SUCCESS(Status
)) return Status
;
769 /* Setup the object */
770 RtlZeroMemory(Directory
, sizeof(OBJECT_DIRECTORY
));
771 ExInitializePushLock(&Directory
->Lock
);
772 Directory
->SessionId
= -1;
774 /* Insert it into the handle table */
775 Status
= ObInsertObject((PVOID
)Directory
,
782 /* Enter SEH to protect write */
785 /* Return the handle back to the caller */
786 *DirectoryHandle
= NewHandle
;
788 _SEH2_EXCEPT(ExSystemExceptionFilter())
790 /* Get the exception code */
791 Status
= _SEH2_GetExceptionCode();
795 /* Return status to caller */