/*
 * 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
ObpCreateDosDevicesDirectory(VOID)
{
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING RootName, TargetName, LinkName;
    HANDLE Handle, SymHandle;
    NTSTATUS Status;

    /* Create the global DosDevices directory \?? */
    RtlInitUnicodeString(&RootName, L"\\GLOBAL??");
    InitializeObjectAttributes(&ObjectAttributes,
                               &RootName,
                               OBJ_PERMANENT,
                               NULL,
                               NULL);
    Status = NtCreateDirectoryObject(&Handle,
                                     DIRECTORY_ALL_ACCESS,
                                     &ObjectAttributes);
    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);

    /* FIXME: Hack Hack! */
    ObSystemDeviceMap = ExAllocatePoolWithTag(NonPagedPool,
                                              sizeof(*ObSystemDeviceMap),
                                              'mDbO');
    if (!ObSystemDeviceMap) return STATUS_INSUFFICIENT_RESOURCES;
    RtlZeroMemory(ObSystemDeviceMap, sizeof(*ObSystemDeviceMap));

    /* Return status */
    return Status;
}

VOID
NTAPI
ObDereferenceDeviceMap(IN PEPROCESS Process)
{
    PDEVICE_MAP DeviceMap;

    /* Get the pointer to this process devicemap and reset it
       holding devicemap lock */
    KeAcquireGuardedMutex(&ObpDeviceMapLock);
    DeviceMap = Process->DeviceMap;
    Process->DeviceMap = NULL;
    KeReleaseGuardedMutex(&ObpDeviceMapLock);

    /* Continue only if there is a devicemap to dereference */
    if (DeviceMap)
    {
        KeAcquireGuardedMutex(&ObpDeviceMapLock);

        /* Delete the device map link and dereference it */
        if (--DeviceMap->ReferenceCount)
        {
            /* Nobody is referencing it anymore, unlink the DOS directory */
            DeviceMap->DosDevicesDirectory->DeviceMap = NULL;

            /* Release the devicemap lock */
            KeReleaseGuardedMutex(&ObpDeviceMapLock);

            /* Dereference the DOS Devices Directory and free the Device Map */
            ObDereferenceObject(DeviceMap->DosDevicesDirectory);
            ExFreePool(DeviceMap);
        }
        else
        {
            /* Release the devicemap lock */
            KeReleaseGuardedMutex(&ObpDeviceMapLock);
        }
    }
}

VOID
NTAPI
ObInheritDeviceMap(IN PEPROCESS Parent,
                   IN PEPROCESS Process)
{
    /* FIXME: Devicemap Support */
}

/*++
* @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 == ObSymbolicLinkType)
                {
                    /* 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 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 != ObDirectoryType)
        {
            /* 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 == ObDirectoryType ?
                                               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 */
                InterlockedExchangeAdd(&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 == ObDirectoryType)
                    {
                        /* 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;
}

VOID
NTAPI
ObQueryDeviceMapInformation(IN PEPROCESS Process,
                            IN PPROCESS_DEVICEMAP_INFORMATION DeviceMapInfo)
{
    /*
    * FIXME: This is an ugly hack for now, to always return the System Device Map
    * instead of returning the Process Device Map. Not important yet since we don't use it
    */

    KeAcquireGuardedMutex(&ObpDeviceMapLock);

    /* Make a copy */
    DeviceMapInfo->Query.DriveMap = ObSystemDeviceMap->DriveMap;
    RtlCopyMemory(DeviceMapInfo->Query.DriveType,
                  ObSystemDeviceMap->DriveType,
                  sizeof(ObSystemDeviceMap->DriveType));

    KeReleaseGuardedMutex(&ObpDeviceMapLock);
}

NTSTATUS
NTAPI
ObIsDosDeviceLocallyMapped(
    IN ULONG Index,
    OUT PUCHAR DosDeviceState)
{
    /* check parameters */
    if (Index < 1 || Index > 26)
    {
        /* invalid index */
        return STATUS_INVALID_PARAMETER;
    }

    /* acquire lock */
    KeAcquireGuardedMutex(&ObpDeviceMapLock);

    /* get drive mapping status */
    *DosDeviceState = (ObSystemDeviceMap->DriveMap & (1 << Index)) != 0;

    /* release lock */
    KeReleaseGuardedMutex(&ObpDeviceMapLock);

    /* done */
    return STATUS_SUCCESS;
}

/* EOF */
