[MOUNTMGR]
authorPierre Schweitzer <pierre@reactos.org>
Tue, 24 Jan 2012 22:54:14 +0000 (22:54 +0000)
committerPierre Schweitzer <pierre@reactos.org>
Tue, 24 Jan 2012 22:54:14 +0000 (22:54 +0000)
Finally add the long awaited Mount Point Manager (aka mountmgr).
It's not complete yet, but will do most of the job it's been designed for.
What's missing is some dos volumes handlers, complete database support (local - registry - is complete, remote - files - is not).
It handles NTFS properties like reparse points.
It also handles devices with drive letters and those without drive letters (by using their unique ID).
Devices removal/arrival is detected through notifications (might be an issue).
Some work will have to be done in storage stack to fully support it.
Most of its IOCTL have been implemented (it's possible, for example, to notify a volume arrival through them, in case notifications don't work).

There's still some work to do on it to have it complete and fully implemented.
Enjoy!

svn path=/branches/usb-bringup-trunk/; revision=55156

15 files changed:
drivers/CMakeLists.txt
drivers/drivers.rbuild
drivers/filters/CMakeLists.txt [new file with mode: 0644]
drivers/filters/directory.rbuild [new file with mode: 0644]
drivers/filters/mountmgr/CMakeLists.txt [new file with mode: 0644]
drivers/filters/mountmgr/database.c [new file with mode: 0644]
drivers/filters/mountmgr/device.c [new file with mode: 0644]
drivers/filters/mountmgr/mntmgr.h [new file with mode: 0644]
drivers/filters/mountmgr/mountmgr.c [new file with mode: 0644]
drivers/filters/mountmgr/mountmgr.rbuild [new file with mode: 0644]
drivers/filters/mountmgr/mountmgr.rc [new file with mode: 0644]
drivers/filters/mountmgr/notify.c [new file with mode: 0644]
drivers/filters/mountmgr/point.c [new file with mode: 0644]
drivers/filters/mountmgr/symlink.c [new file with mode: 0644]
drivers/filters/mountmgr/uniqueid.c [new file with mode: 0644]

index 35be3e0..22fad3a 100644 (file)
@@ -4,6 +4,7 @@ add_subdirectory(battery)
 add_subdirectory(bus)
 add_subdirectory(directx)
 add_subdirectory(filesystems)
+add_subdirectory(filters)
 add_subdirectory(hid)
 add_subdirectory(input)
 add_subdirectory(ksfilter)
index 4e3c60f..67ab810 100644 (file)
@@ -19,6 +19,9 @@
 <directory name="filesystems">
        <xi:include href="filesystems/directory.rbuild" />
 </directory>
+<directory name="filters>
+       <xi:include href="filters/directory.rbuild" />
+</directory>
 <directory name="input">
        <xi:include href="input/directory.rbuild" />
 </directory>
diff --git a/drivers/filters/CMakeLists.txt b/drivers/filters/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6e279b4
--- /dev/null
@@ -0,0 +1,2 @@
+
+add_subdirectory(mountmgr)
diff --git a/drivers/filters/directory.rbuild b/drivers/filters/directory.rbuild
new file mode 100644 (file)
index 0000000..b3b19c4
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<!DOCTYPE group SYSTEM "../../tools/rbuild/project.dtd">
+<group xmlns:xi="http://www.w3.org/2001/XInclude">
+<directory name="mountmgr">
+       <xi:include href="mountmgr/mountmgr.rbuild" />
+</directory>
+</group>
diff --git a/drivers/filters/mountmgr/CMakeLists.txt b/drivers/filters/mountmgr/CMakeLists.txt
new file mode 100644 (file)
index 0000000..62f2ae6
--- /dev/null
@@ -0,0 +1,21 @@
+
+list(APPEND SOURCE
+    database.c
+    device.c
+    mountmgr.c
+    notify.c
+    point.c
+    symlink.c
+    uniqueid.c
+    mountmgr.rc)
+
+allow_warnings(mountmgr)
+
+add_library(mountmgr SHARED ${SOURCE})
+
+set_module_type(mountmgr kernelmodedriver)
+add_importlibs(mountmgr ntoskrnl hal)
+
+add_pch(mountmgr mntmgr.h)
+
+#add_cd_file(TARGET mountmgr DESTINATION reactos/system32/drivers NO_CAB FOR all)
diff --git a/drivers/filters/mountmgr/database.c b/drivers/filters/mountmgr/database.c
new file mode 100644 (file)
index 0000000..d9507fb
--- /dev/null
@@ -0,0 +1,1480 @@
+/*
+ *  ReactOS kernel
+ *  Copyright (C) 2011-2012 ReactOS Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * COPYRIGHT:        See COPYING in the top level directory
+ * PROJECT:          ReactOS kernel
+ * FILE:             drivers/filesystem/mountmgr/mountmgr.c
+ * PURPOSE:          Mount Manager - remote/local database handler
+ * PROGRAMMER:       Pierre Schweitzer (pierre.schweitzer@reactos.org)
+ */
+
+/* INCLUDES *****************************************************************/
+
+#include "mntmgr.h"
+
+#define NDEBUG
+#include <debug.h>
+
+PWSTR DatabasePath = L"\\Registry\\Machine\\System\\MountedDevices";
+PWSTR OfflinePath = L"\\Registry\\Machine\\System\\MountedDevices\\Offline";
+
+UNICODE_STRING RemoteDatabase = RTL_CONSTANT_STRING(L"\\System Volume Information\\MountPointManagerRemoteDatabase");
+UNICODE_STRING RemoteDatabaseFile = RTL_CONSTANT_STRING(L"\\:$MountMgrRemoteDatabase");
+
+/*
+ * @implemented
+ */
+LONG
+GetRemoteDatabaseSize(IN HANDLE Database)
+{
+    NTSTATUS Status;
+    IO_STATUS_BLOCK IoStatusBlock;
+    FILE_STANDARD_INFORMATION StandardInfo;
+
+    /* Just query the size */
+    Status = ZwQueryInformationFile(Database,
+                                    &IoStatusBlock,
+                                    &StandardInfo,
+                                    sizeof(FILE_STANDARD_INFORMATION),
+                                    FileStandardInformation);
+    if (NT_SUCCESS(Status))
+    {
+        return StandardInfo.EndOfFile.LowPart;
+    }
+
+    return 0;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+AddRemoteDatabaseEntry(IN HANDLE Database,
+                       IN PDATABASE_ENTRY Entry)
+{
+    LARGE_INTEGER Size;
+    IO_STATUS_BLOCK IoStatusBlock;
+
+    /* Get size to append data */
+    Size.QuadPart = GetRemoteDatabaseSize(Database);
+
+    return ZwWriteFile(Database, 0, NULL, NULL,
+                       &IoStatusBlock, Entry,
+                       Entry->EntrySize, &Size, NULL);
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+CloseRemoteDatabase(IN HANDLE Database)
+{
+    return ZwClose(Database);
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+TruncateRemoteDatabase(IN HANDLE Database,
+                       IN LONG NewSize)
+{
+    NTSTATUS Status;
+    IO_STATUS_BLOCK IoStatusBlock;
+    FILE_END_OF_FILE_INFORMATION EndOfFile;
+    FILE_ALLOCATION_INFORMATION Allocation;
+
+    EndOfFile.EndOfFile.QuadPart = NewSize;
+    Allocation.AllocationSize.QuadPart = NewSize;
+
+    /* First set EOF */
+    Status = ZwSetInformationFile(Database,
+                                  &IoStatusBlock,
+                                  &EndOfFile,
+                                  sizeof(FILE_END_OF_FILE_INFORMATION),
+                                  FileEndOfFileInformation);
+    if (NT_SUCCESS(Status))
+    {
+        /* And then, properly set allocation information */
+        Status = ZwSetInformationFile(Database,
+                                      &IoStatusBlock,
+                                      &Allocation,
+                                      sizeof(FILE_ALLOCATION_INFORMATION),
+                                      FileAllocationInformation);
+    }
+
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+PDATABASE_ENTRY
+GetRemoteDatabaseEntry(IN HANDLE Database,
+                       IN LONG StartingOffset)
+{
+    NTSTATUS Status;
+    ULONG EntrySize;
+    PDATABASE_ENTRY Entry;
+    LARGE_INTEGER ByteOffset;
+    IO_STATUS_BLOCK IoStatusBlock;
+
+    /* Get the entry at the given position */
+    ByteOffset.QuadPart = StartingOffset;
+    Status = ZwReadFile(Database,
+                        NULL,
+                        NULL,
+                        NULL,
+                        &IoStatusBlock,
+                        &EntrySize,
+                        sizeof(EntrySize),
+                        &ByteOffset,
+                        NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        return NULL;
+    }
+
+    /* If entry doesn't exist, truncate database */
+    if (!EntrySize)
+    {
+        TruncateRemoteDatabase(Database, StartingOffset);
+        return NULL;
+    }
+
+    /* Allocate the entry */
+    Entry = AllocatePool(EntrySize);
+    if (!Entry)
+    {
+        return NULL;
+    }
+
+    /* Effectively read the entry */
+    Status = ZwReadFile(Database,
+                        NULL,
+                        NULL,
+                        NULL,
+                        &IoStatusBlock,
+                        Entry,
+                        EntrySize,
+                        &ByteOffset,
+                        NULL);
+    /* If it fails or returns inconsistent data, drop it (= truncate) */
+    if (!NT_SUCCESS(Status) || IoStatusBlock.Information != EntrySize || EntrySize < sizeof(DATABASE_ENTRY))
+    {
+        TruncateRemoteDatabase(Database, StartingOffset);
+        FreePool(Entry);
+        return NULL;
+    }
+
+    /* Validate entry */
+    if (MAX(Entry->SymbolicNameOffset + Entry->SymbolicNameLength,
+            Entry->UniqueIdOffset + Entry->UniqueIdLength) > EntrySize)
+    {
+        TruncateRemoteDatabase(Database, StartingOffset);
+        FreePool(Entry);
+        return NULL;
+    }
+
+    return Entry;
+}
+
+NTSTATUS
+DeleteRemoteDatabaseEntry(IN HANDLE Database,
+                          IN LONG StartingOffset)
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+DeleteFromLocalDatabaseRoutine(IN PWSTR ValueName,
+                               IN ULONG ValueType,
+                               IN PVOID ValueData,
+                               IN ULONG ValueLength,
+                               IN PVOID Context,
+                               IN PVOID EntryContext)
+{
+    PMOUNTDEV_UNIQUE_ID UniqueId = Context;
+
+    /* Ensure it matches, and delete */
+    if ((UniqueId->UniqueIdLength == ValueLength) &&
+        (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) ==
+         ValueLength))
+    {
+        RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
+                               DatabasePath,
+                               ValueName);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+VOID
+DeleteFromLocalDatabase(IN PUNICODE_STRING SymbolicLink,
+                        IN PMOUNTDEV_UNIQUE_ID UniqueId)
+{
+    RTL_QUERY_REGISTRY_TABLE QueryTable[2];
+
+    RtlZeroMemory(QueryTable, sizeof(QueryTable));
+    QueryTable[0].QueryRoutine = DeleteFromLocalDatabaseRoutine;
+    QueryTable[0].Name = SymbolicLink->Buffer;
+
+    RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
+                           DatabasePath,
+                           QueryTable,
+                           UniqueId,
+                           NULL);
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+WaitForRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension)
+{
+    NTSTATUS Status;
+    LARGE_INTEGER Timeout;
+
+    /* Wait for 7 minutes */
+    Timeout.QuadPart = 0xFA0A1F00;
+    Status = KeWaitForSingleObject(&(DeviceExtension->RemoteDatabaseLock), Executive, KernelMode, FALSE, &Timeout);
+    if (Status != STATUS_TIMEOUT)
+    {
+        return Status;
+    }
+
+    return STATUS_IO_TIMEOUT;
+}
+
+/*
+ * @implemented
+ */
+VOID
+ReleaseRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension)
+{
+    KeReleaseSemaphore(&(DeviceExtension->RemoteDatabaseLock), IO_NO_INCREMENT, 1, FALSE);
+}
+
+VOID
+NTAPI
+ReconcileThisDatabaseWithMasterWorker(IN PVOID Parameter)
+{
+    return;
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+WorkerThread(IN PDEVICE_OBJECT DeviceObject,
+             IN PVOID Context)
+{
+    ULONG i;
+    KEVENT Event;
+    KIRQL OldIrql;
+    NTSTATUS Status;
+    HANDLE SafeEvent;
+    PLIST_ENTRY Entry;
+    LARGE_INTEGER Timeout;
+    PRECONCILE_WORK_ITEM WorkItem;
+    PDEVICE_EXTENSION DeviceExtension;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &SafeVolumes,
+                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+                               NULL,
+                               NULL);
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+    Timeout.LowPart = 0xFFFFFFFF;
+    Timeout.HighPart = 0xFF676980;
+
+    /* Try to wait as long as possible */
+    for (i = (Unloading ? 999 : 0); i < 1000; i++)
+    {
+        Status = ZwOpenEvent(&SafeEvent, EVENT_ALL_ACCESS, &ObjectAttributes);
+        if (NT_SUCCESS(Status))
+        {
+            break;
+        }
+
+        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &Timeout);
+    }
+
+    if (i < 1000)
+    {
+        do
+        {
+            Status = ZwWaitForSingleObject(SafeEvent, FALSE, &Timeout);
+        }
+        while (Status == STATUS_TIMEOUT && !Unloading);
+
+        ZwClose(SafeEvent);
+    }
+
+    DeviceExtension = Context;
+
+    InterlockedExchange(&(DeviceExtension->WorkerThreadStatus), 1);
+
+    /* Acquire workers lock */
+    KeWaitForSingleObject(&(DeviceExtension->WorkerSemaphore), Executive, KernelMode, FALSE, NULL);
+
+    OldIrql = KfAcquireSpinLock(&(DeviceExtension->WorkerLock));
+    /* Ensure there are workers */
+    while (!IsListEmpty(&(DeviceExtension->WorkerQueueListHead)))
+    {
+        /* Unqueue a worker */
+        Entry = RemoveHeadList(&(DeviceExtension->WorkerQueueListHead));
+        WorkItem = CONTAINING_RECORD(Entry,
+                                     RECONCILE_WORK_ITEM,
+                                     WorkerQueueListEntry);
+
+        KfReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
+
+        /* Call it */
+        WorkItem->WorkerRoutine(WorkItem->Context);
+
+        IoFreeWorkItem(WorkItem->WorkItem);
+        FreePool(WorkItem);
+
+        if (InterlockedDecrement(&(DeviceExtension->WorkerReferences)) == 0)
+        {
+            return;
+        }
+
+        KeWaitForSingleObject(&(DeviceExtension->WorkerSemaphore), Executive, KernelMode, FALSE, NULL);
+        OldIrql = KfAcquireSpinLock(&(DeviceExtension->WorkerLock));
+    }
+    KfReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
+
+    InterlockedDecrement(&(DeviceExtension->WorkerReferences));
+
+    /* Reset event */
+    KeSetEvent(&UnloadEvent, IO_NO_INCREMENT, FALSE);
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+QueueWorkItem(IN PDEVICE_EXTENSION DeviceExtension,
+              IN PRECONCILE_WORK_ITEM WorkItem,
+              IN PVOID Context)
+{
+    KIRQL OldIrql;
+
+    WorkItem->Context = Context;
+
+    /* When called, lock is already acquired */
+
+    /* If noone, start to work */
+    if (InterlockedIncrement(&(DeviceExtension->WorkerReferences)))
+    {
+        IoQueueWorkItem(WorkItem->WorkItem, WorkerThread, DelayedWorkQueue, DeviceExtension);
+    }
+
+    /* Otherwise queue worker for delayed execution */
+    OldIrql = KfAcquireSpinLock(&(DeviceExtension->WorkerLock));
+    InsertTailList(&(DeviceExtension->WorkerQueueListHead),
+                   &(WorkItem->WorkerQueueListEntry));
+    KfReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
+
+    KeReleaseSemaphore(&(DeviceExtension->WorkerSemaphore), IO_NO_INCREMENT, 1, FALSE);
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+QueryVolumeName(IN HANDLE RootDirectory,
+                IN PFILE_REPARSE_POINT_INFORMATION ReparsePointInformation,
+                IN PUNICODE_STRING FileName OPTIONAL,
+                OUT PUNICODE_STRING SymbolicName,
+                OUT PUNICODE_STRING VolumeName)
+{
+    HANDLE Handle;
+    NTSTATUS Status;
+    ULONG NeededLength;
+    IO_STATUS_BLOCK IoStatusBlock;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    PFILE_NAME_INFORMATION FileNameInfo;
+    PREPARSE_DATA_BUFFER ReparseDataBuffer;
+
+    if (!FileName)
+    {
+        InitializeObjectAttributes(&ObjectAttributes,
+                                   NULL,
+                                   OBJ_KERNEL_HANDLE,
+                                   RootDirectory,
+                                   NULL);
+    }
+    else
+    {
+        InitializeObjectAttributes(&ObjectAttributes,
+                                   FileName,
+                                   OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
+                                   NULL,
+                                   NULL);
+    }
+
+    /* Open volume */
+    Status = ZwOpenFile(&Handle,
+                        SYNCHRONIZE | FILE_READ_ATTRIBUTES,
+                        &ObjectAttributes,
+                        &IoStatusBlock,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                        (FileName) ? FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT :
+                                     FILE_OPEN_BY_FILE_ID | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    /* Get the reparse point data */
+    ReparseDataBuffer = AllocatePool(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
+    if (!ReparseDataBuffer)
+    {
+        ZwClose(Handle);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    Status = ZwFsControlFile(Handle,
+                             0,
+                             NULL,
+                             NULL,
+                             &IoStatusBlock,
+                             FSCTL_GET_REPARSE_POINT,
+                             NULL,
+                             0,
+                             ReparseDataBuffer,
+                             MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
+    if (!NT_SUCCESS(Status))
+    {
+        FreePool(ReparseDataBuffer);
+        ZwClose(Handle);
+        return Status;
+    }
+
+    /* Check that name can fit in buffer */
+    if (ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength + sizeof(UNICODE_NULL) > SymbolicName->MaximumLength)
+    {
+        FreePool(ReparseDataBuffer);
+        ZwClose(Handle);
+        return STATUS_BUFFER_TOO_SMALL;
+    }
+
+    /* Copy symoblic name */
+    SymbolicName->Length = ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength;
+    RtlCopyMemory(SymbolicName->Buffer,
+                  (PWSTR)((ULONG_PTR)ReparseDataBuffer->MountPointReparseBuffer.PathBuffer +
+                                     ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameOffset),
+                  ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength);
+
+    FreePool(ReparseDataBuffer);
+
+    /* Name has to \ terminated */
+    if (SymbolicName->Buffer[SymbolicName->Length / sizeof(WCHAR) - 1] != L'\\')
+    {
+        ZwClose(Handle);
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* So that we can delete it, and match mountmgr requirements */
+    SymbolicName->Length -= sizeof(WCHAR);
+    SymbolicName->Buffer[SymbolicName->Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+    /* Also ensure it's really a volume name... */
+    if (!MOUNTMGR_IS_VOLUME_NAME(SymbolicName))
+    {
+        ZwClose(Handle);
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Now prepare to really get the name */
+    FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + 2 * sizeof(WCHAR));
+    if (!FileNameInfo)
+    {
+        ZwClose(Handle);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    Status = ZwQueryInformationFile(Handle,
+                                    &IoStatusBlock,
+                                    FileNameInfo,
+                                    sizeof(FILE_NAME_INFORMATION) + 2 * sizeof(WCHAR),
+                                    FileNameInformation);
+    if (Status == STATUS_BUFFER_OVERFLOW)
+    {
+        /* As expected... Reallocate with proper size */
+        NeededLength = FileNameInfo->FileNameLength;
+        FreePool(FileNameInfo);
+
+        FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + NeededLength);
+        if (!FileNameInfo)
+        {
+            ZwClose(Handle);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        /* And query name */
+        Status = ZwQueryInformationFile(Handle,
+                                        &IoStatusBlock,
+                                        FileNameInfo,
+                                        sizeof(FILE_NAME_INFORMATION) + NeededLength,
+                                        FileNameInformation);
+    }
+
+    ZwClose(Handle);
+
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    /* Return the volume name */
+    VolumeName->Length = FileNameInfo->FileNameLength;
+    VolumeName->MaximumLength = FileNameInfo->FileNameLength + sizeof(WCHAR);
+    VolumeName->Buffer = AllocatePool(VolumeName->MaximumLength);
+    if (!VolumeName->Buffer)
+    {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    RtlCopyMemory(VolumeName->Buffer, FileNameInfo->FileName, FileNameInfo->FileNameLength);
+    VolumeName->Buffer[FileNameInfo->FileNameLength / sizeof(WCHAR)] = UNICODE_NULL;
+
+    FreePool(FileNameInfo);
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+VOID
+OnlineMountedVolumes(IN PDEVICE_EXTENSION DeviceExtension,
+                     IN PDEVICE_INFORMATION DeviceInformation)
+{
+    HANDLE Handle;
+    NTSTATUS Status;
+    BOOLEAN RestartScan;
+    IO_STATUS_BLOCK IoStatusBlock;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    PDEVICE_INFORMATION VolumeDeviceInformation;
+    WCHAR FileNameBuffer[0x8], SymbolicNameBuffer[0x64];
+    UNICODE_STRING ReparseFile, FileName, SymbolicName, VolumeName;
+    FILE_REPARSE_POINT_INFORMATION ReparsePointInformation, SavedReparsePointInformation;
+
+    /* Removable devices don't have remote database on them */
+    if (DeviceInformation->Removable)
+    {
+        return;
+    }
+
+    /* Prepare a string with reparse point index */
+    ReparseFile.Length = DeviceInformation->DeviceName.Length + ReparseIndex.Length;
+    ReparseFile.MaximumLength = ReparseFile.Length + sizeof(UNICODE_NULL);
+    ReparseFile.Buffer = AllocatePool(ReparseFile.MaximumLength);
+    if (!ReparseFile.Buffer)
+    {
+        return;
+    }
+
+    RtlCopyMemory(ReparseFile.Buffer, DeviceInformation->DeviceName.Buffer,
+                  DeviceInformation->DeviceName.Length);
+    RtlCopyMemory((PVOID)((ULONG_PTR)ReparseFile.Buffer + DeviceInformation->DeviceName.Length),
+                  ReparseFile.Buffer, ReparseFile.Length);
+    ReparseFile.Buffer[ReparseFile.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &ReparseFile,
+                               OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
+
+    /* Open reparse point */
+    Status = ZwOpenFile(&Handle,
+                        FILE_GENERIC_READ,
+                        &ObjectAttributes,
+                        &IoStatusBlock,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE,
+                        FILE_SYNCHRONOUS_IO_ALERT | FILE_OPEN_REPARSE_POINT);
+    FreePool(ReparseFile.Buffer);
+    if (!NT_SUCCESS(Status))
+    {
+        DeviceInformation->NoDatabase = FALSE;
+        return;
+    }
+
+    /* Query reparse point information
+     * We only pay attention to mout point
+     */
+    RtlZeroMemory(FileNameBuffer, sizeof(FileNameBuffer));
+    FileName.Buffer = FileNameBuffer;
+    FileName.Length = sizeof(FileNameBuffer);
+    FileName.MaximumLength = sizeof(FileNameBuffer);
+    ((PULONG)FileNameBuffer)[0] = IO_REPARSE_TAG_MOUNT_POINT;
+    Status = ZwQueryDirectoryFile(Handle,
+                                  NULL,
+                                  NULL,
+                                  NULL,
+                                  &IoStatusBlock,
+                                  &ReparsePointInformation,
+                                  sizeof(FILE_REPARSE_POINT_INFORMATION),
+                                  FileReparsePointInformation,
+                                  TRUE,
+                                  &FileName,
+                                  FALSE);
+    if (!NT_SUCCESS(Status))
+    {
+        ZwClose(Handle);
+        return;
+    }
+
+    /* Query mount points */
+    while (TRUE)
+    {
+        RestartScan = TRUE;
+        SymbolicName.Length = 0;
+        SymbolicName.MaximumLength = sizeof(SymbolicNameBuffer);
+        SymbolicName.Buffer = SymbolicNameBuffer;
+        RtlCopyMemory(&SavedReparsePointInformation, &ReparsePointInformation, sizeof(FILE_REPARSE_POINT_INFORMATION));
+
+        Status = ZwQueryDirectoryFile(Handle,
+                                      NULL,
+                                      NULL,
+                                      NULL,
+                                      &IoStatusBlock,
+                                      &ReparsePointInformation,
+                                      sizeof(FILE_REPARSE_POINT_INFORMATION),
+                                      FileReparsePointInformation,
+                                      TRUE,
+                                      (RestartScan) ? &FileName : NULL,
+                                      RestartScan);
+         if (!RestartScan)
+         {
+             if (ReparsePointInformation.FileReference == SavedReparsePointInformation.FileReference &&
+                 ReparsePointInformation.Tag == SavedReparsePointInformation.Tag)
+             {
+                 ZwClose(Handle);
+                 return;
+             }
+         }
+         else
+         {
+             RestartScan = FALSE;
+         }
+
+         if (!NT_SUCCESS(Status) || ReparsePointInformation.Tag != IO_REPARSE_TAG_MOUNT_POINT)
+         {
+             ZwClose(Handle);
+             return;
+         }
+
+         /* Get the volume name associated to the mount point */
+         Status = QueryVolumeName(Handle,
+                                  &ReparsePointInformation,
+                                  NULL, &SymbolicName,
+                                  &VolumeName);
+         if (!NT_SUCCESS(Status))
+         {
+             continue;
+         }
+
+         FreePool(VolumeName.Buffer);
+
+         /* Get its information */
+         Status = FindDeviceInfo(DeviceExtension, &SymbolicName,
+                                 FALSE, &VolumeDeviceInformation);
+         if (!NT_SUCCESS(Status))
+         {
+             DeviceInformation->NoDatabase = TRUE;
+             continue;
+         }
+
+         /* If notification are enabled, mark it online */
+         if (!DeviceInformation->SkipNotifications)
+         {
+             PostOnlineNotification(DeviceExtension, &VolumeDeviceInformation->SymbolicName);
+         }
+    }
+}
+
+/*
+ * @implemented
+ */
+VOID
+ReconcileThisDatabaseWithMaster(IN PDEVICE_EXTENSION DeviceExtension,
+                                IN PDEVICE_INFORMATION DeviceInformation)
+{
+    PRECONCILE_WORK_ITEM WorkItem;
+
+    /* Removable devices don't have remote database */
+    if (DeviceInformation->Removable)
+    {
+        return;
+    }
+
+    /* Allocate a work item */
+    WorkItem = AllocatePool(sizeof(RECONCILE_WORK_ITEM));
+    if (!WorkItem)
+    {
+        return;
+    }
+
+    WorkItem->WorkItem = IoAllocateWorkItem(DeviceExtension->DeviceObject);
+    if (!WorkItem->WorkItem)
+    {
+        FreePool(WorkItem);
+        return;
+    }
+
+    /* And queue it */
+    WorkItem->WorkerRoutine = ReconcileThisDatabaseWithMasterWorker;
+    WorkItem->DeviceExtension = DeviceExtension;
+    WorkItem->DeviceInformation = DeviceInformation;
+    QueueWorkItem(DeviceExtension, WorkItem, &(WorkItem->DeviceExtension));
+
+    /* If there's no automount, and automatic letters
+     * all volumes to find those online and notify there presence
+     */
+    if (DeviceExtension->WorkerThreadStatus == 0 &&
+        DeviceExtension->AutomaticDriveLetter == 1 &&
+        DeviceExtension->NoAutoMount == FALSE)
+    {
+        OnlineMountedVolumes(DeviceExtension, DeviceInformation);
+    }
+}
+
+/*
+ * @implemented
+ */
+VOID
+ReconcileAllDatabasesWithMaster(IN PDEVICE_EXTENSION DeviceExtension)
+{
+    PLIST_ENTRY NextEntry;
+    PDEVICE_INFORMATION DeviceInformation;
+
+    /* Browse all the devices */
+    for (NextEntry = DeviceExtension->DeviceListHead.Flink;
+         NextEntry != &(DeviceExtension->DeviceListHead);
+         NextEntry = NextEntry->Flink)
+    {
+        DeviceInformation = CONTAINING_RECORD(NextEntry,
+                                              DEVICE_INFORMATION,
+                                              DeviceListEntry);
+        /* If it's not removable, then, it might have a database to sync */
+        if (!DeviceInformation->Removable)
+        {
+            ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation);
+        }
+    }
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+MigrateRemoteDatabaseWorker(IN PDEVICE_OBJECT DeviceObject,
+                            IN PVOID Context)
+{
+    ULONG Length;
+    NTSTATUS Status;
+    PVOID TmpBuffer;
+    CHAR Disposition;
+    LARGE_INTEGER ByteOffset;
+    PMIGRATE_WORK_ITEM WorkItem;
+    IO_STATUS_BLOCK IoStatusBlock;
+    HANDLE Migrate = 0, Database = 0;
+    PDEVICE_INFORMATION DeviceInformation;
+    BOOLEAN PreviousMode, Complete = FALSE;
+    UNICODE_STRING DatabaseName, DatabaseFile;
+    OBJECT_ATTRIBUTES ObjectAttributes, MigrateAttributes;
+#define TEMP_BUFFER_SIZE 0x200
+
+    /* Extract context */
+    WorkItem = Context;
+    DeviceInformation = WorkItem->DeviceInformation;
+
+    /* Reconstruct appropriate string */
+    DatabaseName.Length = DeviceInformation->DeviceName.Length + RemoteDatabase.Length;
+    DatabaseName.MaximumLength = DatabaseName.Length + sizeof(WCHAR);
+
+    DatabaseFile.Length = DeviceInformation->DeviceName.Length + RemoteDatabaseFile.Length;
+    DatabaseFile.MaximumLength = DatabaseFile.Length + sizeof(WCHAR);
+
+    DatabaseName.Buffer = AllocatePool(DatabaseName.MaximumLength);
+    DatabaseFile.Buffer = AllocatePool(DatabaseFile.MaximumLength);
+    /* Allocate buffer that will be used to swap contents */
+    TmpBuffer = AllocatePool(TEMP_BUFFER_SIZE);
+    if (!DatabaseName.Buffer || !DatabaseFile.Buffer || !TmpBuffer)
+    {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto Cleanup;
+    }
+
+    /* Create the required folder (in which the database will be stored
+     * \System Volume Information at root of the volume
+     */
+    Status = RtlCreateSystemVolumeInformationFolder(&(DeviceInformation->DeviceName));
+    if (!NT_SUCCESS(Status))
+    {
+        goto Cleanup;
+    }
+
+    /* Finish initating strings */
+    RtlCopyMemory(DatabaseName.Buffer, DeviceInformation->DeviceName.Buffer, DeviceInformation->DeviceName.Length);
+    RtlCopyMemory(DatabaseFile.Buffer, DeviceInformation->DeviceName.Buffer, DeviceInformation->DeviceName.Length);
+    RtlCopyMemory(DatabaseName.Buffer + (DeviceInformation->DeviceName.Length / sizeof(WCHAR)),
+                  RemoteDatabase.Buffer, RemoteDatabase.Length);
+    RtlCopyMemory(DatabaseFile.Buffer + (DeviceInformation->DeviceName.Length / sizeof(WCHAR)),
+                  RemoteDatabaseFile.Buffer, RemoteDatabaseFile.Length);
+    DatabaseName.Buffer[DatabaseName.Length / sizeof(WCHAR)] = UNICODE_NULL;
+    DatabaseFile.Buffer[DatabaseFile.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+    /* Create database */
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &DatabaseName,
+                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+                               NULL,
+                               NULL);
+
+    Status = ZwCreateFile(&Database,
+                          SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES |
+                          FILE_READ_ATTRIBUTES | FILE_WRITE_PROPERTIES | FILE_READ_PROPERTIES |
+                          FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA,
+                          &ObjectAttributes,
+                          &IoStatusBlock,
+                          NULL,
+                          FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
+                          0,
+                          FILE_CREATE,
+                          FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
+                          NULL,
+                          0);
+    if (!NT_SUCCESS(Status))
+    {
+        Database = 0;
+        goto Cleanup;
+    }
+
+    InitializeObjectAttributes(&MigrateAttributes,
+                               &DatabaseFile,
+                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+                               NULL,
+                               NULL);
+
+    /* Disable hard errors and open the database that will be copied */
+    PreviousMode = IoSetThreadHardErrorMode(FALSE);
+    Status = ZwCreateFile(&Migrate,
+                          SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES |
+                          FILE_READ_ATTRIBUTES | FILE_WRITE_PROPERTIES | FILE_READ_PROPERTIES |
+                          FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA,
+                          &MigrateAttributes,
+                          &IoStatusBlock,
+                          NULL,
+                          FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
+                          0,
+                          FILE_OPEN,
+                          FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
+                          NULL,
+                          0);
+    IoSetThreadHardErrorMode(PreviousMode);
+    if (!NT_SUCCESS(Status))
+    {
+        Migrate = 0;
+    }
+    if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
+    {
+        Status == STATUS_SUCCESS;
+        Complete = TRUE;
+    }
+    if (!NT_SUCCESS(Status) || Complete)
+    {
+        goto Cleanup;
+    }
+
+    ByteOffset.QuadPart = 0LL;
+    PreviousMode = IoSetThreadHardErrorMode(FALSE);
+    /* Now, loop as long it's possible */
+    while (Status == STATUS_SUCCESS)
+    {
+        /* Read data from existing database */
+        Status = ZwReadFile(Migrate,
+                            NULL,
+                            NULL,
+                            NULL,
+                            &IoStatusBlock,
+                            TmpBuffer,
+                            TEMP_BUFFER_SIZE,
+                            &ByteOffset,
+                            NULL);
+        if (!NT_SUCCESS(Status))
+        {
+            break;
+        }
+
+        /* And write them into new database */
+        Length = IoStatusBlock.Information;
+        Status = ZwWriteFile(Database,
+                             NULL,
+                             NULL,
+                             NULL,
+                             &IoStatusBlock,
+                             TmpBuffer,
+                             Length,
+                             &ByteOffset,
+                             NULL);
+        ByteOffset.QuadPart += Length;
+    }
+    IoSetThreadHardErrorMode(PreviousMode);
+
+    /* Delete old databse if it was well copied */
+    if (Status == STATUS_END_OF_FILE)
+    {
+        Disposition = 1;
+        Status = ZwSetInformationFile(Migrate,
+                                      &IoStatusBlock,
+                                      &Disposition,
+                                      sizeof(Disposition),
+                                      FileDispositionInformation);
+    }
+
+    /* Migration is over */
+
+Cleanup:
+    if (TmpBuffer)
+    {
+        FreePool(TmpBuffer);
+    }
+
+    if (DatabaseFile.Buffer)
+    {
+        FreePool(DatabaseFile.Buffer);
+    }
+
+    if (DatabaseName.Buffer)
+    {
+        FreePool(DatabaseName.Buffer);
+    }
+
+    if (Migrate)
+    {
+        ZwClose(Migrate);
+    }
+
+    if (NT_SUCCESS(Status))
+    {
+        DeviceInformation->Migrated = 1;
+    }
+    else if (Database)
+    {
+        ZwClose(Database);
+    }
+
+    IoFreeWorkItem(WorkItem->WorkItem);
+
+    WorkItem->WorkItem = NULL;
+    WorkItem->Status = Status;
+    WorkItem->Database = Database;
+
+    KeSetEvent(WorkItem->Event, 0, FALSE);
+#undef TEMP_BUFFER_SIZE
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+MigrateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation,
+                      IN OUT PHANDLE Database)
+{
+    KEVENT Event;
+    NTSTATUS Status;
+    PMIGRATE_WORK_ITEM WorkItem;
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    /* Allocate a work item dedicated to migration */
+    WorkItem = AllocatePool(sizeof(MIGRATE_WORK_ITEM));
+    if (!WorkItem)
+    {
+        *Database = 0;
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    RtlZeroMemory(WorkItem, sizeof(MIGRATE_WORK_ITEM));
+    WorkItem->Event = &Event;
+    WorkItem->DeviceInformation = DeviceInformation;
+    WorkItem->WorkItem = IoAllocateWorkItem(DeviceInformation->DeviceExtension->DeviceObject);
+    if (!WorkItem->WorkItem)
+    {
+        FreePool(WorkItem);
+        *Database = 0;
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* And queue it */
+    IoQueueWorkItem(WorkItem->WorkItem,
+                    MigrateRemoteDatabaseWorker,
+                    DelayedWorkQueue,
+                    WorkItem);
+
+    KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+    Status = WorkItem->Status;
+
+    *Database = (NT_SUCCESS(Status) ? WorkItem->Database : 0);
+
+    FreePool(WorkItem);
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+HANDLE
+OpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation,
+                   IN BOOLEAN MigrateDatabase)
+{
+    HANDLE Database;
+    NTSTATUS Status;
+    BOOLEAN PreviousMode;
+    IO_STATUS_BLOCK IoStatusBlock;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    UNICODE_STRING DeviceRemoteDatabase;
+
+    Database = 0;
+
+    /* Get database name */
+    DeviceRemoteDatabase.Length = DeviceInformation->DeviceName.Length + RemoteDatabase.Length;
+    DeviceRemoteDatabase.MaximumLength = DeviceRemoteDatabase.Length + sizeof(WCHAR);
+    DeviceRemoteDatabase.Buffer = AllocatePool(DeviceRemoteDatabase.MaximumLength);
+    if (!DeviceRemoteDatabase.Buffer)
+    {
+        return 0;
+    }
+
+    RtlCopyMemory(DeviceRemoteDatabase.Buffer, DeviceInformation->DeviceName.Buffer, DeviceInformation->DeviceName.Length);
+    RtlCopyMemory(DeviceRemoteDatabase.Buffer + (DeviceInformation->DeviceName.Length / sizeof(WCHAR)),
+                  RemoteDatabase.Buffer, RemoteDatabase.Length);
+    DeviceRemoteDatabase.Buffer[DeviceRemoteDatabase.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+    /* Open database */
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &DeviceRemoteDatabase,
+                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+                               NULL,
+                               NULL);
+
+    /* Disable hard errors */
+    PreviousMode = IoSetThreadHardErrorMode(FALSE);
+
+    Status = ZwCreateFile(&Database,
+                          SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES |
+                          FILE_READ_ATTRIBUTES | FILE_WRITE_PROPERTIES | FILE_READ_PROPERTIES |
+                          FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA,
+                          &ObjectAttributes,
+                          &IoStatusBlock,
+                          NULL,
+                          FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
+                          0,
+                          (!MigrateDatabase || DeviceInformation->Migrated == 0) ? FILE_OPEN_IF : FILE_OPEN,
+                          FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
+                          NULL,
+                          0);
+
+    /* If base it to be migrated and was opened successfully, go ahead */
+    if (MigrateDatabase && NT_SUCCESS(Status))
+    {
+        MigrateRemoteDatabase(DeviceInformation, &Database);
+    }
+
+    IoSetThreadHardErrorMode(PreviousMode);
+    FreePool(DeviceRemoteDatabase.Buffer);
+
+    return Database;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+QueryUniqueIdQueryRoutine(IN PWSTR ValueName,
+                          IN ULONG ValueType,
+                          IN PVOID ValueData,
+                          IN ULONG ValueLength,
+                          IN PVOID Context,
+                          IN PVOID EntryContext)
+{
+    PMOUNTDEV_UNIQUE_ID IntUniqueId;
+    PMOUNTDEV_UNIQUE_ID * UniqueId;
+
+    /* Sanity check */
+    if (ValueLength >= 0x10000)
+    {
+        return STATUS_SUCCESS;
+    }
+
+    /* Allocate the Unique ID */
+    IntUniqueId = AllocatePool(sizeof(UniqueId) + ValueLength);
+    if (IntUniqueId)
+    {
+        /* Copy data & return */
+        IntUniqueId->UniqueIdLength = ValueLength;
+        RtlCopyMemory(&(IntUniqueId->UniqueId), ValueData, ValueLength);
+
+        UniqueId = Context;
+        *UniqueId = IntUniqueId;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+QueryUniqueIdFromMaster(IN PDEVICE_EXTENSION DeviceExtension,
+                        IN PUNICODE_STRING SymbolicName,
+                        OUT PMOUNTDEV_UNIQUE_ID * UniqueId)
+{
+    NTSTATUS Status;
+    PDEVICE_INFORMATION DeviceInformation;
+    RTL_QUERY_REGISTRY_TABLE QueryTable[2];
+
+    /* Query the unique ID */
+    RtlZeroMemory(QueryTable, sizeof(QueryTable));
+    QueryTable[0].QueryRoutine = QueryUniqueIdQueryRoutine;
+    QueryTable[0].Name = SymbolicName->Buffer;
+
+    *UniqueId = NULL;
+    RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
+                           DatabasePath,
+                           QueryTable,
+                           UniqueId,
+                           NULL);
+    /* Unique ID found, no need to go farther */
+    if (*UniqueId)
+    {
+        return STATUS_SUCCESS;
+    }
+
+    /* Otherwise, find associate device information */
+    Status = FindDeviceInfo(DeviceExtension, SymbolicName, FALSE, &DeviceInformation);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    *UniqueId = AllocatePool(DeviceInformation->UniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
+    if (!*UniqueId)
+    {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* Return this unique ID (better than nothing) */
+    (*UniqueId)->UniqueIdLength = DeviceInformation->UniqueId->UniqueIdLength;
+    RtlCopyMemory(&((*UniqueId)->UniqueId), &(DeviceInformation->UniqueId->UniqueId), (*UniqueId)->UniqueIdLength);
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+WriteUniqueIdToMaster(IN PDEVICE_EXTENSION DeviceExtension,
+                      IN PDATABASE_ENTRY DatabaseEntry)
+{
+    NTSTATUS Status;
+    PWCHAR SymbolicName;
+    PLIST_ENTRY NextEntry;
+    UNICODE_STRING SymbolicString;
+    PDEVICE_INFORMATION DeviceInformation;
+
+    /* Create symbolic name from database entry */
+    SymbolicName = AllocatePool(DatabaseEntry->SymbolicNameLength + sizeof(WCHAR));
+    if (!SymbolicName)
+    {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    RtlCopyMemory(SymbolicName,
+                  (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset),
+                  DatabaseEntry->SymbolicNameLength);
+    SymbolicName[DatabaseEntry->SymbolicNameLength / sizeof(WCHAR)] = UNICODE_NULL;
+
+    /* Associate the unique ID with the name from remote database */
+    Status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
+                                   DatabasePath,
+                                   SymbolicName,
+                                   REG_BINARY,
+                                   (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset),
+                                   DatabaseEntry->UniqueIdLength);
+    FreePool(SymbolicName);
+
+    /* Reget symbolic name */
+    SymbolicString.Length = DatabaseEntry->SymbolicNameLength;
+    SymbolicString.MaximumLength = DatabaseEntry->SymbolicNameLength;
+    SymbolicString.Buffer = (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset);
+
+    /* Find the device using this unique ID */
+    for (NextEntry = DeviceExtension->DeviceListHead.Flink;
+         NextEntry != &(DeviceExtension->DeviceListHead);
+         NextEntry = NextEntry->Flink)
+    {
+        DeviceInformation = CONTAINING_RECORD(NextEntry,
+                                              DEVICE_INFORMATION,
+                                              DeviceListEntry);
+
+        if (DeviceInformation->UniqueId->UniqueIdLength != DatabaseEntry->UniqueIdLength)
+        {
+            continue;
+        }
+
+        if (RtlCompareMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset),
+                             DeviceInformation->UniqueId->UniqueId,
+                             DatabaseEntry->UniqueIdLength) == DatabaseEntry->UniqueIdLength)
+        {
+            break;
+        }
+    }
+
+    /* If found, create a mount point */
+    if (NextEntry != &(DeviceExtension->DeviceListHead))
+    {
+        MountMgrCreatePointWorker(DeviceExtension, &SymbolicString, &(DeviceInformation->DeviceName));
+    }
+
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+VOID
+ChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation,
+                             IN PMOUNTDEV_UNIQUE_ID OldUniqueId,
+                             IN PMOUNTDEV_UNIQUE_ID NewUniqueId)
+{
+    LONG Offset = 0;
+    HANDLE Database;
+    PDATABASE_ENTRY Entry, NewEntry;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    /* Open the remote database */
+    Database = OpenRemoteDatabase(DeviceInformation, FALSE);
+    if (!Database)
+    {
+        return;
+    }
+
+    /* Get all the entries */
+    do
+    {
+        Entry = GetRemoteDatabaseEntry(Database, Offset);
+        if (!Entry)
+        {
+            break;
+        }
+
+        /* Not the correct entry, skip it */
+        if (Entry->UniqueIdLength != OldUniqueId->UniqueIdLength)
+        {
+            Offset += Entry->EntrySize;
+            FreePool(Entry);
+            continue;
+        }
+
+        /* Not the correct entry, skip it */
+        if (RtlCompareMemory(OldUniqueId->UniqueId,
+                             (PVOID)((ULONG_PTR)Entry + Entry->UniqueIdOffset),
+                             Entry->UniqueIdLength) != Entry->UniqueIdLength)
+        {
+            Offset += Entry->EntrySize;
+            FreePool(Entry);
+            continue;
+        }
+
+        /* Here, we have the correct entry */
+        NewEntry = AllocatePool(Entry->EntrySize + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength);
+        if (!NewEntry)
+        {
+            Offset += Entry->EntrySize;
+            FreePool(Entry);
+            continue;
+        }
+
+        /* Recreate the entry from the previous one */
+        NewEntry->EntrySize = Entry->EntrySize + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength;
+        NewEntry->DatabaseOffset = Entry->DatabaseOffset;
+        NewEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY);
+        NewEntry->SymbolicNameLength = Entry->SymbolicNameLength;
+        NewEntry->UniqueIdOffset = Entry->SymbolicNameLength + sizeof(DATABASE_ENTRY);
+        NewEntry->UniqueIdLength = NewUniqueId->UniqueIdLength;
+        RtlCopyMemory((PVOID)((ULONG_PTR)NewEntry + NewEntry->SymbolicNameOffset),
+                      (PVOID)((ULONG_PTR)Entry + Entry->SymbolicNameOffset),
+                      NewEntry->SymbolicNameLength);
+        RtlCopyMemory((PVOID)((ULONG_PTR)NewEntry + NewEntry->UniqueIdOffset),
+                      NewUniqueId->UniqueId, NewEntry->UniqueIdLength);
+
+        /* Delete old entry */
+        Status = DeleteRemoteDatabaseEntry(Database, Offset);
+        if (!NT_SUCCESS(Status))
+        {
+            FreePool(Entry);
+            FreePool(NewEntry);
+            break;
+        }
+
+        /* And replace with new one */
+        Status = AddRemoteDatabaseEntry(Database, NewEntry);
+        FreePool(Entry);
+        FreePool(NewEntry);
+    } while (NT_SUCCESS(Status));
+
+    CloseRemoteDatabase(Database);
+
+    return;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+DeleteDriveLetterRoutine(IN PWSTR ValueName,
+                         IN ULONG ValueType,
+                         IN PVOID ValueData,
+                         IN ULONG ValueLength,
+                         IN PVOID Context,
+                         IN PVOID EntryContext)
+{
+    PMOUNTDEV_UNIQUE_ID UniqueId;
+    UNICODE_STRING RegistryEntry;
+
+    if (ValueType != REG_BINARY)
+    {
+        return STATUS_SUCCESS;
+    }
+
+    UniqueId = Context;
+
+    /* First ensure we have the correct data */
+    if (UniqueId->UniqueIdLength != ValueLength)
+    {
+        return STATUS_SUCCESS;
+    }
+
+    if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
+    {
+        return STATUS_SUCCESS;
+    }
+
+    RtlInitUnicodeString(&RegistryEntry, ValueName);
+
+    /* Then, it's a drive letter, erase it */
+    if (IsDriveLetter(&RegistryEntry))
+    {
+        RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
+                               DatabasePath,
+                               ValueName);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+VOID
+DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId)
+{
+    RTL_QUERY_REGISTRY_TABLE QueryTable[2];
+
+    RtlZeroMemory(QueryTable, sizeof(QueryTable));
+    QueryTable[0].QueryRoutine = DeleteDriveLetterRoutine;
+
+    RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
+                           DatabasePath,
+                           QueryTable,
+                           UniqueId,
+                           NULL);
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+DeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName,
+                                IN ULONG ValueType,
+                                IN PVOID ValueData,
+                                IN ULONG ValueLength,
+                                IN PVOID Context,
+                                IN PVOID EntryContext)
+{
+    PMOUNTDEV_UNIQUE_ID UniqueId = Context;
+
+    /* Ensure we have correct input */
+    if (ValueName[0] != L'#' || ValueType != REG_BINARY ||
+        UniqueId->UniqueIdLength != ValueLength)
+    {
+        return STATUS_SUCCESS;
+    }
+
+    /* And then, if unique ID matching, delete entry */
+    if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
+    {
+        RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
+                               DatabasePath,
+                               ValueName);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+VOID
+DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId)
+{
+    RTL_QUERY_REGISTRY_TABLE QueryTable[2];
+
+    RtlZeroMemory(QueryTable, sizeof(QueryTable));
+    QueryTable[0].QueryRoutine = DeleteNoDriveLetterEntryRoutine;
+
+    RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
+                           DatabasePath,
+                           QueryTable,
+                           UniqueId,
+                           NULL);
+}
diff --git a/drivers/filters/mountmgr/device.c b/drivers/filters/mountmgr/device.c
new file mode 100644 (file)
index 0000000..6bc7d81
--- /dev/null
@@ -0,0 +1,1601 @@
+/*
+ *  ReactOS kernel
+ *  Copyright (C) 2011-2012 ReactOS Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * COPYRIGHT:        See COPYING in the top level directory
+ * PROJECT:          ReactOS kernel
+ * FILE:             drivers/filesystem/mountmgr/device.c
+ * PURPOSE:          Mount Manager - Device Control
+ * PROGRAMMER:       Pierre Schweitzer (pierre.schweitzer@reactos.org)
+ */
+
+/* INCLUDES *****************************************************************/
+
+#include "mntmgr.h"
+
+#define NDEBUG
+#include <debug.h>
+
+/*
+ * @implemented
+ */
+NTSTATUS
+MountMgrChangeNotify(IN PDEVICE_EXTENSION DeviceExtension,
+                     IN PIRP Irp)
+{
+    KIRQL OldIrql;
+    NTSTATUS Status;
+    PIO_STACK_LOCATION Stack;
+    PMOUNTMGR_CHANGE_NOTIFY_INFO ChangeNotify;
+
+    /* Get the I/O buffer */
+    Stack = IoGetCurrentIrpStackLocation(Irp);
+    ChangeNotify = (PMOUNTMGR_CHANGE_NOTIFY_INFO)Irp->AssociatedIrp.SystemBuffer;
+
+    /* Validate it */
+    if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO) ||
+        Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* If epic number doesn't match, just return now one */
+    if (DeviceExtension->EpicNumber != ChangeNotify->EpicNumber)
+    {
+        ChangeNotify->EpicNumber = DeviceExtension->EpicNumber;
+        Irp->IoStatus.Information = 0;
+        return STATUS_SUCCESS;
+    }
+
+    /* If IRP is to be canceled, forget about that */
+    IoAcquireCancelSpinLock(&OldIrql);
+    if (Irp->Cancel)
+    {
+        Status = STATUS_CANCELLED;
+    }
+    /* Otherwise queue the IRP to be notified with the next epic number change */
+    else
+    {
+        InsertTailList(&(DeviceExtension->IrpListHead), &(Irp->Tail.Overlay.ListEntry));
+        IoMarkIrpPending(Irp);
+        IoSetCancelRoutine(Irp, MountMgrCancel);
+        Status = STATUS_PENDING;
+    }
+    IoReleaseCancelSpinLock(OldIrql);
+
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+MountmgrWriteNoAutoMount(IN PDEVICE_EXTENSION DeviceExtension)
+{
+    ULONG Value = DeviceExtension->NoAutoMount;
+
+    return RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
+                                 DeviceExtension->RegistryPath.Buffer,
+                                 L"NoAutoMount",
+                                 REG_DWORD,
+                                 &Value,
+                                 sizeof(Value));
+                                 
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+MountMgrSetAutoMount(IN PDEVICE_EXTENSION DeviceExtension,
+                     IN PIRP Irp)
+{
+    PIO_STACK_LOCATION Stack;
+    PMOUNTMGR_SET_AUTO_MOUNT SetState;
+
+    Stack = IoGetCurrentIrpStackLocation(Irp);
+
+    if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_SET_AUTO_MOUNT))
+    {
+        Irp->IoStatus.Information = 0;
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Only change if there's a real difference */
+    SetState = (PMOUNTMGR_SET_AUTO_MOUNT)Irp->AssociatedIrp.SystemBuffer;
+    if (SetState->NewState == !DeviceExtension->NoAutoMount)
+    {
+        Irp->IoStatus.Information = 0;
+        return STATUS_SUCCESS;
+    }
+
+    /* Set new state; ! on purpose */
+    DeviceExtension->NoAutoMount = !SetState->NewState;
+    Irp->IoStatus.Information = 0;
+    return MountmgrWriteNoAutoMount(DeviceExtension);
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+MountMgrQueryAutoMount(IN PDEVICE_EXTENSION DeviceExtension,
+                       IN PIRP Irp)
+{
+    PIO_STACK_LOCATION Stack;
+    PMOUNTMGR_QUERY_AUTO_MOUNT QueryState;
+
+    Stack = IoGetCurrentIrpStackLocation(Irp);
+
+    if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_QUERY_AUTO_MOUNT))
+    {
+        Irp->IoStatus.Information = 0;
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    QueryState = (PMOUNTMGR_QUERY_AUTO_MOUNT)Irp->AssociatedIrp.SystemBuffer;
+    QueryState->CurrentState = !DeviceExtension->NoAutoMount;
+    Irp->IoStatus.Information = sizeof(MOUNTMGR_QUERY_AUTO_MOUNT);
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+ScrubRegistryRoutine(IN PWSTR ValueName,
+                     IN ULONG ValueType,
+                     IN PVOID ValueData,
+                     IN ULONG ValueLength,
+                     IN PVOID Context,
+                     IN PVOID EntryContext)
+{
+    NTSTATUS Status;
+    PLIST_ENTRY NextEntry;
+    PDEVICE_INFORMATION DeviceInfo;
+    PBOOLEAN Continue = EntryContext;
+    PDEVICE_EXTENSION DeviceExtension = Context;
+
+    if (ValueType != REG_BINARY)
+    {
+        return STATUS_SUCCESS;
+    }
+
+    /* Delete values for devices that don't have the matching unique ID */
+    if (!IsListEmpty(&(DeviceExtension->DeviceListHead)))
+    {
+        for (NextEntry = DeviceExtension->DeviceListHead.Flink;
+             NextEntry != &(DeviceExtension->DeviceListHead);
+             NextEntry = NextEntry->Flink)
+        {
+            DeviceInfo = CONTAINING_RECORD(NextEntry,
+                                           DEVICE_INFORMATION,
+                                           DeviceListEntry);
+
+             if (!DeviceInfo->UniqueId || DeviceInfo->UniqueId->UniqueIdLength != ValueLength)
+             {
+                 continue;
+             }
+
+             if (RtlCompareMemory(DeviceInfo->UniqueId->UniqueId, ValueData, ValueLength) == ValueLength)
+             {
+                 return STATUS_SUCCESS;
+             }
+        }
+    }
+
+    /* Wrong unique ID, scrub it */
+    Status = RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
+                                    DatabasePath,
+                                    ValueName);
+    if (!NT_SUCCESS(Status))
+    {
+        *Continue = TRUE;
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    *Continue = FALSE;
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+MountMgrScrubRegistry(IN PDEVICE_EXTENSION DeviceExtension)
+{
+    NTSTATUS Status;
+    BOOLEAN Continue = TRUE;
+    RTL_QUERY_REGISTRY_TABLE QueryTable[2];
+
+    while (Continue)
+    {
+        RtlZeroMemory(QueryTable, sizeof(QueryTable));
+        QueryTable[0].QueryRoutine = ScrubRegistryRoutine;
+        QueryTable[0].EntryContext = &Continue;
+        Continue = FALSE;
+
+        Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
+                                        DatabasePath,
+                                        QueryTable,
+                                        DeviceExtension,
+                                        NULL);
+    }
+
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+MountMgrCreatePoint(IN PDEVICE_EXTENSION DeviceExtension,
+                    IN PIRP Irp)
+{
+    ULONG MaxLength;
+    PIO_STACK_LOCATION Stack;
+    PMOUNTMGR_CREATE_POINT_INPUT Point;
+    UNICODE_STRING DeviceName, SymbolicName;
+
+    Stack = IoGetCurrentIrpStackLocation(Irp);
+
+    if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_CREATE_POINT_INPUT))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    Point = (PMOUNTMGR_CREATE_POINT_INPUT)Irp->AssociatedIrp.SystemBuffer;
+
+    MaxLength = MAX((Point->DeviceNameOffset + Point->DeviceNameLength),
+                    (Point->SymbolicLinkNameLength + Point->SymbolicLinkNameOffset));
+    if (MaxLength >= Stack->Parameters.DeviceIoControl.InputBufferLength)
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Get all the strings and call the worker */
+    SymbolicName.Length = Point->SymbolicLinkNameLength;
+    SymbolicName.MaximumLength = Point->SymbolicLinkNameLength;
+    DeviceName.Length = Point->DeviceNameLength;
+    DeviceName.MaximumLength = Point->DeviceNameLength;
+    SymbolicName.Buffer = (PVOID)((ULONG_PTR)Point + Point->SymbolicLinkNameOffset);
+    DeviceName.Buffer = (PVOID)((ULONG_PTR)Point + Point->DeviceNameOffset);
+
+    return MountMgrCreatePointWorker(DeviceExtension, &SymbolicName, &DeviceName);
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+MountMgrCheckUnprocessedVolumes(IN PDEVICE_EXTENSION DeviceExtension,
+                                IN PIRP Irp)
+{
+    PLIST_ENTRY NextEntry;
+    PDEVICE_INFORMATION DeviceInformation;
+    NTSTATUS ArrivalStatus, Status = STATUS_SUCCESS;
+
+    /* No offline volumes, nothing more to do */
+    if (IsListEmpty(&(DeviceExtension->OfflineDeviceListHead)))
+    {
+        KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
+        return STATUS_SUCCESS;
+    }
+
+    KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
+
+    /* Reactivate all the offline volumes */
+    while (!IsListEmpty(&(DeviceExtension->OfflineDeviceListHead)))
+    {
+        NextEntry = RemoveHeadList(&(DeviceExtension->OfflineDeviceListHead));
+        DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
+
+        ArrivalStatus = MountMgrMountedDeviceArrival(DeviceExtension,
+                                                     &(DeviceInformation->SymbolicName),
+                                                     DeviceInformation->Volume);
+        /* Then, remove them dead information */
+        MountMgrFreeDeadDeviceInfo(DeviceInformation);
+
+        if (NT_SUCCESS(Status))
+        {
+            Status = ArrivalStatus;
+        }
+    }
+
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN
+IsFtVolume(IN PUNICODE_STRING SymbolicName)
+{
+    PIRP Irp;
+    KEVENT Event;
+    NTSTATUS Status;
+    PFILE_OBJECT FileObject;
+    IO_STATUS_BLOCK IoStatusBlock;
+    PARTITION_INFORMATION PartitionInfo;
+    PDEVICE_OBJECT DeviceObject, FileDeviceObject;
+
+    /* Get device object */
+    Status = IoGetDeviceObjectPointer(SymbolicName,
+                                      FILE_READ_ATTRIBUTES,
+                                      &FileObject,
+                                      &DeviceObject);
+    if (!NT_SUCCESS(Status))
+    {
+        return FALSE;
+    }
+
+    /* Get attached device */
+    FileDeviceObject = FileObject->DeviceObject;
+    DeviceObject = IoGetAttachedDeviceReference(FileDeviceObject);
+
+    /* FT volume can't be removable */
+    if (FileDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)
+    {
+        ObfDereferenceObject(DeviceObject);
+        ObfDereferenceObject(FileObject);
+        return FALSE;
+    }
+
+    ObfDereferenceObject(FileObject);
+
+    /* Get partition information */
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+    Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO,
+                                        DeviceObject,
+                                        NULL,
+                                        0,
+                                        &PartitionInfo,
+                                        sizeof(PartitionInfo),
+                                        FALSE,
+                                        &Event,
+                                        &IoStatusBlock);
+    if (!Irp)
+    {
+        ObfDereferenceObject(DeviceObject);
+        return FALSE;
+    }
+
+    Status = IofCallDriver(DeviceObject, Irp);
+    if (Status == STATUS_PENDING)
+    {
+        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+        Status = IoStatusBlock.Status;
+    }
+
+    ObfDereferenceObject(DeviceObject);
+    if (!NT_SUCCESS(Status))
+    {
+        return FALSE;
+    }
+
+    /* Check if this is a FT volume */
+    return IsRecognizedPartition(PartitionInfo.PartitionType);
+}
+
+/*
+ * @implemented
+ */
+VOID
+ProcessSuggestedDriveLetters(IN PDEVICE_EXTENSION DeviceExtension)
+{
+    WCHAR NameBuffer[DRIVE_LETTER_LENGTH / sizeof(WCHAR)];
+    PLIST_ENTRY NextEntry;
+    UNICODE_STRING SymbolicName;
+    PDEVICE_INFORMATION DeviceInformation;
+
+    /* No devices? Nothing to do! */
+    if (IsListEmpty(&(DeviceExtension->DeviceListHead)))
+    {
+        return;
+    }
+
+    /* For all the devices */
+    for (NextEntry = DeviceExtension->DeviceListHead.Flink;
+         NextEntry != &(DeviceExtension->DeviceListHead);
+         NextEntry = NextEntry->Flink)
+    {
+        DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
+
+        /* If no drive letter */
+        if (DeviceInformation->SuggestedDriveLetter == (UCHAR)-1)
+        {
+            /* Ensure it has no entry yet */
+            if (!HasDriveLetter(DeviceInformation) &&
+                !HasNoDriveLetterEntry(DeviceInformation->UniqueId))
+            {
+                /* And create one */
+                CreateNoDriveLetterEntry(DeviceInformation->UniqueId);
+            }
+
+            DeviceInformation->SuggestedDriveLetter = 0;
+        }
+        /* Suggested letter & no entry */
+        else if (DeviceInformation->SuggestedDriveLetter &&
+                 !HasNoDriveLetterEntry(DeviceInformation->UniqueId))
+        {
+            /* Just create a mount point */
+            SymbolicName.Buffer = NameBuffer;
+            RtlCopyMemory(NameBuffer, DosDevices.Buffer, DosDevices.Length);
+            NameBuffer[LETTER_POSITION] = DeviceInformation->SuggestedDriveLetter;
+            NameBuffer[COLON_POSITION] = L':';
+            SymbolicName.Length =
+            SymbolicName.MaximumLength = DRIVE_LETTER_LENGTH;
+
+            MountMgrCreatePointWorker(DeviceExtension, &SymbolicName, &(DeviceInformation->DeviceName));
+        }
+    }
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+MountMgrNextDriveLetterWorker(IN PDEVICE_EXTENSION DeviceExtension,
+                              IN PUNICODE_STRING DeviceName,
+                              OUT PMOUNTMGR_DRIVE_LETTER_INFORMATION DriveLetterInfo)
+{
+    NTSTATUS Status;
+    UCHAR DriveLetter;
+    PLIST_ENTRY NextEntry;
+    PMOUNTDEV_UNIQUE_ID UniqueId;
+    BOOLEAN Removable, GptDriveLetter;
+    PDEVICE_INFORMATION DeviceInformation;
+    WCHAR NameBuffer[DRIVE_LETTER_LENGTH];
+    PSYMLINK_INFORMATION SymlinkInformation;
+    UNICODE_STRING TargetDeviceName, SymbolicName;
+
+    /* First, process suggested letters */
+    if (!DeviceExtension->ProcessedSuggestions)
+    {
+        ProcessSuggestedDriveLetters(DeviceExtension);
+        DeviceExtension->ProcessedSuggestions = TRUE;
+    }
+
+    /* Then, get information about the device */
+    Status = QueryDeviceInformation(DeviceName, &TargetDeviceName, NULL, &Removable, &GptDriveLetter, NULL, NULL, NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    /* Ensure we have such device */
+    NextEntry = DeviceExtension->DeviceListHead.Flink;
+    while (NextEntry != &(DeviceExtension->DeviceListHead))
+    {
+        DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
+
+        if (RtlCompareUnicodeString(&(DeviceInformation->DeviceName), &TargetDeviceName, TRUE) == 0)
+        {
+            break;
+        }
+
+        NextEntry = NextEntry->Flink;
+    }
+
+    if (NextEntry == &(DeviceExtension->DeviceListHead))
+    {
+        FreePool(TargetDeviceName.Buffer);
+        return STATUS_OBJECT_NAME_NOT_FOUND;
+    }
+
+    /* Now, mark we have assigned a letter (assumption) */
+    DeviceInformation->LetterAssigned =
+    DriveLetterInfo->DriveLetterWasAssigned = TRUE;
+
+    /* Browse all the symlink to see if there's already a drive letter */
+    NextEntry = DeviceInformation->SymbolicLinksListHead.Flink;
+    while (NextEntry != &(DeviceInformation->SymbolicLinksListHead))
+    {
+        SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
+
+        /* This is a driver letter & online one, forget about new drive eltter */
+        if (IsDriveLetter(&(SymlinkInformation->Name)) && SymlinkInformation->Online)
+        {
+            DriveLetterInfo->DriveLetterWasAssigned = FALSE;
+            DriveLetterInfo->CurrentDriveLetter = SymlinkInformation->Name.Buffer[LETTER_POSITION];
+            break;
+        }
+
+        NextEntry = NextEntry->Flink;
+    }
+
+    /* If we didn't find a drive letter online
+     * ensure there's no GPT drive letter nor no drive entry
+     */
+    if (NextEntry == &(DeviceInformation->SymbolicLinksListHead))
+    {
+        if (GptDriveLetter || HasNoDriveLetterEntry(DeviceInformation->UniqueId))
+        {
+            DriveLetterInfo->DriveLetterWasAssigned = FALSE;
+            DriveLetterInfo->CurrentDriveLetter = 0;
+
+            goto Release;
+        }
+    }
+
+    /* No, ensure that the device is not automonted nor removable */
+    if (!DeviceExtension->NoAutoMount && !Removable)
+    {
+        if (DriveLetterInfo->DriveLetterWasAssigned)
+        {
+            DriveLetterInfo->DriveLetterWasAssigned = FALSE;
+            DriveLetterInfo->CurrentDriveLetter = 0;
+
+            goto Release;
+        }
+    }
+
+    if (!DriveLetterInfo->DriveLetterWasAssigned)
+    {
+        goto Release;
+    }
+
+    /* Now everything is fine, start processing */
+    if (RtlPrefixUnicodeString(&DeviceFloppy, &TargetDeviceName, TRUE))
+    {
+        DriveLetter = 'A';
+    }
+    else
+    {
+        DriveLetter = 'C' + RtlPrefixUnicodeString(&DeviceCdRom, &TargetDeviceName, TRUE);
+    }
+
+    /* We cannot set NO drive letter */
+    ASSERT(DeviceInformation->SuggestedDriveLetter != (UCHAR)-1);
+
+    /* If we don't have suggested letter but it's a FT volume, fail */
+    if (!DeviceInformation->SuggestedDriveLetter && IsFtVolume(&(DeviceInformation->DeviceName)))
+    {
+        DriveLetterInfo->DriveLetterWasAssigned = FALSE;
+        DriveLetterInfo->CurrentDriveLetter = 0;
+
+        goto Release;
+    }
+
+    /* Prepare buffer */
+    RtlCopyMemory(NameBuffer, DosDevices.Buffer, DosDevices.Length);
+    NameBuffer[COLON_POSITION] = L':';
+    SymbolicName.Buffer = NameBuffer;
+    SymbolicName.Length =
+    SymbolicName.MaximumLength = DRIVE_LETTER_LENGTH;
+
+    /* It's all prepared, create mount point */
+    if (DeviceInformation->SuggestedDriveLetter)
+    {
+        DriveLetterInfo->CurrentDriveLetter = DeviceInformation->SuggestedDriveLetter;
+        NameBuffer[LETTER_POSITION] = DeviceInformation->SuggestedDriveLetter;
+
+        Status = MountMgrCreatePointWorker(DeviceExtension, &SymbolicName, &TargetDeviceName);
+        if (NT_SUCCESS(Status))
+        {
+            goto Release;
+        }
+    }
+
+    /* It failed with this letter... Try another one! */
+    for (DriveLetterInfo->CurrentDriveLetter = DriveLetter;
+         DriveLetterInfo->CurrentDriveLetter <= L'Z';
+         DriveLetterInfo->CurrentDriveLetter++)
+    {
+        NameBuffer[LETTER_POSITION] = DeviceInformation->SuggestedDriveLetter;
+
+        Status = MountMgrCreatePointWorker(DeviceExtension, &SymbolicName, &TargetDeviceName);
+        if (NT_SUCCESS(Status))
+        {
+            break;
+        }
+    }
+
+    /* We failed setting a letter */
+    if (DriveLetterInfo->CurrentDriveLetter > L'Z')
+    {
+        DriveLetterInfo->DriveLetterWasAssigned = FALSE;
+        DriveLetterInfo->CurrentDriveLetter = 0;
+
+        /* Try at least to add a no drive letter entry */
+        Status = QueryDeviceInformation(&TargetDeviceName, NULL, &UniqueId, NULL, NULL, NULL, NULL, NULL);
+        if (NT_SUCCESS(Status))
+        {
+            CreateNoDriveLetterEntry(UniqueId);
+            FreePool(UniqueId);
+        }
+    }
+
+Release:
+    FreePool(TargetDeviceName.Buffer);
+
+    return STATUS_SUCCESS;
+}
+
+
+/*
+ * @implemented
+ */
+NTSTATUS
+MountMgrNextDriveLetter(IN PDEVICE_EXTENSION DeviceExtension,
+                        IN PIRP Irp)
+{
+    NTSTATUS Status;
+    PIO_STACK_LOCATION Stack;
+    UNICODE_STRING DeviceName;
+    PMOUNTMGR_DRIVE_LETTER_TARGET DriveLetterTarget;
+    MOUNTMGR_DRIVE_LETTER_INFORMATION DriveLetterInformation;
+
+    Stack = IoGetNextIrpStackLocation(Irp);
+
+    /* Validate input */
+    if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_DRIVE_LETTER_TARGET) ||
+        Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    DriveLetterTarget = (PMOUNTMGR_DRIVE_LETTER_TARGET)Irp->AssociatedIrp.SystemBuffer;
+    if (DriveLetterTarget->DeviceNameLength + sizeof(USHORT) > Stack->Parameters.DeviceIoControl.InputBufferLength)
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Call the worker */
+    DeviceName.Buffer = DriveLetterTarget->DeviceName;
+    DeviceName.Length =
+    DeviceName.MaximumLength = DriveLetterTarget->DeviceNameLength;
+
+    Status = MountMgrNextDriveLetterWorker(DeviceExtension, &DeviceName,
+                                           &DriveLetterInformation);
+    if (NT_SUCCESS(Status))
+    {
+        *(PMOUNTMGR_DRIVE_LETTER_INFORMATION)Irp->AssociatedIrp.SystemBuffer =
+            DriveLetterInformation;
+        Irp->IoStatus.Information = sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION);
+    }
+
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+MountMgrQuerySystemVolumeNameQueryRoutine(IN PWSTR ValueName,
+                                          IN ULONG ValueType,
+                                          IN PVOID ValueData,
+                                          IN ULONG ValueLength,
+                                          IN PVOID Context,
+                                          IN PVOID EntryContext)
+{
+    UNICODE_STRING ValueString;
+    PUNICODE_STRING SystemVolumeName;
+
+    if (ValueType != REG_SZ)
+    {
+        return STATUS_SUCCESS;
+    }
+
+    RtlInitUnicodeString(&ValueString, ValueData);
+    SystemVolumeName = Context;
+
+    /* Return a string containing system volume name */
+    SystemVolumeName->Length = ValueString.Length;
+    SystemVolumeName->MaximumLength = ValueString.Length + sizeof(WCHAR);
+    SystemVolumeName->Buffer = AllocatePool(SystemVolumeName->MaximumLength);
+    if (SystemVolumeName->Buffer)
+    {
+        RtlCopyMemory(SystemVolumeName->Buffer, ValueData, ValueString.Length);
+        SystemVolumeName->Buffer[ValueString.Length / sizeof(WCHAR)] = UNICODE_NULL;
+    }
+
+    return STATUS_SUCCESS;
+
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+MountMgrQuerySystemVolumeName(OUT PUNICODE_STRING SystemVolumeName)
+{
+    RTL_QUERY_REGISTRY_TABLE QueryTable[2];
+
+    RtlZeroMemory(QueryTable, sizeof(QueryTable));
+    QueryTable[0].QueryRoutine = MountMgrQuerySystemVolumeNameQueryRoutine;
+    QueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
+    QueryTable[0].Name = L"SystemPartition";
+
+    SystemVolumeName->Buffer = NULL;
+
+    RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
+                           L"\\Registry\\Machine\\System\\Setup",
+                           QueryTable,
+                           SystemVolumeName,
+                           NULL);
+
+    if (SystemVolumeName->Buffer)
+    {
+        return STATUS_SUCCESS;
+    }
+
+    return STATUS_UNSUCCESSFUL;
+}
+
+/*
+ * @implemented
+ */
+VOID
+MountMgrAssignDriveLetters(IN PDEVICE_EXTENSION DeviceExtension)
+{
+    NTSTATUS Status;
+    PLIST_ENTRY NextEntry;
+    UNICODE_STRING SystemVolumeName;
+    PDEVICE_INFORMATION DeviceInformation;
+    MOUNTMGR_DRIVE_LETTER_INFORMATION DriveLetterInformation;
+
+    /* First, get system volume name */
+    Status = MountMgrQuerySystemVolumeName(&SystemVolumeName);
+
+    /* If there are no device, it's all done */
+    if (IsListEmpty(&(DeviceExtension->DeviceListHead)))
+    {
+        if (NT_SUCCESS(Status))
+        {
+            FreePool(SystemVolumeName.Buffer);
+        }
+
+        return;
+    }
+
+    /* Now, for all the devices... */
+    for (NextEntry = DeviceExtension->DeviceListHead.Flink;
+         NextEntry != &(DeviceExtension->DeviceListHead);
+         NextEntry = NextEntry->Flink)
+    {
+        DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
+
+        /* If the device doesn't have a letter assigned, do it! */
+        if (!DeviceInformation->LetterAssigned)
+        {
+            MountMgrNextDriveLetterWorker(DeviceExtension,
+                                          &(DeviceInformation->DeviceName),
+                                          &DriveLetterInformation);
+        }
+
+        /* If it was the system volume */
+        if (NT_SUCCESS(Status) && RtlEqualUnicodeString(&SystemVolumeName, &(DeviceInformation->DeviceName), TRUE))
+        {
+            /* Keep track of it */
+            DeviceExtension->DriveLetterData = AllocatePool(DeviceInformation->UniqueId->UniqueIdLength +
+                                                            sizeof(MOUNTDEV_UNIQUE_ID));
+            if (DeviceExtension->DriveLetterData)
+            {
+                RtlCopyMemory(DeviceExtension->DriveLetterData,
+                              DeviceInformation->UniqueId,
+                              DeviceInformation->UniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
+            }
+
+            /* If it was not automount, ensure it gets mounted */
+            if (!DeviceExtension->NoAutoMount)
+            {
+                DeviceExtension->NoAutoMount = TRUE;
+
+                MountMgrNextDriveLetterWorker(DeviceExtension,
+                                              &(DeviceInformation->DeviceName),
+                                              &DriveLetterInformation);
+
+                DeviceExtension->NoAutoMount = FALSE;
+            }
+        }
+    }
+
+    if (NT_SUCCESS(Status))
+    {
+        FreePool(SystemVolumeName.Buffer);
+    }
+}
+
+NTSTATUS
+MountMgrQueryDosVolumePath(IN PDEVICE_EXTENSION DeviceExtension,
+                           IN PIRP Irp)
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS
+MountMgrQueryDosVolumePaths(IN PDEVICE_EXTENSION DeviceExtension,
+                            IN PIRP Irp)
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+MountMgrKeepLinksWhenOffline(IN PDEVICE_EXTENSION DeviceExtension,
+                             IN PIRP Irp)
+{
+    NTSTATUS Status;
+    PIO_STACK_LOCATION Stack;
+    UNICODE_STRING SymbolicName;
+    PMOUNTMGR_TARGET_NAME Target;
+    PDEVICE_INFORMATION DeviceInformation;
+
+    Stack = IoGetNextIrpStackLocation(Irp);
+
+    /* Validate input */
+    if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer;
+    if (Target->DeviceNameLength + sizeof(USHORT) > Stack->Parameters.DeviceIoControl.InputBufferLength)
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    SymbolicName.Length =
+    SymbolicName.MaximumLength = Target->DeviceNameLength;
+    SymbolicName.Buffer = Target->DeviceName;
+
+    /* Find the associated device */
+    Status = FindDeviceInfo(DeviceExtension, &SymbolicName, FALSE, &DeviceInformation);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    /* Mark we want to keep links */
+    DeviceInformation->KeepLinks = TRUE;
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+MountMgrVolumeArrivalNotification(IN PDEVICE_EXTENSION DeviceExtension,
+                                  IN PIRP Irp)
+{
+    NTSTATUS Status;
+    BOOLEAN OldState;
+    PIO_STACK_LOCATION Stack;
+    UNICODE_STRING SymbolicName;
+    PMOUNTMGR_TARGET_NAME Target;
+
+    Stack = IoGetNextIrpStackLocation(Irp);
+
+    /* Validate input */
+    if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer;
+    if (Target->DeviceNameLength + sizeof(USHORT) > Stack->Parameters.DeviceIoControl.InputBufferLength)
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    SymbolicName.Length =
+    SymbolicName.MaximumLength = Target->DeviceNameLength;
+    SymbolicName.Buffer = Target->DeviceName;
+
+    /* Disable hard errors */
+    OldState = PsGetThreadHardErrorsAreDisabled(PsGetCurrentThread());
+    PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), TRUE);
+
+    /* Call real worker */
+    Status = MountMgrMountedDeviceArrival(DeviceExtension, &SymbolicName, TRUE);
+
+    PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), OldState);
+
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+MountMgrQueryPoints(IN PDEVICE_EXTENSION DeviceExtension,
+                    IN PIRP Irp)
+{
+    NTSTATUS Status;
+    PIO_STACK_LOCATION Stack;
+    PMOUNTDEV_UNIQUE_ID UniqueId;
+    PMOUNTMGR_MOUNT_POINT MountPoint;
+    UNICODE_STRING SymbolicName, DeviceName;
+
+    Stack = IoGetNextIrpStackLocation(Irp);
+
+    /* Validate input... */
+    if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_MOUNT_POINT))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    MountPoint = (PMOUNTMGR_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer;
+    if (!MountPoint->SymbolicLinkNameLength)
+    {
+        MountPoint->SymbolicLinkNameOffset = 0;
+    }
+
+    if (!MountPoint->UniqueIdLength)
+    {
+        MountPoint->UniqueIdOffset = 0;
+    }
+
+    if (!MountPoint->DeviceNameLength)
+    {
+        MountPoint->DeviceNameOffset = 0;
+    }
+
+    /* Addresses can't be odd */
+    if ((MountPoint->SymbolicLinkNameOffset & 1) ||
+        (MountPoint->SymbolicLinkNameLength & 1))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    if ((MountPoint->UniqueIdOffset & 1) ||
+        (MountPoint->UniqueIdLength & 1))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    if ((MountPoint->DeviceNameOffset & 1) ||
+        (MountPoint->DeviceNameLength & 1))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* We can't go beyond */
+    if (MountPoint->SymbolicLinkNameLength + MountPoint->UniqueIdLength +
+        MountPoint->DeviceNameLength < Stack->Parameters.DeviceIoControl.InputBufferLength)
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_MOUNT_POINTS))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* If caller provided a Symlink, use it */
+    if (MountPoint->SymbolicLinkNameLength != 0)
+    {
+        if (MountPoint->SymbolicLinkNameLength > MAXSHORT)
+        {
+            return STATUS_INVALID_PARAMETER;
+        }
+
+        SymbolicName.Length = MountPoint->SymbolicLinkNameLength;
+        SymbolicName.MaximumLength = MountPoint->SymbolicLinkNameLength + sizeof(WCHAR);
+        SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength);
+        if (!SymbolicName.Buffer)
+        {
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        RtlCopyMemory(SymbolicName.Buffer,
+                      (PWSTR)((ULONG_PTR)MountPoint + MountPoint->SymbolicLinkNameOffset),
+                      SymbolicName.Length);
+        SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+        /* Query links using it */
+        Status = QueryPointsFromSymbolicLinkName(DeviceExtension, &SymbolicName, Irp);
+        FreePool(SymbolicName.Buffer);
+    }
+    /* If user provided an unique ID */
+    else if (MountPoint->UniqueIdLength != 0)
+    {
+        UniqueId = AllocatePool(MountPoint->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
+        if (!UniqueId)
+        {
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        UniqueId->UniqueIdLength = MountPoint->UniqueIdLength;
+        RtlCopyMemory(UniqueId->UniqueId,
+                      (PVOID)((ULONG_PTR)MountPoint + MountPoint->UniqueIdOffset),
+                      MountPoint->UniqueIdLength);
+
+         /* Query links using it */
+         Status = QueryPointsFromMemory(DeviceExtension, Irp, UniqueId, NULL);
+         FreePool(UniqueId);
+    }
+    /* If caller provided a device name */
+    else if (MountPoint->DeviceNameLength != 0)
+    {
+        if (MountPoint->DeviceNameLength > MAXSHORT)
+        {
+            return STATUS_INVALID_PARAMETER;
+        }
+
+        DeviceName.Length = MountPoint->DeviceNameLength;
+        DeviceName.MaximumLength = MountPoint->DeviceNameLength + sizeof(WCHAR);
+        DeviceName.Buffer = AllocatePool(DeviceName.MaximumLength);
+        if (!DeviceName.Buffer)
+        {
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        RtlCopyMemory(DeviceName.Buffer,
+                      (PWSTR)((ULONG_PTR)MountPoint + MountPoint->DeviceNameOffset),
+                      DeviceName.Length);
+        DeviceName.Buffer[DeviceName.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+         /* Query links using it */
+        Status = QueryPointsFromMemory(DeviceExtension, Irp, NULL, &DeviceName);
+        FreePool(DeviceName.Buffer);
+    }
+    else
+    {
+        /* Otherwise, query all links */
+        Status = QueryPointsFromMemory(DeviceExtension, Irp, NULL, NULL);
+    }
+
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+MountMgrDeletePoints(IN PDEVICE_EXTENSION DeviceExtension,
+                     IN PIRP Irp)
+{
+    ULONG Link;
+    NTSTATUS Status;
+    BOOLEAN CreateNoDrive;
+    PIO_STACK_LOCATION Stack;
+    PMOUNTDEV_UNIQUE_ID UniqueId;
+    PMOUNTMGR_MOUNT_POINT MountPoint;
+    PMOUNTMGR_MOUNT_POINTS MountPoints;
+    UNICODE_STRING SymbolicName, DeviceName;
+
+    Stack = IoGetNextIrpStackLocation(Irp);
+
+    /* Validate input */
+    if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_MOUNT_POINT))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Query points */
+    MountPoint = (PMOUNTMGR_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer;
+    CreateNoDrive = (MountPoint->SymbolicLinkNameOffset && MountPoint->SymbolicLinkNameLength);
+
+    Status = MountMgrQueryPoints(DeviceExtension, Irp);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    /* For all the points matching the request */
+    MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer;
+    for (Link = 0; Link < MountPoints->NumberOfMountPoints; Link++)
+    {
+        SymbolicName.Length = MountPoints->MountPoints[Link].SymbolicLinkNameLength;
+        SymbolicName.MaximumLength = SymbolicName.Length + sizeof(WCHAR);
+        SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength);
+        if (!SymbolicName.Buffer)
+        {
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        RtlCopyMemory(SymbolicName.Buffer,
+                      (PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].SymbolicLinkNameOffset),
+                      SymbolicName.Length);
+        SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+        /* Create a no drive entry for the drive letters */
+        if (CreateNoDrive && IsDriveLetter(&SymbolicName))
+        {
+            UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
+            if (UniqueId)
+            {
+                UniqueId->UniqueIdLength = MountPoints->MountPoints[Link].UniqueIdLength;
+                RtlCopyMemory(UniqueId->UniqueId,
+                              (PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset),
+                              MountPoints->MountPoints[Link].UniqueIdLength);
+
+                CreateNoDriveLetterEntry(UniqueId);
+                FreePool(UniqueId);
+            }
+        }
+
+        /* If there are no link any more, and no need to create a no drive entry */
+        if (Link == 0 && !CreateNoDrive)
+        {
+            /* Then, delete everything */
+            UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength);
+            if (UniqueId)
+            {
+                RtlCopyMemory(UniqueId,
+                              (PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset),
+                              MountPoints->MountPoints[Link].UniqueIdLength);
+
+                DeleteNoDriveLetterEntry(UniqueId);
+                FreePool(UniqueId);
+            }
+        }
+
+        /* Delete all the information about the mount point */
+        GlobalDeleteSymbolicLink(&SymbolicName);
+        DeleteSymbolicLinkNameFromMemory(DeviceExtension, &SymbolicName, FALSE);
+        RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, DatabasePath, SymbolicName.Buffer);
+        FreePool(SymbolicName.Buffer);
+
+        /* Notify the change */
+        DeviceName.Length = DeviceName.MaximumLength =
+        MountPoints->MountPoints[Link].DeviceNameLength;
+        DeviceName.Buffer = (PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].DeviceNameOffset);
+        MountMgrNotifyNameChange(DeviceExtension, &DeviceName, TRUE);
+    }
+
+    MountMgrNotify(DeviceExtension);
+
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+MountMgrDeletePointsDbOnly(IN PDEVICE_EXTENSION DeviceExtension,
+                           IN PIRP Irp)
+{
+    ULONG Link;
+    NTSTATUS Status;
+    UNICODE_STRING SymbolicName;
+    PMOUNTDEV_UNIQUE_ID UniqueId;
+    PMOUNTMGR_MOUNT_POINTS MountPoints;
+
+    /* Query points */
+    Status = MountMgrQueryPoints(DeviceExtension, Irp);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer;
+    if (MountPoints->NumberOfMountPoints == 0)
+    {
+        return Status;
+    }
+
+    /* For all the mount points */
+    for (Link = 0; Link < MountPoints->NumberOfMountPoints; Link++)
+    {
+        SymbolicName.Length = MountPoints->MountPoints[Link].SymbolicLinkNameLength;
+        SymbolicName.MaximumLength = SymbolicName.Length + sizeof(WCHAR);
+        SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength);
+        if (!SymbolicName.Buffer)
+        {
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        RtlCopyMemory(SymbolicName.Buffer,
+                      (PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].SymbolicLinkNameOffset),
+                      SymbolicName.Length);
+        SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+        /* If the only mount point is a drive letter, then create a no letter drive entry */
+        if (MountPoints->NumberOfMountPoints == 1 && IsDriveLetter(&SymbolicName))
+        {
+            UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
+            if (UniqueId)
+            {
+                UniqueId->UniqueIdLength = MountPoints->MountPoints[Link].UniqueIdLength;
+                RtlCopyMemory(UniqueId->UniqueId,
+                              (PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset),
+                              MountPoints->MountPoints[Link].UniqueIdLength);
+
+                CreateNoDriveLetterEntry(UniqueId);
+                FreePool(UniqueId);
+            }
+        }
+
+        /* Simply delete mount point from DB */
+        DeleteSymbolicLinkNameFromMemory(DeviceExtension, &SymbolicName, TRUE);
+        RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, DatabasePath, SymbolicName.Buffer);
+        FreePool(SymbolicName.Buffer);
+    }
+
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+MountMgrVolumeMountPointChanged(IN PDEVICE_EXTENSION DeviceExtension,
+                                IN PIRP Irp,
+                                IN NTSTATUS LockStatus, 
+                                OUT PUNICODE_STRING SourceDeviceName,
+                                OUT PUNICODE_STRING SourceSymbolicName,
+                                OUT PUNICODE_STRING TargetVolumeName)
+{
+    HANDLE Handle;
+    NTSTATUS Status;
+    PFILE_OBJECT FileObject;
+    PIO_STACK_LOCATION Stack;
+    ULONG Length, SavedLength;
+    BOOLEAN FOReferenced = FALSE;
+    IO_STATUS_BLOCK IoStatusBlock;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    PDEVICE_INFORMATION DeviceInformation;
+    OBJECT_NAME_INFORMATION ObjectNameInfo;
+    FILE_FS_DEVICE_INFORMATION FsDeviceInfo;
+    PFILE_NAME_INFORMATION FileNameInfo = NULL;
+    PMOUNTMGR_VOLUME_MOUNT_POINT VolumeMountPoint;
+    POBJECT_NAME_INFORMATION ObjectNameInfoPtr = NULL;
+    UNICODE_STRING SourceVolumeName, TargetDeviceName;
+
+    Stack = IoGetNextIrpStackLocation(Irp);
+
+    /* Validate input */
+    if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_VOLUME_MOUNT_POINT))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    VolumeMountPoint = (PMOUNTMGR_VOLUME_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer;
+
+    if (VolumeMountPoint->SourceVolumeNameLength + VolumeMountPoint->TargetVolumeNameLength <
+        Stack->Parameters.DeviceIoControl.InputBufferLength)
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Get source volume name */
+    SourceVolumeName.Length =
+    SourceVolumeName.MaximumLength = VolumeMountPoint->SourceVolumeNameLength;
+    SourceVolumeName.Buffer = (PWSTR)((ULONG_PTR)VolumeMountPoint + VolumeMountPoint->SourceVolumeNameOffset);
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &SourceVolumeName,
+                               OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
+
+    /* Open it */
+    Status = ZwOpenFile(&Handle,
+                        SYNCHRONIZE | FILE_READ_ATTRIBUTES,
+                        &ObjectAttributes,
+                        &IoStatusBlock,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                        FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    TargetDeviceName.Buffer = NULL;
+
+    /* Query its attributes */
+    Status = ZwQueryVolumeInformationFile(Handle,
+                                          &IoStatusBlock,
+                                          &FsDeviceInfo,
+                                          sizeof(FsDeviceInfo),
+                                          FileFsDeviceInformation);
+    if (!NT_SUCCESS(Status))
+    {
+        goto Cleanup;
+    }
+
+    if (FsDeviceInfo.DeviceType != FILE_DEVICE_DISK && FsDeviceInfo.DeviceType != FILE_DEVICE_VIRTUAL_DISK)
+    {
+        goto Cleanup;
+    }
+
+    if (FsDeviceInfo.Characteristics != (FILE_REMOTE_DEVICE | FILE_REMOVABLE_MEDIA))
+    {
+        goto Cleanup;
+    }
+
+    /* Reference it */
+    Status = ObReferenceObjectByHandle(Handle, 0, IoFileObjectType, KernelMode, (PVOID *)&FileObject, NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        goto Cleanup;
+    }
+    FOReferenced = TRUE;
+
+    /* Get file name */
+    FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION));
+    if (!FileNameInfo)
+    {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto Cleanup;
+    }
+
+    Status = ZwQueryInformationFile(Handle, &IoStatusBlock, FileNameInfo,
+                                    sizeof(FILE_NAME_INFORMATION),
+                                    FileNameInformation);
+    if (Status == STATUS_BUFFER_OVERFLOW)
+    {
+        /* Now we have real length, use it */
+        Length = FileNameInfo->FileNameLength;
+        FreePool(FileNameInfo);
+
+        FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + Length);
+        if (!FileNameInfo)
+        {
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto Cleanup;
+        }
+
+        /* Really query file name */
+        Status = ZwQueryInformationFile(Handle, &IoStatusBlock, FileNameInfo,
+                                        sizeof(FILE_NAME_INFORMATION) + Length,
+                                        FileNameInformation);
+    }
+
+    if (!NT_SUCCESS(Status))
+    {
+        goto Cleanup;
+    }
+
+    /* Get symbolic name */
+    ObjectNameInfoPtr = &ObjectNameInfo;
+    SavedLength = sizeof(OBJECT_NAME_INFORMATION);
+    Status = ObQueryNameString(FileObject->DeviceObject, ObjectNameInfoPtr, sizeof(OBJECT_NAME_INFORMATION), &Length);
+    if (Status == STATUS_INFO_LENGTH_MISMATCH)
+    {
+        /* Once again, with proper size, it works better */
+        ObjectNameInfoPtr = AllocatePool(Length);
+        if (!ObjectNameInfoPtr)
+        {
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto Cleanup;
+        }
+
+        SavedLength = Length;
+        Status = ObQueryNameString(FileObject->DeviceObject, ObjectNameInfoPtr, SavedLength, &Length);
+    }
+
+    if (!NT_SUCCESS(Status))
+    {
+        goto Cleanup;
+    }
+
+    /* Now, query the device name */
+    Status = QueryDeviceInformation(&ObjectNameInfoPtr->Name, SourceDeviceName,
+                                    NULL, NULL, NULL, NULL, NULL, NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        goto Cleanup;
+    }
+
+    /* For target volume name, use input */
+    TargetVolumeName->Length =
+    TargetVolumeName->MaximumLength = VolumeMountPoint->TargetVolumeNameLength;
+    TargetVolumeName->Buffer = (PWSTR)((ULONG_PTR)VolumeMountPoint + VolumeMountPoint->TargetVolumeNameOffset);
+
+    /* Query its device name */
+    Status = QueryDeviceInformation(TargetVolumeName, &TargetDeviceName,
+                                    NULL, NULL, NULL, NULL, NULL, NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        goto Cleanup;
+    }
+
+    /* Return symbolic name */
+    SourceSymbolicName->Length =
+    SourceSymbolicName->MaximumLength = FileNameInfo->FileNameLength;
+    SourceSymbolicName->Buffer = (PWSTR)FileNameInfo;
+    /* memmove allows memory overlap */
+    RtlMoveMemory(SourceSymbolicName->Buffer, FileNameInfo->FileName, SourceSymbolicName->Length);
+    FileNameInfo = NULL;
+
+    /* Notify the change */
+    MountMgrNotify(DeviceExtension);
+    MountMgrNotifyNameChange(DeviceExtension, &TargetDeviceName, TRUE);
+
+    /* If we are locked, sync databases if possible */
+    if (NT_SUCCESS(LockStatus))
+    {
+        Status = FindDeviceInfo(DeviceExtension, SourceDeviceName, FALSE, &DeviceInformation);
+        if (NT_SUCCESS(Status))
+        {
+            ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation);
+        }
+        else
+        {
+            Status = STATUS_PENDING;
+        }
+    }
+
+Cleanup:
+    if (TargetDeviceName.Buffer)
+    {
+        FreePool(TargetDeviceName.Buffer);
+    }
+
+    if (ObjectNameInfoPtr && ObjectNameInfoPtr != &ObjectNameInfo)
+    {
+        FreePool(ObjectNameInfoPtr);
+    }
+
+    if (FileNameInfo)
+    {
+        FreePool(FileNameInfo);
+    }
+
+    if (FOReferenced)
+    {
+        ObfDereferenceObject(FileObject);
+    }
+
+    return Status;
+}
+
+NTSTATUS
+MountMgrVolumeMountPointCreated(IN PDEVICE_EXTENSION DeviceExtension,
+                                IN PIRP Irp,
+                                IN NTSTATUS LockStatus)
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS
+MountMgrVolumeMountPointDeleted(IN PDEVICE_EXTENSION DeviceExtension,
+                                IN PIRP Irp,
+                                IN NTSTATUS LockStatus)
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+MountMgrDeviceControl(IN PDEVICE_OBJECT DeviceObject,
+                      IN PIRP Irp)
+{
+    PIO_STACK_LOCATION Stack;
+    NTSTATUS Status, LockStatus;
+    PDEVICE_EXTENSION DeviceExtension;
+
+    Stack = IoGetNextIrpStackLocation(Irp);
+    DeviceExtension = DeviceObject->DeviceExtension;
+
+    KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
+
+    switch (Stack->Parameters.DeviceIoControl.IoControlCode)
+    {
+        case IOCTL_MOUNTMGR_CREATE_POINT:
+            Status = MountMgrCreatePoint(DeviceExtension, Irp);
+            break;
+
+        case IOCTL_MOUNTMGR_DELETE_POINTS:
+            Status = MountMgrDeletePoints(DeviceExtension, Irp);
+
+        case IOCTL_MOUNTMGR_QUERY_POINTS:
+            Status = MountMgrQueryPoints(DeviceExtension, Irp);
+            break;
+
+        case IOCTL_MOUNTMGR_DELETE_POINTS_DBONLY:
+            Status = MountMgrDeletePointsDbOnly(DeviceExtension, Irp);
+            break;
+
+        case IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER:
+            Status = MountMgrNextDriveLetter(DeviceExtension, Irp);
+            break;
+
+        case IOCTL_MOUNTMGR_AUTO_DL_ASSIGNMENTS:
+            DeviceExtension->AutomaticDriveLetter = TRUE;
+            Status = STATUS_SUCCESS;
+
+            MountMgrAssignDriveLetters(DeviceExtension);
+            ReconcileAllDatabasesWithMaster(DeviceExtension);
+            WaitForOnlinesToComplete(DeviceExtension);
+            break;
+
+        case IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED:
+            KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
+
+            LockStatus = WaitForRemoteDatabaseSemaphore(DeviceExtension);
+            KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
+            Status = MountMgrVolumeMountPointCreated(DeviceExtension, Irp, LockStatus);
+            if (NT_SUCCESS(LockStatus))
+            {
+                ReleaseRemoteDatabaseSemaphore(DeviceExtension);
+            }
+
+            break;
+
+        case IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED:
+            KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
+
+            LockStatus = WaitForRemoteDatabaseSemaphore(DeviceExtension);
+            KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
+            Status = MountMgrVolumeMountPointDeleted(DeviceExtension, Irp, LockStatus);
+            if (NT_SUCCESS(LockStatus))
+            {
+                ReleaseRemoteDatabaseSemaphore(DeviceExtension);
+            }
+
+            break;
+
+        case IOCTL_MOUNTMGR_CHANGE_NOTIFY:
+            Status = MountMgrChangeNotify(DeviceExtension, Irp);
+            break;
+
+        case IOCTL_MOUNTMGR_KEEP_LINKS_WHEN_OFFLINE:
+            Status = MountMgrKeepLinksWhenOffline(DeviceExtension, Irp);
+            break;
+
+        case IOCTL_MOUNTMGR_CHECK_UNPROCESSED_VOLUMES:
+            Status = MountMgrCheckUnprocessedVolumes(DeviceExtension, Irp);
+            goto Complete;
+
+        case IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION:
+            KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
+            Status = MountMgrVolumeArrivalNotification(DeviceExtension, Irp);
+            goto Complete;
+
+        case IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH:
+            Status = MountMgrQueryDosVolumePath(DeviceExtension, Irp);
+            break;
+
+        case IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS:
+            Status = MountMgrQueryDosVolumePaths(DeviceExtension, Irp);
+            break;
+
+        case IOCTL_MOUNTMGR_SCRUB_REGISTRY:
+            Status = MountMgrScrubRegistry(DeviceExtension);
+            break;
+
+        case IOCTL_MOUNTMGR_QUERY_AUTO_MOUNT:
+            Status = MountMgrQueryAutoMount(DeviceExtension, Irp);
+            break;
+
+        case IOCTL_MOUNTMGR_SET_AUTO_MOUNT:
+            Status = MountMgrSetAutoMount(DeviceExtension, Irp);
+            break;
+
+        default:
+            Status = STATUS_INVALID_DEVICE_REQUEST;
+    }
+
+    KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
+
+    if (Status != STATUS_PENDING)
+    {
+        goto Complete;
+    }
+
+    return Status;
+
+Complete:
+    Irp->IoStatus.Status = Status;
+    IofCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return Status;
+}
diff --git a/drivers/filters/mountmgr/mntmgr.h b/drivers/filters/mountmgr/mntmgr.h
new file mode 100644 (file)
index 0000000..bcf9965
--- /dev/null
@@ -0,0 +1,452 @@
+#ifndef _MNTMGR_H_
+#define _MNTMGR_H_
+
+#include <ntifs.h>
+#include <ntddk.h>
+#include <mountdev.h>
+#include <ntddvol.h>
+#include <wdmguid.h>
+#include <ioevent.h>
+#include <psfuncs.h>
+#include <ntdddisk.h>
+#include <ntddvol.h>
+
+/* Enter FIXME */
+#ifdef IsEqualGUID
+#undef IsEqualGUID
+#endif
+
+#define IsEqualGUID(rguid1, rguid2) (!RtlCompareMemory(rguid1, rguid2, sizeof(GUID)))
+
+#define FILE_READ_PROPERTIES  0x00000008
+#define FILE_WRITE_PROPERTIES 0x00000010
+
+#define GPT_BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER 0x80000000
+/* Leave FIXME */
+
+typedef struct _DEVICE_EXTENSION
+{
+    PDEVICE_OBJECT DeviceObject;           // 0x0
+    PDRIVER_OBJECT DriverObject;           // 0x4
+    LIST_ENTRY DeviceListHead;             // 0x8
+    LIST_ENTRY OfflineDeviceListHead;      // 0x10
+    PVOID NotificationEntry;               // 0x18
+    KSEMAPHORE DeviceLock;                 // 0x1C
+    KSEMAPHORE RemoteDatabaseLock;         // 0x30
+    ULONG AutomaticDriveLetter;            // 0x44
+    LIST_ENTRY IrpListHead;                // 0x48
+    ULONG EpicNumber;                      // 0x50
+    LIST_ENTRY SavedLinksListHead;         // 0x54
+    BOOLEAN ProcessedSuggestions;          // 0x5C
+    BOOLEAN NoAutoMount;                   // 0x5D
+    LIST_ENTRY WorkerQueueListHead;        // 0x60
+    KSEMAPHORE WorkerSemaphore;            // 0x68
+    LONG WorkerReferences;                 // 0x7C
+    KSPIN_LOCK WorkerLock;                 // 0x80
+    LIST_ENTRY UniqueIdWorkerItemListHead; // 0x84
+    PMOUNTDEV_UNIQUE_ID DriveLetterData;   // 0x8C
+    UNICODE_STRING RegistryPath;           // 0x90
+    LONG WorkerThreadStatus;               // 0x98
+    LIST_ENTRY OnlineNotificationListHead; // 0x9C
+    ULONG OnlineNotificationWorkerActive;  // 0xA4
+    ULONG OnlineNotificationCount;         // 0xA8
+    KEVENT OnlineNotificationEvent;        // 0xAC
+} DEVICE_EXTENSION, *PDEVICE_EXTENSION;    // 0xBC
+
+typedef struct _DEVICE_INFORMATION
+{
+    LIST_ENTRY DeviceListEntry;             // 0x00
+    LIST_ENTRY SymbolicLinksListHead;       // 0x08
+    LIST_ENTRY ReplicatedUniqueIdsListHead; // 0x10
+    LIST_ENTRY AssociatedDevicesHead;       // 0x18
+    UNICODE_STRING SymbolicName;            // 0x20
+    PMOUNTDEV_UNIQUE_ID UniqueId;           // 0x28
+    UNICODE_STRING DeviceName;              // 0x2C
+    BOOLEAN KeepLinks;                      // 0x34
+    UCHAR SuggestedDriveLetter;             // 0x35
+    BOOLEAN Volume;                         // 0x36
+    BOOLEAN Removable;                      // 0x37
+    BOOLEAN LetterAssigned;                 // 0x38
+    BOOLEAN NeedsReconcile;                 // 0x39
+    BOOLEAN NoDatabase;                     // 0x3A
+    BOOLEAN SkipNotifications;              // 0x3B
+    ULONG Migrated;                         // 0x3C
+    LONG MountState;                        // 0x40
+    PVOID TargetDeviceNotificationEntry;    // 0x44
+    PDEVICE_EXTENSION DeviceExtension;      // 0x48
+} DEVICE_INFORMATION, *PDEVICE_INFORMATION; // 0x4C
+
+typedef struct _SYMLINK_INFORMATION
+{
+    LIST_ENTRY SymbolicLinksListEntry;        // 0x00
+    UNICODE_STRING Name;                      // 0x08
+    BOOLEAN Online;                           // 0x10
+} SYMLINK_INFORMATION, *PSYMLINK_INFORMATION; // 0x14
+
+typedef struct _SAVED_LINK_INFORMATION
+{
+    LIST_ENTRY SavedLinksListEntry;                 // 0x0
+    LIST_ENTRY SymbolicLinksListHead;               // 0x8
+    PMOUNTDEV_UNIQUE_ID UniqueId;                   // 0x10
+} SAVED_LINK_INFORMATION, *PSAVED_LINK_INFORMATION; // 0x14
+
+typedef struct _UNIQUE_ID_REPLICATE
+{
+    LIST_ENTRY ReplicatedUniqueIdsListEntry;  // 0x0
+    PMOUNTDEV_UNIQUE_ID UniqueId;             // 0x8
+} UNIQUE_ID_REPLICATE, *PUNIQUE_ID_REPLICATE; // 0xC
+
+typedef struct _DATABASE_ENTRY
+{
+    ULONG EntrySize;                // 0x00
+    ULONG DatabaseOffset;           // 0x04
+    USHORT SymbolicNameOffset;      // 0x08
+    USHORT SymbolicNameLength;      // 0x0A
+    USHORT UniqueIdOffset;          // 0x0C
+    USHORT UniqueIdLength;          // 0x0E
+} DATABASE_ENTRY, *PDATABASE_ENTRY; // 0x10
+
+typedef struct _ASSOCIATED_DEVICE_ENTRY
+{
+    LIST_ENTRY AssociatedDevicesEntry;                // 0x00
+    PDEVICE_INFORMATION DeviceInformation;            // 0x08
+    UNICODE_STRING String;                            // 0x0C
+} ASSOCIATED_DEVICE_ENTRY, *PASSOCIATED_DEVICE_ENTRY; // 0x14
+
+typedef struct _ONLINE_NOTIFICATION_WORK_ITEM
+{
+    WORK_QUEUE_ITEM;                                              // 0x00
+    PDEVICE_EXTENSION DeviceExtension;                            // 0x10
+    UNICODE_STRING SymbolicName;                                  // 0x14
+} ONLINE_NOTIFICATION_WORK_ITEM, *PONLINE_NOTIFICATION_WORK_ITEM; // 0x1C
+
+typedef struct _RECONCILE_WORK_ITEM
+{
+    LIST_ENTRY WorkerQueueListEntry;            // 0x00
+    PIO_WORKITEM WorkItem;                      // 0x08
+    PWORKER_THREAD_ROUTINE WorkerRoutine;       // 0x0C
+    PVOID Context;                              // 0x10
+    PDEVICE_EXTENSION DeviceExtension;          // 0x14
+    PDEVICE_INFORMATION DeviceInformation;      // 0x18
+} RECONCILE_WORK_ITEM, *PRECONCILE_WORK_ITEM;   // 0x1C
+
+typedef struct _MIGRATE_WORK_ITEM
+{
+    PIO_WORKITEM WorkItem;                 // 0x0
+    PDEVICE_INFORMATION DeviceInformation; // 0x4
+    PKEVENT Event;                         // 0x8
+    NTSTATUS Status;                       // 0x0C
+    HANDLE Database;                       // 0x10
+} MIGRATE_WORK_ITEM, *PMIGRATE_WORK_ITEM;  // 0x14
+
+typedef struct _UNIQUE_ID_WORK_ITEM
+{
+    LIST_ENTRY UniqueIdWorkerItemListEntry;   // 0x0
+    PIO_WORKITEM WorkItem;                    // 0x8
+    PDEVICE_EXTENSION DeviceExtension;        // 0xC
+    PIRP Irp;                                 // 0x10
+    PVOID IrpBuffer;                          // 0x14
+    PKEVENT Event;                            // 0x1C
+    UNICODE_STRING DeviceName;                // 0x20
+    ULONG IrpBufferLength;                    // 0x28
+    ULONG StackSize;                          // 0x2C
+} UNIQUE_ID_WORK_ITEM, *PUNIQUE_ID_WORK_ITEM; // 0x30
+
+PDEVICE_OBJECT gdeviceObject;
+
+/* Memory allocation helpers */
+#define AllocatePool(Size) ExAllocatePoolWithTag(PagedPool, Size, 'AtnM')
+#define FreePool(P)        ExFreePoolWithTag(P, 'AtnM')
+
+/* Misc macros */
+#define MAX(a, b)          ((a > b) ? a : b)
+
+#define LETTER_POSITION     0xC
+#define COLON_POSITION      0xD
+#define DRIVE_LETTER_LENGTH 0x1C
+
+/* mountmgr.c */
+
+extern UNICODE_STRING DosDevicesMount;
+extern UNICODE_STRING ReparseIndex;
+extern UNICODE_STRING DeviceFloppy;
+extern UNICODE_STRING DeviceMount;
+extern UNICODE_STRING DeviceCdRom;
+extern UNICODE_STRING SafeVolumes;
+extern UNICODE_STRING DosDevices;
+extern UNICODE_STRING DosGlobal;
+extern UNICODE_STRING Global;
+extern UNICODE_STRING Volume;
+extern KEVENT UnloadEvent;
+extern LONG Unloading;
+
+DRIVER_INITIALIZE DriverEntry;
+
+VOID
+MountMgrCancel(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp
+);
+
+NTSTATUS
+MountMgrMountedDeviceArrival(
+    IN PDEVICE_EXTENSION Extension,
+    IN PUNICODE_STRING SymbolicName,
+    IN BOOLEAN FromVolume
+);
+
+VOID
+MountMgrMountedDeviceRemoval(
+    IN PDEVICE_EXTENSION Extension,
+    IN PUNICODE_STRING DeviceName
+);
+
+NTSTATUS
+FindDeviceInfo(
+    IN PDEVICE_EXTENSION DeviceExtension,
+    IN PUNICODE_STRING SymbolicName,
+    IN BOOLEAN DeviceNameGiven,
+    OUT PDEVICE_INFORMATION * DeviceInformation
+);
+
+VOID
+MountMgrFreeDeadDeviceInfo(
+    IN PDEVICE_INFORMATION DeviceInformation
+);
+
+NTSTATUS
+QueryDeviceInformation(
+    IN PUNICODE_STRING SymbolicName,
+    OUT PUNICODE_STRING DeviceName OPTIONAL,
+    OUT PMOUNTDEV_UNIQUE_ID * UniqueId OPTIONAL,
+    OUT PBOOLEAN Removable OPTIONAL,
+    OUT PBOOLEAN GptDriveLetter OPTIONAL,
+    OUT PBOOLEAN HasGuid OPTIONAL,
+    IN OUT LPGUID StableGuid OPTIONAL,
+    OUT PBOOLEAN Valid OPTIONAL
+);
+
+BOOLEAN
+HasDriveLetter(
+    IN PDEVICE_INFORMATION DeviceInformation
+);
+
+/* database.c */
+
+extern PWSTR DatabasePath;
+extern PWSTR OfflinePath;
+
+VOID
+ReconcileThisDatabaseWithMaster(
+    IN PDEVICE_EXTENSION DeviceExtension,
+    IN PDEVICE_INFORMATION DeviceInformation
+);
+
+NTSTATUS
+WaitForRemoteDatabaseSemaphore(
+    IN PDEVICE_EXTENSION DeviceExtension
+);
+
+VOID
+ReleaseRemoteDatabaseSemaphore(
+    IN PDEVICE_EXTENSION DeviceExtension
+);
+
+VOID
+ChangeRemoteDatabaseUniqueId(
+    IN PDEVICE_INFORMATION DeviceInformation,
+    IN PMOUNTDEV_UNIQUE_ID OldUniqueId,
+    IN PMOUNTDEV_UNIQUE_ID NewUniqueId
+);
+
+VOID
+ReconcileAllDatabasesWithMaster(
+    IN PDEVICE_EXTENSION DeviceExtension
+);
+
+VOID
+DeleteFromLocalDatabase(
+    IN PUNICODE_STRING SymbolicLink,
+    IN PMOUNTDEV_UNIQUE_ID UniqueId
+);
+
+VOID
+DeleteRegistryDriveLetter(
+    IN PMOUNTDEV_UNIQUE_ID UniqueId
+);
+
+VOID
+DeleteNoDriveLetterEntry(
+    IN PMOUNTDEV_UNIQUE_ID UniqueId
+);
+
+NTSTATUS
+QueryVolumeName(
+    IN HANDLE RootDirectory,
+    IN PFILE_REPARSE_POINT_INFORMATION ReparsePointInformation,
+    IN PUNICODE_STRING FileName OPTIONAL,
+    OUT PUNICODE_STRING SymbolicName,
+    OUT PUNICODE_STRING VolumeName
+);
+
+/* device.c */
+
+DRIVER_DISPATCH MountMgrDeviceControl;
+
+/* notify.c */
+VOID
+IssueUniqueIdChangeNotifyWorker(
+    IN PUNIQUE_ID_WORK_ITEM WorkItem,
+    IN PMOUNTDEV_UNIQUE_ID UniqueId
+);
+
+VOID
+WaitForOnlinesToComplete(
+    IN PDEVICE_EXTENSION DeviceExtension
+);
+
+VOID
+RegisterForTargetDeviceNotification(
+    IN PDEVICE_EXTENSION DeviceExtension,
+    IN PDEVICE_INFORMATION DeviceInformation
+);
+
+VOID
+SendOnlineNotification(
+    IN PUNICODE_STRING SymbolicName
+);
+
+VOID
+IssueUniqueIdChangeNotify(
+    IN PDEVICE_EXTENSION DeviceExtension,
+    IN PUNICODE_STRING DeviceName,
+    IN PMOUNTDEV_UNIQUE_ID UniqueId
+);
+
+VOID
+PostOnlineNotification(
+    IN PDEVICE_EXTENSION DeviceExtension,
+    IN PUNICODE_STRING SymbolicName
+);
+
+VOID
+MountMgrNotify(
+    IN PDEVICE_EXTENSION DeviceExtension
+);
+
+VOID
+MountMgrNotifyNameChange(
+    IN PDEVICE_EXTENSION DeviceExtension,
+    IN PUNICODE_STRING DeviceName,
+    IN BOOLEAN ValidateVolume
+);
+
+/* uniqueid.c */
+VOID
+MountMgrUniqueIdChangeRoutine(
+    IN PDEVICE_EXTENSION DeviceExtension,
+    IN PMOUNTDEV_UNIQUE_ID OldUniqueId,
+    IN PMOUNTDEV_UNIQUE_ID NewUniqueId
+);
+
+VOID
+CreateNoDriveLetterEntry(
+    IN PMOUNTDEV_UNIQUE_ID UniqueId
+);
+
+BOOLEAN
+HasNoDriveLetterEntry(
+    IN PMOUNTDEV_UNIQUE_ID UniqueId
+);
+
+/* point.c */
+NTSTATUS
+MountMgrCreatePointWorker(
+    IN PDEVICE_EXTENSION DeviceExtension,
+    IN PUNICODE_STRING SymbolicLinkName,
+    IN PUNICODE_STRING DeviceName
+);
+
+NTSTATUS
+QueryPointsFromSymbolicLinkName(
+    IN PDEVICE_EXTENSION DeviceExtension,
+    IN PUNICODE_STRING SymbolicName,
+    IN PIRP Irp
+);
+
+NTSTATUS
+QueryPointsFromMemory(
+    IN PDEVICE_EXTENSION DeviceExtension,
+    IN PIRP Irp,
+    IN PMOUNTDEV_UNIQUE_ID UniqueId OPTIONAL,
+    IN PUNICODE_STRING SymbolicName OPTIONAL
+);
+
+/* symlink.c */
+NTSTATUS
+GlobalCreateSymbolicLink(
+    IN PUNICODE_STRING DosName,
+    IN PUNICODE_STRING DeviceName
+);
+
+NTSTATUS
+GlobalDeleteSymbolicLink(
+    IN PUNICODE_STRING DosName
+);
+
+NTSTATUS
+QuerySuggestedLinkName(
+    IN PUNICODE_STRING SymbolicName,
+    OUT PUNICODE_STRING SuggestedLinkName,
+    OUT PBOOLEAN UseOnlyIfThereAreNoOtherLinks
+);
+
+NTSTATUS
+QuerySymbolicLinkNamesFromStorage(
+    IN PDEVICE_EXTENSION DeviceExtension,
+    IN PDEVICE_INFORMATION DeviceInformation,
+    IN PUNICODE_STRING SuggestedLinkName,
+    IN BOOLEAN UseOnlyIfThereAreNoOtherLinks,
+    OUT PUNICODE_STRING * SymLinks,
+    OUT PULONG SymLinkCount,
+    IN BOOLEAN HasGuid,
+    IN LPGUID Guid
+);
+
+PSAVED_LINK_INFORMATION
+RemoveSavedLinks(
+    IN PDEVICE_EXTENSION DeviceExtension,
+    IN PMOUNTDEV_UNIQUE_ID UniqueId
+);
+
+BOOLEAN
+RedirectSavedLink(
+    IN PSAVED_LINK_INFORMATION SavedLinkInformation,
+    IN PUNICODE_STRING DosName,
+    IN PUNICODE_STRING NewLink
+);
+
+VOID
+SendLinkCreated(
+    IN PUNICODE_STRING SymbolicName
+);
+
+NTSTATUS
+CreateNewVolumeName(
+    OUT PUNICODE_STRING VolumeName,
+    IN PGUID VolumeGuid OPTIONAL
+);
+
+BOOLEAN
+IsDriveLetter(
+    PUNICODE_STRING SymbolicName
+);
+
+VOID
+DeleteSymbolicLinkNameFromMemory(
+    IN PDEVICE_EXTENSION DeviceExtension,
+    IN PUNICODE_STRING SymbolicLink,
+    IN BOOLEAN MarkOffline
+);
+
+#endif /* _MNTMGR_H_ */
diff --git a/drivers/filters/mountmgr/mountmgr.c b/drivers/filters/mountmgr/mountmgr.c
new file mode 100644 (file)
index 0000000..dffc91f
--- /dev/null
@@ -0,0 +1,1896 @@
+/*
+ *  ReactOS kernel
+ *  Copyright (C) 2011 ReactOS Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * COPYRIGHT:        See COPYING in the top level directory
+ * PROJECT:          ReactOS kernel
+ * FILE:             drivers/filesystem/mountmgr/mountmgr.c
+ * PURPOSE:          Mount Manager
+ * PROGRAMMER:       Pierre Schweitzer (pierre.schweitzer@reactos.org)
+ *                   Alex Ionescu (alex.ionescu@reactos.org)
+ */
+
+/* INCLUDES *****************************************************************/
+
+#include "mntmgr.h"
+
+#define NDEBUG
+#include <debug.h>
+
+/* FIXME */
+GUID MountedDevicesGuid = {0x53F5630D, 0xB6BF, 0x11D0, {0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B}};
+
+KEVENT UnloadEvent;
+LONG Unloading;
+
+PWSTR Cunc = L"\\??\\C:";
+
+/*
+ * TODO:
+ * - DeleteRemoteDatabaseEntry
+ * - MountMgrQueryDosVolumePath
+ * - MountMgrQueryDosVolumePaths
+ * - MountMgrQueryVolumePaths
+ * - MountMgrValidateBackPointer
+ * - MountMgrVolumeMountPointCreated
+ * - MountMgrVolumeMountPointDeleted
+ * - ReconcileThisDatabaseWithMasterWorker
+ */
+
+/*
+ * @implemented
+ */
+BOOLEAN
+IsOffline(PUNICODE_STRING SymbolicName)
+{
+    NTSTATUS Status;
+    ULONG IsOffline, Default;
+    RTL_QUERY_REGISTRY_TABLE QueryTable[2];
+
+    /* Prepare to look in the registry to see if
+     * given volume is offline
+     */
+    RtlZeroMemory(QueryTable, sizeof(QueryTable));
+    QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
+    QueryTable[0].Name = SymbolicName->Buffer;
+    QueryTable[0].EntryContext = &IsOffline;
+    QueryTable[0].DefaultType = REG_DWORD;
+    QueryTable[0].DefaultLength = sizeof(ULONG);
+    QueryTable[0].DefaultData = &Default;
+
+    Default = 0;
+
+    /* Query status */
+    Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
+                                    OfflinePath,
+                                    QueryTable,
+                                    NULL,
+                                    NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        IsOffline = 0;
+    }
+
+    return (IsOffline != 0);
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN
+HasDriveLetter(IN PDEVICE_INFORMATION DeviceInformation)
+{
+    PLIST_ENTRY NextEntry;
+    PSYMLINK_INFORMATION SymlinkInfo;
+
+    /* To have a drive letter, a device must have symbolic links */
+    if (IsListEmpty(&(DeviceInformation->SymbolicLinksListHead)))
+    {
+        return FALSE;
+    }
+
+    /* Browse all the links untill a drive letter is found */
+    NextEntry = &(DeviceInformation->SymbolicLinksListHead);
+    do
+    {
+        SymlinkInfo = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
+
+        if (SymlinkInfo->Online)
+        {
+            if (IsDriveLetter(&(SymlinkInfo->Name)))
+            {
+                return TRUE;
+            }
+        }
+
+        NextEntry = NextEntry->Flink;
+    } while (NextEntry != &(DeviceInformation->SymbolicLinksListHead));
+
+    return FALSE;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+CreateNewDriveLetterName(OUT PUNICODE_STRING DriveLetter,
+                         IN PUNICODE_STRING DeviceName,
+                         IN UCHAR Letter,
+                         IN PMOUNTDEV_UNIQUE_ID UniqueId OPTIONAL)
+{
+    NTSTATUS Status;
+
+    /* Allocate a big enough buffer to contain the symbolic link */
+    DriveLetter->MaximumLength = sizeof(DosDevices.Buffer) + 3 * sizeof(WCHAR);
+    DriveLetter->Buffer = AllocatePool(sizeof(DosDevices.Buffer) + 3 * sizeof(WCHAR));
+    if (!DriveLetter->Buffer)
+    {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* Copy prefix */
+    RtlCopyUnicodeString(DriveLetter, &DosDevices);
+
+    /* Update string to reflect real contents */
+    DriveLetter->Length = sizeof(DosDevices.Buffer) + 2 * sizeof(WCHAR);
+    DriveLetter->Buffer[(sizeof(DosDevices.Buffer) + 2 * sizeof(WCHAR)) / sizeof (WCHAR)] = UNICODE_NULL;
+    DriveLetter->Buffer[(sizeof(DosDevices.Buffer) + sizeof(WCHAR)) / sizeof (WCHAR)] = L':';
+
+    /* If caller wants a no drive entry */
+    if (Letter == (UCHAR)-1)
+    {
+        /* Then, create a no letter entry */
+        CreateNoDriveLetterEntry(UniqueId);
+        FreePool(DriveLetter->Buffer);
+        return STATUS_UNSUCCESSFUL;
+    }
+    else if (Letter)
+    {
+        /* Use the letter given by the caller */
+        DriveLetter->Buffer[sizeof(DosDevices.Buffer) / sizeof(WCHAR)] = (WCHAR)Letter;
+        Status = GlobalCreateSymbolicLink(DriveLetter, DeviceName);
+        if (NT_SUCCESS(Status))
+        {
+            return Status;
+        }
+    }
+
+    /* If caller didn't provide a letter, let's find one for him.
+     * If device is a floppy, start with letter A
+     */
+    if (RtlPrefixUnicodeString(&DeviceFloppy, DeviceName, TRUE))
+    {
+        Letter = 'A';
+    }
+    else
+    {
+        /* Otherwise, if device is a cd rom, then, start with D.
+         * Finally, if a disk, use C
+         */
+        Letter = RtlPrefixUnicodeString(&DeviceCdRom, DeviceName, TRUE) + 'C';
+    }
+
+    /* Try to affect a letter (up to Z, ofc) until it's possible */
+    for (; Letter <= 'Z'; Letter++)
+    {
+        DriveLetter->Buffer[sizeof(DosDevices.Buffer) / sizeof(WCHAR)] = (WCHAR)Letter;
+        Status = GlobalCreateSymbolicLink(DriveLetter, DeviceName);
+        if (NT_SUCCESS(Status))
+        {
+            return Status;
+        }
+    }
+
+    /* We failed to allocate a letter */
+    FreePool(DriveLetter->Buffer);
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+QueryDeviceInformation(IN PUNICODE_STRING SymbolicName,
+                       OUT PUNICODE_STRING DeviceName OPTIONAL,
+                       OUT PMOUNTDEV_UNIQUE_ID * UniqueId OPTIONAL,
+                       OUT PBOOLEAN Removable OPTIONAL,
+                       OUT PBOOLEAN GptDriveLetter OPTIONAL,
+                       OUT PBOOLEAN HasGuid OPTIONAL,
+                       IN OUT LPGUID StableGuid OPTIONAL,
+                       OUT PBOOLEAN Valid OPTIONAL)
+{
+    PIRP Irp;
+    USHORT Size;
+    KEVENT Event;
+    NTSTATUS Status;
+    BOOLEAN IsRemovable;
+    PMOUNTDEV_NAME Name;
+    PMOUNTDEV_UNIQUE_ID Id;
+    PFILE_OBJECT FileObject;
+    PIO_STACK_LOCATION Stack;
+    PDEVICE_OBJECT DeviceObject;
+    IO_STATUS_BLOCK IoStatusBlock;
+    PARTITION_INFORMATION_EX PartitionInfo;
+    STORAGE_DEVICE_NUMBER StorageDeviceNumber;
+    VOLUME_GET_GPT_ATTRIBUTES_INFORMATION GptAttributes;
+
+    /* Get device associated with the symbolic name */
+    Status = IoGetDeviceObjectPointer(SymbolicName,
+                                      FILE_READ_ATTRIBUTES,
+                                      &FileObject,
+                                      &DeviceObject);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    /* The associate FO can't have a file name */
+    if (FileObject->FileName.Length)
+    {
+        ObfDereferenceObject(FileObject);
+        return STATUS_OBJECT_NAME_NOT_FOUND;
+    }
+
+    /* Check if it's removable & return to the user (if asked to) */
+    IsRemovable = (FileObject->DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA);
+    if (Removable)
+    {
+        *Removable = IsRemovable;
+    }
+
+    /* Get the attached device */
+    DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
+
+    /* If we've been asked for a GPT drive letter */
+    if (GptDriveLetter)
+    {
+        /* Consider it has one */
+        *GptDriveLetter = TRUE;
+
+        if (!IsRemovable)
+        {
+            /* Query the GPT attributes */
+            KeInitializeEvent(&Event, NotificationEvent, FALSE);
+            Irp = IoBuildDeviceIoControlRequest(IOCTL_VOLUME_GET_GPT_ATTRIBUTES,
+                                                DeviceObject,
+                                                NULL,
+                                                0,
+                                                &GptAttributes,
+                                                sizeof(GptAttributes),
+                                                FALSE,
+                                                &Event,
+                                                &IoStatusBlock);
+            if (!Irp)
+            {
+                ObfDereferenceObject(DeviceObject);
+                ObfDereferenceObject(FileObject);
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+
+            Status = IofCallDriver(DeviceObject, Irp);
+            if (Status == STATUS_PENDING)
+            {
+                KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+                Status =  IoStatusBlock.Status;
+            }
+
+            /* In case of failure, don't fail, that's no vital */
+            if (!NT_SUCCESS(Status))
+            {
+                Status = STATUS_SUCCESS;
+            }
+            /* Check if it has a drive letter */
+            else if (!(GptAttributes.GptAttributes &
+                       GPT_BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER))
+            {
+                *GptDriveLetter = FALSE;
+            }
+        }
+    }
+
+    /* If caller wants to know if there's valid contents */
+    if (Valid)
+    {
+        /* Suppose it's not OK */
+        *Valid = FALSE;
+
+        if (!IsRemovable)
+        {
+            /* Query partitions information */
+            KeInitializeEvent(&Event, NotificationEvent, FALSE);
+            Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX,
+                                                DeviceObject,
+                                                NULL,
+                                                0,
+                                                &PartitionInfo,
+                                                sizeof(PartitionInfo),
+                                                FALSE,
+                                                &Event,
+                                                &IoStatusBlock);
+            if (!Irp)
+            {
+                ObfDereferenceObject(DeviceObject);
+                ObfDereferenceObject(FileObject);
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+
+            Status = IofCallDriver(DeviceObject, Irp);
+            if (Status == STATUS_PENDING)
+            {
+                KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+                Status =  IoStatusBlock.Status;
+            }
+
+            /* Once again here, failure isn't major */
+            if (!NT_SUCCESS(Status))
+            {
+                Status = STATUS_SUCCESS;
+            }
+            /* Verify we know something in */
+            else if (PartitionInfo.PartitionStyle == PARTITION_STYLE_MBR &&
+                     IsRecognizedPartition(PartitionInfo.Mbr.PartitionType))
+            {
+                *Valid = TRUE;
+            }
+
+            /* It looks correct, ensure it is & query device number */
+            if (*Valid)
+            {
+                KeInitializeEvent(&Event, NotificationEvent, FALSE);
+                Irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_DEVICE_NUMBER,
+                                                    DeviceObject,
+                                                    NULL,
+                                                    0,
+                                                    &StorageDeviceNumber,
+                                                    sizeof(StorageDeviceNumber),
+                                                    FALSE,
+                                                    &Event,
+                                                    &IoStatusBlock);
+                if (!Irp)
+                {
+                    ObfDereferenceObject(DeviceObject);
+                    ObfDereferenceObject(FileObject);
+                    return STATUS_INSUFFICIENT_RESOURCES;
+                }
+
+                Status = IofCallDriver(DeviceObject, Irp);
+                if (Status == STATUS_PENDING)
+                {
+                    KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+                    Status =  IoStatusBlock.Status;
+                }
+
+                if (!NT_SUCCESS(Status))
+                {
+                    Status = STATUS_SUCCESS;
+                }
+                else
+                {
+                    *Valid = FALSE;
+                }
+            }
+        }
+    }
+
+    /* If caller needs device name */
+    if (DeviceName)
+    {
+        /* Allocate a buffer just to request length */
+        Name = AllocatePool(sizeof(MOUNTDEV_NAME));
+        if (!Name)
+        {
+            ObfDereferenceObject(DeviceObject);
+            ObfDereferenceObject(FileObject);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        /* Query device name */
+        KeInitializeEvent(&Event, NotificationEvent, FALSE);
+        Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
+                                            DeviceObject,
+                                            NULL,
+                                            0,
+                                            Name,
+                                            sizeof(MOUNTDEV_NAME),
+                                            FALSE,
+                                            &Event,
+                                            &IoStatusBlock);
+        if (!Irp)
+        {
+            FreePool(Name);
+            ObDereferenceObject(DeviceObject);
+            ObDereferenceObject(FileObject);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        Stack = IoGetNextIrpStackLocation(Irp);
+        Stack->FileObject = FileObject;
+
+        Status = IofCallDriver(DeviceObject, Irp);
+        if (Status == STATUS_PENDING)
+        {
+            KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+            Status = IoStatusBlock.Status;
+        }
+
+        /* Now, we've got the correct length */
+        if (Status == STATUS_BUFFER_OVERFLOW)
+        {
+            Size = Name->NameLength + sizeof(MOUNTDEV_NAME);
+
+            FreePool(Name);
+
+            /* Allocate proper size */
+            Name = AllocatePool(Size);
+            if (!Name)
+            {
+                ObfDereferenceObject(DeviceObject);
+                ObfDereferenceObject(FileObject);
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+
+            /* And query name (for real that time) */
+            KeInitializeEvent(&Event, NotificationEvent, FALSE);
+            Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
+                                                DeviceObject,
+                                                NULL,
+                                                0,
+                                                Name,
+                                                Size,
+                                                FALSE,
+                                                &Event,
+                                                &IoStatusBlock);
+            if (!Irp)
+            {
+                FreePool(Name);
+                ObDereferenceObject(DeviceObject);
+                ObDereferenceObject(FileObject);
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+
+            Stack = IoGetNextIrpStackLocation(Irp);
+            Stack->FileObject = FileObject;
+
+            Status = IofCallDriver(DeviceObject, Irp);
+            if (Status == STATUS_PENDING)
+            {
+                KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+                Status = IoStatusBlock.Status;
+            }
+        }
+
+        /* Here we can't fail and assume default value */
+        if (!NT_SUCCESS(Status))
+        {
+            FreePool(Name);
+            ObDereferenceObject(DeviceObject);
+            ObDereferenceObject(FileObject);
+            return Status;
+        }
+
+        /* Copy back found name to the caller */
+        DeviceName->Length = Name->NameLength;
+        DeviceName->MaximumLength = Name->NameLength + sizeof(WCHAR);
+        DeviceName->Buffer = AllocatePool(DeviceName->MaximumLength);
+        if (!DeviceName->Buffer)
+        {
+            FreePool(Name);
+            ObDereferenceObject(DeviceObject);
+            ObDereferenceObject(FileObject);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        RtlCopyMemory(DeviceName->Buffer, Name->Name, Name->NameLength);
+        DeviceName->Buffer[Name->NameLength / sizeof(WCHAR)] = UNICODE_NULL;
+        FreePool(Name);
+    }
+
+    /* If caller wants device unique ID */
+    if (UniqueId)
+    {
+        /* Prepare buffer to probe length */
+        Id = AllocatePool(sizeof(MOUNTDEV_UNIQUE_ID));
+        if (!Id)
+        {
+            ObDereferenceObject(DeviceObject);
+            ObDereferenceObject(FileObject);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        /* Query unique ID length */
+        KeInitializeEvent(&Event, NotificationEvent, FALSE);
+        Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_UNIQUE_ID,
+                                            DeviceObject,
+                                            NULL,
+                                            0,
+                                            Id,
+                                            sizeof(MOUNTDEV_UNIQUE_ID),
+                                            FALSE,
+                                            &Event,
+                                            &IoStatusBlock);
+        if (!Irp)
+        {
+            FreePool(Id);
+            ObfDereferenceObject(DeviceObject);
+            ObfDereferenceObject(FileObject);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        Stack = IoGetNextIrpStackLocation(Irp);
+        Stack->FileObject = FileObject;
+
+        Status = IofCallDriver(DeviceObject, Irp);
+        if (Status == STATUS_PENDING)
+        {
+            KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+            Status = IoStatusBlock.Status;
+        }
+
+        /* Retry with appropriate length */
+        if (Status == STATUS_BUFFER_OVERFLOW)
+        {
+            Size = Id->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID);
+
+            FreePool(Id);
+
+            /* Allocate the correct buffer */
+            Id = AllocatePool(Size);
+            if (!Id)
+            {
+                ObfDereferenceObject(DeviceObject);
+                ObfDereferenceObject(FileObject);
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+
+            /* Query unique ID */
+            KeInitializeEvent(&Event, NotificationEvent, FALSE);
+            Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
+                                                DeviceObject,
+                                                NULL,
+                                                0,
+                                                Id,
+                                                Size,
+                                                FALSE,
+                                                &Event,
+                                                &IoStatusBlock);
+            if (!Irp)
+            {
+                FreePool(Id);
+                ObDereferenceObject(DeviceObject);
+                ObDereferenceObject(FileObject);
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+
+            Stack = IoGetNextIrpStackLocation(Irp);
+            Stack->FileObject = FileObject;
+
+            Status = IofCallDriver(DeviceObject, Irp);
+            if (Status == STATUS_PENDING)
+            {
+                KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+                Status = IoStatusBlock.Status;
+            }
+        }
+
+        /* Hands back unique ID */
+        if (NT_SUCCESS(Status))
+        {
+            *UniqueId = Id;
+        }
+        else
+        {
+            /* In case of failure, also free the rest */
+            FreePool(Id);
+            if (DeviceName->Length)
+            {
+                FreePool(DeviceName->Buffer);
+            }
+
+            ObDereferenceObject(DeviceObject);
+            ObDereferenceObject(FileObject);
+
+            return Status;
+        }
+    }
+
+    /* If user wants to know about GUID */
+    if (HasGuid)
+    {
+        /* Query device stable GUID */
+        KeInitializeEvent(&Event, NotificationEvent, FALSE);
+        Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_STABLE_GUID,
+                                            DeviceObject,
+                                            NULL,
+                                            0,
+                                            StableGuid,
+                                            sizeof(GUID),
+                                            FALSE,
+                                            &Event,
+                                            &IoStatusBlock);
+        if (!Irp)
+        {
+            ObfDereferenceObject(DeviceObject);
+            ObfDereferenceObject(FileObject);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        Stack = IoGetNextIrpStackLocation(Irp);
+        Stack->FileObject = FileObject;
+
+        Status = IofCallDriver(DeviceObject, Irp);
+        if (Status == STATUS_PENDING)
+        {
+            KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+            Status = IoStatusBlock.Status;
+        }
+
+        *HasGuid = NT_SUCCESS(Status);
+    }
+
+    ObfDereferenceObject(DeviceObject);
+    ObfDereferenceObject(FileObject);
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+FindDeviceInfo(IN PDEVICE_EXTENSION DeviceExtension,
+               IN PUNICODE_STRING SymbolicName,
+               IN BOOLEAN DeviceNameGiven,
+               OUT PDEVICE_INFORMATION * DeviceInformation)
+{
+    NTSTATUS Status;
+    PLIST_ENTRY NextEntry;
+    UNICODE_STRING DeviceName;
+    PDEVICE_INFORMATION DeviceInfo;
+
+    /* If a device name was given, use it */
+    if (DeviceNameGiven)
+    {
+        DeviceName.Length = SymbolicName->Length;
+        DeviceName.Buffer = SymbolicName->Buffer;
+    }
+    else
+    {
+        /* Otherwise, query it */
+        Status = QueryDeviceInformation(SymbolicName,
+                                        &DeviceName,
+                                        NULL, NULL,
+                                        NULL, NULL,
+                                        NULL, NULL);
+        if (!NT_SUCCESS(Status))
+        {
+            return Status;
+        }
+    }
+
+    /* Look for device information matching devive */
+    for (NextEntry = DeviceExtension->DeviceListHead.Flink;
+         NextEntry != &(DeviceExtension->DeviceListHead);
+         NextEntry = NextEntry->Flink)
+    {
+        DeviceInfo = CONTAINING_RECORD(NextEntry,
+                                       DEVICE_INFORMATION,
+                                       DeviceListEntry);
+
+        if (RtlEqualUnicodeString(&DeviceName, &(DeviceInfo->DeviceName), TRUE))
+        {
+            break;
+        }
+    }
+
+    /* Release our buffer if required */
+    if (!DeviceNameGiven)
+    {
+        FreePool(DeviceName.Buffer);
+    }
+
+    /* Return found intormation */
+    if (NextEntry == &(DeviceExtension->DeviceListHead))
+    {
+        return STATUS_OBJECT_NAME_NOT_FOUND;
+    }
+
+    *DeviceInformation = DeviceInfo;
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+VOID
+MountMgrFreeDeadDeviceInfo(IN PDEVICE_INFORMATION DeviceInformation)
+{
+    FreePool(DeviceInformation->SymbolicName.Buffer);
+    FreePool(DeviceInformation);
+}
+
+/*
+ * @implemented
+ */
+VOID
+MountMgrFreeMountedDeviceInfo(IN PDEVICE_INFORMATION DeviceInformation)
+{
+    PLIST_ENTRY NextEntry;
+    PSYMLINK_INFORMATION SymLink;
+    PUNIQUE_ID_REPLICATE UniqueId;
+    PASSOCIATED_DEVICE_ENTRY AssociatedDevice;
+
+    /* Purge symbolic links list */
+    while (!IsListEmpty(&(DeviceInformation->SymbolicLinksListHead)))
+    {
+        NextEntry = RemoveHeadList(&(DeviceInformation->SymbolicLinksListHead));
+        SymLink = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
+
+        GlobalDeleteSymbolicLink(&(SymLink->Name));
+        FreePool(SymLink->Name.Buffer);
+    }
+
+    /* Purge replicated unique IDs list */
+    while (!IsListEmpty(&(DeviceInformation->ReplicatedUniqueIdsListHead)))
+    {
+        NextEntry = RemoveHeadList(&(DeviceInformation->ReplicatedUniqueIdsListHead));
+        UniqueId = CONTAINING_RECORD(NextEntry, UNIQUE_ID_REPLICATE, ReplicatedUniqueIdsListEntry);
+
+        FreePool(UniqueId->UniqueId);
+        FreePool(UniqueId);
+    }
+
+    while (!IsListEmpty(&(DeviceInformation->AssociatedDevicesHead)))
+    {
+        NextEntry = RemoveHeadList(&(DeviceInformation->AssociatedDevicesHead));
+        AssociatedDevice = CONTAINING_RECORD(NextEntry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
+
+        FreePool(AssociatedDevice->String.Buffer);
+        FreePool(AssociatedDevice);
+    }
+
+    /* Free the rest of the buffers */
+    FreePool(DeviceInformation->SymbolicName.Buffer);
+    if (DeviceInformation->KeepLinks)
+    {
+        FreePool(DeviceInformation->UniqueId);
+    }
+    FreePool(DeviceInformation->DeviceName.Buffer);
+
+    /* Finally, stop waiting for notifications for this device */
+    if (DeviceInformation->TargetDeviceNotificationEntry)
+    {
+        IoUnregisterPlugPlayNotification(DeviceInformation->TargetDeviceNotificationEntry);
+    }
+}
+
+/*
+ * @implemented
+ */
+VOID
+MountMgrFreeSavedLink(IN PSAVED_LINK_INFORMATION SavedLinkInformation)
+{
+    PLIST_ENTRY NextEntry;
+    PSYMLINK_INFORMATION SymlinkInformation;
+
+    /* For all the saved links */
+    while (!IsListEmpty(&(SavedLinkInformation->SymbolicLinksListHead)))
+    {
+        NextEntry = RemoveHeadList(&(SavedLinkInformation->SymbolicLinksListHead));
+        SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
+
+        /* Remove from system & free */
+        GlobalDeleteSymbolicLink(&(SymlinkInformation->Name));
+        FreePool(SymlinkInformation->Name.Buffer);
+        FreePool(SymlinkInformation);
+    }
+
+    /* And free unique ID & entry */
+    FreePool(SavedLinkInformation->UniqueId);
+    FreePool(SavedLinkInformation);
+}
+
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+MountMgrUnload(IN struct _DRIVER_OBJECT *DriverObject)
+{
+    PLIST_ENTRY NextEntry;
+    PUNIQUE_ID_WORK_ITEM WorkItem;
+    PDEVICE_EXTENSION DeviceExtension;
+    PDEVICE_INFORMATION DeviceInformation;
+    PSAVED_LINK_INFORMATION SavedLinkInformation;
+
+    /* Don't get notification any longer */
+    IoUnregisterShutdownNotification(gdeviceObject);
+
+    /* Free registry buffer */
+    DeviceExtension = gdeviceObject->DeviceExtension;
+    if (DeviceExtension->RegistryPath.Buffer)
+    {
+        FreePool(DeviceExtension->RegistryPath.Buffer);
+        DeviceExtension->RegistryPath.Buffer = NULL;
+    }
+
+    InterlockedExchange(&Unloading, TRUE);
+
+    KeInitializeEvent(&UnloadEvent, NotificationEvent, FALSE);
+
+    /* Wait for workers to finish */
+    if (InterlockedIncrement(&DeviceExtension->WorkerReferences))
+    {
+        KeReleaseSemaphore(&(DeviceExtension->WorkerSemaphore),
+                           IO_NO_INCREMENT, 1, FALSE);
+
+        KeWaitForSingleObject(&UnloadEvent, Executive, KernelMode, FALSE, NULL);
+    }
+    else
+    {
+        InterlockedDecrement(&(DeviceExtension->WorkerReferences));
+    }
+
+    /* Don't get any notification any longer² */
+    IoUnregisterPlugPlayNotification(DeviceExtension->NotificationEntry);
+
+    /* Acquire the driver exclusively */
+    KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode,
+                          FALSE, NULL);
+
+    /* Clear offline devices list */
+    while (!IsListEmpty(&(DeviceExtension->OfflineDeviceListHead)))
+    {
+        NextEntry = RemoveHeadList(&(DeviceExtension->OfflineDeviceListHead));
+        DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
+        MountMgrFreeDeadDeviceInfo(DeviceInformation);
+    }
+
+    /* Clear saved links list */
+    while (!IsListEmpty(&(DeviceExtension->SavedLinksListHead)))
+    {
+        NextEntry = RemoveHeadList(&(DeviceExtension->SavedLinksListHead));
+        SavedLinkInformation = CONTAINING_RECORD(NextEntry, SAVED_LINK_INFORMATION, SavedLinksListEntry);
+        MountMgrFreeSavedLink(SavedLinkInformation);
+    }
+
+    /* Clear workers list */
+    while (!IsListEmpty(&(DeviceExtension->UniqueIdWorkerItemListHead)))
+    {
+        NextEntry = RemoveHeadList(&(DeviceExtension->UniqueIdWorkerItemListHead));
+        WorkItem = CONTAINING_RECORD(NextEntry, UNIQUE_ID_WORK_ITEM, UniqueIdWorkerItemListEntry);
+
+        KeResetEvent(&UnloadEvent);
+        WorkItem->Event = &UnloadEvent;
+
+        KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT,
+                           1, FALSE);
+
+        IoCancelIrp(WorkItem->Irp);
+        KeWaitForSingleObject(&UnloadEvent, Executive, KernelMode, FALSE, NULL);
+
+        IoFreeIrp(WorkItem->Irp);
+        FreePool(WorkItem->DeviceName.Buffer);
+        FreePool(WorkItem->IrpBuffer);
+        FreePool(WorkItem);
+
+        KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode,
+                              FALSE, NULL);
+    }
+
+    /* If we have drive letter data, release */
+    if (DeviceExtension->DriveLetterData)
+    {
+        FreePool(DeviceExtension->DriveLetterData);
+        DeviceExtension->DriveLetterData = NULL;
+    }
+
+    /* Release driver & quit */
+    KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
+
+    GlobalDeleteSymbolicLink(&DosDevicesMount);
+    IoDeleteDevice(gdeviceObject);
+}
+
+/*
+ * @implemented
+ */
+ULONG
+MountmgrReadNoAutoMount(IN PUNICODE_STRING RegistryPath)
+{
+    NTSTATUS Status;
+    ULONG Result, Default = 0;
+    RTL_QUERY_REGISTRY_TABLE QueryTable[2];
+
+    RtlZeroMemory(QueryTable, sizeof(QueryTable));
+
+    /* Simply read data from register */
+    QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
+    QueryTable[0].Name = L"NoAutoMount";
+    QueryTable[0].EntryContext = &Result;
+    QueryTable[0].DefaultType = REG_NONE;
+    QueryTable[0].DefaultData = &Default;
+    QueryTable[0].DefaultLength = sizeof(ULONG);
+
+    Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
+                                    RegistryPath->Buffer,
+                                    QueryTable,
+                                    NULL,
+                                    NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        return Default;
+    }
+
+    return Result;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+MountMgrMountedDeviceArrival(IN PDEVICE_EXTENSION DeviceExtension,
+                             IN PUNICODE_STRING SymbolicName,
+                             IN BOOLEAN FromVolume)
+{
+    WCHAR Letter;
+    GUID StableGuid;
+    HANDLE LinkHandle;
+    ULONG SymLinkCount, i;
+    PLIST_ENTRY NextEntry;
+    PUNICODE_STRING SymLinks;
+    NTSTATUS Status, IntStatus;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    PSYMLINK_INFORMATION SymlinkInformation;
+    PMOUNTDEV_UNIQUE_ID UniqueId, NewUniqueId;
+    PSAVED_LINK_INFORMATION SavedLinkInformation;
+    PDEVICE_INFORMATION DeviceInformation, CurrentDevice;
+    WCHAR CSymLinkBuffer[MAX_PATH], LinkTargetBuffer[MAX_PATH];
+    UNICODE_STRING TargetDeviceName, SuggestedLinkName, DeviceName, VolumeName, DriveLetter, LinkTarget, CSymLink;
+    BOOLEAN HasGuid, HasGptDriveLetter, Valid, UseOnlyIfThereAreNoOtherLinks, IsDrvLetter, IsOff, IsVolumeName, LinkError;
+
+    /* New device = new structure to represent it */
+    DeviceInformation = AllocatePool(sizeof(DEVICE_INFORMATION));
+    if (!DeviceInformation)
+    {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* Initialise device structure */
+    RtlZeroMemory(DeviceInformation, sizeof(DEVICE_INFORMATION));
+    InitializeListHead(&(DeviceInformation->SymbolicLinksListHead));
+    InitializeListHead(&(DeviceInformation->ReplicatedUniqueIdsListHead));
+    InitializeListHead(&(DeviceInformation->AssociatedDevicesHead));
+    DeviceInformation->SymbolicName.Length = SymbolicName->Length;
+    DeviceInformation->SymbolicName.MaximumLength = SymbolicName->Length + sizeof(UNICODE_NULL);
+    DeviceInformation->SymbolicName.Buffer = AllocatePool(DeviceInformation->SymbolicName.MaximumLength);
+    if (!DeviceInformation->SymbolicName.Buffer)
+    {
+        FreePool(DeviceInformation);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* Copy symbolic name */
+    RtlCopyMemory(DeviceInformation->SymbolicName.Buffer, SymbolicName->Buffer, SymbolicName->Length);
+    DeviceInformation->SymbolicName.Buffer[DeviceInformation->SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
+    DeviceInformation->Volume = FromVolume;
+    DeviceInformation->DeviceExtension = DeviceExtension;
+
+    /* Query as much data as possible about device */
+    Status = QueryDeviceInformation(SymbolicName,
+                                    &TargetDeviceName,
+                                    &UniqueId,
+                                    &(DeviceInformation->Removable),
+                                    &HasGptDriveLetter,
+                                    &HasGuid,
+                                    &StableGuid,
+                                    &Valid);
+    if (!NT_SUCCESS(Status))
+    {
+        KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
+
+        for (NextEntry = DeviceExtension->OfflineDeviceListHead.Flink;
+             NextEntry != &(DeviceExtension->OfflineDeviceListHead);
+             NextEntry = NextEntry->Flink)
+        {
+            CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
+
+            if (RtlEqualUnicodeString(&(DeviceInformation->SymbolicName), &(CurrentDevice->SymbolicName), TRUE))
+            {
+                break;            
+            }
+        }
+
+        if (NextEntry != &(DeviceExtension->OfflineDeviceListHead))
+        {
+            MountMgrFreeDeadDeviceInfo(DeviceInformation);
+        }
+        else
+        {
+            InsertTailList(&(DeviceExtension->OfflineDeviceListHead), &(DeviceInformation->DeviceListEntry));
+        }
+
+        KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
+
+        return Status;
+    }
+
+    /* Save gathered data */
+    DeviceInformation->UniqueId = UniqueId;
+    DeviceInformation->DeviceName = TargetDeviceName;
+    DeviceInformation->KeepLinks = FALSE;
+
+    /* If we found system partition, mark it */
+    if (DeviceExtension->DriveLetterData && UniqueId->UniqueIdLength == DeviceExtension->DriveLetterData->UniqueIdLength)
+    {
+        if (RtlCompareMemory(UniqueId->UniqueId, DeviceExtension->DriveLetterData->UniqueId, UniqueId->UniqueIdLength)
+            == UniqueId->UniqueIdLength)
+        {
+            IoSetSystemPartition(&TargetDeviceName);
+        }
+    }
+
+    /* Check suggested link name */
+    Status = QuerySuggestedLinkName(&(DeviceInformation->SymbolicName),
+                                    &SuggestedLinkName,
+                                    &UseOnlyIfThereAreNoOtherLinks);
+    if (!NT_SUCCESS(Status))
+    {
+        SuggestedLinkName.Buffer = NULL;
+    }
+
+    /* If it's OK, set it and save its letter (if any) */
+    if (SuggestedLinkName.Buffer && IsDriveLetter(&SuggestedLinkName))
+    {
+        DeviceInformation->SuggestedDriveLetter = SuggestedLinkName.Buffer[LETTER_POSITION];
+    }
+
+    /* Acquire driver exclusively */
+    KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
+
+    /* Check if we already have device in to prevent double registration */
+    for (NextEntry = DeviceExtension->DeviceListHead.Flink;
+         NextEntry != &(DeviceExtension->DeviceListHead);
+         NextEntry = NextEntry->Flink)
+    {
+        CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
+
+        if (RtlEqualUnicodeString(&(DeviceInformation->DeviceName), &TargetDeviceName, TRUE))
+        {
+            break;            
+        }
+    }
+
+    /* If we found it, clear ours, and return success, all correct */
+    if (NextEntry != &(DeviceExtension->DeviceListHead))
+    {
+        if (SuggestedLinkName.Buffer)
+        {
+            FreePool(SuggestedLinkName.Buffer);
+        }
+
+        FreePool(UniqueId);
+        FreePool(TargetDeviceName.Buffer);
+        FreePool(DeviceInformation->DeviceName.Buffer);
+        FreePool(DeviceInformation);
+
+        KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
+
+        return STATUS_SUCCESS;
+    }
+
+    /* Check if there are symlinks associated with our device in registry */
+    Status = QuerySymbolicLinkNamesFromStorage(DeviceExtension,
+                                               DeviceInformation,
+                                               (SuggestedLinkName.Buffer) ? &SuggestedLinkName : NULL,
+                                               UseOnlyIfThereAreNoOtherLinks,
+                                               &SymLinks,
+                                               &SymLinkCount,
+                                               HasGuid,
+                                               &StableGuid);
+
+    /* If our device is a CD-ROM */
+    if (RtlPrefixUnicodeString(&DeviceCdRom, &TargetDeviceName, TRUE))
+    {
+        LinkTarget.Length = 0;
+        LinkTarget.MaximumLength = sizeof(LinkTargetBuffer);
+        LinkTarget.Buffer = LinkTargetBuffer;
+
+        RtlCopyMemory(CSymLinkBuffer, Cunc, sizeof(Cunc));
+        RtlInitUnicodeString(&CSymLink, CSymLinkBuffer);
+
+        /* Start checking all letters that could have been associated */
+        for (Letter = L'D'; Letter <= L'Z'; Letter++)
+        {
+            CSymLink.Buffer[LETTER_POSITION] = Letter;
+
+            InitializeObjectAttributes(&ObjectAttributes,
+                                       &CSymLink,
+                                       OBJ_CASE_INSENSITIVE,
+                                       NULL,
+                                       NULL);
+
+            /* Try to open the associated symlink */
+            Status = ZwOpenSymbolicLinkObject(&LinkHandle, SYMBOLIC_LINK_QUERY, &ObjectAttributes);
+            if (!NT_SUCCESS(Status))
+            {
+                continue;
+            }
+
+            /* And query its target */
+            Status = ZwQuerySymbolicLinkObject(LinkHandle, &LinkTarget, NULL);
+            ZwClose(LinkHandle);
+
+            if (!NT_SUCCESS(Status))
+            {
+                continue;
+            }
+
+            IntStatus = STATUS_UNSUCCESSFUL;
+            if (!RtlEqualUnicodeString(&LinkTarget, &DeviceInformation->DeviceName, FALSE))
+            {
+                continue;
+            }
+
+            /* This link is matching our device, whereas it's not supposed to have any
+             * symlink associated.
+             * Delete it
+             */
+            if (!SymLinkCount)
+            {
+                IoDeleteSymbolicLink(&CSymLink);
+                continue;
+            }
+
+            /* Now, for all the symlinks, check for ours */
+            for (i = 0; i < SymLinkCount; i++)
+            {
+                if (IsDriveLetter(&(SymLinks[i])))
+                {
+                    /* If it exists, that's correct */
+                    if (SymLinks[i].Buffer[LETTER_POSITION] == Letter)
+                    {
+                        IntStatus = STATUS_SUCCESS;
+                    }
+                }
+            }
+
+            /* Useless link, delete it */
+            if (IntStatus == STATUS_UNSUCCESSFUL)
+            {
+                IoDeleteSymbolicLink(&CSymLink);
+            }
+        }
+    }
+
+    /* Suggested name is no longer required */
+    if (SuggestedLinkName.Buffer)
+    {
+        FreePool(SuggestedLinkName.Buffer);
+    }
+
+    /* If if failed, ensure we don't take symlinks into account */
+    if (!NT_SUCCESS(Status))
+    {
+        SymLinks = NULL;
+        SymLinkCount = 0;
+    }
+
+    /* Now we queried them, remove the symlinks */
+    SavedLinkInformation = RemoveSavedLinks(DeviceExtension, UniqueId);
+
+    IsDrvLetter = FALSE;
+    IsOff = FALSE;
+    IsVolumeName = FALSE;
+    /* For all the symlinks */
+    for (i = 0; i < SymLinkCount; i++)
+    {
+        /* Check if our device is a volume */
+        if (MOUNTMGR_IS_VOLUME_NAME(&(SymLinks[i])))
+        {
+            IsVolumeName = TRUE;
+        }
+        /* If it has a drive letter */
+        else if (IsDriveLetter(&(SymLinks[i])))
+        {
+            if (IsDrvLetter)
+            {
+                DeleteFromLocalDatabase(&(SymLinks[i]), UniqueId);
+                continue;
+            }
+            else
+            {
+                IsDrvLetter = TRUE;
+            }
+        }
+
+        /* And recreate the symlink to our device */
+        Status = GlobalCreateSymbolicLink(&(SymLinks[i]), &TargetDeviceName);
+        if (!NT_SUCCESS(Status))
+        {
+            LinkError = TRUE;
+
+            if ((SavedLinkInformation && !RedirectSavedLink(SavedLinkInformation, &(SymLinks[i]), &TargetDeviceName)) ||
+                !SavedLinkInformation)
+            {
+                Status = QueryDeviceInformation(&(SymLinks[i]), &DeviceName, NULL, NULL, NULL, NULL, NULL, NULL);
+                if (NT_SUCCESS(Status))
+                {
+                    LinkError = RtlEqualUnicodeString(&TargetDeviceName, &DeviceName, TRUE);
+                    FreePool(DeviceName.Buffer);
+                }
+
+                if (!LinkError)
+                {
+                    if (IsDriveLetter(&(SymLinks[i])))
+                    {
+                        IsDrvLetter = FALSE;
+                        DeleteFromLocalDatabase(&(SymLinks[i]), UniqueId);
+                    }
+
+                    FreePool(SymLinks[i].Buffer);
+                    continue;
+                }
+            }
+        }
+
+        /* Check if was offline */
+        if (IsOffline(&(SymLinks[i])))
+        {
+            IsOff = TRUE;
+        }
+
+        /* Finally, associate this symlink with the device */
+        SymlinkInformation = AllocatePool(sizeof(SYMLINK_INFORMATION));
+        if (!SymlinkInformation)
+        {
+            GlobalDeleteSymbolicLink(&(SymLinks[i]));
+            FreePool(SymLinks[i].Buffer);
+            continue;
+        }
+
+        SymlinkInformation->Name = SymLinks[i];
+        SymlinkInformation->Online = TRUE;
+
+        InsertTailList(&(DeviceInformation->SymbolicLinksListHead),
+                       &(SymlinkInformation->SymbolicLinksListEntry));
+    }
+
+    /* Now, for all the recreated symlinks, notify their recreation */
+    for (NextEntry = DeviceInformation->SymbolicLinksListHead.Flink;
+         NextEntry != &(DeviceInformation->SymbolicLinksListHead);
+         NextEntry = NextEntry->Flink)
+    {
+        SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
+
+        SendLinkCreated(&(SymlinkInformation->Name));
+    }
+
+    /* If we had saved links, it's time to free them */
+    if (SavedLinkInformation)
+    {
+        MountMgrFreeSavedLink(SavedLinkInformation);
+    }
+
+    /* If our device doesn't have a volume name */
+    if (!IsVolumeName)
+    {
+        /* It's time to create one */
+        Status = CreateNewVolumeName(&VolumeName, NULL);
+        if (NT_SUCCESS(Status))
+        {
+            /* Write it to global database */
+            RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
+                                  DatabasePath,
+                                  VolumeName.Buffer,
+                                  REG_BINARY,
+                                  UniqueId->UniqueId,
+                                  UniqueId->UniqueIdLength);
+
+            /* And create the symlink */
+            GlobalCreateSymbolicLink(&VolumeName, &TargetDeviceName);
+
+            SymlinkInformation = AllocatePool(sizeof(SYMLINK_INFORMATION));
+            if (!SymlinkInformation)
+            {
+                FreePool(VolumeName.Buffer);
+            }
+            /* Finally, associate it with the device and notify creation */
+            else
+            {
+                SymlinkInformation->Name = VolumeName;
+                SymlinkInformation->Online = TRUE;
+                InsertTailList(&(DeviceInformation->SymbolicLinksListHead),
+                               &(SymlinkInformation->SymbolicLinksListEntry));
+
+                SendLinkCreated(&VolumeName);
+            }
+        }
+    }
+
+    /* If we found a drive letter, then, ignore the suggested one */
+    if (IsDrvLetter)
+    {
+        DeviceInformation->SuggestedDriveLetter = 0;
+    }
+    /* Else, it's time to set up one */
+    else if (!DeviceExtension->NoAutoMount && !DeviceInformation->Removable &&
+             DeviceExtension->AutomaticDriveLetter && HasGptDriveLetter &&
+             DeviceInformation->SuggestedDriveLetter &&
+             !HasNoDriveLetterEntry(UniqueId))
+    {
+        /* Create a new drive letter */
+        Status = CreateNewDriveLetterName(&DriveLetter, &TargetDeviceName,
+                                          DeviceInformation->SuggestedDriveLetter,
+                                          NULL);
+        if (!NT_SUCCESS(Status))
+        {
+            CreateNoDriveLetterEntry(UniqueId);
+        }
+        else
+        {
+            /* Save it to global database */
+            RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
+                                  DatabasePath,
+                                  DriveLetter.Buffer,
+                                  REG_BINARY,
+                                  UniqueId->UniqueId,
+                                  UniqueId->UniqueIdLength);
+
+            /* Associate it with the device and notify creation */
+            SymlinkInformation = AllocatePool(sizeof(SYMLINK_INFORMATION));
+            if (!SymlinkInformation)
+            {
+                FreePool(DriveLetter.Buffer);
+            }
+            else
+            {
+                SymlinkInformation->Name = DriveLetter;
+                SymlinkInformation->Online = TRUE;
+                InsertTailList(&(DeviceInformation->SymbolicLinksListHead),
+                               &(SymlinkInformation->SymbolicLinksListEntry));
+
+                SendLinkCreated(&DriveLetter);
+            }
+        }
+    }
+
+    /* If required, register for notifications about the device */
+    if (!FromVolume)
+    {
+        RegisterForTargetDeviceNotification(DeviceExtension, DeviceInformation);
+    }
+
+    /* Finally, insert the device into our devices list */
+    InsertTailList(&(DeviceExtension->DeviceListHead), &(DeviceInformation->DeviceListEntry));
+
+    /* Copy device unique ID */
+    NewUniqueId = AllocatePool(UniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
+    if (NewUniqueId)
+    {
+        NewUniqueId->UniqueIdLength = UniqueId->UniqueIdLength;
+        RtlCopyMemory(NewUniqueId->UniqueId, UniqueId->UniqueId, UniqueId->UniqueIdLength);
+    }
+
+    /* If device's offline or valid, skip its notifications */
+    if (IsOff || Valid)
+    {
+        DeviceInformation->SkipNotifications = TRUE;
+    }
+
+    /* In case device is valid and is set to no automount,
+     * set it offline.
+     */
+    if (DeviceExtension->NoAutoMount || IsDrvLetter)
+    {
+        IsOff = !DeviceInformation->SkipNotifications;
+    }
+    else
+    {
+        IsOff = FALSE;
+    }
+
+    /* Finally, release the exclusive lock */
+    KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
+
+    /* If device is not offline, notify its arrival */
+    if (!IsOff)
+    {
+        SendOnlineNotification(SymbolicName);
+    }
+
+    /* If we had symlinks (from storage), free them */
+    if (SymLinks)
+    {
+        FreePool(SymLinks);
+    }
+
+    /* Notify about unique id change */
+    if (NewUniqueId)
+    {
+        IssueUniqueIdChangeNotify(DeviceExtension, SymbolicName, NewUniqueId);
+        FreePool(NewUniqueId);
+    }
+
+    /* If this drive was set to have a drive letter automatically
+     * Now it's back, local databases sync will be required
+     */
+    if (DeviceExtension->AutomaticDriveLetter)
+    {
+        KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
+
+        ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation);
+
+        NextEntry = DeviceExtension->DeviceListHead.Flink;
+        CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
+        while (CurrentDevice != DeviceInformation)
+        {
+            if (!CurrentDevice->NoDatabase)
+            {
+                ReconcileThisDatabaseWithMaster(DeviceExtension, CurrentDevice);
+            }
+
+            NextEntry = NextEntry->Flink;
+            CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
+        }
+
+        KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+VOID
+MountMgrMountedDeviceRemoval(IN PDEVICE_EXTENSION DeviceExtension,
+                             IN PUNICODE_STRING DeviceName)
+{
+    PLIST_ENTRY NextEntry, DeviceEntry;
+    PUNIQUE_ID_REPLICATE UniqueIdReplicate;
+    PSYMLINK_INFORMATION SymlinkInformation;
+    PASSOCIATED_DEVICE_ENTRY AssociatedDevice;
+    PSAVED_LINK_INFORMATION SavedLinkInformation;
+    PDEVICE_INFORMATION DeviceInformation, CurrentDevice;
+
+    /* Acquire device exclusively */
+    KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
+
+    /* Look for the leaving device */
+    for (NextEntry = DeviceExtension->DeviceListHead.Flink;
+         NextEntry != &(DeviceExtension->DeviceListHead);
+         NextEntry = NextEntry->Flink)
+    {
+        DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
+
+        if (!RtlCompareUnicodeString(&(DeviceInformation->SymbolicName), DeviceName, TRUE))
+        {
+            break;
+        }
+    }
+
+    /* If we found it */
+    if (NextEntry != &(DeviceExtension->DeviceListHead))
+    {
+        /* If it's asked to keep links, then, prepare to save them */
+        if (DeviceInformation->KeepLinks)
+        {
+            SavedLinkInformation = AllocatePool(sizeof(SAVED_LINK_INFORMATION));
+            if (!SavedLinkInformation)
+            {
+                DeviceInformation->KeepLinks = FALSE;
+            }
+        }
+
+        /* If it's possible (and asked), start to save them */
+        if (DeviceInformation->KeepLinks)
+        {
+            InsertTailList(&(DeviceExtension->SavedLinksListHead), &(SavedLinkInformation->SavedLinksListEntry));
+            InitializeListHead(&(SavedLinkInformation->SymbolicLinksListHead));
+            SavedLinkInformation->UniqueId = DeviceInformation->UniqueId;
+        }
+
+        /* For all the symlinks */
+        while (!IsListEmpty(&(DeviceInformation->SymbolicLinksListHead)))
+        {
+            NextEntry = RemoveHeadList(&(DeviceInformation->SymbolicLinksListHead));
+            SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
+
+            /* If we have to, save the link */
+            if (DeviceInformation->KeepLinks)
+            {
+                InsertTailList(&(SavedLinkInformation->SymbolicLinksListHead), &(SymlinkInformation->SymbolicLinksListEntry));
+            }
+            /* Otherwise, just release it */
+            else
+            {
+                GlobalDeleteSymbolicLink(&(SymlinkInformation->Name));
+                FreePool(SymlinkInformation->Name.Buffer);
+                FreePool(SymlinkInformation);
+            }
+        }
+
+        /* Free all the replicated unique IDs */
+        while (!IsListEmpty(&(DeviceInformation->ReplicatedUniqueIdsListHead)))
+        {
+            NextEntry = RemoveHeadList(&(DeviceInformation->ReplicatedUniqueIdsListHead));
+            UniqueIdReplicate = CONTAINING_RECORD(NextEntry, UNIQUE_ID_REPLICATE, ReplicatedUniqueIdsListEntry);
+
+
+            FreePool(UniqueIdReplicate->UniqueId);
+            FreePool(UniqueIdReplicate);
+        }
+
+        while (!IsListEmpty(&(DeviceInformation->AssociatedDevicesHead)))
+        {
+            NextEntry = RemoveHeadList(&(DeviceInformation->AssociatedDevicesHead));
+            AssociatedDevice = CONTAINING_RECORD(NextEntry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
+
+            DeviceInformation->NoDatabase = TRUE;
+            FreePool(AssociatedDevice->String.Buffer);
+            FreePool(AssociatedDevice);
+        }
+
+        /* Remove device from the device list */
+        RemoveEntryList(&(DeviceInformation->DeviceListEntry));
+
+        /* If there are still devices, check if some were associated with ours */
+        if (!IsListEmpty(&(DeviceInformation->DeviceListEntry)))
+        {
+            for (NextEntry = DeviceExtension->DeviceListHead.Flink;
+                 NextEntry != &(DeviceExtension->DeviceListHead);
+                 NextEntry = NextEntry->Flink)
+            {
+                CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
+
+                /* And then, remove them */
+                DeviceEntry = CurrentDevice->AssociatedDevicesHead.Flink;
+                while (DeviceEntry != &(CurrentDevice->AssociatedDevicesHead))
+                {
+                    AssociatedDevice = CONTAINING_RECORD(NextEntry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
+                    DeviceEntry = DeviceEntry->Flink;
+
+                    if (AssociatedDevice->DeviceInformation != DeviceInformation)
+                    {
+                        continue;
+                    }
+
+                    RemoveEntryList(&(AssociatedDevice->AssociatedDevicesEntry));
+                    FreePool(AssociatedDevice->String.Buffer);
+                    FreePool(AssociatedDevice);
+                }
+            }
+        }
+
+        /* Finally, clean up device name, symbolic name */
+        FreePool(DeviceInformation->SymbolicName.Buffer);
+        if (!DeviceInformation->KeepLinks)
+        {
+            FreePool(DeviceInformation->UniqueId);
+        }
+        FreePool(DeviceInformation->DeviceName.Buffer);
+
+        /* Unregister notifications */
+        if (DeviceInformation->TargetDeviceNotificationEntry)
+        {
+            IoUnregisterPlugPlayNotification(DeviceInformation->TargetDeviceNotificationEntry);
+        }
+
+        /*  And leave */
+        FreePool(DeviceInformation);
+    }
+    else
+    {
+        /* We didn't find device, perhaps because it was offline */
+        for (NextEntry = DeviceExtension->OfflineDeviceListHead.Flink;
+             NextEntry != &(DeviceExtension->OfflineDeviceListHead);
+             NextEntry = NextEntry->Flink)
+        {
+            DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
+
+            /* It was, remove it */
+            if (RtlCompareUnicodeString(&(DeviceInformation->SymbolicName), DeviceName, TRUE) == 0)
+            {
+                RemoveEntryList(&(DeviceInformation->DeviceListEntry));
+                MountMgrFreeDeadDeviceInfo(DeviceInformation);
+                break;
+            }
+        }
+    }
+
+    /* Releave driver */
+    KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+MountMgrMountedDeviceNotification(IN PVOID NotificationStructure,
+                                  IN PVOID Context)
+{
+    BOOLEAN OldState;
+    PDEVICE_EXTENSION DeviceExtension;
+    PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notification;
+
+    /* Notification for a device arrived */
+    /* Disable hard errors */
+    OldState = PsGetThreadHardErrorsAreDisabled(PsGetCurrentThread());
+    PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), TRUE);
+
+    DeviceExtension = Context;
+    Notification = NotificationStructure;
+
+    /* Dispatch according to the event */
+    if (IsEqualGUID(&(Notification->Event), &GUID_DEVICE_INTERFACE_ARRIVAL))
+    {
+        MountMgrMountedDeviceArrival(DeviceExtension, Notification->SymbolicLinkName, FALSE);
+    }
+    else if (IsEqualGUID(&(Notification->Event), &GUID_DEVICE_INTERFACE_REMOVAL))
+    {
+        MountMgrMountedDeviceRemoval(DeviceExtension, Notification->SymbolicLinkName);
+    }
+
+    /* Reset hard errors */
+    PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), OldState);
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+MountMgrCreateClose(IN PDEVICE_OBJECT DeviceObject,
+                    IN PIRP Irp)
+{
+    PIO_STACK_LOCATION Stack;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    Stack = IoGetCurrentIrpStackLocation(Irp);
+
+    /* Allow driver opening for communication
+     * as long as it's not taken for a directory
+     */
+    if (Stack->MajorFunction == IRP_MJ_CREATE &&
+        Stack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
+    {
+        Status = STATUS_NOT_A_DIRECTORY;
+    }
+
+    Irp->IoStatus.Status = Status;
+    Irp->IoStatus.Information = 0;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+VOID
+MountMgrCancel(IN PDEVICE_OBJECT DeviceObject,
+               IN PIRP Irp)
+{
+    RemoveEntryList(&(Irp->Tail.Overlay.ListEntry));
+
+    IoReleaseCancelSpinLock(Irp->CancelIrql);
+
+    Irp->IoStatus.Information = 0;
+    Irp->IoStatus.Status = STATUS_CANCELLED;
+    IofCompleteRequest(Irp, IO_NO_INCREMENT);
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+MountMgrCleanup(IN PDEVICE_OBJECT DeviceObject,
+                IN PIRP Irp)
+{
+    PIRP ListIrp;
+    KIRQL OldIrql;
+    PLIST_ENTRY NextEntry;
+    PFILE_OBJECT FileObject;
+    PIO_STACK_LOCATION Stack;
+    PDEVICE_EXTENSION DeviceExtension;
+
+    DeviceExtension = DeviceObject->DeviceExtension;
+    Stack = IoGetCurrentIrpStackLocation(Irp);
+    FileObject = Stack->FileObject;
+
+    IoAcquireCancelSpinLock(&OldIrql);
+
+    /* If IRP list if empty, it's OK */
+    if (IsListEmpty(&(DeviceExtension->IrpListHead)))
+    {
+        IoReleaseCancelSpinLock(OldIrql);
+
+        Irp->IoStatus.Status = STATUS_SUCCESS;
+        Irp->IoStatus.Information = 0;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+        return STATUS_SUCCESS;
+    }
+
+    /* Otherwise, cancel all the IRPs */
+    NextEntry = &(DeviceExtension->IrpListHead);
+    do
+    {
+        ListIrp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
+        if (IoGetCurrentIrpStackLocation(ListIrp)->FileObject == FileObject)
+        {
+            ListIrp->Cancel = TRUE;
+            ListIrp->CancelIrql = OldIrql;
+            ListIrp->CancelRoutine = NULL;
+            MountMgrCancel(DeviceObject, ListIrp);
+
+            IoAcquireCancelSpinLock(&OldIrql);
+        }
+
+        NextEntry = NextEntry->Flink;
+    }
+    while (NextEntry != &(DeviceExtension->IrpListHead));
+
+    IoReleaseCancelSpinLock(OldIrql);
+
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+    Irp->IoStatus.Information = 0;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+MountMgrShutdown(IN PDEVICE_OBJECT DeviceObject,
+                 IN PIRP Irp)
+{
+    PDEVICE_EXTENSION DeviceExtension;
+
+    DeviceExtension = DeviceObject->DeviceExtension;
+
+    InterlockedExchange(&Unloading, TRUE);
+
+    KeInitializeEvent(&UnloadEvent, NotificationEvent, FALSE);
+
+    /* Wait for workers */
+    if (InterlockedIncrement(&(DeviceExtension->WorkerReferences)))
+    {
+        KeReleaseSemaphore(&(DeviceExtension->WorkerSemaphore),
+                           IO_NO_INCREMENT,
+                           1,
+                           FALSE);
+        KeWaitForSingleObject(&UnloadEvent, Executive, KernelMode, FALSE, NULL);
+    }
+    else
+    {
+        InterlockedDecrement(&(DeviceExtension->WorkerReferences));
+    }
+
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+    Irp->IoStatus.Information = 0;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return STATUS_SUCCESS;
+}
+
+/* FUNCTIONS ****************************************************************/
+
+NTSTATUS
+NTAPI
+DriverEntry(IN PDRIVER_OBJECT DriverObject,
+            IN PUNICODE_STRING RegistryPath)
+{
+    NTSTATUS Status;
+    PDEVICE_OBJECT DeviceObject;
+    PDEVICE_EXTENSION DeviceExtension;
+
+    RtlCreateRegistryKey(RTL_REGISTRY_ABSOLUTE, DatabasePath);
+
+    Status = IoCreateDevice(DriverObject,
+                            sizeof(DEVICE_EXTENSION),
+                            &DeviceMount,
+                            FILE_DEVICE_NETWORK,
+                            FILE_DEVICE_SECURE_OPEN,
+                            FALSE,
+                            &DeviceObject);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    DriverObject->DriverUnload = MountMgrUnload;
+
+    DeviceExtension = DeviceObject->DeviceExtension;
+    RtlZeroMemory(DeviceExtension, sizeof(DEVICE_EXTENSION));
+    DeviceExtension->DeviceObject = DeviceObject;
+    DeviceExtension->DriverObject = DriverObject;
+
+    InitializeListHead(&(DeviceExtension->DeviceListHead));
+    InitializeListHead(&(DeviceExtension->OfflineDeviceListHead));
+
+    KeInitializeSemaphore(&(DeviceExtension->DeviceLock), 1, 1);
+    KeInitializeSemaphore(&(DeviceExtension->RemoteDatabaseLock), 1, 1);
+
+    InitializeListHead(&(DeviceExtension->IrpListHead));
+    DeviceExtension->EpicNumber = 1;
+
+    InitializeListHead(&(DeviceExtension->SavedLinksListHead));
+
+    InitializeListHead(&(DeviceExtension->WorkerQueueListHead));
+    KeInitializeSemaphore(&(DeviceExtension->WorkerSemaphore), 0, MAXLONG);
+    DeviceExtension->WorkerReferences = -1;
+    KeInitializeSpinLock(&(DeviceExtension->WorkerLock));
+
+    InitializeListHead(&(DeviceExtension->UniqueIdWorkerItemListHead));
+    InitializeListHead(&(DeviceExtension->OnlineNotificationListHead));
+    DeviceExtension->OnlineNotificationCount = 1;
+
+    DeviceExtension->RegistryPath.Length = RegistryPath->Length;
+    DeviceExtension->RegistryPath.MaximumLength = RegistryPath->Length + sizeof(WCHAR);
+    DeviceExtension->RegistryPath.Buffer = AllocatePool(DeviceExtension->RegistryPath.MaximumLength);
+    if (!DeviceExtension->RegistryPath.Buffer)
+    {
+        IoDeleteDevice(DeviceObject);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    RtlCopyUnicodeString(&(DeviceExtension->RegistryPath), RegistryPath);
+
+    DeviceExtension->NoAutoMount = MountmgrReadNoAutoMount(&(DeviceExtension->RegistryPath));
+
+    GlobalCreateSymbolicLink(&DosDevicesMount, &DeviceMount);
+
+    /* Register for device arrival & removal. Ask to be notified for already
+     * present devices
+     */
+    Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
+                                            PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
+                                            &MountedDevicesGuid,
+                                            DriverObject,
+                                            MountMgrMountedDeviceNotification,
+                                            DeviceObject,
+                                            &(DeviceExtension->NotificationEntry));
+
+    if (!NT_SUCCESS(Status))
+    {
+        IoDeleteDevice(DeviceObject);
+        return Status;
+    }
+
+    DriverObject->MajorFunction[IRP_MJ_CREATE]         =
+    DriverObject->MajorFunction[IRP_MJ_CLOSE]          = MountMgrCreateClose;
+    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MountMgrDeviceControl;
+    DriverObject->MajorFunction[IRP_MJ_CLEANUP]        = MountMgrCleanup;
+    DriverObject->MajorFunction[IRP_MJ_SHUTDOWN]       = MountMgrShutdown;
+
+    gdeviceObject = DeviceObject;
+
+    Status = IoRegisterShutdownNotification(DeviceObject);
+    if (!NT_SUCCESS(Status))
+    {
+        IoDeleteDevice(DeviceObject);
+    }
+
+    return Status;
+}
diff --git a/drivers/filters/mountmgr/mountmgr.rbuild b/drivers/filters/mountmgr/mountmgr.rbuild
new file mode 100644 (file)
index 0000000..cce556e
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<module name="mountmgr" type="kernelmodedriver" installbase="system32/drivers" installname="mountmgr.sys">
+       <bootstrap installbase="$(CDOUTPUT)" />
+       <define name="NTDDI_VERSION">0x05020400</define>
+       <include base="mountmgr">.</include>
+       <library>ntoskrnl</library>
+       <library>hal</library>
+       <library>ioevent</library>
+       <library>wdmguid</library>
+       <file>database.c</file>
+       <file>device.c</file>
+       <file>mountmgr.c</file>
+       <file>notify.c</file>
+       <file>point.c</file>
+       <file>symlink.c</file>
+       <file>uniqueid.c</file>
+       <file>mountmgr.rc</file>
+       <pch>mntmgr.h</pch>
+</module>
diff --git a/drivers/filters/mountmgr/mountmgr.rc b/drivers/filters/mountmgr/mountmgr.rc
new file mode 100644 (file)
index 0000000..cf38d59
--- /dev/null
@@ -0,0 +1,6 @@
+
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION   "Mount Point Manager\0"
+#define REACTOS_STR_INTERNAL_NAME      "mountmgr.sys\0"
+#define REACTOS_STR_ORIGINAL_FILENAME  "mountmgr.sys\0"
+#include <reactos/version.rc>
diff --git a/drivers/filters/mountmgr/notify.c b/drivers/filters/mountmgr/notify.c
new file mode 100644 (file)
index 0000000..2b2acbc
--- /dev/null
@@ -0,0 +1,757 @@
+/*
+ *  ReactOS kernel
+ *  Copyright (C) 2011 ReactOS Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * COPYRIGHT:        See COPYING in the top level directory
+ * PROJECT:          ReactOS kernel
+ * FILE:             drivers/filesystem/mountmgr/notify.c
+ * PURPOSE:          Mount Manager - Notifications handlers
+ * PROGRAMMER:       Pierre Schweitzer (pierre.schweitzer@reactos.org)
+ *                   Alex Ionescu (alex.ionescu@reactos.org)
+ */
+
+/* INCLUDES *****************************************************************/
+
+#include "mntmgr.h"
+
+#define NDEBUG
+#include <debug.h>
+
+/*
+ * @implemented
+ */
+VOID
+SendOnlineNotification(IN PUNICODE_STRING SymbolicName)
+{
+    PIRP Irp;
+    KEVENT Event;
+    NTSTATUS Status;
+    PFILE_OBJECT FileObject;
+    PIO_STACK_LOCATION Stack;
+    PDEVICE_OBJECT DeviceObject;
+    IO_STATUS_BLOCK IoStatusBlock;
+
+    /* Get device object */
+    Status = IoGetDeviceObjectPointer(SymbolicName,
+                                      FILE_READ_ATTRIBUTES,
+                                      &FileObject,
+                                      &DeviceObject);
+    if (!NT_SUCCESS(Status))
+    {
+        return;
+    }
+
+    /* And attached device object */
+    DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
+
+    /* And send VOLUME_ONLINE */
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+    Irp = IoBuildDeviceIoControlRequest(IOCTL_VOLUME_ONLINE,
+                                        DeviceObject,
+                                        NULL, 0,
+                                        NULL, 0,
+                                        FALSE,
+                                        &Event,
+                                        &IoStatusBlock);
+    if (!Irp)
+    {
+        goto Cleanup;
+    }
+
+    Stack = IoGetNextIrpStackLocation(Irp);
+    Stack->FileObject = FileObject;
+
+    Status = IofCallDriver(DeviceObject, Irp);
+    if (Status == STATUS_PENDING)
+    {
+        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+    }
+
+Cleanup:
+    ObfDereferenceObject(DeviceObject);
+    ObfDereferenceObject(FileObject);
+
+    return;
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+SendOnlineNotificationWorker(IN PVOID Parameter)
+{
+    KIRQL OldIrql;
+    PLIST_ENTRY Head;
+    PDEVICE_EXTENSION DeviceExtension;
+    PONLINE_NOTIFICATION_WORK_ITEM WorkItem;
+    PONLINE_NOTIFICATION_WORK_ITEM NewWorkItem;
+
+    WorkItem = (PONLINE_NOTIFICATION_WORK_ITEM)Parameter;
+    DeviceExtension = WorkItem->DeviceExtension;
+
+    /* First, send the notification */
+    SendOnlineNotification(&(WorkItem->SymbolicName));
+
+    OldIrql = KfAcquireSpinLock(&(DeviceExtension->WorkerLock));
+    /* If there are no notifications running any longer, reset event */
+    if (--DeviceExtension->OnlineNotificationCount == 0)
+    {
+        KeSetEvent(&(DeviceExtension->OnlineNotificationEvent), 0, FALSE);
+    }
+
+    /* If there are still notifications in queue */
+    if (!IsListEmpty(&(DeviceExtension->OnlineNotificationListHead)))
+    {
+        /* Queue a new one for execution */
+        Head = RemoveHeadList(&(DeviceExtension->OnlineNotificationListHead));
+        NewWorkItem = CONTAINING_RECORD(Head, ONLINE_NOTIFICATION_WORK_ITEM, List);
+        KfReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
+        NewWorkItem->List.Blink = NULL;
+        NewWorkItem->List.Flink = NULL;
+        ExQueueWorkItem((PWORK_QUEUE_ITEM)NewWorkItem, DelayedWorkQueue);
+    }
+    else
+    {
+        /* Mark it's over */
+        DeviceExtension->OnlineNotificationWorkerActive = 0;
+        KfReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
+    }
+
+    FreePool(WorkItem->SymbolicName.Buffer);
+    FreePool(WorkItem);
+
+    return;
+}
+
+/*
+ * @implemented
+ */
+VOID
+PostOnlineNotification(IN PDEVICE_EXTENSION DeviceExtension,
+                       IN PUNICODE_STRING SymbolicName)
+{
+    KIRQL OldIrql;
+    PONLINE_NOTIFICATION_WORK_ITEM WorkItem;
+
+    /* Allocate a notification work item */
+    WorkItem = AllocatePool(sizeof(ONLINE_NOTIFICATION_WORK_ITEM));
+    if (!WorkItem)
+    {
+        return;
+    }
+
+    WorkItem->List.Flink = NULL;
+    WorkItem->DeviceExtension = DeviceExtension;
+    WorkItem->WorkerRoutine = SendOnlineNotificationWorker;
+    WorkItem->Parameter = WorkItem;
+    WorkItem->SymbolicName.Length = SymbolicName->Length;
+    WorkItem->SymbolicName.MaximumLength = SymbolicName->Length + sizeof(WCHAR);
+    WorkItem->SymbolicName.Buffer = AllocatePool(WorkItem->SymbolicName.MaximumLength);
+    if (!WorkItem->SymbolicName.Buffer)
+    {
+        FreePool(WorkItem);
+        return;
+    }
+
+    RtlCopyMemory(WorkItem->SymbolicName.Buffer, SymbolicName->Buffer, SymbolicName->Length);
+    WorkItem->SymbolicName.Buffer[SymbolicName->Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+    OldIrql = KfAcquireSpinLock(&(DeviceExtension->WorkerLock));
+    DeviceExtension->OnlineNotificationCount++;
+
+    /* If no worker are active */
+    if (DeviceExtension->OnlineNotificationWorkerActive == 0)
+    {
+        /* Queue that one for execution */
+        DeviceExtension->OnlineNotificationWorkerActive == 1;
+        ExQueueWorkItem((PWORK_QUEUE_ITEM)WorkItem, DelayedWorkQueue);
+    }
+    else
+    {
+        /* Otherwise, just put it in the queue list */
+        InsertTailList(&(DeviceExtension->OnlineNotificationListHead), &(WorkItem->List));
+    }
+
+    KfReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
+
+    return;
+}
+
+/*
+ * @implemented
+ */
+VOID
+WaitForOnlinesToComplete(IN PDEVICE_EXTENSION DeviceExtension)
+{
+    KIRQL OldIrql;
+
+    KeInitializeEvent(&(DeviceExtension->OnlineNotificationEvent), NotificationEvent, FALSE);
+
+    OldIrql = KfAcquireSpinLock(&(DeviceExtension->WorkerLock));
+
+    /* Just wait all the worker are done */
+    if (DeviceExtension->OnlineNotificationCount != 1)
+    {
+        DeviceExtension->OnlineNotificationCount--;
+        KfReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
+
+        KeWaitForSingleObject(&(DeviceExtension->OnlineNotificationEvent),
+                              Executive,
+                              KernelMode,
+                              FALSE,
+                              NULL);
+
+        OldIrql = KfAcquireSpinLock(&(DeviceExtension->WorkerLock));
+        DeviceExtension->OnlineNotificationCount++;
+    }
+
+    KfReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql);
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+MountMgrTargetDeviceNotification(IN PVOID NotificationStructure,
+                                 IN PVOID Context)
+{
+    PDEVICE_EXTENSION DeviceExtension;
+    PDEVICE_INFORMATION DeviceInformation;
+    PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notification;
+
+    DeviceInformation = Context;
+    DeviceExtension = DeviceInformation->DeviceExtension;
+    Notification = NotificationStructure;
+
+    /* If it's to signal that removal is complete, then, execute the function */
+    if (IsEqualGUID(&(Notification->Event), &GUID_TARGET_DEVICE_REMOVE_COMPLETE))
+    {
+        MountMgrMountedDeviceRemoval(DeviceExtension, Notification->SymbolicLinkName);
+    }
+    /* It it's to signal that a volume has been mounted
+     * Verify if a database sync is required and execute it
+     */
+    else if (IsEqualGUID(&(Notification->Event), &GUID_IO_VOLUME_MOUNT))
+    {
+        if (InterlockedCompareExchange(&(DeviceInformation->MountState),
+                                       FALSE,
+                                       TRUE) == TRUE)
+        {
+            InterlockedDecrement(&(DeviceInformation->MountState));
+        }
+        else
+        {
+            if (DeviceInformation->NeedsReconcile)
+            {
+                DeviceInformation->NeedsReconcile = FALSE;
+                ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation);
+            }
+        }
+    }
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+VOID
+RegisterForTargetDeviceNotification(IN PDEVICE_EXTENSION DeviceExtension,
+                                    IN PDEVICE_INFORMATION DeviceInformation)
+{
+    NTSTATUS Status;
+    PFILE_OBJECT FileObject;
+    PDEVICE_OBJECT DeviceObject;
+
+    /* Get device object */
+    Status = IoGetDeviceObjectPointer(&(DeviceInformation->DeviceName),
+                                      FILE_READ_ATTRIBUTES,
+                                      &FileObject,
+                                      &DeviceObject);
+    if (!NT_SUCCESS(Status))
+    {
+        return;
+    }
+
+    /* And simply register for notifications */
+    Status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange,
+                                            0, FileObject,
+                                            DeviceExtension->DriverObject,
+                                            MountMgrTargetDeviceNotification,
+                                            DeviceInformation,
+                                            &(DeviceInformation->TargetDeviceNotificationEntry));
+    if (!NT_SUCCESS(Status))
+    {
+        DeviceInformation->TargetDeviceNotificationEntry = NULL;
+    }
+
+    ObfDereferenceObject(FileObject);
+
+    return;
+}
+
+/*
+ * @implemented
+ */
+VOID
+MountMgrNotify(IN PDEVICE_EXTENSION DeviceExtension)
+{
+    PIRP Irp;
+    KIRQL OldIrql;
+    LIST_ENTRY CopyList;
+    PLIST_ENTRY NextEntry;
+
+    /* Increase the epic number */
+    DeviceExtension->EpicNumber++;
+
+    InitializeListHead(&CopyList);
+
+    /* Copy all the pending IRPs for notification */
+    IoAcquireCancelSpinLock(&OldIrql);
+    while (!IsListEmpty(&(DeviceExtension->IrpListHead)))
+    {
+        NextEntry = RemoveHeadList(&(DeviceExtension->IrpListHead));
+        Irp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
+        InsertTailList(&CopyList, &(Irp->Tail.Overlay.ListEntry));
+    }
+    IoReleaseCancelSpinLock(OldIrql);
+
+    /* Then, notifiy them one by one */
+    while (!IsListEmpty(&CopyList))
+    {
+        NextEntry = RemoveHeadList(&CopyList);
+        Irp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
+
+        *((PULONG)Irp->AssociatedIrp.SystemBuffer) = DeviceExtension->EpicNumber;
+        Irp->IoStatus.Information = sizeof(DeviceExtension->EpicNumber);
+
+        IofCompleteRequest(Irp, IO_NO_INCREMENT);
+    }
+}
+
+/*
+ * @implemented
+ */
+VOID
+MountMgrNotifyNameChange(IN PDEVICE_EXTENSION DeviceExtension,
+                         IN PUNICODE_STRING DeviceName,
+                         IN BOOLEAN ValidateVolume)
+{
+    PIRP Irp;
+    KEVENT Event;
+    NTSTATUS Status;
+    PLIST_ENTRY NextEntry;
+    PFILE_OBJECT FileObject;
+    PIO_STACK_LOCATION Stack;
+    PDEVICE_OBJECT DeviceObject;
+    IO_STATUS_BLOCK IoStatusBlock;
+    PDEVICE_RELATIONS DeviceRelations;
+    PDEVICE_INFORMATION DeviceInformation;
+    TARGET_DEVICE_CUSTOM_NOTIFICATION DeviceNotification;
+
+    /* If we have to validate volume */
+    if (ValidateVolume)
+    {
+        /* Then, ensure we can find the device */
+        NextEntry = DeviceExtension->DeviceListHead.Flink;
+        while (NextEntry != &(DeviceExtension->DeviceListHead))
+        {
+            DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
+            if (RtlCompareUnicodeString(DeviceName, &(DeviceInformation->DeviceName), TRUE) == 0)
+            {
+                break;
+            }
+        }
+
+        if (NextEntry == &(DeviceExtension->DeviceListHead) ||
+            !DeviceInformation->Volume)
+        {
+            return;
+        }
+    }
+
+    /* Then, get device object */
+    Status = IoGetDeviceObjectPointer(DeviceName,
+                                      FILE_READ_ATTRIBUTES,
+                                      &FileObject,
+                                      &DeviceObject);
+    if (!NT_SUCCESS(Status))
+    {
+        return;
+    }
+
+    DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    /* Set up empty IRP (yes, yes!) */
+    Irp = IoBuildDeviceIoControlRequest(0,
+                                        DeviceObject,
+                                        NULL,
+                                        0,
+                                        NULL,
+                                        0,
+                                        FALSE,
+                                        &Event,
+                                        &IoStatusBlock);
+    if (!Irp)
+    {
+        ObfDereferenceObject(DeviceObject);
+        ObfDereferenceObject(FileObject);
+    }
+
+    Stack = IoGetNextIrpStackLocation(Irp);
+
+    Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+    Irp->IoStatus.Information = 0;
+
+    /* Properly set it, we want to query device relations */
+    Stack->MajorFunction = IRP_MJ_PNP;
+    Stack->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
+    Stack->Parameters.QueryDeviceRelations.Type = TargetDeviceRelation;
+    Stack->FileObject = FileObject;
+
+    /* And call driver */
+    Status = IofCallDriver(DeviceObject, Irp);
+    if (Status == STATUS_PENDING)
+    {
+        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+        Status = IoStatusBlock.Status;
+    }
+
+    ObfDereferenceObject(DeviceObject);
+    ObfDereferenceObject(FileObject);
+
+    if (!NT_SUCCESS(Status))
+    {
+        return;
+    }
+
+    /* Validate device return */
+    DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
+    if (DeviceRelations->Count < 1)
+    {
+        ExFreePool(DeviceRelations);
+        return;
+    }
+
+    DeviceObject = DeviceRelations->Objects[0];
+    ExFreePool(DeviceRelations);
+
+    /* Set up real notification */
+    DeviceNotification.Version = 1;
+    DeviceNotification.Size = sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION);
+    DeviceNotification.Event = GUID_IO_VOLUME_NAME_CHANGE;
+    DeviceNotification.FileObject = NULL;
+    DeviceNotification.NameBufferOffset = -1;
+
+    /* And report */
+    IoReportTargetDeviceChangeAsynchronous(DeviceObject,
+                                           &DeviceNotification,
+                                           NULL, NULL);
+
+    ObfDereferenceObject(DeviceObject);
+
+    return;
+}
+
+/*
+ * @implemented
+ */
+VOID
+RemoveWorkItem(IN PUNIQUE_ID_WORK_ITEM WorkItem)
+{
+    PDEVICE_EXTENSION DeviceExtension;
+
+    KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
+
+    /* If even if being worked, it's too late */
+    if (WorkItem->Event)
+    {
+        KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
+        KeSetEvent(WorkItem->Event, 0, FALSE);
+    }
+    else
+    {
+        /* Otherwise, remove it from the list, and delete it */
+        RemoveEntryList(&(WorkItem->UniqueIdWorkerItemListEntry));
+        KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
+        IoFreeIrp(WorkItem->Irp);
+        FreePool(WorkItem->DeviceName.Buffer);
+        FreePool(WorkItem->IrpBuffer);
+        FreePool(WorkItem);
+    }
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+UniqueIdChangeNotifyWorker(IN PDEVICE_OBJECT DeviceObject,
+                           IN PVOID Context)
+{
+    PUNIQUE_ID_WORK_ITEM WorkItem = Context;
+    PMOUNTDEV_UNIQUE_ID OldUniqueId, NewUniqueId;
+    PMOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT UniqueIdChange;
+
+    /* Validate worker */
+    if (!NT_SUCCESS(WorkItem->Irp->IoStatus.Status))
+    {
+        RemoveWorkItem(WorkItem);
+        return;
+    }
+
+    UniqueIdChange = WorkItem->Irp->AssociatedIrp.SystemBuffer;
+    /* Get the old unique ID */
+    OldUniqueId = AllocatePool(UniqueIdChange->OldUniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
+    if (!OldUniqueId)
+    {
+        RemoveWorkItem(WorkItem);
+        return;
+    }
+
+    OldUniqueId->UniqueIdLength = UniqueIdChange->OldUniqueIdLength;
+    RtlCopyMemory(OldUniqueId->UniqueId,
+                  (PVOID)((ULONG_PTR)UniqueIdChange + UniqueIdChange->OldUniqueIdOffset),
+                  UniqueIdChange->OldUniqueIdLength);
+
+    /* Get the new unique ID */
+    NewUniqueId = AllocatePool(UniqueIdChange->NewUniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
+    if (!NewUniqueId)
+    {
+        FreePool(OldUniqueId);
+        RemoveWorkItem(WorkItem);
+        return;
+    }
+
+    NewUniqueId->UniqueIdLength = UniqueIdChange->NewUniqueIdLength;
+    RtlCopyMemory(NewUniqueId->UniqueId,
+                  (PVOID)((ULONG_PTR)UniqueIdChange + UniqueIdChange->NewUniqueIdOffset),
+                  UniqueIdChange->NewUniqueIdLength);
+
+    /* Call the real worker */
+    MountMgrUniqueIdChangeRoutine(WorkItem->DeviceExtension, OldUniqueId, NewUniqueId);
+    IssueUniqueIdChangeNotifyWorker(WorkItem, NewUniqueId);
+
+    FreePool(NewUniqueId);
+    FreePool(OldUniqueId);
+
+    return;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+UniqueIdChangeNotifyCompletion(IN PDEVICE_OBJECT DeviceObject,
+                               IN PIRP Irp,
+                               IN PVOID Context)
+{
+    PUNIQUE_ID_WORK_ITEM WorkItem = Context;
+
+    /* Simply queue the work item */
+    IoQueueWorkItem(WorkItem->WorkItem,
+                    UniqueIdChangeNotifyWorker,
+                    DelayedWorkQueue,
+                    WorkItem);
+
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+/*
+ * @implemented
+ */
+VOID
+IssueUniqueIdChangeNotifyWorker(IN PUNIQUE_ID_WORK_ITEM WorkItem,
+                                IN PMOUNTDEV_UNIQUE_ID UniqueId)
+{
+    PIRP Irp;
+    NTSTATUS Status;
+    PFILE_OBJECT FileObject;
+    PIO_STACK_LOCATION Stack;
+    PDEVICE_OBJECT DeviceObject;
+
+    /* Get the device object */
+    Status = IoGetDeviceObjectPointer(&(WorkItem->DeviceName),
+                                      FILE_READ_ATTRIBUTES,
+                                      &FileObject,
+                                      &DeviceObject);
+    if (!NT_SUCCESS(Status))
+    {
+        RemoveWorkItem(WorkItem);
+        return;
+    }
+
+    /* And then, the attached device */
+    DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
+
+    /* Initialize the IRP */
+    Irp = WorkItem->Irp;
+    IoInitializeIrp(Irp, IoSizeOfIrp(WorkItem->StackSize), WorkItem->StackSize);
+
+    if (InterlockedExchange((PLONG)&(WorkItem->Event), 0) != 0)
+    {
+        ObfDereferenceObject(FileObject);
+        ObfDereferenceObject(DeviceObject);
+        RemoveWorkItem(WorkItem);
+        return;
+    }
+
+    Irp->AssociatedIrp.SystemBuffer = WorkItem->IrpBuffer;
+    Irp->Tail.Overlay.Thread = PsGetCurrentThread();
+    RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, UniqueId, UniqueId->UniqueIdLength + sizeof(USHORT));
+
+    Stack = IoGetNextIrpStackLocation(Irp);
+
+    Stack->Parameters.DeviceIoControl.InputBufferLength = UniqueId->UniqueIdLength + sizeof(USHORT);
+    Stack->Parameters.DeviceIoControl.OutputBufferLength = WorkItem->IrpBufferLength;
+    Stack->Parameters.DeviceIoControl.Type3InputBuffer = 0;
+    Stack->Parameters.DeviceIoControl.IoControlCode = IOCTL_MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY;
+    Stack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
+
+    Status = IoSetCompletionRoutineEx(WorkItem->DeviceExtension->DeviceObject,
+                                      Irp,
+                                      UniqueIdChangeNotifyCompletion,
+                                      WorkItem,
+                                      TRUE, TRUE, TRUE);
+    if (!NT_SUCCESS(Status))
+    {
+        ObfDereferenceObject(FileObject);
+        ObfDereferenceObject(DeviceObject);
+        RemoveWorkItem(WorkItem);
+        return;
+    }
+
+    /* Call the driver */
+    IofCallDriver(DeviceObject, Irp);
+    ObfDereferenceObject(FileObject);
+    ObfDereferenceObject(DeviceObject);
+}
+
+/*
+ * @implemented
+ */
+VOID
+IssueUniqueIdChangeNotify(IN PDEVICE_EXTENSION DeviceExtension,
+                          IN PUNICODE_STRING DeviceName,
+                          IN PMOUNTDEV_UNIQUE_ID UniqueId)
+{
+    NTSTATUS Status;
+    PVOID IrpBuffer = NULL;
+    PFILE_OBJECT FileObject;
+    PDEVICE_OBJECT DeviceObject;
+    PUNIQUE_ID_WORK_ITEM WorkItem = NULL;
+
+    /* Get the associated device object */
+    Status = IoGetDeviceObjectPointer(DeviceName,
+                                      FILE_READ_ATTRIBUTES,
+                                      &FileObject,
+                                      &DeviceObject);
+    if (!NT_SUCCESS(Status))
+    {
+        return;
+    }
+
+    /* And then, get attached device */
+    DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
+
+    ObfDereferenceObject(FileObject);
+
+    /* Allocate a work item */
+    WorkItem = AllocatePool(sizeof(UNIQUE_ID_WORK_ITEM));
+    if (!WorkItem)
+    {
+        ObfDereferenceObject(DeviceObject);
+        return;
+    }
+
+    WorkItem->Event = NULL;
+    WorkItem->WorkItem = IoAllocateWorkItem(DeviceExtension->DeviceObject);
+    if (!WorkItem->WorkItem)
+    {
+        ObfDereferenceObject(DeviceObject);
+        goto Cleanup;
+    }
+
+    WorkItem->DeviceExtension = DeviceExtension;
+    WorkItem->StackSize = DeviceObject->StackSize;
+    /* Already provide the IRP */
+    WorkItem->Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
+
+    ObfDereferenceObject(DeviceObject);
+
+    if (!WorkItem->Irp)
+    {
+        goto Cleanup;
+    }
+
+    /* Ensure it has enough space */
+    IrpBuffer = AllocatePool(sizeof(MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT) + 1024);
+    if (!IrpBuffer)
+    {
+        goto Cleanup;
+    }
+
+    WorkItem->DeviceName.Length = DeviceName->Length;
+    WorkItem->DeviceName.MaximumLength = DeviceName->Length + sizeof(WCHAR);
+    WorkItem->DeviceName.Buffer = AllocatePool(WorkItem->DeviceName.MaximumLength);
+    if (!WorkItem->DeviceName.Buffer)
+    {
+        goto Cleanup;
+    }
+
+    RtlCopyMemory(WorkItem->DeviceName.Buffer, DeviceName->Buffer, DeviceName->Length);
+    WorkItem->DeviceName.Buffer[DeviceName->Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+    WorkItem->IrpBuffer = IrpBuffer;
+    WorkItem->IrpBufferLength = sizeof(MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT) + 1024;
+
+    /* Add the worker in the list */
+    KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
+    InsertHeadList(&(DeviceExtension->UniqueIdWorkerItemListHead), &(WorkItem->UniqueIdWorkerItemListEntry));
+    KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
+
+    /* And call the worker */
+    IssueUniqueIdChangeNotifyWorker(WorkItem, UniqueId);
+
+    return;
+
+Cleanup:
+    if (IrpBuffer)
+    {
+        FreePool(IrpBuffer);
+    }
+
+    if (WorkItem->Irp)
+    {
+        IoFreeIrp(WorkItem->Irp);
+    }
+
+    if (WorkItem->WorkItem)
+    {
+        IoFreeWorkItem(WorkItem->WorkItem);
+    }
+
+    if (WorkItem)
+    {
+        FreePool(WorkItem);
+    }
+}
diff --git a/drivers/filters/mountmgr/point.c b/drivers/filters/mountmgr/point.c
new file mode 100644 (file)
index 0000000..5f8d33f
--- /dev/null
@@ -0,0 +1,572 @@
+/*
+ *  ReactOS kernel
+ *  Copyright (C) 2011-2012 ReactOS Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * COPYRIGHT:        See COPYING in the top level directory
+ * PROJECT:          ReactOS kernel
+ * FILE:             drivers/filesystem/mountmgr/point.c
+ * PURPOSE:          Mount Manager - Mount points
+ * PROGRAMMER:       Pierre Schweitzer (pierre.schweitzer@reactos.org)
+ */
+
+/* INCLUDES *****************************************************************/
+
+#include "mntmgr.h"
+
+#define NDEBUG
+#include <debug.h>
+
+/*
+ * @implemented
+ */
+NTSTATUS
+MountMgrCreatePointWorker(IN PDEVICE_EXTENSION DeviceExtension,
+                          IN PUNICODE_STRING SymbolicLinkName,
+                          IN PUNICODE_STRING DeviceName)
+{
+    NTSTATUS Status;
+    PLIST_ENTRY DeviceEntry;
+    PMOUNTDEV_UNIQUE_ID UniqueId;
+    PSYMLINK_INFORMATION SymlinkInformation;
+    UNICODE_STRING SymLink, TargetDeviceName;
+    PDEVICE_INFORMATION DeviceInformation, DeviceInfo;
+
+    /* Get device name */
+    Status = QueryDeviceInformation(SymbolicLinkName,
+                                    &TargetDeviceName,
+                                    NULL, NULL, NULL,
+                                    NULL, NULL, NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    /* First of all, try to find device */
+    for (DeviceEntry = DeviceExtension->DeviceListHead.Flink;
+         DeviceEntry != &(DeviceExtension->DeviceListHead);
+         DeviceEntry = DeviceEntry->Flink)
+    {
+        DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry);
+
+        if (RtlCompareUnicodeString(&TargetDeviceName, &(DeviceInformation->DeviceName), TRUE) == 0)
+        {
+            break;
+        }
+    }
+
+    /* Copy symbolic link name and null terminate it */
+    SymLink.Buffer = AllocatePool(SymbolicLinkName->Length + sizeof(UNICODE_NULL));
+    if (!SymLink.Buffer)
+    {
+        FreePool(TargetDeviceName.Buffer);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    RtlCopyMemory(SymLink.Buffer, SymbolicLinkName->Buffer, SymbolicLinkName->Length);
+    SymLink.Buffer[SymbolicLinkName->Length / sizeof(WCHAR)] = UNICODE_NULL;
+    SymLink.Length = SymbolicLinkName->Length;
+    SymLink.MaximumLength = SymbolicLinkName->Length + sizeof(UNICODE_NULL);
+
+    /* If we didn't find device */
+    if (DeviceEntry == &(DeviceExtension->DeviceListHead))
+    {
+        /* Then, try with unique ID */
+        Status = QueryDeviceInformation(SymbolicLinkName,
+                                        NULL, &UniqueId,
+                                        NULL, NULL, NULL,
+                                        NULL, NULL);
+        if (!NT_SUCCESS(Status))
+        {
+            FreePool(TargetDeviceName.Buffer);
+            FreePool(SymLink.Buffer);
+            return Status;
+        }
+
+        /* Create a link to the device */
+        Status = GlobalCreateSymbolicLink(&SymLink, &TargetDeviceName);
+        if (!NT_SUCCESS(Status))
+        {
+            FreePool(UniqueId);
+            FreePool(TargetDeviceName.Buffer);
+            FreePool(SymLink.Buffer);
+            return Status;
+        }
+
+        /* If caller provided driver letter, delete it */
+        if (IsDriveLetter(&SymLink))
+        {
+            DeleteRegistryDriveLetter(UniqueId);
+        }
+
+        /* Device will be identified with its unique ID */
+        Status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
+                                       DatabasePath,
+                                       SymLink.Buffer,
+                                       REG_BINARY,
+                                       UniqueId->UniqueId,
+                                       UniqueId->UniqueIdLength);
+
+        FreePool(UniqueId);
+        FreePool(TargetDeviceName.Buffer);
+        FreePool(SymLink.Buffer);
+        return Status;
+    }
+
+    /* If call provided a driver letter whereas device already has one
+     * fail, this is not doable
+     */
+    if (IsDriveLetter(&SymLink) && HasDriveLetter(DeviceInformation))
+    {
+        FreePool(TargetDeviceName.Buffer);
+        FreePool(SymLink.Buffer);
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Now, create a link */
+    Status = GlobalCreateSymbolicLink(&SymLink, &TargetDeviceName);
+    FreePool(TargetDeviceName.Buffer);
+    if (!NT_SUCCESS(Status))
+    {
+        FreePool(SymLink.Buffer);
+        return Status;
+    }
+
+    /* Associate Unique ID <-> symbolic name */
+    UniqueId = DeviceInformation->UniqueId;
+    Status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
+                                   DatabasePath,
+                                   SymLink.Buffer,
+                                   REG_BINARY,
+                                   UniqueId->UniqueId,
+                                   UniqueId->UniqueIdLength);
+    if (!NT_SUCCESS(Status))
+    {
+        GlobalDeleteSymbolicLink(&SymLink);
+        FreePool(SymLink.Buffer);
+        return Status;
+    }
+
+    /* Now, prepare to save the link with the device */
+    SymlinkInformation = AllocatePool(sizeof(SYMLINK_INFORMATION));
+    if (!SymlinkInformation)
+    {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        GlobalDeleteSymbolicLink(&SymLink);
+        FreePool(SymLink.Buffer);
+        return Status;
+    }
+
+    SymlinkInformation->Name.Length = SymLink.Length;
+    SymlinkInformation->Name.MaximumLength = SymLink.Length + sizeof(UNICODE_NULL);
+    SymlinkInformation->Name.Buffer = AllocatePool(SymlinkInformation->Name.MaximumLength);
+    if (!SymlinkInformation->Name.Buffer)
+    {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        FreePool(SymlinkInformation);
+        GlobalDeleteSymbolicLink(&SymLink);
+        FreePool(SymLink.Buffer);
+        return Status;
+    }
+
+    /* Save the link and mark it online */
+    RtlCopyMemory(SymlinkInformation->Name.Buffer, SymLink.Buffer, SymlinkInformation->Name.Length);
+    SymlinkInformation->Name.Buffer[SymlinkInformation->Name.Length / sizeof(WCHAR)] = UNICODE_NULL;
+    SymlinkInformation->Online = TRUE;
+    InsertTailList(&DeviceInformation->SymbolicLinksListHead, &SymlinkInformation->SymbolicLinksListEntry);
+    SendLinkCreated(&(SymlinkInformation->Name));
+
+    /* If we have a drive letter */
+    if (IsDriveLetter(&SymLink))
+    {
+        /* Then, delete the no drive letter entry */
+        DeleteNoDriveLetterEntry(UniqueId);
+
+        /* And post online notification if asked */
+        if (!DeviceInformation->SkipNotifications)
+        {
+            PostOnlineNotification(DeviceExtension, &DeviceInformation->SymbolicName);
+        }
+    }
+
+    /* If that's a volume with automatic drive letter, it's now time to resync databases */
+    if (MOUNTMGR_IS_VOLUME_NAME(&SymLink) && DeviceExtension->AutomaticDriveLetter)
+    {
+        for (DeviceEntry = DeviceExtension->DeviceListHead.Flink;
+             DeviceEntry != &(DeviceExtension->DeviceListHead);
+             DeviceEntry = DeviceEntry->Flink)
+        {
+            DeviceInfo = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry);
+
+            /* If there's one, ofc! */
+            if (!DeviceInfo->NoDatabase)
+            {
+                ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInfo);
+            }
+        }
+    }
+
+    /* Notify & quit */
+    FreePool(SymLink.Buffer);
+    MountMgrNotify(DeviceExtension);
+
+    if (!DeviceInformation->Volume)
+    {
+        MountMgrNotifyNameChange(DeviceExtension, DeviceName, FALSE);
+    }
+
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+QueryPointsFromMemory(IN PDEVICE_EXTENSION DeviceExtension,
+                      IN PIRP Irp,
+                      IN PMOUNTDEV_UNIQUE_ID UniqueId OPTIONAL,
+                      IN PUNICODE_STRING SymbolicName OPTIONAL)
+{
+    NTSTATUS Status;
+    PIO_STACK_LOCATION Stack;
+    UNICODE_STRING DeviceName;
+    ULONG TotalSize, TotalSymLinks;
+    PMOUNTMGR_MOUNT_POINTS MountPoints;
+    PDEVICE_INFORMATION DeviceInformation;
+    PLIST_ENTRY DeviceEntry, SymlinksEntry;
+    PSYMLINK_INFORMATION SymlinkInformation;
+
+    /* If we got a symbolic link, query device */
+    if (SymbolicName)
+    {
+        Status = QueryDeviceInformation(SymbolicName,
+                                        &DeviceName,
+                                        NULL, NULL,
+                                        NULL, NULL,
+                                        NULL, NULL);
+        if (!NT_SUCCESS(Status))
+        {
+            return Status;
+        }
+    }
+
+    /* Browse all the links to count number of links & size used */
+    TotalSize = 0;
+    TotalSymLinks = 0;
+    for (DeviceEntry = DeviceExtension->DeviceListHead.Flink;
+         DeviceEntry != &(DeviceExtension->DeviceListHead);
+         DeviceEntry = DeviceEntry->Flink)
+    {
+        DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry);
+
+        /* If we were given an unique ID, it has to match */
+        if (UniqueId)
+        {
+            if (UniqueId->UniqueIdLength != DeviceInformation->UniqueId->UniqueIdLength)
+            {
+                continue;
+            }
+
+            if (RtlCompareMemory(UniqueId->UniqueId,
+                                 DeviceInformation->UniqueId->UniqueId,
+                                 UniqueId->UniqueIdLength) != UniqueId->UniqueIdLength)
+            {
+                continue;
+            }
+        }
+        /* Or, if we had a symlink, it has to match */
+        else if (SymbolicName)
+        {
+            if (!RtlEqualUnicodeString(&DeviceName, &(DeviceInformation->DeviceName), TRUE))
+            {
+                continue;
+            }
+        }
+
+        /* Once here, it matched, save device name & unique ID size */
+        TotalSize += DeviceInformation->DeviceName.Length + DeviceInformation->UniqueId->UniqueIdLength;
+
+        /* And count number of symlinks (and their size) */
+        for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink;
+             SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead);
+             SymlinksEntry = SymlinksEntry->Flink)
+        {
+            SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
+
+            TotalSize += SymlinkInformation->Name.Length;
+            TotalSymLinks++;
+        }
+
+        /* We had a specific item to find
+         * if we reach that point, we found it, no need to continue
+         */
+        if (UniqueId || SymbolicName)
+        {
+            break;
+        }
+    }
+
+    /* If we were looking for specific item, ensure we found it */
+    if (UniqueId || SymbolicName)
+    {
+        if (DeviceEntry == &(DeviceExtension->DeviceListHead))
+        {
+            if (DeviceName.Buffer)
+            {
+                FreePool(DeviceName.Buffer);
+            }
+
+            return STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    /* Now, ensure output buffer can hold everything */
+    Stack = IoGetNextIrpStackLocation(Irp);
+    MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer;
+
+    /* Ensure we set output to let user reallocate! */
+    MountPoints->Size = sizeof(MOUNTMGR_MOUNT_POINTS) + TotalSize;
+    MountPoints->NumberOfMountPoints = TotalSymLinks;
+
+    if (MountPoints->Size > Stack->Parameters.DeviceIoControl.OutputBufferLength)
+    {
+        return STATUS_BUFFER_OVERFLOW;
+    }
+
+    /* Now, start putting mount points */
+    TotalSymLinks = 0;
+    TotalSize = 0;
+    for (DeviceEntry = DeviceExtension->DeviceListHead.Flink;
+         DeviceEntry != &(DeviceExtension->DeviceListHead);
+         DeviceEntry = DeviceEntry->Flink)
+    {
+        DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry);
+
+        /* Find back correct mount point */
+        if (UniqueId)
+        {
+            if (!UniqueId->UniqueIdLength != DeviceInformation->UniqueId->UniqueIdLength)
+            {
+                continue;
+            }
+
+            if (RtlCompareMemory(UniqueId->UniqueId,
+                                 DeviceInformation->UniqueId->UniqueId,
+                                 UniqueId->UniqueIdLength) != UniqueId->UniqueIdLength)
+            {
+                continue;
+            }
+        }
+        else if (SymbolicName)
+        {
+            if (!RtlEqualUnicodeString(&DeviceName, &(DeviceInformation->DeviceName), TRUE))
+            {
+                continue;
+            }
+        }
+
+        /* Now we've got it, but all the data */
+        for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink;
+             SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead);
+             SymlinksEntry = SymlinksEntry->Flink)
+        {
+            SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
+
+
+            MountPoints->MountPoints[TotalSymLinks].SymbolicLinkNameOffset = sizeof(MOUNTMGR_MOUNT_POINTS) +
+                                                                             TotalSize;
+            MountPoints->MountPoints[TotalSymLinks].SymbolicLinkNameLength = SymlinkInformation->Name.Length;
+            MountPoints->MountPoints[TotalSymLinks].UniqueIdOffset = sizeof(MOUNTMGR_MOUNT_POINTS) +
+                                                                     SymlinkInformation->Name.Length +
+                                                                     TotalSize;
+            MountPoints->MountPoints[TotalSymLinks].UniqueIdLength = DeviceInformation->UniqueId->UniqueIdLength;
+            MountPoints->MountPoints[TotalSymLinks].DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINTS) +
+                                                                       SymlinkInformation->Name.Length +
+                                                                       DeviceInformation->UniqueId->UniqueIdLength +
+                                                                       TotalSize;
+            MountPoints->MountPoints[TotalSymLinks].DeviceNameLength = DeviceInformation->DeviceName.Length;
+
+            RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[TotalSymLinks].SymbolicLinkNameOffset),
+                          SymlinkInformation->Name.Buffer, SymlinkInformation->Name.Length);
+            RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[TotalSymLinks].UniqueIdOffset),
+                          DeviceInformation->UniqueId->UniqueId, DeviceInformation->UniqueId->UniqueIdLength);
+            RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[TotalSymLinks].DeviceNameOffset),
+                  DeviceInformation->DeviceName.Buffer, DeviceInformation->DeviceName.Length);
+
+            /* Update counters */
+            TotalSymLinks++;
+            TotalSize += SymlinkInformation->Name.Length + DeviceInformation->UniqueId->UniqueIdLength +
+                         DeviceInformation->DeviceName.Length;
+        }
+
+        if (UniqueId || SymbolicName)
+        {
+            break;
+        }
+    }
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+QueryPointsFromSymbolicLinkName(IN PDEVICE_EXTENSION DeviceExtension,
+                                IN PUNICODE_STRING SymbolicName,
+                                IN PIRP Irp)
+{
+    NTSTATUS Status;
+    ULONG TotalLength;
+    PIO_STACK_LOCATION Stack;
+    UNICODE_STRING DeviceName;
+    PMOUNTMGR_MOUNT_POINTS MountPoints;
+    PDEVICE_INFORMATION DeviceInformation;
+    PLIST_ENTRY DeviceEntry, SymlinksEntry;
+    PSYMLINK_INFORMATION SymlinkInformation;
+
+    /* Find device */
+    Status = QueryDeviceInformation(SymbolicName, &DeviceName,
+                                    NULL, NULL, NULL,
+                                    NULL, NULL, NULL);
+    if (NT_SUCCESS(Status))
+    {
+        /* Look for the device information */
+        for (DeviceEntry = DeviceExtension->DeviceListHead.Flink;
+             DeviceEntry != &(DeviceExtension->DeviceListHead);
+             DeviceEntry = DeviceEntry->Flink)
+        {
+            DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry);
+
+            if (RtlEqualUnicodeString(&DeviceName, &(DeviceInformation->DeviceName), TRUE) == 0)
+            {
+                break;
+            }
+        }
+
+        FreePool(DeviceName.Buffer);
+
+        if (DeviceEntry == &(DeviceExtension->DeviceListHead))
+        {
+            return STATUS_INVALID_PARAMETER;
+        }
+
+        /* Check for the link */
+        for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink;
+             SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead);
+             SymlinksEntry = DeviceEntry->Flink)
+        {
+            SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
+
+            if (RtlEqualUnicodeString(SymbolicName, &SymlinkInformation->Name, TRUE) == 0)
+            {
+                break;
+            }
+        }
+
+        if (SymlinksEntry == &(DeviceInformation->SymbolicLinksListHead))
+        {
+            return STATUS_INVALID_PARAMETER;
+        }
+    }
+    else
+    {
+        /* Browse all the devices to try to find the one
+         * that has the given link...
+         */
+        for (DeviceEntry = DeviceExtension->DeviceListHead.Flink;
+             DeviceEntry != &(DeviceExtension->DeviceListHead);
+             DeviceEntry = DeviceEntry->Flink)
+        {
+            DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry);
+
+            for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink;
+                 SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead);
+                 SymlinksEntry = SymlinksEntry->Flink)
+            {
+                SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
+
+                if (RtlEqualUnicodeString(SymbolicName, &SymlinkInformation->Name, TRUE) == 0)
+                {
+                    break;
+                }
+            }
+
+            if (SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead))
+            {
+                break;
+            }
+        }
+
+        /* Even that way we didn't find, give up! */
+        if (DeviceEntry == &(DeviceExtension->DeviceListHead))
+        {
+            return STATUS_OBJECT_NAME_NOT_FOUND;
+        }
+    }
+
+    /* Get output buffer */
+    Stack = IoGetNextIrpStackLocation(Irp);
+    MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer;
+
+    /* Compute output length */
+    TotalLength = DeviceInformation->UniqueId->UniqueIdLength +
+                  SymlinkInformation->Name.Length + DeviceInformation->DeviceName.Length;
+
+    /* Give length to allow reallocation */
+    MountPoints->Size = sizeof(MOUNTMGR_MOUNT_POINTS) + TotalLength;
+    MountPoints->NumberOfMountPoints = 1;
+
+    if (MountPoints->Size > Stack->Parameters.DeviceIoControl.OutputBufferLength)
+    {
+        return STATUS_BUFFER_OVERFLOW;
+    }
+
+    /* Write out data */
+    MountPoints->MountPoints[0].SymbolicLinkNameOffset = sizeof(MOUNTMGR_MOUNT_POINTS);
+    MountPoints->MountPoints[0].SymbolicLinkNameLength = SymlinkInformation->Name.Length;
+    /* If link is online write it's unique ID, otherwise, forget about it */
+    if (SymlinkInformation->Online)
+    {
+        MountPoints->MountPoints[0].UniqueIdOffset = sizeof(MOUNTMGR_MOUNT_POINTS) +
+                                                     SymlinkInformation->Name.Length;
+        MountPoints->MountPoints[0].UniqueIdLength = DeviceInformation->UniqueId->UniqueIdLength;
+    }
+    else
+    {
+        MountPoints->MountPoints[0].UniqueIdOffset = 0;
+        MountPoints->MountPoints[0].UniqueIdLength = 0;
+    }
+
+    MountPoints->MountPoints[0].DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINTS) +
+                                                   SymlinkInformation->Name.Length +
+                                                   DeviceInformation->UniqueId->UniqueIdLength;
+    MountPoints->MountPoints[0].DeviceNameLength = DeviceInformation->DeviceName.Length;
+
+    RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[0].SymbolicLinkNameOffset),
+                  SymlinkInformation->Name.Buffer, SymlinkInformation->Name.Length);
+
+    if (SymlinkInformation->Online)
+    {
+        RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[0].UniqueIdOffset),
+                      DeviceInformation->UniqueId->UniqueId, DeviceInformation->UniqueId->UniqueIdLength);
+    }
+
+    RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[0].DeviceNameOffset),
+                  DeviceInformation->DeviceName.Buffer, DeviceInformation->DeviceName.Length);
+
+    return STATUS_SUCCESS;
+}
diff --git a/drivers/filters/mountmgr/symlink.c b/drivers/filters/mountmgr/symlink.c
new file mode 100644 (file)
index 0000000..68a3980
--- /dev/null
@@ -0,0 +1,1004 @@
+/*
+ *  ReactOS kernel
+ *  Copyright (C) 2011-2012 ReactOS Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * COPYRIGHT:        See COPYING in the top level directory
+ * PROJECT:          ReactOS kernel
+ * FILE:             drivers/filesystem/mountmgr/symlink.c
+ * PURPOSE:          Mount Manager - Symbolic links functions
+ * PROGRAMMER:       Pierre Schweitzer (pierre.schweitzer@reactos.org)
+ */
+
+/* INCLUDES *****************************************************************/
+
+#include "mntmgr.h"
+
+#define NDEBUG
+#include <debug.h>
+
+UNICODE_STRING DeviceMount = RTL_CONSTANT_STRING(MOUNTMGR_DEVICE_NAME);
+UNICODE_STRING DosDevicesMount = RTL_CONSTANT_STRING(L"\\DosDevices\\MountPointManager");
+UNICODE_STRING DosDevices = RTL_CONSTANT_STRING(L"\\DosDevices\\");
+UNICODE_STRING DeviceFloppy = RTL_CONSTANT_STRING(L"\\Device\\Floppy");
+UNICODE_STRING DeviceCdRom = RTL_CONSTANT_STRING(L"\\Device\\CdRom");
+UNICODE_STRING DosGlobal = RTL_CONSTANT_STRING(L"\\GLOBAL??\\");
+UNICODE_STRING Global = RTL_CONSTANT_STRING(L"\\??\\");
+UNICODE_STRING SafeVolumes = RTL_CONSTANT_STRING(L"\\Device\\VolumesSafeForWriteAccess");
+UNICODE_STRING Volume = RTL_CONSTANT_STRING(L"\\??\\Volume");
+UNICODE_STRING ReparseIndex = RTL_CONSTANT_STRING(L"\\$Extend\\$Reparse:$R:$INDEX_ALLOCATION");
+
+/*
+ * @implemented
+ */
+NTSTATUS
+CreateStringWithGlobal(IN PUNICODE_STRING DosName,
+                       OUT PUNICODE_STRING GlobalString)
+{
+    UNICODE_STRING IntGlobal;
+
+    if (RtlPrefixUnicodeString(&DosDevices, DosName, TRUE))
+    {
+        /* DOS device - use DOS global */
+        IntGlobal.Length = DosName->Length - DosDevices.Length + DosGlobal.Length;
+        IntGlobal.MaximumLength = IntGlobal.Length + sizeof(WCHAR);
+        IntGlobal.Buffer = AllocatePool(IntGlobal.MaximumLength);
+        if (!IntGlobal.Buffer)
+        {
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        RtlCopyMemory(IntGlobal.Buffer, DosGlobal.Buffer, DosGlobal.Length);
+        RtlCopyMemory(IntGlobal.Buffer + (DosGlobal.Length / sizeof(WCHAR)),
+                      DosName->Buffer + (DosDevices.Length / sizeof(WCHAR)),
+                      DosName->Length - DosDevices.Length);
+        IntGlobal.Buffer[IntGlobal.Length / sizeof(WCHAR)] = UNICODE_NULL;
+    }
+    else
+    {
+        if (RtlPrefixUnicodeString(&Global, DosName, TRUE))
+        {
+            /* Switch to DOS global */
+            IntGlobal.Length = DosName->Length - Global.Length + DosGlobal.Length;
+            IntGlobal.MaximumLength = IntGlobal.Length + sizeof(WCHAR);
+            IntGlobal.Buffer = AllocatePool(IntGlobal.MaximumLength);
+            if (!IntGlobal.Buffer)
+            {
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+
+            RtlCopyMemory(IntGlobal.Buffer, DosGlobal.Buffer, DosGlobal.Length);
+            RtlCopyMemory(IntGlobal.Buffer + (DosGlobal.Length / sizeof(WCHAR)),
+                          DosName->Buffer + (Global.Length / sizeof(WCHAR)),
+                          DosName->Length - Global.Length);
+            IntGlobal.Buffer[IntGlobal.Length / sizeof(WCHAR)] = UNICODE_NULL;
+        }
+        else
+        {
+            /* Simply duplicate string */
+            IntGlobal.Length = DosName->Length;
+            IntGlobal.MaximumLength = DosName->MaximumLength;
+            IntGlobal.Buffer = AllocatePool(IntGlobal.MaximumLength);
+            if (!IntGlobal.Buffer)
+            {
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+
+            RtlCopyMemory(IntGlobal.Buffer, DosName->Buffer, IntGlobal.MaximumLength);
+        }
+    }
+
+    /* Return string */
+    GlobalString->Length = IntGlobal.Length;
+    GlobalString->MaximumLength = IntGlobal.MaximumLength;
+    GlobalString->Buffer = IntGlobal.Buffer;
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+GlobalCreateSymbolicLink(IN PUNICODE_STRING DosName,
+                         IN PUNICODE_STRING DeviceName)
+{
+    NTSTATUS Status;
+    UNICODE_STRING GlobalName;
+
+    /* First create the global string */
+    Status = CreateStringWithGlobal(DosName, &GlobalName);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    /* Then, create the symlink */
+    Status = IoCreateSymbolicLink(&GlobalName, DosName);
+
+    FreePool(GlobalName.Buffer);
+
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+GlobalDeleteSymbolicLink(IN PUNICODE_STRING DosName)
+{
+    NTSTATUS Status;
+    UNICODE_STRING GlobalName;
+
+    /* Recreate the string (to find the link) */
+    Status = CreateStringWithGlobal(DosName, &GlobalName);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    /* And delete the link */
+    Status = IoDeleteSymbolicLink(&GlobalName);
+
+    FreePool(GlobalName.Buffer);
+
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+VOID
+SendLinkCreated(IN PUNICODE_STRING SymbolicName)
+{
+    PIRP Irp;
+    KEVENT Event;
+    ULONG NameSize;
+    NTSTATUS Status;
+    PFILE_OBJECT FileObject;
+    PIO_STACK_LOCATION Stack;
+    PMOUNTDEV_NAME Name = NULL;
+    PDEVICE_OBJECT DeviceObject;
+    IO_STATUS_BLOCK IoStatusBlock;
+
+    /* Get the device associated with the name */
+    Status = IoGetDeviceObjectPointer(SymbolicName,
+                                      FILE_READ_ATTRIBUTES,
+                                      &FileObject,
+                                      &DeviceObject);
+    if (!NT_SUCCESS(Status))
+    {
+        return;
+    }
+
+    /* Get attached device (will notify it) */
+    DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
+
+    /* NameSize is the size of the whole MOUNTDEV_NAME struct */
+    NameSize = sizeof(USHORT) + SymbolicName->Length;
+    Name = AllocatePool(NameSize);
+    if (!Name)
+    {
+        goto Cleanup;
+    }
+
+    /* Initialize struct */
+    Name->NameLength = SymbolicName->Length;
+    RtlCopyMemory(Name->Name, SymbolicName->Buffer, SymbolicName->Length);
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+    /* Microsoft does it twice... Once with limited access, second with any
+     * So, first one here
+     */
+    Irp = IoBuildDeviceIoControlRequest(CTL_CODE(MOUNTDEVCONTROLTYPE, 4, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
+                                        DeviceObject,
+                                        Name,
+                                        NameSize,
+                                        NULL,
+                                        0,
+                                        FALSE,
+                                        &Event,
+                                        &IoStatusBlock);
+    /* This one can fail, no one matters */
+    if (Irp)
+    {
+        Stack = IoGetNextIrpStackLocation(Irp);
+        Stack->FileObject = FileObject;
+
+        Status = IofCallDriver(DeviceObject, Irp);
+        if (Status == STATUS_PENDING)
+        {
+            KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+        }
+    }
+
+    /* Then, second one */
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+    Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_LINK_CREATED,
+                                        DeviceObject,
+                                        Name,
+                                        NameSize,
+                                        NULL,
+                                        0,
+                                        FALSE,
+                                        &Event,
+                                        &IoStatusBlock);
+    if (!Irp)
+    {
+        goto Cleanup;
+    }
+
+    Stack = IoGetNextIrpStackLocation(Irp);
+    Stack->FileObject = FileObject;
+
+    /* Really notify */
+    Status = IofCallDriver(DeviceObject, Irp);
+    if (Status == STATUS_PENDING)
+    {
+        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+    }
+
+Cleanup:
+    if (Name)
+    {
+        FreePool(Name);
+    }
+
+    ObfDereferenceObject(DeviceObject);
+    ObfDereferenceObject(FileObject);
+
+    return;
+}
+
+/*
+ * @implemented
+ */
+VOID
+SendLinkDeleted(IN PUNICODE_STRING DeviceName,
+                IN PUNICODE_STRING SymbolicName)
+{
+    PIRP Irp;
+    KEVENT Event;
+    ULONG NameSize;
+    NTSTATUS Status;
+    PFILE_OBJECT FileObject;
+    PIO_STACK_LOCATION Stack;
+    PMOUNTDEV_NAME Name = NULL;
+    PDEVICE_OBJECT DeviceObject;
+    IO_STATUS_BLOCK IoStatusBlock;
+
+    /* Get the device associated with the name */
+    Status = IoGetDeviceObjectPointer(DeviceName,
+                                      FILE_READ_ATTRIBUTES,
+                                      &FileObject,
+                                      &DeviceObject);
+    if (!NT_SUCCESS(Status))
+    {
+        return;
+    }
+
+    /* Get attached device (will notify it) */
+    DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
+
+    /* NameSize is the size of the whole MOUNTDEV_NAME struct */
+    NameSize = sizeof(USHORT) + SymbolicName->Length;
+    Name = AllocatePool(NameSize);
+    if (!Name)
+    {
+        goto Cleanup;
+    }
+
+    /* Initialize struct */
+    Name->NameLength = SymbolicName->Length;
+    RtlCopyMemory(Name->Name, SymbolicName->Buffer, SymbolicName->Length);
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+    /* Cf: SendLinkCreated comment */
+    Irp = IoBuildDeviceIoControlRequest(CTL_CODE(MOUNTDEVCONTROLTYPE, 5, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
+                                        DeviceObject,
+                                        Name,
+                                        NameSize,
+                                        NULL,
+                                        0,
+                                        FALSE,
+                                        &Event,
+                                        &IoStatusBlock);
+    /* This one can fail, no one matters */
+    if (Irp)
+    {
+        Stack = IoGetNextIrpStackLocation(Irp);
+        Stack->FileObject = FileObject;
+
+        Status = IofCallDriver(DeviceObject, Irp);
+        if (Status == STATUS_PENDING)
+        {
+            KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+        }
+    }
+
+    /* Then, second one */
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+    Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_LINK_DELETED,
+                                        DeviceObject,
+                                        Name,
+                                        NameSize,
+                                        NULL,
+                                        0,
+                                        FALSE,
+                                        &Event,
+                                        &IoStatusBlock);
+    if (!Irp)
+    {
+        goto Cleanup;
+    }
+
+    Stack = IoGetNextIrpStackLocation(Irp);
+    Stack->FileObject = FileObject;
+
+    /* Really notify */
+    Status = IofCallDriver(DeviceObject, Irp);
+    if (Status == STATUS_PENDING)
+    {
+        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+    }
+
+Cleanup:
+    if (Name)
+    {
+        FreePool(Name);
+    }
+
+    ObfDereferenceObject(DeviceObject);
+    ObfDereferenceObject(FileObject);
+
+    return;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+SymbolicLinkNamesFromUniqueIdCount(IN PWSTR ValueName,
+                                   IN ULONG ValueType,
+                                   IN PVOID ValueData,
+                                   IN ULONG ValueLength,
+                                   IN PVOID Context,
+                                   IN PVOID EntryContext)
+{
+    UNICODE_STRING ValueNameString;
+    PMOUNTDEV_UNIQUE_ID UniqueId = Context;
+
+    if (ValueName[0] != L'#' || ValueType != REG_BINARY ||
+        (UniqueId->UniqueIdLength != ValueLength))
+    {
+        return STATUS_SUCCESS;
+    }
+
+    if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
+    {
+        return STATUS_SUCCESS;
+    }
+
+    /* That one matched, increase count */
+    RtlInitUnicodeString(&ValueNameString, ValueName);
+    if (ValueNameString.Length)
+    {
+        (*((PULONG)EntryContext))++;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+SymbolicLinkNamesFromUniqueIdQuery(IN PWSTR ValueName,
+                                   IN ULONG ValueType,
+                                   IN PVOID ValueData,
+                                   IN ULONG ValueLength,
+                                   IN PVOID Context,
+                                   IN PVOID EntryContext)
+{
+    UNICODE_STRING ValueNameString;
+    PMOUNTDEV_UNIQUE_ID UniqueId = Context;
+    /* Unicode strings table */
+    PUNICODE_STRING ReturnString = EntryContext;
+
+    if (ValueName[0] != L'#' || ValueType != REG_BINARY ||
+        (UniqueId->UniqueIdLength != ValueLength))
+    {
+        return STATUS_SUCCESS;
+    }
+
+    if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
+    {
+        return STATUS_SUCCESS;
+    }
+
+    /* Unique ID matches, let's put the symlink */
+    RtlInitUnicodeString(&ValueNameString, ValueName);
+    if (!ValueNameString.Length)
+    {
+        return STATUS_SUCCESS;
+    }
+
+    /* Allocate string to copy */
+    ValueNameString.Buffer = AllocatePool(ValueNameString.MaximumLength);
+    if (!ValueNameString.Buffer)
+    {
+        return STATUS_SUCCESS;
+    }
+
+    /* Copy */
+    RtlCopyMemory(ValueNameString.Buffer, ValueName, ValueNameString.Length);
+    ValueNameString.Buffer[ValueNameString.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+    while (ReturnString->Length)
+    {
+        ReturnString++;
+    }
+
+    /* And return that string */
+    *ReturnString = ValueNameString;
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+CreateNewVolumeName(OUT PUNICODE_STRING VolumeName,
+                    IN PGUID VolumeGuid OPTIONAL)
+{
+    GUID Guid;
+    NTSTATUS Status;
+    UNICODE_STRING GuidString;
+
+    /* If no GUID was provided, then create one */
+    if (!VolumeGuid)
+    {
+        Status = ExUuidCreate(&Guid);
+        if (!NT_SUCCESS(Status))
+        {
+            return Status;
+        }
+    }
+    else
+    {
+        RtlCopyMemory(&Guid, VolumeGuid, sizeof(GUID));
+    }
+
+    /* Convert GUID to string */
+    Status = RtlStringFromGUID(&Guid, &GuidString);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    /* Size for volume namespace, litteral GUID, and null char */
+    VolumeName->MaximumLength = 0x14 + 0x4C + sizeof(UNICODE_NULL);
+    VolumeName->Buffer = AllocatePool(0x14 + 0x4C + sizeof(UNICODE_NULL));
+    if (!VolumeName->Buffer)
+    {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+    }
+    else
+    {
+        RtlCopyUnicodeString(VolumeName, &Volume);
+        RtlAppendUnicodeStringToString(VolumeName, &GuidString);
+        VolumeName->Buffer[VolumeName->Length / sizeof(WCHAR)] = UNICODE_NULL;
+        Status = STATUS_SUCCESS;
+    }
+
+    ExFreePoolWithTag(GuidString.Buffer, 0);
+
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+QuerySymbolicLinkNamesFromStorage(IN PDEVICE_EXTENSION DeviceExtension,
+                                  IN PDEVICE_INFORMATION DeviceInformation,
+                                  IN PUNICODE_STRING SuggestedLinkName,
+                                  IN BOOLEAN UseOnlyIfThereAreNoOtherLinks,
+                                  OUT PUNICODE_STRING * SymLinks,
+                                  OUT PULONG SymLinkCount,
+                                  IN BOOLEAN HasGuid,
+                                  IN LPGUID Guid)
+{
+    NTSTATUS Status;
+    BOOLEAN WriteNew;
+    RTL_QUERY_REGISTRY_TABLE QueryTable[2];
+
+    /* First of all, count links */
+    RtlZeroMemory(QueryTable, sizeof(QueryTable));
+    QueryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdCount;
+    QueryTable[0].EntryContext = SymLinkCount;
+    *SymLinkCount = 0;
+
+    Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
+                                    DatabasePath,
+                                    QueryTable,
+                                    DeviceInformation->UniqueId,
+                                    NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        *SymLinkCount = 0;
+    }
+
+    /* Check if we have to write a new one first */
+    if (SuggestedLinkName && !IsDriveLetter(SuggestedLinkName) &&
+        UseOnlyIfThereAreNoOtherLinks && *SymLinkCount == 0)
+    {
+        WriteNew = TRUE;
+    }
+    else
+    {
+        WriteNew = FALSE;
+    }
+
+    /* If has GUID, it makes one more link */
+    if (HasGuid)
+    {
+        (*SymLinkCount)++;
+    }
+
+    if (WriteNew)
+    {
+        /* Write link */
+        RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
+                              DatabasePath,
+                              SuggestedLinkName->Buffer,
+                              REG_BINARY,
+                              DeviceInformation->UniqueId->UniqueId,
+                              DeviceInformation->UniqueId->UniqueIdLength);
+
+        /* And recount all the needed links */
+        RtlZeroMemory(QueryTable, sizeof(QueryTable));
+        QueryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdCount;
+        QueryTable[0].EntryContext = SymLinkCount;
+        *SymLinkCount = 0;
+
+        Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
+                                        DatabasePath,
+                                        QueryTable,
+                                        DeviceInformation->UniqueId,
+                                        NULL);
+        if (!NT_SUCCESS(Status))
+        {
+            return STATUS_NOT_FOUND;
+        }
+    }
+
+    /* Not links found? */
+    if (!*SymLinkCount)
+    {
+        return STATUS_NOT_FOUND;
+    }
+
+    /* Allocate a buffer big enough to hold symlinks (table of unicode strings) */
+    *SymLinks = AllocatePool(*SymLinkCount * sizeof(UNICODE_STRING));
+    if (!*SymLinks)
+    {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* Prepare to query links */
+    RtlZeroMemory(*SymLinks, *SymLinkCount * sizeof(UNICODE_STRING));
+    RtlZeroMemory(QueryTable, sizeof(QueryTable));
+    QueryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdQuery;
+
+    /* No GUID? Keep it that way */
+    if (!HasGuid)
+    {
+        QueryTable[0].EntryContext = *SymLinks;
+    }
+    /* Otherwise, first create volume name */
+    else
+    {
+        Status = CreateNewVolumeName(SymLinks[0], Guid);
+        if (!NT_SUCCESS(Status))
+        {
+            FreePool(*SymLinks);
+            return Status;
+        }
+
+        /* Skip first link (ours) */
+        QueryTable[0].EntryContext = *SymLinks + 1;
+    }
+
+    /* Now, query */
+    RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
+                           DatabasePath,
+                           QueryTable,
+                           DeviceInformation->UniqueId,
+                           NULL);
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+PSAVED_LINK_INFORMATION
+RemoveSavedLinks(IN PDEVICE_EXTENSION DeviceExtension,
+                 IN PMOUNTDEV_UNIQUE_ID UniqueId)
+{
+    PLIST_ENTRY NextEntry;
+    PSAVED_LINK_INFORMATION SavedLinkInformation;
+
+    /* No saved links? Easy! */
+    if (IsListEmpty(&(DeviceExtension->SavedLinksListHead)))
+    {
+        return NULL;
+    }
+
+    /* Now, browse saved links */
+    for (NextEntry = DeviceExtension->SavedLinksListHead.Flink;
+         NextEntry != &(DeviceExtension->SavedLinksListHead);
+         NextEntry = NextEntry->Flink)
+    {
+        SavedLinkInformation = CONTAINING_RECORD(NextEntry,
+                                                 SAVED_LINK_INFORMATION,
+                                                 SavedLinksListEntry);
+
+        /* Find the one that matches */
+        if (SavedLinkInformation->UniqueId->UniqueIdLength == UniqueId->UniqueIdLength)
+        {
+            if (RtlCompareMemory(SavedLinkInformation->UniqueId->UniqueId,
+                                 UniqueId->UniqueId,
+                                 UniqueId->UniqueIdLength) ==
+                UniqueId->UniqueIdLength)
+            {
+                /* Remove it and return it */
+                RemoveEntryList(&(SavedLinkInformation->SavedLinksListEntry));
+                return SavedLinkInformation;
+            }
+        }
+    }
+
+    /* None found (none removed) */
+    return NULL;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+QuerySuggestedLinkName(IN PUNICODE_STRING SymbolicName,
+                       OUT PUNICODE_STRING SuggestedLinkName,
+                       OUT PBOOLEAN UseOnlyIfThereAreNoOtherLinks)
+{
+    PIRP Irp;
+    KEVENT Event;
+    NTSTATUS Status;
+    USHORT NameLength;
+    PFILE_OBJECT FileObject;
+    PDEVICE_OBJECT DeviceObject;
+    IO_STATUS_BLOCK IoStatusBlock;
+    PIO_STACK_LOCATION IoStackLocation;
+    PMOUNTDEV_SUGGESTED_LINK_NAME IoCtlSuggested;
+
+    /* First, get device */
+    Status = IoGetDeviceObjectPointer(SymbolicName,
+                                      FILE_READ_ATTRIBUTES,
+                                      &FileObject,
+                                      &DeviceObject);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    /* Then, get attached device */
+    DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
+
+    /* Then, prepare buffer to query suggested name */
+    IoCtlSuggested = AllocatePool(sizeof(MOUNTDEV_SUGGESTED_LINK_NAME));
+    if (!IoCtlSuggested)
+    {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto Dereference;
+    }
+
+    /* Prepare request */
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+    Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME,
+                                        DeviceObject,
+                                        NULL,
+                                        0,
+                                        IoCtlSuggested,
+                                        sizeof(MOUNTDEV_SUGGESTED_LINK_NAME),
+                                        FALSE,
+                                        &Event,
+                                        &IoStatusBlock);
+    if (!Irp)
+    {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto Release;
+    }
+
+    IoStackLocation = IoGetNextIrpStackLocation(Irp);
+    IoStackLocation->FileObject = FileObject;
+
+    /* And ask */
+    Status = IofCallDriver(DeviceObject, Irp);
+    if (Status == STATUS_PENDING)
+    {
+        KeWaitForSingleObject(&Event, Executive, KernelMode,
+                              FALSE, NULL);
+        Status = IoStatusBlock.Status;
+    }
+
+    /* Overflow? Normal */
+    if (Status == STATUS_BUFFER_OVERFLOW)
+    {
+        /* Reallocate big enough buffer */
+        NameLength = IoCtlSuggested->NameLength + sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
+        FreePool(IoCtlSuggested);
+
+        IoCtlSuggested = AllocatePool(NameLength);
+        if (!IoCtlSuggested)
+        {
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto Dereference;
+        }
+
+        /* And reask */
+        KeInitializeEvent(&Event, NotificationEvent, FALSE);
+        Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME,
+                                            DeviceObject,
+                                            NULL,
+                                            0,
+                                            IoCtlSuggested,
+                                            NameLength,
+                                            FALSE,
+                                            &Event,
+                                            &IoStatusBlock);
+        if (!Irp)
+        {
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto Release;
+        }
+
+        IoStackLocation = IoGetNextIrpStackLocation(Irp);
+        IoStackLocation->FileObject = FileObject;
+
+        Status = IofCallDriver(DeviceObject, Irp);
+        if (Status == STATUS_PENDING)
+        {
+            KeWaitForSingleObject(&Event, Executive, KernelMode,
+                                  FALSE, NULL);
+            Status = IoStatusBlock.Status;
+        }
+    }
+
+    if (!NT_SUCCESS(Status))
+    {
+        goto Release;
+    }
+
+    /* Now we have suggested name, copy it */
+    SuggestedLinkName->Length = IoCtlSuggested->NameLength;
+    SuggestedLinkName->MaximumLength = IoCtlSuggested->NameLength + sizeof(UNICODE_NULL);
+    SuggestedLinkName->Buffer = AllocatePool(IoCtlSuggested->NameLength + sizeof(UNICODE_NULL));
+    if (!SuggestedLinkName->Buffer)
+    {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+    }
+    else
+    {
+        RtlCopyMemory(SuggestedLinkName->Buffer, IoCtlSuggested->Name, IoCtlSuggested->NameLength);
+        SuggestedLinkName->Buffer[SuggestedLinkName->Length / sizeof(WCHAR)] = UNICODE_NULL;
+    }
+
+    /* Also return its priority */
+    *UseOnlyIfThereAreNoOtherLinks = IoCtlSuggested->UseOnlyIfThereAreNoOtherLinks;
+
+Release:
+    FreePool(IoCtlSuggested);
+
+Dereference:
+    ObfDereferenceObject(DeviceObject);
+    ObfDereferenceObject(FileObject);
+
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN
+RedirectSavedLink(IN PSAVED_LINK_INFORMATION SavedLinkInformation,
+                  IN PUNICODE_STRING DosName,
+                  IN PUNICODE_STRING NewLink)
+{
+    PLIST_ENTRY NextEntry;
+    PSYMLINK_INFORMATION SymlinkInformation;
+
+    /* Find the link */
+    for (NextEntry = SavedLinkInformation->SymbolicLinksListHead.Flink;
+         NextEntry != &(SavedLinkInformation->SymbolicLinksListHead);
+         NextEntry = NextEntry->Flink)
+    {
+        SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
+
+        if (!RtlEqualUnicodeString(DosName, &(SymlinkInformation->Name), TRUE))
+        {
+            /* Delete old link */
+            GlobalDeleteSymbolicLink(DosName);
+            /* Set its new location */
+            GlobalCreateSymbolicLink(DosName, NewLink);
+
+            /* And remove it from the list (not valid any more) */
+            RemoveEntryList(&(SymlinkInformation->SymbolicLinksListEntry));
+            FreePool(SymlinkInformation->Name.Buffer);
+            FreePool(SymlinkInformation);
+                
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+/*
+ * @implemented
+ */
+VOID
+DeleteSymbolicLinkNameFromMemory(IN PDEVICE_EXTENSION DeviceExtension,
+                                 IN PUNICODE_STRING SymbolicLink,
+                                 IN BOOLEAN MarkOffline)
+{
+    PLIST_ENTRY DeviceEntry, SymbolEntry;
+    PDEVICE_INFORMATION DeviceInformation;
+    PSYMLINK_INFORMATION SymlinkInformation;
+
+    /* First of all, ensure we have devices */
+    if (IsListEmpty(&(DeviceExtension->DeviceListHead)))
+    {
+        return;
+    }
+
+    /* Then, look for the symbolic name */
+    for (DeviceEntry = DeviceExtension->DeviceListHead.Flink;
+         DeviceEntry != &(DeviceExtension->DeviceListHead);
+         DeviceEntry = DeviceEntry->Flink)
+    {
+        DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry);
+
+        for (SymbolEntry = DeviceInformation->SymbolicLinksListHead.Flink;
+             SymbolEntry != &(DeviceInformation->SymbolicLinksListHead);
+             SymbolEntry = SymbolEntry->Flink)
+        {
+            SymlinkInformation = CONTAINING_RECORD(SymbolEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
+
+            /* One we have found it */
+            if (RtlCompareUnicodeString(SymbolicLink, &(SymlinkInformation->Name), TRUE) == 0)
+            {
+                /* Check if caller just want it to be offline */
+                if (MarkOffline)
+                {
+                    SymlinkInformation->Online = FALSE;
+                }
+                else
+                {
+                    /* If not, delete it & notify */
+                    SendLinkDeleted(&(DeviceInformation->SymbolicName), SymbolicLink);
+                    RemoveEntryList(&(SymlinkInformation->SymbolicLinksListEntry));
+
+                    FreePool(SymlinkInformation->Name.Buffer);
+                    FreePool(SymlinkInformation);
+                }
+
+                /* No need to go farther */
+                return;
+            }
+        }
+    }
+
+    return;
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN
+IsDriveLetter(PUNICODE_STRING SymbolicName)
+{
+    WCHAR Letter;
+    BOOLEAN Result = FALSE;
+
+    /* We must have a precise length */
+    if (SymbolicName->Length != sizeof(DosDevices.Buffer) + 2 * sizeof(WCHAR))
+    {
+        return FALSE;
+    }
+
+    /* Check if len is correct */
+    Letter = SymbolicName->Buffer[sizeof(DosDevices.Buffer) / sizeof(WCHAR)];
+    if (((Letter >= L'A' && Letter <= L'Z') || Letter == (WCHAR)-1) &&
+        SymbolicName->Buffer[(sizeof(DosDevices.Buffer) + sizeof(WCHAR)) / sizeof (WCHAR)] == L':')
+    {
+        /* In case it's not a normal drive letter, check differently */
+        SymbolicName->Length = sizeof(DosDevices.Buffer);
+        Result = RtlEqualUnicodeString(SymbolicName, &DosDevices, TRUE);
+        SymbolicName->Length = sizeof(DosDevices.Buffer) + 2 * sizeof(WCHAR);
+    }
+
+    return Result;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+MountMgrQuerySymbolicLink(IN PUNICODE_STRING SymbolicName,
+                          IN OUT PUNICODE_STRING LinkTarget)
+{
+    NTSTATUS Status;
+    HANDLE LinkHandle;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+
+    /* Open the symbolic link */
+    InitializeObjectAttributes(&ObjectAttributes,
+                               SymbolicName,
+                               OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
+
+    Status = ZwOpenSymbolicLinkObject(&LinkHandle,
+                                      GENERIC_READ,
+                                      &ObjectAttributes);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    /* Query its target */
+    Status = ZwQuerySymbolicLinkObject(LinkHandle,
+                                       LinkTarget,
+                                       NULL);
+
+    ZwClose(LinkHandle);
+
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    if (LinkTarget->Length <= sizeof(WCHAR))
+    {
+        return Status;
+    }
+
+    /* If it's not finished by \, just return */
+    if (LinkTarget->Buffer[LinkTarget->Length / sizeof(WCHAR) - 1] != L'\\')
+    {
+        return Status;
+    }
+
+    /* Otherwise, ensure to drop the tailing \ */
+    LinkTarget->Length -= sizeof(WCHAR);
+    LinkTarget->Buffer[LinkTarget->Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+    return Status;
+}
diff --git a/drivers/filters/mountmgr/uniqueid.c b/drivers/filters/mountmgr/uniqueid.c
new file mode 100644 (file)
index 0000000..66160ff
--- /dev/null
@@ -0,0 +1,436 @@
+/*
+ *  ReactOS kernel
+ *  Copyright (C) 2011 ReactOS Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * COPYRIGHT:        See COPYING in the top level directory
+ * PROJECT:          ReactOS kernel
+ * FILE:             drivers/filesystem/mountmgr/uniqueid.c
+ * PURPOSE:          Mount Manager - Unique ID
+ * PROGRAMMER:       Pierre Schweitzer (pierre.schweitzer@reactos.org)
+ */
+
+/* INCLUDES *****************************************************************/
+
+#include "mntmgr.h"
+
+#define NDEBUG
+#include <debug.h>
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+ChangeUniqueIdRoutine(IN PWSTR ValueName,
+                      IN ULONG ValueType,
+                      IN PVOID ValueData,
+                      IN ULONG ValueLength,
+                      IN PVOID Context,
+                      IN PVOID EntryContext)
+{
+    PMOUNTDEV_UNIQUE_ID OldUniqueId = Context;
+    PMOUNTDEV_UNIQUE_ID NewUniqueId = EntryContext;
+
+    /* Validate parameters not to corrupt registry */
+    if ((ValueType != REG_BINARY) ||
+        (OldUniqueId->UniqueIdLength != ValueLength))
+    {
+        return STATUS_SUCCESS;
+    }
+
+    if (RtlCompareMemory(OldUniqueId->UniqueId, ValueData, ValueLength) == ValueLength)
+    {
+        /* Write new data */
+        RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
+                              DatabasePath,
+                              ValueName,
+                              REG_BINARY,
+                              NewUniqueId,
+                              NewUniqueId->UniqueIdLength);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+VOID
+MountMgrUniqueIdChangeRoutine(IN PDEVICE_EXTENSION DeviceExtension,
+                              IN PMOUNTDEV_UNIQUE_ID OldUniqueId,
+                              IN PMOUNTDEV_UNIQUE_ID NewUniqueId)
+{
+    NTSTATUS Status;
+    BOOLEAN ResyncNeeded;
+    PUNIQUE_ID_REPLICATE DuplicateId;
+    PDEVICE_INFORMATION DeviceInformation;
+    RTL_QUERY_REGISTRY_TABLE QueryTable[2];
+    PMOUNTDEV_UNIQUE_ID UniqueId, NewDuplicateId;
+    PLIST_ENTRY ListHead, NextEntry, ReplicatedHead, NextReplicated;
+
+    /* Synchronise with remote databases */
+    Status = WaitForRemoteDatabaseSemaphore(DeviceExtension);
+    KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
+
+    RtlZeroMemory(QueryTable, sizeof(QueryTable));
+    QueryTable[0].QueryRoutine = ChangeUniqueIdRoutine;
+    QueryTable[0].EntryContext = NewUniqueId;
+
+    /* Write new data */
+    RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
+                           DatabasePath,
+                           QueryTable,
+                           OldUniqueId,
+                           NULL);
+
+    /* Browse all the devices to find the one that
+     * owns the old unique ID
+     */
+    ListHead = &(DeviceExtension->DeviceListHead);
+    NextEntry = ListHead->Flink;
+    while (ListHead != NextEntry)
+    {
+        DeviceInformation = CONTAINING_RECORD(NextEntry,
+                                              DEVICE_INFORMATION,
+                                              DeviceListEntry);
+
+        if (DeviceInformation->UniqueId->UniqueIdLength == OldUniqueId->UniqueIdLength &&
+            RtlCompareMemory(OldUniqueId->UniqueId,
+                             DeviceInformation->UniqueId->UniqueId,
+                             OldUniqueId->UniqueIdLength) == OldUniqueId->UniqueIdLength)
+        {
+            break;
+        }
+
+        NextEntry = NextEntry->Flink;
+    }
+
+    /* If we didn't find any release everything and quit */
+    if (ListHead == NextEntry)
+    {
+        KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT,
+                           1, FALSE);
+
+        if (NT_SUCCESS(Status))
+        {
+            ReleaseRemoteDatabaseSemaphore(DeviceExtension);
+        }
+
+        return;
+    }
+
+    /* If lock failed, then, just update this database */
+    if (!NT_SUCCESS(Status))
+    {
+        ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation);
+        KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT,
+                           1, FALSE);
+        return;
+    }
+
+    /* Allocate new unique ID */
+    UniqueId = AllocatePool(NewUniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
+    if (!UniqueId)
+    {
+        KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT,
+                           1, FALSE);
+        ReleaseRemoteDatabaseSemaphore(DeviceExtension);
+        return;
+    }
+
+    /* Release old one */
+    FreePool(DeviceInformation->UniqueId);
+    /* And set new one */
+    DeviceInformation->UniqueId = UniqueId;
+    UniqueId->UniqueIdLength = NewUniqueId->UniqueIdLength;
+    RtlCopyMemory(UniqueId->UniqueId, NewUniqueId->UniqueId, NewUniqueId->UniqueIdLength);
+
+    /* Now, check if it's required to update replicated unique IDs as well */
+    ListHead = &(DeviceExtension->DeviceListHead);
+    NextEntry = ListHead->Flink;
+    while (ListHead != NextEntry)
+    {
+        DeviceInformation = CONTAINING_RECORD(NextEntry,
+                                              DEVICE_INFORMATION,
+                                              DeviceListEntry);
+        ResyncNeeded = FALSE;
+
+        ReplicatedHead = &(DeviceInformation->ReplicatedUniqueIdsListHead);
+        NextReplicated = ReplicatedHead->Flink;
+        while (ReplicatedHead != NextReplicated)
+        {
+            DuplicateId = CONTAINING_RECORD(NextReplicated,
+                                            UNIQUE_ID_REPLICATE,
+                                            ReplicatedUniqueIdsListEntry);
+
+            if (DuplicateId->UniqueId->UniqueIdLength == OldUniqueId->UniqueIdLength)
+            {
+                if (RtlCompareMemory(DuplicateId->UniqueId->UniqueId,
+                                     OldUniqueId->UniqueId,
+                                     OldUniqueId->UniqueIdLength) == OldUniqueId->UniqueIdLength)
+                {
+                    /* It was our old unique ID */
+                    NewDuplicateId = AllocatePool(NewUniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
+                    if (NewDuplicateId)
+                    {
+                        /* Update it */
+                        ResyncNeeded = TRUE;
+                        FreePool(DuplicateId->UniqueId);
+
+                        DuplicateId->UniqueId = NewDuplicateId;
+                        DuplicateId->UniqueId->UniqueIdLength = NewUniqueId->UniqueIdLength;
+                        RtlCopyMemory(NewDuplicateId->UniqueId, NewUniqueId->UniqueId, NewUniqueId->UniqueIdLength);
+                    }
+                }
+            }
+
+            NextReplicated = NextReplicated->Flink;
+        }
+
+        /* If resync is required on this device, do it */
+        if (ResyncNeeded)
+        {
+            ChangeRemoteDatabaseUniqueId(DeviceInformation, OldUniqueId, NewUniqueId);
+        }
+
+        NextEntry = NextEntry->Flink;
+    }
+
+    KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
+    ReleaseRemoteDatabaseSemaphore(DeviceExtension);
+
+    return;
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN
+IsUniqueIdPresent(IN PDEVICE_EXTENSION DeviceExtension,
+                  IN PDATABASE_ENTRY DatabaseEntry)
+{
+    PLIST_ENTRY NextEntry;
+    PDEVICE_INFORMATION DeviceInformation;
+
+    /* If no device, no unique ID (O'rly?!)
+     * ./)/).
+     * (°-°)
+     * (___) ORLY?
+     *  " "
+     */
+    if (IsListEmpty(&(DeviceExtension->DeviceListHead)))
+    {
+        return FALSE;
+    }
+
+    /* Now we know that we have devices, find the one */
+    for (NextEntry = DeviceExtension->DeviceListHead.Flink;
+         NextEntry != &(DeviceExtension->DeviceListHead);
+         NextEntry = NextEntry->Flink)
+    {
+        DeviceInformation = CONTAINING_RECORD(NextEntry,
+                                              DEVICE_INFORMATION,
+                                              DeviceListEntry);
+
+        if (DeviceInformation->UniqueId->UniqueIdLength != DatabaseEntry->UniqueIdLength)
+        {
+            continue;
+        }
+
+        /* It's matching! */
+        if (RtlCompareMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset),
+                             DeviceInformation->UniqueId->UniqueId,
+                             DatabaseEntry->UniqueIdLength) == DatabaseEntry->UniqueIdLength)
+        {
+            return TRUE;
+        }
+    }
+
+    /* No luck... */
+    return FALSE;
+}
+
+/*
+ * @implemented
+ */
+VOID
+CreateNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId)
+{
+    UUID Guid;
+    PWCHAR String;
+    UNICODE_STRING GuidString;
+
+    /* Entry with no drive letter are made that way:
+     * Instead of having a path with the letter,
+     * you have GUID with the unique ID.
+     */
+    if (!NT_SUCCESS(ExUuidCreate(&Guid)))
+    {
+        return;
+    }
+
+    /* Convert to string */
+    if (!NT_SUCCESS(RtlStringFromGUID(&Guid, &GuidString)))
+    {
+        return;
+    }
+
+    /* No letter entries must start with #, so allocate a proper string */
+    String = AllocatePool(GuidString.Length + 2 * sizeof(WCHAR));
+    if (!String)
+    {
+        ExFreePoolWithTag(GuidString.Buffer, 0);
+        return;
+    }
+
+    /* Write the complete string */
+    String[0] = L'#';
+    RtlCopyMemory(String + 1, GuidString.Buffer, GuidString.Length);
+    String[GuidString.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+    /* Don't need that one anymore */
+    ExFreePoolWithTag(GuidString.Buffer, 0);
+
+    /* Write the entry */
+    RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
+                          DatabasePath,
+                          String,
+                          REG_BINARY,
+                          UniqueId->UniqueId,
+                          UniqueId->UniqueIdLength);
+
+    FreePool(String);
+
+    return;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+CheckForNoDriveLetterEntry(IN PWSTR ValueName,
+                           IN ULONG ValueType,
+                           IN PVOID ValueData,
+                           IN ULONG ValueLength,
+                           IN PVOID Context,
+                           IN PVOID EntryContext)
+{
+    PBOOLEAN EntryPresent = EntryContext;
+    PMOUNTDEV_UNIQUE_ID UniqueId = Context;
+
+    /* Check if matches no drive letter entry */
+    if (ValueName[0] != L'#' || ValueType != REG_BINARY ||
+        UniqueId->UniqueIdLength != ValueLength)
+    {
+        return STATUS_SUCCESS;
+    }
+
+    /* Compare unique ID */
+    if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) == ValueLength)
+    {
+        *EntryPresent = TRUE;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN
+HasNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId)
+{
+    BOOLEAN EntryPresent = FALSE;
+    RTL_QUERY_REGISTRY_TABLE QueryTable[2];
+
+    RtlZeroMemory(QueryTable, sizeof(QueryTable));
+    QueryTable[0].QueryRoutine = CheckForNoDriveLetterEntry;
+    QueryTable[0].EntryContext = &EntryPresent;
+
+    RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
+                           DatabasePath,
+                           QueryTable,
+                           UniqueId,
+                           NULL);
+
+    return EntryPresent;
+}
+
+/*
+ * @implemented
+ */
+VOID
+UpdateReplicatedUniqueIds(IN PDEVICE_INFORMATION DeviceInformation, IN PDATABASE_ENTRY DatabaseEntry)
+{
+    PLIST_ENTRY NextEntry;
+    PUNIQUE_ID_REPLICATE ReplicatedUniqueId, NewEntry;
+
+    /* Browse all the device replicated unique IDs */
+    for (NextEntry = DeviceInformation->ReplicatedUniqueIdsListHead.Flink;
+         NextEntry != &(DeviceInformation->ReplicatedUniqueIdsListHead);
+         NextEntry = NextEntry->Flink)
+    {
+        ReplicatedUniqueId = CONTAINING_RECORD(NextEntry,
+                                               UNIQUE_ID_REPLICATE,
+                                               ReplicatedUniqueIdsListEntry);
+
+        if (ReplicatedUniqueId->UniqueId->UniqueIdLength != DatabaseEntry->UniqueIdLength)
+        {
+            continue;
+        }
+
+        /* If we find the UniqueId to update, break */
+        if (RtlCompareMemory(ReplicatedUniqueId->UniqueId->UniqueId,
+                             (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset),
+                             ReplicatedUniqueId->UniqueId->UniqueIdLength) == ReplicatedUniqueId->UniqueId->UniqueIdLength)
+        {
+            break;
+        }
+    }
+
+    /* We found the unique ID, no need to continue */
+    if (NextEntry != &(DeviceInformation->ReplicatedUniqueIdsListHead))
+    {
+        return;
+    }
+
+    /* Allocate a new entry for unique ID */
+    NewEntry = AllocatePool(sizeof(UNIQUE_ID_REPLICATE));
+    if (!NewEntry)
+    {
+        return;
+    }
+
+    /* Allocate the unique ID */
+    NewEntry->UniqueId = AllocatePool(DatabaseEntry->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
+    if (!NewEntry->UniqueId)
+    {
+        FreePool(NewEntry);
+        return;
+    }
+
+    /* Copy */
+    NewEntry->UniqueId->UniqueIdLength = DatabaseEntry->UniqueIdLength;
+    RtlCopyMemory(NewEntry->UniqueId->UniqueId,
+                  (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset),
+                  DatabaseEntry->UniqueIdLength);
+    /* And insert into replicated unique IDs list */
+    InsertTailList(&DeviceInformation->ReplicatedUniqueIdsListHead, &NewEntry->ReplicatedUniqueIdsListEntry);
+
+    return;
+}