2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ob/namespce.c
5 * PURPOSE: Manages all functions related to the Object Manager name-
6 * space, such as finding objects or querying their names.
7 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 * Thomas Weidenmueller (w3seek@reactos.org)
12 /* INCLUDES ******************************************************************/
14 #define NTDDI_VERSION NTDDI_WINXP
17 #include <internal/debug.h>
19 POBJECT_DIRECTORY NameSpaceRoot
= NULL
;
20 POBJECT_DIRECTORY ObpTypeDirectoryObject
= NULL
;
22 /* PRIVATE FUNCTIONS *********************************************************/
26 ObDereferenceDeviceMap(IN PEPROCESS Process
)
29 PDEVICE_MAP DeviceMap
= Process
->DeviceMap
;
31 /* FIXME: We don't use Process Devicemaps yet */
34 /* FIXME: Acquire the DeviceMap Spinlock */
35 // KeAcquireSpinLock(DeviceMap->Lock, &OldIrql);
37 /* Delete the device map link and dereference it */
38 Process
->DeviceMap
= NULL
;
39 if (--DeviceMap
->ReferenceCount
)
41 /* Nobody is referencing it anymore, unlink the DOS directory */
42 DeviceMap
->DosDevicesDirectory
->DeviceMap
= NULL
;
44 /* FIXME: Release the DeviceMap Spinlock */
45 // KeReleasepinLock(DeviceMap->Lock, OldIrql);
47 /* Dereference the DOS Devices Directory and free the Device Map */
48 ObDereferenceObject(DeviceMap
->DosDevicesDirectory
);
49 ExFreePool(DeviceMap
);
53 /* FIXME: Release the DeviceMap Spinlock */
54 // KeReleasepinLock(DeviceMap->Lock, OldIrql);
61 ObInheritDeviceMap(IN PEPROCESS Parent
,
64 /* FIXME: Devicemap Support */
68 * @name ObpDeleteNameCheck
70 * The ObpDeleteNameCheck routine checks if a named object should be
71 * removed from the object directory namespace.
74 * Pointer to the object to check for possible removal.
78 * @remarks An object is removed if the following 4 criteria are met:
79 * 1) The object has 0 handles open
80 * 2) The object is in the directory namespace and has a name
81 * 3) The object is not permanent
86 ObpDeleteNameCheck(IN PVOID Object
)
88 POBJECT_HEADER ObjectHeader
;
89 OBP_LOOKUP_CONTEXT Context
;
90 POBJECT_HEADER_NAME_INFO ObjectNameInfo
;
91 POBJECT_TYPE ObjectType
;
92 PVOID Directory
= NULL
;
94 /* Get object structures */
95 ObjectHeader
= OBJECT_TO_OBJECT_HEADER(Object
);
96 ObjectNameInfo
= OBJECT_HEADER_TO_NAME_INFO(ObjectHeader
);
97 ObjectType
= ObjectHeader
->Type
;
100 * Check if the handle count is 0, if the object is named,
101 * and if the object isn't a permanent object.
103 if (!(ObjectHeader
->HandleCount
) &&
105 (ObjectNameInfo
->Name
.Length
) &&
106 !(ObjectHeader
->Flags
& OB_FLAG_PERMANENT
))
108 /* Make sure it's still inserted */
109 Context
.Directory
= ObjectNameInfo
->Directory
;
110 Context
.DirectoryLocked
= TRUE
;
111 Object
= ObpLookupEntryDirectory(ObjectNameInfo
->Directory
,
112 &ObjectNameInfo
->Name
,
116 if ((Object
) && !(ObjectHeader
->HandleCount
))
118 /* First delete it from the directory */
119 ObpDeleteEntryDirectory(&Context
);
121 /* Now check if we have a security callback */
122 if (ObjectType
->TypeInfo
.SecurityRequired
)
125 ObjectType
->TypeInfo
.SecurityProcedure(Object
,
126 DeleteSecurityDescriptor
,
138 ExFreePool(ObjectNameInfo
->Name
.Buffer
);
139 RtlInitEmptyUnicodeString(&ObjectNameInfo
->Name
, NULL
, 0);
141 /* Clear the current directory and de-reference it */
142 Directory
= ObjectNameInfo
->Directory
;
143 ObjectNameInfo
->Directory
= NULL
;
146 /* Check if we were inserted in a directory */
149 /* We were, so dereference the directory and the object as well */
150 ObDereferenceObject(Directory
);
151 ObDereferenceObject(Object
);
158 ObFindObject(IN HANDLE RootHandle
,
159 IN PUNICODE_STRING ObjectName
,
161 IN KPROCESSOR_MODE AccessMode
,
162 IN PVOID
*ReturnedObject
,
163 IN POBJECT_TYPE ObjectType
,
164 IN POBP_LOOKUP_CONTEXT Context
,
165 IN PACCESS_STATE AccessState
,
166 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos
,
167 IN PVOID ParseContext
,
168 OUT PVOID ExpectedObject
)
171 PVOID CurrentDirectory
= NULL
;
172 PVOID CurrentObject
= NULL
;
173 POBJECT_HEADER CurrentHeader
;
174 NTSTATUS Status
= STATUS_SUCCESS
;
176 POBJECT_HEADER_NAME_INFO ObjectNameInfo
;
177 UNICODE_STRING RemainingPath
, PartName
;
178 BOOLEAN InsideRoot
= FALSE
;
179 OB_PARSE_METHOD ParseRoutine
;
183 OBTRACE(OB_NAMESPACE_DEBUG
,
184 "%s - Finding Object: %wZ. Expecting: %p\n",
188 *ReturnedObject
= NULL
;
190 /* Check if we got a Root Directory */
193 /* We did. Reference it */
194 Status
= ObReferenceObjectByHandle(RootHandle
,
200 if (!NT_SUCCESS(Status
)) return Status
;
203 CurrentHeader
= OBJECT_TO_OBJECT_HEADER(RootDirectory
);
205 /* The name cannot start with a separator, unless this is a file */
206 if ((ObjectName
->Buffer
) &&
207 (ObjectName
->Buffer
[0] == OBJ_NAME_PATH_SEPARATOR
) &&
208 (CurrentHeader
->Type
!= IoFileObjectType
))
210 /* The syntax is bad, so fail this request */
211 ObDereferenceObject(RootDirectory
);
212 return STATUS_OBJECT_PATH_SYNTAX_BAD
;
215 /* Don't parse a Directory */
216 if (CurrentHeader
->Type
!= ObDirectoryType
)
218 /* Make sure the Object Type has a parse routine */
219 ParseRoutine
= CurrentHeader
->Type
->TypeInfo
.ParseProcedure
;
222 /* We can't parse a name if we don't have a parse routine */
223 ObDereferenceObject(RootDirectory
);
224 return STATUS_INVALID_HANDLE
;
230 /* Start with the full name */
231 RemainingPath
= *ObjectName
;
233 /* Call the Parse Procedure */
234 Status
= ParseRoutine(RootDirectory
,
245 /* Check for success or failure, so not reparse */
246 if ((Status
!= STATUS_REPARSE
) &&
247 (Status
!= STATUS_REPARSE_OBJECT
))
249 /* Check for failure */
250 if (!NT_SUCCESS(Status
))
252 /* Parse routine might not have cleared this, do it */
253 CurrentObject
= NULL
;
255 else if (!CurrentObject
)
257 /* Modify status to reflect failure inside Ob */
258 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
261 /* We're done, return the status and object */
262 *ReturnedObject
= CurrentObject
;
263 ObDereferenceObject(RootDirectory
);
266 else if ((!ObjectName
->Length
) ||
267 (!ObjectName
->Buffer
) ||
268 (ObjectName
->Buffer
[0] == OBJ_NAME_PATH_SEPARATOR
))
270 /* Reparsed to the root directory, so start over */
271 ObDereferenceObject(RootDirectory
);
272 RootDirectory
= NameSpaceRoot
;
274 /* Don't use this anymore, since we're starting at root */
280 else if (!(ObjectName
->Length
) || !(ObjectName
->Buffer
))
282 /* Just return the Root Directory if we didn't get a name*/
283 Status
= ObReferenceObjectByPointer(RootDirectory
,
287 if (NT_SUCCESS(Status
)) *ReturnedObject
= RootDirectory
;
288 ObDereferenceObject(RootDirectory
);
294 /* We did not get a Root Directory, so use the root */
295 RootDirectory
= NameSpaceRoot
;
297 /* It must start with a path separator */
298 if (!(ObjectName
->Length
) ||
299 !(ObjectName
->Buffer
) ||
300 (ObjectName
->Buffer
[0] != OBJ_NAME_PATH_SEPARATOR
))
302 /* This name is invalid, so fail */
303 return STATUS_OBJECT_PATH_SYNTAX_BAD
;
306 /* Check if the name is only the path separator */
307 if (ObjectName
->Length
== sizeof(OBJ_NAME_PATH_SEPARATOR
))
309 /* So the caller only wants the root directory; do we have one? */
312 /* This must be the first time we're creating it... right? */
315 /* Yes, so return it to ObInsert so that it can create it */
316 Status
= ObReferenceObjectByPointer(ExpectedObject
,
320 if (NT_SUCCESS(Status
)) *ReturnedObject
= ExpectedObject
;
325 /* This should never really happen */
327 return STATUS_INVALID_PARAMETER
;
332 /* We do have the root directory, so just return it */
333 Status
= ObReferenceObjectByPointer(RootDirectory
,
337 if (NT_SUCCESS(Status
)) *ReturnedObject
= RootDirectory
;
345 RemainingPath
= *ObjectName
;
350 /* Check if we should use the Root Directory */
353 /* Yes, use the root directory and remember that */
354 CurrentDirectory
= RootDirectory
;
358 /* Check if the name starts with a path separator */
359 if ((RemainingPath
.Length
) &&
360 (RemainingPath
.Buffer
[0] == OBJ_NAME_PATH_SEPARATOR
))
362 /* Skip the path separator */
363 RemainingPath
.Buffer
++;
364 RemainingPath
.Length
-= sizeof(OBJ_NAME_PATH_SEPARATOR
);
367 /* Find the next Part Name */
368 PartName
= RemainingPath
;
369 while (RemainingPath
.Length
)
371 /* Break if we found the \ ending */
372 if (RemainingPath
.Buffer
[0] == OBJ_NAME_PATH_SEPARATOR
) break;
375 RemainingPath
.Buffer
++;
376 RemainingPath
.Length
-= sizeof(OBJ_NAME_PATH_SEPARATOR
);
379 /* Get its size and make sure it's valid */
380 if (!(PartName
.Length
-= RemainingPath
.Length
))
382 Status
= STATUS_OBJECT_NAME_INVALID
;
387 Context
->DirectoryLocked
= TRUE
;
388 Context
->Directory
= CurrentDirectory
;
389 CurrentObject
= ObpLookupEntryDirectory(CurrentDirectory
,
396 /* We didn't find it... do we still have a path? */
397 if (RemainingPath
.Length
)
399 /* Then tell the caller the path wasn't found */
400 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
403 else if (!ExpectedObject
)
405 /* Otherwise, we have a path, but the name isn't valid */
406 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
410 /* Reference newly to be inserted object */
411 ObReferenceObject(ExpectedObject
);
412 CurrentHeader
= OBJECT_TO_OBJECT_HEADER(ExpectedObject
);
414 /* Create Object Name */
415 NewName
= ExAllocatePoolWithTag(NonPagedPool
,
416 PartName
.MaximumLength
,
418 ObjectNameInfo
= OBJECT_HEADER_TO_NAME_INFO(CurrentHeader
);
421 RtlMoveMemory(NewName
, PartName
.Buffer
, PartName
.MaximumLength
);
424 if (ObjectNameInfo
->Name
.Buffer
) ExFreePool(ObjectNameInfo
->Name
.Buffer
);
427 ObjectNameInfo
->Name
.Buffer
= NewName
;
428 ObjectNameInfo
->Name
.Length
= PartName
.Length
;
429 ObjectNameInfo
->Name
.MaximumLength
= PartName
.MaximumLength
;
431 /* Rereference the Directory and insert */
432 ObReferenceObject(CurrentDirectory
);
433 ObpInsertEntryDirectory(CurrentDirectory
, Context
, CurrentHeader
);
435 /* Return Status and the Expected Object */
436 Status
= STATUS_SUCCESS
;
437 CurrentObject
= ExpectedObject
;
439 /* Get out of here */
444 /* We found it, so now get its header */
445 CurrentHeader
= OBJECT_TO_OBJECT_HEADER(CurrentObject
);
448 * Check for a parse Procedure, but don't bother to parse for an insert
449 * unless it's a Symbolic Link, in which case we MUST parse
451 ParseRoutine
= CurrentHeader
->Type
->TypeInfo
.ParseProcedure
;
453 (!ExpectedObject
|| ParseRoutine
== ObpParseSymbolicLink
))
455 /* Use the Root Directory next time */
458 /* Call the Parse Procedure */
459 Status
= ParseRoutine(CurrentObject
,
470 /* Check if we have to reparse */
471 if ((Status
== STATUS_REPARSE
) ||
472 (Status
== STATUS_REPARSE_OBJECT
))
474 /* Start over from root if we got sent back there */
475 if ((Status
== STATUS_REPARSE_OBJECT
) ||
476 (ObjectName
->Buffer
[0] == OBJ_NAME_PATH_SEPARATOR
))
478 /* Check if we got a root directory */
481 /* Stop using it, because we have a new directory now */
482 ObDereferenceObject(RootDirectory
);
487 RootDirectory
= NameSpaceRoot
;
489 /* Check for reparse status */
490 if (Status
== STATUS_REPARSE_OBJECT
)
492 /* Did we actually get an object to which to reparse? */
495 /* We didn't, so set a failure status */
496 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
500 /* We did, so we're free to parse the new object */
506 /* Restart the search */
509 else if (RootDirectory
== NameSpaceRoot
)
511 /* We got STATUS_REPARSE but are at the Root Directory */
512 CurrentObject
= NULL
;
513 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
516 else if (!NT_SUCCESS(Status
))
519 CurrentObject
= NULL
;
521 else if (!CurrentObject
)
523 /* We didn't reparse but we didn't find the Object Either */
524 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
527 /* Break out of the loop */
532 /* No parse routine...do we still have a remaining name? */
533 if (!RemainingPath
.Length
)
535 /* Are we creating an object? */
538 /* We don't... reference the Object */
539 Status
= ObReferenceObjectByPointer(CurrentObject
,
543 if (!NT_SUCCESS(Status
)) CurrentObject
= NULL
;
546 /* And get out of the reparse loop */
551 /* We still have a name; check if this is a directory object */
552 if (CurrentHeader
->Type
== ObDirectoryType
)
554 /* Restart from this directory */
555 CurrentDirectory
= CurrentObject
;
559 /* We still have a name, but no parse routine for it */
560 Status
= STATUS_OBJECT_TYPE_MISMATCH
;
561 CurrentObject
= NULL
;
568 /* Write what we found, and if it's null, check if we got success */
569 if (!(*ReturnedObject
= CurrentObject
) && (NT_SUCCESS(Status
)))
571 /* Nothing found... but we have success. Correct the status code */
572 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
575 /* Check if we had a root directory */
579 ObDereferenceObject(RootDirectory
);
582 /* Return status to caller */
583 OBTRACE(OB_NAMESPACE_DEBUG
,
584 "%s - Found Object: %p. Expected: %p\n",
591 /* PUBLIC FUNCTIONS *********************************************************/
595 ObQueryNameString(IN PVOID Object
,
596 OUT POBJECT_NAME_INFORMATION ObjectNameInfo
,
598 OUT PULONG ReturnLength
)
600 POBJECT_HEADER_NAME_INFO LocalInfo
;
601 POBJECT_HEADER ObjectHeader
;
602 POBJECT_DIRECTORY ParentDirectory
;
606 /* Get the Kernel Meta-Structures */
607 ObjectHeader
= OBJECT_TO_OBJECT_HEADER(Object
);
608 LocalInfo
= OBJECT_HEADER_TO_NAME_INFO(ObjectHeader
);
610 /* Check if a Query Name Procedure is available */
611 if (ObjectHeader
->Type
->TypeInfo
.QueryNameProcedure
)
613 /* Call the procedure */
614 return ObjectHeader
->Type
->TypeInfo
.QueryNameProcedure(Object
,
622 /* Check if the object doesn't even have a name */
623 if (!(LocalInfo
) || !(LocalInfo
->Name
.Buffer
))
625 /* We're returning the name structure */
626 *ReturnLength
= sizeof(OBJECT_NAME_INFORMATION
);
628 /* Check if we were given enough space */
629 if (*ReturnLength
> Length
) return STATUS_INFO_LENGTH_MISMATCH
;
631 /* Return an empty buffer */
632 RtlInitEmptyUnicodeString(&ObjectNameInfo
->Name
, NULL
, 0);
633 return STATUS_SUCCESS
;
637 * Find the size needed for the name. We won't do
638 * this during the Name Creation loop because we want
639 * to let the caller know that the buffer isn't big
640 * enough right at the beginning, not work our way through
641 * and find out at the end
643 if (Object
== NameSpaceRoot
)
645 /* Size of the '\' string */
646 NameSize
= sizeof(OBJ_NAME_PATH_SEPARATOR
);
650 /* Get the Object Directory and add name of Object */
651 ParentDirectory
= LocalInfo
->Directory
;
652 NameSize
= sizeof(OBJ_NAME_PATH_SEPARATOR
) + LocalInfo
->Name
.Length
;
654 /* Loop inside the directory to get the top-most one (meaning root) */
655 while ((ParentDirectory
!= NameSpaceRoot
) && (ParentDirectory
))
657 /* Get the Name Information */
658 LocalInfo
= OBJECT_HEADER_TO_NAME_INFO(
659 OBJECT_TO_OBJECT_HEADER(ParentDirectory
));
661 /* Add the size of the Directory Name */
662 if (LocalInfo
&& LocalInfo
->Directory
)
664 /* Size of the '\' string + Directory Name */
665 NameSize
+= sizeof(OBJ_NAME_PATH_SEPARATOR
) +
666 LocalInfo
->Name
.Length
;
668 /* Move to next parent Directory */
669 ParentDirectory
= LocalInfo
->Directory
;
673 /* Directory with no name. We append "...\" */
674 NameSize
+= sizeof(L
"...") + sizeof(OBJ_NAME_PATH_SEPARATOR
);
680 /* Finally, add the name of the structure and the null char */
681 *ReturnLength
= NameSize
+
682 sizeof(OBJECT_NAME_INFORMATION
) +
683 sizeof(UNICODE_NULL
);
685 /* Check if we were given enough space */
686 if (*ReturnLength
> Length
) return STATUS_INFO_LENGTH_MISMATCH
;
689 * Now we will actually create the name. We work backwards because
690 * it's easier to start off from the Name we have and walk up the
691 * parent directories. We use the same logic as Name Length calculation.
693 LocalInfo
= OBJECT_HEADER_TO_NAME_INFO(ObjectHeader
);
694 ObjectName
= (PWCH
)((ULONG_PTR
)ObjectNameInfo
+ *ReturnLength
);
695 *--ObjectName
= UNICODE_NULL
;
697 /* Check if the object is actually the Root directory */
698 if (Object
== NameSpaceRoot
)
700 /* This is already the Root Directory, return "\\" */
701 *--ObjectName
= OBJ_NAME_PATH_SEPARATOR
;
702 ObjectNameInfo
->Name
.Length
= (USHORT
)NameSize
;
703 ObjectNameInfo
->Name
.MaximumLength
= (USHORT
)(NameSize
+
704 sizeof(UNICODE_NULL
));
705 ObjectNameInfo
->Name
.Buffer
= ObjectName
;
706 return STATUS_SUCCESS
;
710 /* Start by adding the Object's Name */
711 ObjectName
= (PWCH
)((ULONG_PTR
)ObjectName
-
712 LocalInfo
->Name
.Length
);
713 RtlMoveMemory(ObjectName
,
714 LocalInfo
->Name
.Buffer
,
715 LocalInfo
->Name
.Length
);
717 /* Now parse the Parent directories until we reach the top */
718 ParentDirectory
= LocalInfo
->Directory
;
719 while ((ParentDirectory
!= NameSpaceRoot
) && (ParentDirectory
))
721 /* Get the name information */
722 LocalInfo
= OBJECT_HEADER_TO_NAME_INFO(
723 OBJECT_TO_OBJECT_HEADER(ParentDirectory
));
726 *(--ObjectName
) = OBJ_NAME_PATH_SEPARATOR
;
728 /* Add the Parent Directory's Name */
729 if (LocalInfo
&& LocalInfo
->Name
.Buffer
)
732 ObjectName
= (PWCH
)((ULONG_PTR
)ObjectName
-
733 LocalInfo
->Name
.Length
);
734 RtlMoveMemory(ObjectName
,
735 LocalInfo
->Name
.Buffer
,
736 LocalInfo
->Name
.Length
);
738 /* Move to next parent */
739 ParentDirectory
= LocalInfo
->Directory
;
743 /* Directory without a name, we add "..." */
744 DPRINT("Nameless Directory\n");
745 ObjectName
-= sizeof(L
"...");
751 /* Add Root Directory Name */
752 *(--ObjectName
) = OBJ_NAME_PATH_SEPARATOR
;
753 ObjectNameInfo
->Name
.Length
= (USHORT
)NameSize
;
754 ObjectNameInfo
->Name
.MaximumLength
= (USHORT
)(NameSize
+
755 sizeof(UNICODE_NULL
));
756 ObjectNameInfo
->Name
.Buffer
= ObjectName
;
760 return STATUS_SUCCESS
;
765 ObQueryDeviceMapInformation(IN PEPROCESS Process
,
766 IN PPROCESS_DEVICEMAP_INFORMATION DeviceMapInfo
)
771 * FIXME: This is an ugly hack for now, to always return the System Device Map
772 * instead of returning the Process Device Map. Not important yet since we don't use it
775 /* FIXME: Acquire the DeviceMap Spinlock */
776 // KeAcquireSpinLock(DeviceMap->Lock, &OldIrql);
779 DeviceMapInfo
->Query
.DriveMap
= ObSystemDeviceMap
->DriveMap
;
780 RtlMoveMemory(DeviceMapInfo
->Query
.DriveType
,
781 ObSystemDeviceMap
->DriveType
,
782 sizeof(ObSystemDeviceMap
->DriveType
));
784 /* FIXME: Release the DeviceMap Spinlock */
785 // KeReleasepinLock(DeviceMap->Lock, OldIrql);