reactos/ntoskrnl/ob/obname.c
Timo Kreuzer 2c909db295 [NTOS:OB] Rename object types to their official names to satisfy WinDbg
- Rename ObDirectoryType to ObpDirectoryObjectType and remove it from NDK (this is not exported!)
- Rename ObSymbolicLinkType to ObpSymbolicLinkObjectType
- Remove duplicated ObpTypeObjectType from ob.h
2018-08-17 20:28:45 +02:00

1276 lines
47 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ob/obname.c
* PURPOSE: Manages all functions related to the Object Manager name-
* space, such as finding objects or querying their names.
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* Eric Kohl
* Thomas Weidenmueller (w3seek@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
BOOLEAN ObpCaseInsensitive = TRUE;
POBJECT_DIRECTORY ObpRootDirectoryObject;
POBJECT_DIRECTORY ObpTypeDirectoryObject;
/* DOS Device Prefix \??\ and \?? */
ALIGNEDNAME ObpDosDevicesShortNamePrefix = {{L'\\',L'?',L'?',L'\\'}};
ALIGNEDNAME ObpDosDevicesShortNameRoot = {{L'\\',L'?',L'?',L'\0'}};
UNICODE_STRING ObpDosDevicesShortName =
{
sizeof(ObpDosDevicesShortNamePrefix),
sizeof(ObpDosDevicesShortNamePrefix),
(PWSTR)&ObpDosDevicesShortNamePrefix
};
/* PRIVATE FUNCTIONS *********************************************************/
NTSTATUS
NTAPI
INIT_FUNCTION
ObpCreateGlobalDosDevicesSD(OUT PSECURITY_DESCRIPTOR *SecurityDescriptor)
{
PSECURITY_DESCRIPTOR Sd = NULL;
PACL Dacl;
ULONG AclSize, SdSize;
NTSTATUS Status;
AclSize = sizeof(ACL) +
sizeof(ACE) + RtlLengthSid(SeWorldSid) +
sizeof(ACE) + RtlLengthSid(SeLocalSystemSid) +
sizeof(ACE) + RtlLengthSid(SeWorldSid) +
sizeof(ACE) + RtlLengthSid(SeAliasAdminsSid) +
sizeof(ACE) + RtlLengthSid(SeLocalSystemSid) +
sizeof(ACE) + RtlLengthSid(SeCreatorOwnerSid);
SdSize = sizeof(SECURITY_DESCRIPTOR) + AclSize;
/* Allocate the SD and ACL */
Sd = ExAllocatePoolWithTag(PagedPool, SdSize, TAG_SD);
if (Sd == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Initialize the SD */
Status = RtlCreateSecurityDescriptor(Sd,
SECURITY_DESCRIPTOR_REVISION);
if (!NT_SUCCESS(Status))
return Status;
Dacl = (PACL)((INT_PTR)Sd + sizeof(SECURITY_DESCRIPTOR));
/* Initialize the DACL */
RtlCreateAcl(Dacl, AclSize, ACL_REVISION);
/* Add the ACEs */
RtlAddAccessAllowedAce(Dacl,
ACL_REVISION,
GENERIC_READ | GENERIC_EXECUTE,
SeWorldSid);
RtlAddAccessAllowedAce(Dacl,
ACL_REVISION,
GENERIC_ALL,
SeLocalSystemSid);
RtlAddAccessAllowedAceEx(Dacl,
ACL_REVISION,
INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
GENERIC_EXECUTE,
SeWorldSid);
RtlAddAccessAllowedAceEx(Dacl,
ACL_REVISION,
INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
GENERIC_ALL,
SeAliasAdminsSid);
RtlAddAccessAllowedAceEx(Dacl,
ACL_REVISION,
INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
GENERIC_ALL,
SeLocalSystemSid);
RtlAddAccessAllowedAceEx(Dacl,
ACL_REVISION,
INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
GENERIC_ALL,
SeCreatorOwnerSid);
/* Attach the DACL to the SD */
Status = RtlSetDaclSecurityDescriptor(Sd,
TRUE,
Dacl,
FALSE);
if (!NT_SUCCESS(Status))
goto done;
*SecurityDescriptor = Sd;
done:
if (!NT_SUCCESS(Status))
{
if (Sd != NULL)
ExFreePoolWithTag(Sd, TAG_SD);
}
return Status;
}
NTSTATUS
NTAPI
INIT_FUNCTION
ObpCreateDosDevicesDirectory(VOID)
{
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING RootName, TargetName, LinkName;
HANDLE Handle, SymHandle;
PSECURITY_DESCRIPTOR DosDevicesSD = NULL;
NTSTATUS Status;
/* Create a custom security descriptor for the global DosDevices directory */
Status = ObpCreateGlobalDosDevicesSD(&DosDevicesSD);
if (!NT_SUCCESS(Status))
return Status;
/* Create the global DosDevices directory \?? */
RtlInitUnicodeString(&RootName, L"\\GLOBAL??");
InitializeObjectAttributes(&ObjectAttributes,
&RootName,
OBJ_PERMANENT,
NULL,
DosDevicesSD);
Status = NtCreateDirectoryObject(&Handle,
DIRECTORY_ALL_ACCESS,
&ObjectAttributes);
ExFreePoolWithTag(DosDevicesSD, TAG_SD);
if (!NT_SUCCESS(Status)) return Status;
/* Create the system device map */
Status = ObpCreateDeviceMap(Handle);
if (!NT_SUCCESS(Status))
return Status;
/*********************************************\
|*** HACK until we support device mappings ***|
|*** Add a symlink \??\ <--> \GLOBAL??\ ***|
\*********************************************/
RtlInitUnicodeString(&LinkName, L"\\??");
InitializeObjectAttributes(&ObjectAttributes,
&LinkName,
OBJ_PERMANENT,
NULL,
NULL);
Status = NtCreateSymbolicLinkObject(&SymHandle,
SYMBOLIC_LINK_ALL_ACCESS,
&ObjectAttributes,
&RootName);
if (NT_SUCCESS(Status)) NtClose(SymHandle);
/*********************************************\
\*********************************************/
// FIXME: Create a device mapping for the global \?? directory
/*
* Initialize the \??\GLOBALROOT symbolic link
* pointing to the root directory \ .
*/
RtlInitUnicodeString(&LinkName, L"GLOBALROOT");
RtlInitUnicodeString(&TargetName, L"");
InitializeObjectAttributes(&ObjectAttributes,
&LinkName,
OBJ_PERMANENT,
Handle,
NULL);
Status = NtCreateSymbolicLinkObject(&SymHandle,
SYMBOLIC_LINK_ALL_ACCESS,
&ObjectAttributes,
&TargetName);
if (NT_SUCCESS(Status)) NtClose(SymHandle);
/*
* Initialize the \??\Global symbolic link pointing to the global
* DosDevices directory \?? . It is used to access the global \??
* by user-mode components which, by default, use a per-session
* DosDevices directory.
*/
RtlInitUnicodeString(&LinkName, L"Global");
InitializeObjectAttributes(&ObjectAttributes,
&LinkName,
OBJ_PERMANENT,
Handle,
NULL);
Status = NtCreateSymbolicLinkObject(&SymHandle,
SYMBOLIC_LINK_ALL_ACCESS,
&ObjectAttributes,
&RootName);
if (NT_SUCCESS(Status)) NtClose(SymHandle);
/* Close the directory handle */
NtClose(Handle);
if (!NT_SUCCESS(Status)) return Status;
/*
* Initialize the \DosDevices symbolic link pointing to the global
* DosDevices directory \?? , for backward compatibility with
* Windows NT-2000 systems.
*/
RtlCreateUnicodeString(&LinkName, L"\\DosDevices");
RtlInitUnicodeString(&RootName, (PCWSTR)&ObpDosDevicesShortNameRoot);
InitializeObjectAttributes(&ObjectAttributes,
&LinkName,
OBJ_PERMANENT,
NULL,
NULL);
Status = NtCreateSymbolicLinkObject(&SymHandle,
SYMBOLIC_LINK_ALL_ACCESS,
&ObjectAttributes,
&RootName);
if (NT_SUCCESS(Status)) NtClose(SymHandle);
/* Return status */
return Status;
}
/*++
* @name ObpDeleteNameCheck
*
* The ObpDeleteNameCheck routine checks if a named object should be
* removed from the object directory namespace.
*
* @param Object
* Pointer to the object to check for possible removal.
*
* @return None.
*
* @remarks An object is removed if the following 4 criteria are met:
* 1) The object has 0 handles open
* 2) The object is in the directory namespace and has a name
* 3) The object is not permanent
*
*--*/
VOID
NTAPI
ObpDeleteNameCheck(IN PVOID Object)
{
POBJECT_HEADER ObjectHeader;
OBP_LOOKUP_CONTEXT Context;
POBJECT_HEADER_NAME_INFO ObjectNameInfo;
POBJECT_TYPE ObjectType;
PVOID Directory = NULL;
/* Get object structures */
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
ObjectNameInfo = ObpReferenceNameInfo(ObjectHeader);
ObjectType = ObjectHeader->Type;
/*
* Check if the handle count is 0, if the object is named,
* and if the object isn't a permanent object.
*/
if (!(ObjectHeader->HandleCount) &&
(ObjectNameInfo) &&
(ObjectNameInfo->Name.Length) &&
(ObjectNameInfo->Directory) &&
!(ObjectHeader->Flags & OB_FLAG_PERMANENT))
{
/* Setup a lookup context */
ObpInitializeLookupContext(&Context);
/* Lock the directory */
ObpAcquireDirectoryLockExclusive(ObjectNameInfo->Directory, &Context);
/* Do the lookup */
Object = ObpLookupEntryDirectory(ObjectNameInfo->Directory,
&ObjectNameInfo->Name,
0,
FALSE,
&Context);
if (Object)
{
/* Lock the object */
ObpAcquireObjectLock(ObjectHeader);
/* Make sure we can still delete the object */
if (!(ObjectHeader->HandleCount) &&
!(ObjectHeader->Flags & OB_FLAG_PERMANENT))
{
/* First delete it from the directory */
ObpDeleteEntryDirectory(&Context);
/* Check if this is a symbolic link */
if (ObjectType == ObpSymbolicLinkObjectType)
{
/* Remove internal name */
ObpDeleteSymbolicLinkName(Object);
}
/* Check if the kernel exclusive is set */
ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
if ((ObjectNameInfo) &&
(ObjectNameInfo->QueryReferences & OB_FLAG_KERNEL_EXCLUSIVE))
{
/* Remove protection flag */
InterlockedExchangeAdd((PLONG)&ObjectNameInfo->QueryReferences,
-OB_FLAG_KERNEL_EXCLUSIVE);
}
/* Get the directory */
Directory = ObjectNameInfo->Directory;
}
/* Release the lock */
ObpReleaseObjectLock(ObjectHeader);
}
/* Cleanup after lookup */
ObpReleaseLookupContext(&Context);
/* Remove another query reference since we added one on top */
ObpDereferenceNameInfo(ObjectNameInfo);
/* Check if we were inserted in a directory */
if (Directory)
{
/* We were, so first remove the extra reference we had added */
ObpDereferenceNameInfo(ObjectNameInfo);
/* Now dereference the object as well */
ObDereferenceObject(Object);
}
}
else
{
/* Remove the reference we added */
ObpDereferenceNameInfo(ObjectNameInfo);
}
}
NTSTATUS
NTAPI
ObpLookupObjectName(IN HANDLE RootHandle OPTIONAL,
IN OUT PUNICODE_STRING ObjectName,
IN ULONG Attributes,
IN POBJECT_TYPE ObjectType,
IN KPROCESSOR_MODE AccessMode,
IN OUT PVOID ParseContext,
IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
IN PVOID InsertObject OPTIONAL,
IN OUT PACCESS_STATE AccessState,
OUT POBP_LOOKUP_CONTEXT LookupContext,
OUT PVOID *FoundObject)
{
PVOID Object;
POBJECT_HEADER ObjectHeader;
UNICODE_STRING ComponentName, RemainingName;
BOOLEAN Reparse = FALSE, SymLink = FALSE;
POBJECT_DIRECTORY Directory = NULL, ParentDirectory = NULL, RootDirectory;
POBJECT_DIRECTORY ReferencedDirectory = NULL, ReferencedParentDirectory = NULL;
KIRQL CalloutIrql;
OB_PARSE_METHOD ParseRoutine;
NTSTATUS Status;
KPROCESSOR_MODE AccessCheckMode;
PWCHAR NewName;
POBJECT_HEADER_NAME_INFO ObjectNameInfo;
ULONG MaxReparse = 30;
PAGED_CODE();
OBTRACE(OB_NAMESPACE_DEBUG,
"%s - Finding Object: %wZ. Expecting: %p\n",
__FUNCTION__,
ObjectName,
InsertObject);
/* Initialize starting state */
ObpInitializeLookupContext(LookupContext);
*FoundObject = NULL;
Status = STATUS_SUCCESS;
Object = NULL;
/* Check if case-insensitivity is checked */
if (ObpCaseInsensitive)
{
/* Check if the object type requests this */
if (!(ObjectType) || (ObjectType->TypeInfo.CaseInsensitive))
{
/* Add the flag to disable case sensitivity */
Attributes |= OBJ_CASE_INSENSITIVE;
}
}
/* Check if this is a access checks are being forced */
AccessCheckMode = (Attributes & OBJ_FORCE_ACCESS_CHECK) ?
UserMode : AccessMode;
/* Check if we got a Root Directory */
if (RootHandle)
{
/* We did. Reference it */
Status = ObReferenceObjectByHandle(RootHandle,
0,
NULL,
AccessMode,
(PVOID*)&RootDirectory,
NULL);
if (!NT_SUCCESS(Status)) return Status;
/* Get the header */
ObjectHeader = OBJECT_TO_OBJECT_HEADER(RootDirectory);
/* The name cannot start with a separator, unless this is a file */
if ((ObjectName->Buffer) &&
(ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR) &&
(ObjectHeader->Type != IoFileObjectType))
{
/* The syntax is bad, so fail this request */
ObDereferenceObject(RootDirectory);
return STATUS_OBJECT_PATH_SYNTAX_BAD;
}
/* Don't parse a Directory */
if (ObjectHeader->Type != ObpDirectoryObjectType)
{
/* Make sure the Object Type has a parse routine */
ParseRoutine = ObjectHeader->Type->TypeInfo.ParseProcedure;
if (!ParseRoutine)
{
/* We can't parse a name if we don't have a parse routine */
ObDereferenceObject(RootDirectory);
return STATUS_INVALID_HANDLE;
}
/* Set default parse count */
MaxReparse = 30;
/* Now parse */
while (TRUE)
{
/* Start with the full name */
RemainingName = *ObjectName;
/* Call the Parse Procedure */
ObpCalloutStart(&CalloutIrql);
Status = ParseRoutine(RootDirectory,
ObjectType,
AccessState,
AccessCheckMode,
Attributes,
ObjectName,
&RemainingName,
ParseContext,
SecurityQos,
&Object);
ObpCalloutEnd(CalloutIrql, "Parse", ObjectHeader->Type, Object);
/* Check for success or failure, so not reparse */
if ((Status != STATUS_REPARSE) &&
(Status != STATUS_REPARSE_OBJECT))
{
/* Check for failure */
if (!NT_SUCCESS(Status))
{
/* Parse routine might not have cleared this, do it */
Object = NULL;
}
else if (!Object)
{
/* Modify status to reflect failure inside Ob */
Status = STATUS_OBJECT_NAME_NOT_FOUND;
}
/* We're done, return the status and object */
*FoundObject = Object;
ObDereferenceObject(RootDirectory);
return Status;
}
else if ((!ObjectName->Length) ||
(!ObjectName->Buffer) ||
(ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR))
{
/* Reparsed to the root directory, so start over */
ObDereferenceObject(RootDirectory);
RootDirectory = ObpRootDirectoryObject;
/* Don't use this anymore, since we're starting at root */
RootHandle = NULL;
goto ParseFromRoot;
}
else if (--MaxReparse)
{
/* Try reparsing again */
continue;
}
else
{
/* Reparsed too many times */
ObDereferenceObject(RootDirectory);
/* Return the object and normalized status */
*FoundObject = Object;
if (!Object) Status = STATUS_OBJECT_NAME_NOT_FOUND;
return Status;
}
}
}
else if (!(ObjectName->Length) || !(ObjectName->Buffer))
{
/* Just return the Root Directory if we didn't get a name */
Status = ObReferenceObjectByPointer(RootDirectory,
0,
ObjectType,
AccessMode);
if (NT_SUCCESS(Status)) Object = RootDirectory;
/* Remove the first reference we added and return the object */
ObDereferenceObject(RootDirectory);
*FoundObject = Object;
return Status;
}
}
else
{
/* We did not get a Root Directory, so use the root */
RootDirectory = ObpRootDirectoryObject;
/* It must start with a path separator */
if (!(ObjectName->Length) ||
!(ObjectName->Buffer) ||
(ObjectName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR))
{
/* This name is invalid, so fail */
return STATUS_OBJECT_PATH_SYNTAX_BAD;
}
/* Check if the name is only the path separator */
if (ObjectName->Length == sizeof(OBJ_NAME_PATH_SEPARATOR))
{
/* So the caller only wants the root directory; do we have one? */
if (!RootDirectory)
{
/* This must be the first time we're creating it... right? */
if (InsertObject)
{
/* Yes, so return it to ObInsert so that it can create it */
Status = ObReferenceObjectByPointer(InsertObject,
0,
ObjectType,
AccessMode);
if (NT_SUCCESS(Status)) *FoundObject = InsertObject;
return Status;
}
else
{
/* This should never really happen */
ASSERT(FALSE);
return STATUS_INVALID_PARAMETER;
}
}
else
{
/* We do have the root directory, so just return it */
Status = ObReferenceObjectByPointer(RootDirectory,
0,
ObjectType,
AccessMode);
if (NT_SUCCESS(Status)) *FoundObject = RootDirectory;
return Status;
}
}
else
{
ParseFromRoot:
/* FIXME: Check if we have a device map */
/* Check if this is a possible DOS name */
if (!((ULONG_PTR)(ObjectName->Buffer) & 7))
{
/*
* This could be one. Does it match the prefix?
* Note that as an optimization, the match is done as 64-bit
* compare since the prefix is "\??\" which is exactly 8 bytes.
*
* In the second branch, we test for "\??" which is also valid.
* This time, we use a 32-bit compare followed by a Unicode
* character compare (16-bit), since the sum is 6 bytes.
*/
if ((ObjectName->Length >= ObpDosDevicesShortName.Length) &&
(*(PULONGLONG)(ObjectName->Buffer) ==
ObpDosDevicesShortNamePrefix.Alignment.QuadPart))
{
/* FIXME! */
}
else if ((ObjectName->Length == ObpDosDevicesShortName.Length -
sizeof(WCHAR)) &&
(*(PULONG)(ObjectName->Buffer) ==
ObpDosDevicesShortNameRoot.Alignment.LowPart) &&
(*((PWCHAR)(ObjectName->Buffer) + 2) ==
(WCHAR)(ObpDosDevicesShortNameRoot.Alignment.HighPart)))
{
/* FIXME! */
}
}
}
}
/* Check if we were reparsing a symbolic link */
if (!SymLink)
{
/* Allow reparse */
Reparse = TRUE;
MaxReparse = 30;
}
/* Reparse */
while (Reparse && MaxReparse)
{
/* Get the name */
RemainingName = *ObjectName;
/* Disable reparsing again */
Reparse = FALSE;
/* Start parse loop */
while (TRUE)
{
/* Clear object */
Object = NULL;
/* Check if the name starts with a path separator */
if ((RemainingName.Length) &&
(RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR))
{
/* Skip the path separator */
RemainingName.Buffer++;
RemainingName.Length -= sizeof(OBJ_NAME_PATH_SEPARATOR);
}
/* Find the next Part Name */
ComponentName = RemainingName;
while (RemainingName.Length)
{
/* Break if we found the \ ending */
if (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR) break;
/* Move on */
RemainingName.Buffer++;
RemainingName.Length -= sizeof(OBJ_NAME_PATH_SEPARATOR);
}
/* Get its size and make sure it's valid */
ComponentName.Length -= RemainingName.Length;
if (!ComponentName.Length)
{
/* Invalid size, fail */
Status = STATUS_OBJECT_NAME_INVALID;
break;
}
/* Check if we're in the root */
if (!Directory) Directory = RootDirectory;
/* Check if this is a user-mode call that needs to traverse */
if ((AccessCheckMode != KernelMode) &&
!(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE))
{
/* We shouldn't have referenced a directory yet */
ASSERT(ReferencedDirectory == NULL);
/* Reference the directory */
ObReferenceObject(Directory);
ReferencedDirectory = Directory;
/* Check if we have a parent directory */
if (ParentDirectory)
{
/* Check for traverse access */
if (!ObpCheckTraverseAccess(ParentDirectory,
DIRECTORY_TRAVERSE,
AccessState,
FALSE,
AccessCheckMode,
&Status))
{
/* We don't have it, fail */
break;
}
}
}
/* Check if we don't have a remaining name yet */
if (!RemainingName.Length)
{
/* Check if we don't have a referenced directory yet */
if (!ReferencedDirectory)
{
/* Reference it */
ObReferenceObject(Directory);
ReferencedDirectory = Directory;
}
/* Check if we are inserting an object */
if (InsertObject)
{
/* Lock the directory */
ObpAcquireDirectoryLockExclusive(Directory, LookupContext);
}
}
/* Do the lookup */
Object = ObpLookupEntryDirectory(Directory,
&ComponentName,
Attributes,
InsertObject ? FALSE : TRUE,
LookupContext);
if (!Object)
{
/* We didn't find it... do we still have a path? */
if (RemainingName.Length)
{
/* Then tell the caller the path wasn't found */
Status = STATUS_OBJECT_PATH_NOT_FOUND;
break;
}
else if (!InsertObject)
{
/* Otherwise, we have a path, but the name isn't valid */
Status = STATUS_OBJECT_NAME_NOT_FOUND;
break;
}
/* Check create access for the object */
if (!ObCheckCreateObjectAccess(Directory,
ObjectType == ObpDirectoryObjectType ?
DIRECTORY_CREATE_SUBDIRECTORY :
DIRECTORY_CREATE_OBJECT,
AccessState,
&ComponentName,
FALSE,
AccessCheckMode,
&Status))
{
/* We don't have create access, fail */
break;
}
/* Get the object header */
ObjectHeader = OBJECT_TO_OBJECT_HEADER(InsertObject);
/* FIXME: Check if this is a Section Object or Sym Link */
/* FIXME: If it is, then check if this isn't session 0 */
/* FIXME: If it isn't, check for SeCreateGlobalPrivilege */
/* FIXME: If privilege isn't there, check for unsecure name */
/* FIXME: If it isn't a known unsecure name, then fail */
/* Create Object Name */
NewName = ExAllocatePoolWithTag(PagedPool,
ComponentName.Length,
OB_NAME_TAG);
if (!(NewName) ||
!(ObpInsertEntryDirectory(Directory,
LookupContext,
ObjectHeader)))
{
/* Either couldn't allocate the name, or insert failed */
if (NewName) ExFreePoolWithTag(NewName, OB_NAME_TAG);
/* Fail due to memory reasons */
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
/* Reference newly to be inserted object */
ObReferenceObject(InsertObject);
/* Get the name information */
ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
/* Reference the directory */
ObReferenceObject(Directory);
/* Copy the Name */
RtlCopyMemory(NewName,
ComponentName.Buffer,
ComponentName.Length);
/* Check if we had an old name */
if (ObjectNameInfo->Name.Buffer)
{
/* Free it */
ExFreePoolWithTag(ObjectNameInfo->Name.Buffer, OB_NAME_TAG);
}
/* Write new one */
ObjectNameInfo->Name.Buffer = NewName;
ObjectNameInfo->Name.Length = ComponentName.Length;
ObjectNameInfo->Name.MaximumLength = ComponentName.Length;
/* Return Status and the Expected Object */
Status = STATUS_SUCCESS;
Object = InsertObject;
/* Get out of here */
break;
}
ReparseObject:
/* We found it, so now get its header */
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
/*
* Check for a parse Procedure, but don't bother to parse for an insert
* unless it's a Symbolic Link, in which case we MUST parse
*/
ParseRoutine = ObjectHeader->Type->TypeInfo.ParseProcedure;
if ((ParseRoutine) &&
(!(InsertObject) || (ParseRoutine == ObpParseSymbolicLink)))
{
/* Use the Root Directory next time */
Directory = NULL;
/* Increment the pointer count */
InterlockedExchangeAddSizeT(&ObjectHeader->PointerCount, 1);
/* Cleanup from the first lookup */
ObpReleaseLookupContext(LookupContext);
/* Check if we have a referenced directory */
if (ReferencedDirectory)
{
/* We do, dereference it */
ObDereferenceObject(ReferencedDirectory);
ReferencedDirectory = NULL;
}
/* Check if we have a referenced parent directory */
if (ReferencedParentDirectory)
{
/* We do, dereference it */
ObDereferenceObject(ReferencedParentDirectory);
ReferencedParentDirectory = NULL;
}
/* Call the Parse Procedure */
ObpCalloutStart(&CalloutIrql);
Status = ParseRoutine(Object,
ObjectType,
AccessState,
AccessCheckMode,
Attributes,
ObjectName,
&RemainingName,
ParseContext,
SecurityQos,
&Object);
ObpCalloutEnd(CalloutIrql, "Parse", ObjectHeader->Type, Object);
/* Remove our extra reference */
ObDereferenceObject(&ObjectHeader->Body);
/* Check if we have to reparse */
if ((Status == STATUS_REPARSE) ||
(Status == STATUS_REPARSE_OBJECT))
{
/* Reparse again */
Reparse = TRUE;
--MaxReparse;
if (MaxReparse == 0)
{
Object = NULL;
break;
}
/* Start over from root if we got sent back there */
if ((Status == STATUS_REPARSE_OBJECT) ||
(ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR))
{
/* Check if we got a root directory */
if (RootHandle)
{
/* Stop using it, because we have a new directory now */
ObDereferenceObject(RootDirectory);
RootHandle = NULL;
}
/* Start at Root */
ParentDirectory = NULL;
RootDirectory = ObpRootDirectoryObject;
/* Check for reparse status */
if (Status == STATUS_REPARSE_OBJECT)
{
/* Don't reparse again */
Reparse = FALSE;
/* Did we actually get an object to which to reparse? */
if (!Object)
{
/* We didn't, so set a failure status */
Status = STATUS_OBJECT_NAME_NOT_FOUND;
}
else
{
/* We did, so we're free to parse the new object */
goto ReparseObject;
}
}
else
{
/* This is a symbolic link */
SymLink = TRUE;
goto ParseFromRoot;
}
}
else if (RootDirectory == ObpRootDirectoryObject)
{
/* We got STATUS_REPARSE but are at the Root Directory */
Object = NULL;
Status = STATUS_OBJECT_NAME_NOT_FOUND;
Reparse = FALSE;
}
}
else if (!NT_SUCCESS(Status))
{
/* Total failure */
Object = NULL;
}
else if (!Object)
{
/* We didn't reparse but we didn't find the Object Either */
Status = STATUS_OBJECT_NAME_NOT_FOUND;
}
/* Break out of the loop */
break;
}
else
{
/* No parse routine...do we still have a remaining name? */
if (!RemainingName.Length)
{
/* Are we creating an object? */
if (!InsertObject)
{
/* Check if this is a user-mode call that needs to traverse */
if ((AccessCheckMode != KernelMode) &&
!(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE))
{
/* Check if we can get it */
if (!ObpCheckTraverseAccess(Directory,
DIRECTORY_TRAVERSE,
AccessState,
FALSE,
AccessCheckMode,
&Status))
{
/* We don't have access, fail */
Object = NULL;
break;
}
}
/* Reference the Object */
Status = ObReferenceObjectByPointer(Object,
0,
ObjectType,
AccessMode);
if (!NT_SUCCESS(Status)) Object = NULL;
}
/* And get out of the reparse loop */
break;
}
else
{
/* We still have a name; check if this is a directory object */
if (ObjectHeader->Type == ObpDirectoryObjectType)
{
/* Check if we have a referenced parent directory */
if (ReferencedParentDirectory)
{
/* Dereference it */
ObDereferenceObject(ReferencedParentDirectory);
}
/* Restart the lookup from this directory */
ReferencedParentDirectory = ReferencedDirectory;
ParentDirectory = Directory;
Directory = Object;
ReferencedDirectory = NULL;
}
else
{
/* We still have a name, but no parse routine for it */
Status = STATUS_OBJECT_TYPE_MISMATCH;
Object = NULL;
break;
}
}
}
}
}
/* Check if we failed */
if (!NT_SUCCESS(Status))
{
/* Cleanup after lookup */
ObpReleaseLookupContext(LookupContext);
}
/* Check if we have a device map and dereference it if so */
//if (DeviceMap) ObfDereferenceDeviceMap(DeviceMap);
/* Check if we have a referenced directory and dereference it if so */
if (ReferencedDirectory) ObDereferenceObject(ReferencedDirectory);
/* Check if we have a referenced parent directory */
if (ReferencedParentDirectory)
{
/* We do, dereference it */
ObDereferenceObject(ReferencedParentDirectory);
}
/* Set the found object and check if we got one */
*FoundObject = Object;
if (!Object)
{
/* Nothing was found. Did we reparse or get success? */
if ((Status == STATUS_REPARSE) || (NT_SUCCESS(Status)))
{
/* Set correct failure */
Status = STATUS_OBJECT_NAME_NOT_FOUND;
}
}
/* Check if we had a root directory */
if (RootHandle) ObDereferenceObject(RootDirectory);
/* Return status to caller */
OBTRACE(OB_NAMESPACE_DEBUG,
"%s - Found Object: %p. Expected: %p\n",
__FUNCTION__,
*FoundObject,
InsertObject);
return Status;
}
/* PUBLIC FUNCTIONS *********************************************************/
NTSTATUS
NTAPI
ObQueryNameString(IN PVOID Object,
OUT POBJECT_NAME_INFORMATION ObjectNameInfo,
IN ULONG Length,
OUT PULONG ReturnLength)
{
POBJECT_HEADER_NAME_INFO LocalInfo;
POBJECT_HEADER ObjectHeader;
POBJECT_DIRECTORY ParentDirectory;
ULONG NameSize;
PWCH ObjectName;
BOOLEAN ObjectIsNamed;
NTSTATUS Status = STATUS_SUCCESS;
/* Get the Kernel Meta-Structures */
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
LocalInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
/* Check if a Query Name Procedure is available */
if (ObjectHeader->Type->TypeInfo.QueryNameProcedure)
{
/* Call the procedure inside SEH */
ObjectIsNamed = ((LocalInfo) && (LocalInfo->Name.Length > 0));
_SEH2_TRY
{
Status = ObjectHeader->Type->TypeInfo.QueryNameProcedure(Object,
ObjectIsNamed,
ObjectNameInfo,
Length,
ReturnLength,
KernelMode);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
return Status;
}
/* Check if the object doesn't even have a name */
if (!(LocalInfo) || !(LocalInfo->Name.Buffer))
{
Status = STATUS_SUCCESS;
_SEH2_TRY
{
/* We're returning the name structure */
*ReturnLength = sizeof(OBJECT_NAME_INFORMATION);
/* Check if we were given enough space */
if (*ReturnLength > Length)
{
Status = STATUS_INFO_LENGTH_MISMATCH;
}
else
{
/* Return an empty buffer */
RtlInitEmptyUnicodeString(&ObjectNameInfo->Name, NULL, 0);
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
return Status;
}
/*
* Find the size needed for the name. We won't do
* this during the Name Creation loop because we want
* to let the caller know that the buffer isn't big
* enough right at the beginning, not work our way through
* and find out at the end
*/
_SEH2_TRY
{
if (Object == ObpRootDirectoryObject)
{
/* Size of the '\' string */
NameSize = sizeof(OBJ_NAME_PATH_SEPARATOR);
}
else
{
/* Get the Object Directory and add name of Object */
ParentDirectory = LocalInfo->Directory;
NameSize = sizeof(OBJ_NAME_PATH_SEPARATOR) + LocalInfo->Name.Length;
/* Loop inside the directory to get the top-most one (meaning root) */
while ((ParentDirectory != ObpRootDirectoryObject) && (ParentDirectory))
{
/* Get the Name Information */
LocalInfo = OBJECT_HEADER_TO_NAME_INFO(
OBJECT_TO_OBJECT_HEADER(ParentDirectory));
/* Add the size of the Directory Name */
if (LocalInfo && LocalInfo->Directory)
{
/* Size of the '\' string + Directory Name */
NameSize += sizeof(OBJ_NAME_PATH_SEPARATOR) +
LocalInfo->Name.Length;
/* Move to next parent Directory */
ParentDirectory = LocalInfo->Directory;
}
else
{
/* Directory with no name. We append "...\" */
NameSize += sizeof(L"...") + sizeof(OBJ_NAME_PATH_SEPARATOR);
break;
}
}
}
/* Finally, add the name of the structure and the null char */
*ReturnLength = NameSize +
sizeof(OBJECT_NAME_INFORMATION) +
sizeof(UNICODE_NULL);
/* Check if we were given enough space */
if (*ReturnLength > Length) _SEH2_YIELD(return STATUS_INFO_LENGTH_MISMATCH);
/*
* Now we will actually create the name. We work backwards because
* it's easier to start off from the Name we have and walk up the
* parent directories. We use the same logic as Name Length calculation.
*/
LocalInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
ObjectName = (PWCH)((ULONG_PTR)ObjectNameInfo + *ReturnLength);
*--ObjectName = UNICODE_NULL;
/* Check if the object is actually the Root directory */
if (Object == ObpRootDirectoryObject)
{
/* This is already the Root Directory, return "\\" */
*--ObjectName = OBJ_NAME_PATH_SEPARATOR;
ObjectNameInfo->Name.Length = (USHORT)NameSize;
ObjectNameInfo->Name.MaximumLength = (USHORT)(NameSize +
sizeof(UNICODE_NULL));
ObjectNameInfo->Name.Buffer = ObjectName;
Status = STATUS_SUCCESS;
}
else
{
/* Start by adding the Object's Name */
ObjectName = (PWCH)((ULONG_PTR)ObjectName -
LocalInfo->Name.Length);
RtlCopyMemory(ObjectName,
LocalInfo->Name.Buffer,
LocalInfo->Name.Length);
/* Now parse the Parent directories until we reach the top */
ParentDirectory = LocalInfo->Directory;
while ((ParentDirectory != ObpRootDirectoryObject) && (ParentDirectory))
{
/* Get the name information */
LocalInfo = OBJECT_HEADER_TO_NAME_INFO(
OBJECT_TO_OBJECT_HEADER(ParentDirectory));
/* Add the "\" */
*(--ObjectName) = OBJ_NAME_PATH_SEPARATOR;
/* Add the Parent Directory's Name */
if (LocalInfo && LocalInfo->Name.Buffer)
{
/* Add the name */
ObjectName = (PWCH)((ULONG_PTR)ObjectName -
LocalInfo->Name.Length);
RtlCopyMemory(ObjectName,
LocalInfo->Name.Buffer,
LocalInfo->Name.Length);
/* Move to next parent */
ParentDirectory = LocalInfo->Directory;
}
else
{
/* Directory without a name, we add "..." */
ObjectName = (PWCH)((ULONG_PTR)ObjectName -
sizeof(L"...") +
sizeof(UNICODE_NULL));
RtlCopyMemory(ObjectName, L"...", sizeof(L"..."));
break;
}
}
/* Add Root Directory Name */
*(--ObjectName) = OBJ_NAME_PATH_SEPARATOR;
ObjectNameInfo->Name.Length = (USHORT)NameSize;
ObjectNameInfo->Name.MaximumLength =
(USHORT)(NameSize + sizeof(UNICODE_NULL));
ObjectNameInfo->Name.Buffer = ObjectName;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
/* Return success */
return Status;
}
/* EOF */