2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ob/dirobj.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 %ld\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 ObpAcquireNameInformation(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 ObpReleaseNameInformation(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 ExFreePool(CurrentEntry
);
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();
365 NTSTATUS Status
= STATUS_SUCCESS
;
368 /* Check if we need to do any probing */
369 if (PreviousMode
!= KernelMode
)
373 /* Probe the return handle */
374 ProbeForWriteHandle(DirectoryHandle
);
378 /* Get the error code */
379 Status
= _SEH_GetExceptionCode();
382 if(!NT_SUCCESS(Status
)) return Status
;
385 /* Open the directory object */
386 Status
= ObOpenObjectByName(ObjectAttributes
,
393 if (NT_SUCCESS(Status
))
397 /* Write back the handle to the caller */
398 *DirectoryHandle
= Directory
;
400 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
)
402 /* Get the exception code */
403 Status
= _SEH_GetExceptionCode();
408 /* Return the status to the caller */
413 * @name NtQueryDirectoryObject
416 * The NtQueryDirectoryObject routine reads information from a directory in
417 * the system namespace.
419 * @param DirectoryHandle
420 * Handle obtained with NtOpenDirectoryObject which
421 * must grant DIRECTORY_QUERY access to the directory object.
424 * Buffer to hold the data read.
426 * @param BufferLength
427 * Size of the buffer in bytes.
429 * @param ReturnSingleEntry
430 * When TRUE, only 1 entry is written in DirObjInformation;
431 * otherwise as many as will fit in the buffer.
434 * If TRUE start reading at index 0.
435 * If FALSE start reading at the index specified by *ObjectIndex.
438 * Zero based index into the directory, interpretation
439 * depends on RestartScan.
441 * @param ReturnLength
442 * Caller supplied storage for the number of bytes
445 * @return STATUS_SUCCESS or appropriate error value.
447 * @remarks Although you can iterate over the directory by calling this
448 * function multiple times, the directory is unlocked between
449 * calls. This means that another thread can change the directory
450 * and so iterating doesn't guarantee a consistent picture of the
451 * directory. Best thing is to retrieve all directory entries in
457 NtQueryDirectoryObject(IN HANDLE DirectoryHandle
,
459 IN ULONG BufferLength
,
460 IN BOOLEAN ReturnSingleEntry
,
461 IN BOOLEAN RestartScan
,
462 IN OUT PULONG Context
,
463 OUT PULONG ReturnLength OPTIONAL
)
465 POBJECT_DIRECTORY Directory
;
466 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
467 ULONG SkipEntries
= 0;
468 NTSTATUS Status
= STATUS_SUCCESS
;
470 POBJECT_DIRECTORY_INFORMATION DirectoryInfo
;
471 ULONG Length
, TotalLength
;
472 ULONG Count
, CurrentEntry
;
474 POBJECT_DIRECTORY_ENTRY Entry
;
475 POBJECT_HEADER ObjectHeader
;
476 POBJECT_HEADER_NAME_INFO ObjectNameInfo
;
479 OBP_LOOKUP_CONTEXT LookupContext
;
482 /* Initialize lookup */
483 ObpInitializeDirectoryLookup(&LookupContext
);
485 /* Check if we need to do any probing */
486 if (PreviousMode
!= KernelMode
)
490 /* Probe the buffer (assuming it will hold Unicode characters) */
491 ProbeForWrite(Buffer
, BufferLength
, sizeof(WCHAR
));
493 /* Probe the context and copy it unless scan-restart was requested */
494 ProbeForWriteUlong(Context
);
495 if (!RestartScan
) SkipEntries
= *Context
;
497 /* Probe the return length if the caller specified one */
498 if (ReturnLength
) ProbeForWriteUlong(ReturnLength
);
502 /* Get the exception code */
503 Status
= _SEH_GetExceptionCode();
506 if(!NT_SUCCESS(Status
)) return Status
;
508 else if (!RestartScan
)
510 /* This is kernel mode, save the context without probing, if needed */
511 SkipEntries
= *Context
;
514 /* Allocate a buffer */
515 LocalBuffer
= ExAllocatePoolWithTag(PagedPool
,
516 sizeof(OBJECT_DIRECTORY_INFORMATION
) +
519 if (!LocalBuffer
) return STATUS_INSUFFICIENT_RESOURCES
;
520 RtlZeroMemory(LocalBuffer
, BufferLength
);
522 /* Get a reference to directory */
523 Status
= ObReferenceObjectByHandle(DirectoryHandle
,
529 if (!NT_SUCCESS(Status
))
531 /* Free the buffer and fail */
532 ExFreePool(LocalBuffer
);
536 /* Lock directory in shared mode */
537 ObpAcquireDirectoryLockShared(Directory
, &LookupContext
);
539 /* Start at position 0 */
540 DirectoryInfo
= (POBJECT_DIRECTORY_INFORMATION
)LocalBuffer
;
541 TotalLength
= sizeof(OBJECT_DIRECTORY_INFORMATION
);
543 /* Start with 0 entries */
547 /* Set default status and start looping */
548 Status
= STATUS_NO_MORE_ENTRIES
;
549 for (Hash
= 0; Hash
< 37; Hash
++)
551 /* Get this entry and loop all of them */
552 Entry
= Directory
->HashBuckets
[Hash
];
555 /* Check if we should process this entry */
556 if (SkipEntries
== CurrentEntry
++)
558 /* Get the header data */
559 ObjectHeader
= OBJECT_TO_OBJECT_HEADER(Entry
->Object
);
560 ObjectNameInfo
= OBJECT_HEADER_TO_NAME_INFO(ObjectHeader
);
562 /* Get the object name */
565 /* Use the one we have */
566 Name
= ObjectNameInfo
->Name
;
570 /* Otherwise, use an empty one */
571 RtlInitEmptyUnicodeString(&Name
, NULL
, 0);
574 /* Calculate the length for this entry */
575 Length
= sizeof(OBJECT_DIRECTORY_INFORMATION
) +
576 Name
.Length
+ sizeof(UNICODE_NULL
) +
577 ObjectHeader
->Type
->Name
.Length
+ sizeof(UNICODE_NULL
);
579 /* Make sure this entry won't overflow */
580 if ((TotalLength
+ Length
) > BufferLength
)
582 /* Check if the caller wanted only an entry */
583 if (ReturnSingleEntry
)
585 /* Then we'll fail and ask for more buffer */
586 TotalLength
+= Length
;
587 Status
= STATUS_BUFFER_TOO_SMALL
;
591 /* Otherwise, we'll say we're done for now */
592 Status
= STATUS_MORE_ENTRIES
;
595 /* Decrease the entry since we didn't process */
600 /* Now fill in the buffer */
601 DirectoryInfo
->Name
.Length
= Name
.Length
;
602 DirectoryInfo
->Name
.MaximumLength
= Name
.Length
+
603 sizeof(UNICODE_NULL
);
604 DirectoryInfo
->Name
.Buffer
= Name
.Buffer
;
605 DirectoryInfo
->TypeName
.Length
= ObjectHeader
->
607 DirectoryInfo
->TypeName
.MaximumLength
= ObjectHeader
->
609 sizeof(UNICODE_NULL
);
610 DirectoryInfo
->TypeName
.Buffer
= ObjectHeader
->
614 Status
= STATUS_SUCCESS
;
616 /* Increase statistics */
617 TotalLength
+= Length
;
621 /* If the caller only wanted an entry, bail out */
622 if (ReturnSingleEntry
) goto Quickie
;
624 /* Increase the key by one */
628 /* Move to the next directory */
629 Entry
= Entry
->ChainLink
;
634 /* Make sure we got success */
635 if (NT_SUCCESS(Status
))
637 /* Clear the current pointer and set it */
638 RtlZeroMemory(DirectoryInfo
, sizeof(OBJECT_DIRECTORY_INFORMATION
));
641 /* Set the buffer here now and loop entries */
642 p
= (PWSTR
)DirectoryInfo
;
643 DirectoryInfo
= LocalBuffer
;
646 /* Copy the name buffer */
648 DirectoryInfo
->Name
.Buffer
,
649 DirectoryInfo
->Name
.Length
);
651 /* Now fixup the pointers */
652 DirectoryInfo
->Name
.Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+
654 (ULONG_PTR
)LocalBuffer
));
656 /* Advance in buffer and NULL-terminate */
657 p
= (PVOID
)((ULONG_PTR
)p
+ DirectoryInfo
->Name
.Length
);
660 /* Now copy the type name buffer */
662 DirectoryInfo
->TypeName
.Buffer
,
663 DirectoryInfo
->TypeName
.Length
);
665 /* Now fixup the pointers */
666 DirectoryInfo
->TypeName
.Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+
668 (ULONG_PTR
)LocalBuffer
));
670 /* Advance in buffer and NULL-terminate */
671 p
= (PVOID
)((ULONG_PTR
)p
+ DirectoryInfo
->TypeName
.Length
);
674 /* Move to the next entry */
679 *Context
= CurrentEntry
;
684 /* Copy the buffer */
685 RtlCopyMemory(Buffer
,
687 (TotalLength
<= BufferLength
) ?
688 TotalLength
: BufferLength
);
690 /* Check if the caller requested the return length and return it*/
691 if (ReturnLength
) *ReturnLength
= TotalLength
;
693 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
)
695 Status
= _SEH_GetExceptionCode();
699 /* Unlock the directory */
700 ObpReleaseDirectoryLock(Directory
, &LookupContext
);
702 /* Dereference the directory and free our buffer */
703 ObDereferenceObject(Directory
);
704 ExFreePool(LocalBuffer
);
706 /* Return status to caller */
711 * @name NtCreateDirectoryObject
714 * The NtOpenDirectoryObject routine creates or opens a directory object.
716 * @param DirectoryHandle
717 * Variable which receives the directory handle.
719 * @param DesiredAccess
720 * Desired access to the directory.
722 * @param ObjectAttributes
723 * Structure describing the directory.
725 * @return STATUS_SUCCESS or appropriate error value.
732 NtCreateDirectoryObject(OUT PHANDLE DirectoryHandle
,
733 IN ACCESS_MASK DesiredAccess
,
734 IN POBJECT_ATTRIBUTES ObjectAttributes
)
736 POBJECT_DIRECTORY Directory
;
738 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
739 NTSTATUS Status
= STATUS_SUCCESS
;
742 /* Check if we need to do any probing */
743 if(PreviousMode
!= KernelMode
)
747 /* Probe the return handle */
748 ProbeForWriteHandle(DirectoryHandle
);
752 /* Get the error code */
753 Status
= _SEH_GetExceptionCode();
756 if(!NT_SUCCESS(Status
)) return Status
;
759 /* Create the object */
760 Status
= ObCreateObject(PreviousMode
,
765 sizeof(OBJECT_DIRECTORY
),
769 if (!NT_SUCCESS(Status
)) return Status
;
771 /* Setup the object */
772 RtlZeroMemory(Directory
, sizeof(OBJECT_DIRECTORY
));
773 ExInitializePushLock((PULONG_PTR
)&Directory
->Lock
);
774 Directory
->SessionId
= -1;
776 /* Insert it into the handle table */
777 Status
= ObInsertObject((PVOID
)Directory
,
784 /* Enter SEH to protect write */
787 /* Return the handle back to the caller */
788 *DirectoryHandle
= NewHandle
;
790 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
)
792 /* Get the exception code */
793 Status
= _SEH_GetExceptionCode();
797 /* Return status to caller */