mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 01:55:19 +00:00
1417 lines
52 KiB
C
1417 lines
52 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
|
|
};
|
|
|
|
WCHAR ObpUnsecureGlobalNamesBuffer[128] = {0};
|
|
ULONG ObpUnsecureGlobalNamesLength = sizeof(ObpUnsecureGlobalNamesBuffer);
|
|
|
|
/* PRIVATE FUNCTIONS *********************************************************/
|
|
|
|
CODE_SEG("INIT")
|
|
NTSTATUS
|
|
NTAPI
|
|
ObpGetDosDevicesProtection(OUT PSECURITY_DESCRIPTOR SecurityDescriptor)
|
|
{
|
|
PACL Dacl;
|
|
ULONG AclSize;
|
|
NTSTATUS Status;
|
|
|
|
/* Initialize the SD */
|
|
Status = RtlCreateSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
if (ObpProtectionMode & 1)
|
|
{
|
|
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);
|
|
|
|
/* Allocate the ACL */
|
|
Dacl = ExAllocatePoolWithTag(PagedPool, AclSize, TAG_DACL);
|
|
if (Dacl == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
/* Initialize the DACL */
|
|
Status = RtlCreateAcl(Dacl, AclSize, ACL_REVISION);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
/* Add the ACEs */
|
|
Status = RtlAddAccessAllowedAce(Dacl,
|
|
ACL_REVISION,
|
|
GENERIC_READ | GENERIC_EXECUTE,
|
|
SeWorldSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
Status = RtlAddAccessAllowedAce(Dacl,
|
|
ACL_REVISION,
|
|
GENERIC_ALL,
|
|
SeLocalSystemSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
Status = RtlAddAccessAllowedAceEx(Dacl,
|
|
ACL_REVISION,
|
|
INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
|
|
GENERIC_EXECUTE,
|
|
SeWorldSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
Status = RtlAddAccessAllowedAceEx(Dacl,
|
|
ACL_REVISION,
|
|
INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
|
|
GENERIC_ALL,
|
|
SeAliasAdminsSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
Status = RtlAddAccessAllowedAceEx(Dacl,
|
|
ACL_REVISION,
|
|
INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
|
|
GENERIC_ALL,
|
|
SeLocalSystemSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
Status = RtlAddAccessAllowedAceEx(Dacl,
|
|
ACL_REVISION,
|
|
INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
|
|
GENERIC_ALL,
|
|
SeCreatorOwnerSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
}
|
|
else
|
|
{
|
|
AclSize = sizeof(ACL) +
|
|
sizeof(ACE) + RtlLengthSid(SeLocalSystemSid) +
|
|
sizeof(ACE) + RtlLengthSid(SeWorldSid) +
|
|
sizeof(ACE) + RtlLengthSid(SeLocalSystemSid);
|
|
|
|
/* Allocate the ACL */
|
|
Dacl = ExAllocatePoolWithTag(PagedPool, AclSize, TAG_DACL);
|
|
if (Dacl == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
/* Initialize the DACL */
|
|
Status = RtlCreateAcl(Dacl, AclSize, ACL_REVISION);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
/* Add the ACEs */
|
|
Status = RtlAddAccessAllowedAce(Dacl,
|
|
ACL_REVISION,
|
|
GENERIC_READ | GENERIC_EXECUTE | GENERIC_WRITE,
|
|
SeWorldSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
Status = RtlAddAccessAllowedAce(Dacl,
|
|
ACL_REVISION,
|
|
GENERIC_ALL,
|
|
SeLocalSystemSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
Status = RtlAddAccessAllowedAceEx(Dacl,
|
|
ACL_REVISION,
|
|
INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
|
|
GENERIC_ALL,
|
|
SeWorldSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
}
|
|
|
|
/* Attach the DACL to the SD */
|
|
Status = RtlSetDaclSecurityDescriptor(SecurityDescriptor, TRUE, Dacl, FALSE);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
CODE_SEG("INIT")
|
|
VOID
|
|
NTAPI
|
|
ObpFreeDosDevicesProtection(OUT PSECURITY_DESCRIPTOR SecurityDescriptor)
|
|
{
|
|
PACL Dacl;
|
|
NTSTATUS Status;
|
|
BOOLEAN DaclPresent, DaclDefaulted;
|
|
|
|
Status = RtlGetDaclSecurityDescriptor(SecurityDescriptor, &DaclPresent, &Dacl, &DaclDefaulted);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
ASSERT(DaclPresent);
|
|
ASSERT(Dacl != NULL);
|
|
ExFreePoolWithTag(Dacl, TAG_DACL);
|
|
}
|
|
|
|
CODE_SEG("INIT")
|
|
NTSTATUS
|
|
NTAPI
|
|
ObpCreateDosDevicesDirectory(VOID)
|
|
{
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING RootName, TargetName, LinkName;
|
|
HANDLE Handle, SymHandle;
|
|
SECURITY_DESCRIPTOR DosDevicesSD;
|
|
NTSTATUS Status;
|
|
|
|
/*
|
|
* Enable LUID mappings only if not explicitely disabled
|
|
* and if protection mode is set
|
|
*/
|
|
if (ObpProtectionMode == 0 || ObpLUIDDeviceMapsDisabled != 0)
|
|
ObpLUIDDeviceMapsEnabled = 0;
|
|
else
|
|
ObpLUIDDeviceMapsEnabled = 1;
|
|
|
|
/* Create a custom security descriptor for the global DosDevices directory */
|
|
Status = ObpGetDosDevicesProtection(&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);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
/* Create the system device map */
|
|
Status = ObSetDeviceMap(NULL, Handle);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
/*
|
|
* Initialize the \??\GLOBALROOT symbolic link
|
|
* pointing to the root directory \ .
|
|
*/
|
|
RtlInitUnicodeString(&LinkName, L"GLOBALROOT");
|
|
RtlInitUnicodeString(&TargetName, L"");
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&LinkName,
|
|
OBJ_PERMANENT,
|
|
Handle,
|
|
&DosDevicesSD);
|
|
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,
|
|
&DosDevicesSD);
|
|
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))
|
|
goto done;
|
|
|
|
/*
|
|
* Initialize the \DosDevices symbolic link pointing to the global
|
|
* DosDevices directory \?? , for backward compatibility with
|
|
* Windows NT-2000 systems.
|
|
*/
|
|
RtlInitUnicodeString(&LinkName, L"\\DosDevices");
|
|
RtlInitUnicodeString(&RootName, (PCWSTR)&ObpDosDevicesShortNameRoot);
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&LinkName,
|
|
OBJ_PERMANENT,
|
|
NULL,
|
|
&DosDevicesSD);
|
|
Status = NtCreateSymbolicLinkObject(&SymHandle,
|
|
SYMBOLIC_LINK_ALL_ACCESS,
|
|
&ObjectAttributes,
|
|
&RootName);
|
|
if (NT_SUCCESS(Status)) NtClose(SymHandle);
|
|
|
|
done:
|
|
ObpFreeDosDevicesProtection(&DosDevicesSD);
|
|
|
|
/* 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 and lock it */
|
|
ObpInitializeLookupContext(&Context);
|
|
ObpAcquireLookupContextLock(&Context, ObjectNameInfo->Directory);
|
|
|
|
/* 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 flag 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);
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
ObpIsUnsecureName(IN PUNICODE_STRING ObjectName,
|
|
IN BOOLEAN CaseInSensitive)
|
|
{
|
|
BOOLEAN Unsecure;
|
|
PWSTR UnsecureBuffer;
|
|
UNICODE_STRING UnsecureName;
|
|
|
|
/* No unsecure names known, quit */
|
|
if (ObpUnsecureGlobalNamesBuffer[0] == UNICODE_NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* By default, we have a secure name */
|
|
Unsecure = FALSE;
|
|
/* We will browse the whole string */
|
|
UnsecureBuffer = &ObpUnsecureGlobalNamesBuffer[0];
|
|
while (TRUE)
|
|
{
|
|
/* Initialize the unicode string */
|
|
RtlInitUnicodeString(&UnsecureName, UnsecureBuffer);
|
|
/* We're at the end of the multisz string! */
|
|
if (UnsecureName.Length == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Does the unsecure name prefix the object name?
|
|
* If so, that's an unsecure name, and return so
|
|
*/
|
|
if (RtlPrefixUnicodeString(&UnsecureName, ObjectName, CaseInSensitive))
|
|
{
|
|
Unsecure = TRUE;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Move to the next string. As a reminder, ObpUnsecureGlobalNamesBuffer is
|
|
* a multisz, so we move the string next to the current UNICODE_NULL char
|
|
*/
|
|
UnsecureBuffer = (PWSTR)((ULONG_PTR)UnsecureBuffer + UnsecureName.Length + sizeof(UNICODE_NULL));
|
|
}
|
|
|
|
/* Return our findings */
|
|
return Unsecure;
|
|
}
|
|
|
|
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;
|
|
PDEVICE_MAP DeviceMap = NULL;
|
|
UNICODE_STRING LocalName;
|
|
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;
|
|
}
|
|
|
|
LocalName = *ObjectName;
|
|
}
|
|
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:
|
|
LocalName = *ObjectName;
|
|
|
|
/* Deference the device map if we already have one */
|
|
if (DeviceMap != NULL)
|
|
{
|
|
ObfDereferenceDeviceMap(DeviceMap);
|
|
DeviceMap = NULL;
|
|
}
|
|
|
|
/* 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))
|
|
{
|
|
DeviceMap = ObpReferenceDeviceMap();
|
|
/* We have a local mapping, drop the ?? prefix */
|
|
if (DeviceMap != NULL && DeviceMap->DosDevicesDirectory != NULL)
|
|
{
|
|
LocalName.Length -= ObpDosDevicesShortName.Length;
|
|
LocalName.MaximumLength -= ObpDosDevicesShortName.Length;
|
|
LocalName.Buffer += (ObpDosDevicesShortName.Length / sizeof(WCHAR));
|
|
|
|
/* We'll browse that local directory */
|
|
Directory = DeviceMap->DosDevicesDirectory;
|
|
}
|
|
}
|
|
else if ((ObjectName->Length == ObpDosDevicesShortName.Length -
|
|
sizeof(WCHAR)) &&
|
|
(*(PULONG)(ObjectName->Buffer) ==
|
|
ObpDosDevicesShortNameRoot.Alignment.LowPart) &&
|
|
(*((PWCHAR)(ObjectName->Buffer) + 2) ==
|
|
(WCHAR)(ObpDosDevicesShortNameRoot.Alignment.HighPart)))
|
|
{
|
|
DeviceMap = ObpReferenceDeviceMap();
|
|
|
|
/* Caller is looking for the directory itself */
|
|
if (DeviceMap != NULL && DeviceMap->DosDevicesDirectory != NULL)
|
|
{
|
|
Status = ObReferenceObjectByPointer(DeviceMap->DosDevicesDirectory,
|
|
0,
|
|
ObjectType,
|
|
AccessMode);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
*FoundObject = DeviceMap->DosDevicesDirectory;
|
|
}
|
|
|
|
ObfDereferenceDeviceMap(DeviceMap);
|
|
return Status;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check if we were reparsing a symbolic link */
|
|
if (!SymLink)
|
|
{
|
|
/* Allow reparse */
|
|
Reparse = TRUE;
|
|
MaxReparse = 30;
|
|
}
|
|
|
|
/* Reparse */
|
|
while (Reparse && MaxReparse)
|
|
{
|
|
/* Get the name */
|
|
RemainingName = LocalName;
|
|
|
|
/* 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 lookup context */
|
|
ObpAcquireLookupContextLock(LookupContext, Directory);
|
|
}
|
|
}
|
|
|
|
/* 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);
|
|
|
|
/*
|
|
* Deny object creation if:
|
|
* That's a section object or a symbolic link
|
|
* Which isn't in the same section that root directory
|
|
* That doesn't have the SeCreateGlobalPrivilege
|
|
* And that is not a known unsecure name
|
|
*/
|
|
if (RootDirectory->SessionId != -1)
|
|
{
|
|
if (ObjectHeader->Type == MmSectionObjectType ||
|
|
ObjectHeader->Type == ObpSymbolicLinkObjectType)
|
|
{
|
|
if (RootDirectory->SessionId != PsGetCurrentProcessSessionId() &&
|
|
!SeSinglePrivilegeCheck(SeCreateGlobalPrivilege, AccessCheckMode) &&
|
|
!ObpIsUnsecureName(&ComponentName, BooleanFlagOn(Attributes, OBJ_CASE_INSENSITIVE)))
|
|
{
|
|
Status = STATUS_ACCESS_DENIED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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 */
|