mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 04:11:30 +00:00
ba447018c8
Effectively mountmgr.sys is an essential part of the storage stack
435 lines
14 KiB
C
435 lines
14 KiB
C
/*
|
|
* 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)
|
|
*/
|
|
|
|
#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;
|
|
}
|